[
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- Backup*.rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n##\n## Visual studio for Mac\n##\n\n\n# globs\nMakefile.in\n*.userprefs\n*.usertasks\nconfig.make\nconfig.status\naclocal.m4\ninstall-sh\nautom4te.cache/\n*.tar.gz\ntarballs/\ntest-results/\n\n# Mac bundle stuff\n*.dmg\n*.app\n\n# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore\n# Windows thumbnail cache files\nThumbs.db\nehthumbs.db\nehthumbs_vista.db\n\n# Dump file\n*.stackdump\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msix\n*.msm\n*.msp\n\n# Windows shortcuts\n*.lnk\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n##\n## Visual Studio Code\n##\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n\n# Test configuration\n/src/Octonica.ClickHouseClient.Tests/clickHouse.dbconfig\n\n# Benchmark configuration\n/src/Octonica.ClickHouseClient.Benchmarks/clickHouse.dbconfig\n\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "### Octonica.ClickHouseClient Next Version, Unscheduled\n\n#### New Feature\n\n* Add method `ClickHouseDataReader.ConfigureColumnReader` which sets a value cast callback function for the column.\n The callback function is invoked whenever the reader reads a non-null value from the column. This feature could be useful in\n cases when ClcikHouseClient doesn't have a built-in type conversion rule, for expample, `double -> decimal`, `Guid -> string` or `uint -> int`.\n\n#### Improvement\n\n* Add method `ClickHouseParameterCollection.AddRange` receiveng any enumerable collection of `ClickHouseParameter`.\n\n### Octonica.ClickHouseClient v2.2.11, 2023-01-11\n\n#### Bug Fix\n\n* Fix null reference exception when the garbage collector calls the finalizer for `ClickHouseConnection` ([#70](https://github.com/Octonica/ClickHouseClient/issues/70)).\n\n### Octonica.ClickHouseClient v2.2.10, 2022-12-30\n\n#### New Feature\n\n* Add support for the type `Bool` ([#56](https://github.com/Octonica/ClickHouseClient/issues/56)).\n\n#### Bug Fix\n\n* Return the correct non-generic enumerator for `ClickHouseParameterCollection` (PR [#65](https://github.com/Octonica/ClickHouseClient/pull/65)).\n\n#### Improvement\n\n* Remove arguments with default values from constructors of `ClickHouseConnection`. It makes possible to call the constructor `ClickHouseConnection(sting)` using reflection\n ([#54](https://github.com/Octonica/ClickHouseClient/issues/54)).\n\n### Octonica.ClickHouseClient v2.2.9, 2022-04-27\n\n#### New Feature\n\n* New mode of passing parameters to a query - `Interpolate`. In this mode values are interpolated into the query text as constant literals.\n Parameter mode can be set for a connection (the property `ParametersMode` in the connection string), for a command (the property `ClickHouseCommand.ParametersMode`) or\n for a single parameter (`ClickHouseParameter.ParameterMode`)\n ([#49](https://github.com/Octonica/ClickHouseClient/issues/49), PR [#42](https://github.com/Octonica/ClickHouseClient/pull/42)).\n\n#### Improvement\n\n* Set `DateTimeKind.Unspecified` when cast a value of ClickHouse types `Date` and `Date32` to the .NET type `DateTime` (PR [#45](https://github.com/Octonica/ClickHouseClient/pull/45)).\n\n### Octonica.ClickHouseClient v2.2.8, 2022-01-09\n\n#### Bug Fix\n\n* Fix getting a time zone from IANA code. This fix is only applicable to the .NET 6 version of ClickHouseClient running on Windows ([#40](https://github.com/Octonica/ClickHouseClient/issues/40)).\n\n#### Improvement\n\n* Make possible to open a connection to the server with an unrecognized time zone. The `TimeZoneNotFoundException` may be thrown later when reading\n  the column of type `DateTime` or `DateTime64` ([#40](https://github.com/Octonica/ClickHouseClient/issues/40)).\n\n#### Miscellaneous\n\n* Default protocol revision is set to 54452. This change was made because the minimal protocol revison with profile events was updated in the ClickHouse v21.12.\n\n### Octonica.ClickHouseClient v2.2.7, 2021-12-04\n\n#### New Feature\n\n* .NET 6.0 support ([#33](https://github.com/Octonica/ClickHouseClient/issues/33)):\n  * New API for time zones. Remove dependency from the package TimeZoneConverter;\n  * Change mapping of the ClickHouse type `Date` from `DateTime` to `DateOnly`. This affects the behavior of methods `ClickHouseDataReader.GetValue` and `ClickHouseDataReader.GetValues`;\n  * Add the method `ClickHouseDataReader.GetDate` for reading values of types `Date` and `Date32`.\n* Add methods to the `ClickHouseDataReader` for reading values of well-known types ([#38](https://github.com/Octonica/ClickHouseClient/issues/38)):\n  * `GetBigInteger`;\n  * `GetIPAddress`;\n  * `GetSByte`;\n  * `GetUInt16`;\n  * `GetUInt32`;\n  * `GetUInt64`.\n* Add support for the type `Date32` ([#36](https://github.com/Octonica/ClickHouseClient/issues/36)).\n* Add support for profile events. Profile events are disabled by default. To enable it set the value of the property `ClickHouseCommand.IgnoreProfileEvents` to `false`.\n Please note that the method `ClickHouseDataReader.NextResult` (or `NextResultAsync`) should be called for switching between regular data and profile events.\n\n#### Bug Fix\n\n* Fix reading empty values of the type `LowCardinality(String)` ([#37](https://github.com/Octonica/ClickHouseClient/issues/37)).\n\n#### Miscellaneous\n\n* Default protocol revision is set to 54450.\n\n### Octonica.ClickHouseClient release v2.1.2, 2021-11-07\n\n#### New Feature\n\n* Add support for Transport Layer Security (TLS) connection ([#35](https://github.com/Octonica/ClickHouseClient/issues/35)).\n\n### Octonica.ClickHouseClient release v2.1.1, 2021-09-16\n\n#### Backward Incompatible Change\n\n* Classes from the namespace `Octonica.ClickHouseClient` that are now sealed and therefore can't be inherited:\n  * `ClickHouseColumnWriter`;\n  * `ClickHouseCommand`;\n  * `ClickHouseConnection`;\n  * `ClickHouseConnectionSettings`;\n  * `ClickHouseDataReader`;\n  * `ClickHouseParameter`;\n  * `ClickHouseParameterCollection`;\n  * `ClickHouseServerInfo`;\n  * `ClickHouseTableColumnCollection`;\n  * `ClickHouseTableProvider`;\n  * `ClickHouseTableProviderCollection`.\n* Classes, enums and interfaces from the namespace `Octonica.ClickHouseClient.Protocol` that are no longer public:\n  * `BlockFieldCodes`;\n  * `CompressionAlgorithm`;\n  * `IClickHouseTableWriter`;\n  * `NullableObjTableColumn<TObj>`.\n* The class `ClickHouseColumnSettings` was moved from the namespace `Octonica.ClickHouseClient.Protocol` to the namespace `Octonica.ClickHouseClient`.\n* The class `Revisions` from the namespace `Octonica.ClickHouseClient.Protocol` was renamed to `ClickHouseProtocolRevisions`.\n\n#### Improvement\n\n* Add XML documentation comments to the NuGet package.\n\n#### Bug Fix\n\n* Fix reading and writing values of the type `Array(LowCardinality(T))` ([#34](https://github.com/Octonica/ClickHouseClient/issues/34)).\n* Fix error handling for `ClickHouseColumnWriter`.\n\n### Octonica.ClickHouseClient release v1.3.1, 2021-07-13\n\n#### New Feature\n\n* Values of the type `FixedString` can be converted to the type `char[]`.\n* Values of the type `String` can be converted to types `char[]` and `byte[]`.\n\n#### Miscellaneous\n\n* Basic interfaces of the column reader were modified. Despite these interfaces are public,\n  they are supposedly used only by an internal part of ClickHouseClient.\n\n### Octonica.ClickHouseClient release v1.2.1, 2021-06-25\n\n### New Feature\n\n* Add support for user-defined tables in queries. New property `ClickHouseCommand.TableProviders` provides access to a collection of user-defined\n  tables associated with the command. See the section *'Table-valued parameters'* of [Parameters](docs/Parameters.md) for details ([#24](https://github.com/Octonica/ClickHouseClient/issues/24)).\n* Add property `ClickHouseColumnWriter.MaxBlockSize`. This property allows to set the maximal number of rows which can be sent to the server as\n  a single block of data. If an input table contains more rows than `MaxBlockSize` it will be sent to the server by parts ([#26](https://github.com/Octonica/ClickHouseClient/issues/26)).\n* Add support for the experimental type `Map(key, value)` ([#31](https://github.com/Octonica/ClickHouseClient/issues/31)).\n* Add support for long integer types `Int128`, `UInt128`, `Int256` and `UInt256` ([#27](https://github.com/Octonica/ClickHouseClient/issues/27)).\n\n#### Improvement\n\n* Improve performance of reading and writing values of primitive types.\n* Improve connection state management and error handling.\n\n### Octonica.ClickHouseClient release v1.1.13, 2021-05-29\n\n#### Bug Fix\n\n* Fix conversion from `System.Guid` to `UUID`. This bug affected `ClickHouseColumnWriter`.\n  It caused writing of corrupted values to a column of type `UUID` ([#29](https://github.com/Octonica/ClickHouseClient/issues/29)).\n\n#### New Feature\n\n* Add method `ClickHouseConnection.TryPing`. This method allows to send 'Ping' message and wait for response from the server.\n\n#### Improvement\n\n* Add cast from `UInt8` to `bool`. `ClickHouseDataReader.GetBoolean` no longer throws an exception for values of type `UInt8`.\n\n### Octonica.ClickHouseClient release v1.1.12, 2021-05-19\n\n#### Backward Incompatible Change\n\n* `ClickHouseDataReader.GetField` and `ClickHouseColumnWriter.GetField` now return `typeof(T)` instead of `typeof(Nullable<T>)` for nullable fields.\n  It is possible to get original type of a column from field's type info: `ClickClickHouseDataReader.GetFieldTypeInfo(int ordinal).GetFieldType()`.\n* Stricter column type check. `ClickHouseColumnWriter` throws an exception when a type of a column is ambiguous\n  (for example, a column's type implements both `IReadOnlyList<int>` and `IReadOnlyList<int?>`).\n\n#### New Feature\n\n* Add support for named tuples.\n* Add a way to explicitly set a type of a column. The type could be defined in `ClickHouseColumnSettings`. `ClickHouseDataReader` will try to convert\n  a column's value to this type. `ClickHouseColumnWriter` will expect a column to be a collection of items of this type.\n* Add support for `IReadOnlyList<object>`, `IList<object>`, `IEnumerable<object>` and `IAsyncEnumerable<object>` to `ClickHouseColumnWriter` ([#21](https://github.com/Octonica/ClickHouseClient/issues/21)).\n\n#### Bug Fix\n\n* Add recognition of escape sequences in enum's item names.\n\n### Octonica.ClickHouseClient release v1.1.9, 2021-05-07\n\n#### New Feature\n\n* Parameters in the format `@paramName` are supported in the text of a query ([#19](https://github.com/Octonica/ClickHouseClient/issues/19)).\n\n### Octonica.ClickHouseClient release v1.1.8, 2021-04-25\n\n#### New Feature\n\n* `ClickHouseCommand.ExecuteDbDataReader` supports non-default command behavior ([#18](https://github.com/Octonica/ClickHouseClient/issues/18)).\n* Added method `GetTypeArgument` to the interface `IClickHouseTypeInfo`. This method allows to get additional arguments of the type (scale, precision, timezone, size).\n\n### Octonica.ClickHouseClient release v1.1.7, 2021-03-15\n\n#### Bug Fix\n\n* Fixed error handling for `ClickHouseConnection.Open`. The socket was not properly disposed when error occurred during opening a connection.\n\n### Octonica.ClickHouseClient release v1.1.6, 2021-03-08\n\n#### Backward Incompatible Change\n\n* ClickHouseParameter can't be added to several parameter collections. Use the method `ClickHouseParameter.Clone` to create a parameter's copy which doesn't belong to the collection.\n\n#### New Feature\n\n* Octonica.ClickHouseClient for .NET 5.0 was added to NuGet package.\n* Added ClickHouseDbProviderFactory which implements DbProviderFactory.\n* `ReadOnlyMemory<char>` or `Memory<char>` can be used instead of `string` when writing values to ClickHouse.\n* `ReadOnlyMemory<T>` or `Memory<T>` can be used instead of `T[]` (array of `T`) when writing values to ClickHouse.\n\n#### Bug Fix\n\n* Fixed possible race condition when disposing a connection from different threads ([#16](https://github.com/Octonica/ClickHouseClient/issues/16)).\n\n#### Improvement\n\n* Improved implementation of various classes from `System.Data.Common` namespace, such as `DbConnection`, `DbCommand` and `DbParameter`.\n\n### Octonica.ClickHouseClient release v1.0.17, 2020-12-10\n\n#### Bug Fix\n\n* Fixed execution of queries which affect large (greater than 2^31) number of rows ([#15](https://github.com/Octonica/ClickHouseClient/issues/15)).\n* Fixed comparison of parameter's names in ClickHouseParameterCollection.\n\n#### Improvement\n\n* Added public method `ClickHouseParameter.IsValidParameterName` which allows to check if the string can be used as the name of a parameter.\n\n### Octonica.ClickHouseClient release v1.0.14, 2020-12-02\n\n#### Bug Fix\n\n* The driver was incompatible with ClickHouse v2.10 and higher.\n* Fixed writing columns from a source which contains more rows than `rowCount`.\n* Fixed writing columns from a source which implements `IList<T>` but doesn't implement `IReadOnlyList<T>`.\n\n### Octonica.ClickHouseClient release v1.0.13, 2020-11-11\n\n#### Backward Incompatible Change\n\n* The default name of the client changed from `Octonica.ClickHouse` to `Octonica.ClickHouseClient`.\n\n#### New Feature\n\n* Added type `DateTime64`.\n* Implemented methods `NextResult` and `NextResultAsync` in `ClickHouseDataReader`. These methods can be used to read totals and extremes ([#11](https://github.com/Octonica/ClickHouseClient/issues/11)).\n* Added `Extremes` property to `ClickHouseCommand`. It allows to toggle `extremes` setting for the query.\n* Added `TimeZone` property to `ClickHouseParameter`. It allows to specify the timezone for datetime types.\n* Array can be used as the value of command parameter. Added properties `IsArray` and `ArrayRank` to `ClickHouseParameter` ([#14](https://github.com/Octonica/ClickHouseClient/issues/14)).\n\n#### Bug Fix\n\n* The type `UInt64` was mapped to the type `UInt32` in the command parameter.\n\n#### Improvement\n\n* Detection of attempts to connect to ClickHouse server with HTTP protocol ([#10](https://github.com/Octonica/ClickHouseClient/issues/10)).\n* `ReadWriteTimeout` is respected in async network operations if `cancellationToken` is not defined (i.e. `CanellationToken.None`).\n\n#### Miscellaneous\n\n* Default protocol revision is set to 54441.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2019-2026 Octonica\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2026 Octonica\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "ClickHouse .NET Core driver\n===============\n\nThis is an implementation of .NET Core driver for ClickHouse in a form of ADO.NET DbProvider API. This driver supports all ADO.NET features (with some exclusions like transaction support).\n\n### Features\n* supports binary protocol\n* compression (send and recieve)\n* timezones\n* most clickhouse [column types](docs/TypeMapping.md) are supported ([aggregating ones](https://clickhouse.tech/docs/en/sql_reference/data_types/aggregatefunction/) are under development)\n* full support for .net async ADO.NET API\n* no unsafe code\n* ~~tested~~- used in production\n* c# named tuple and record support\n* [Dapper](https://dapperlib.github.io/Dapper/) support (example in [#19](https://github.com/Octonica/ClickHouseClient/issues/19))\n* [Linq To DB](https://github.com/linq2db/linq2db) support\n\n### Usage\nInstall from [NuGet](https://www.nuget.org/packages/Octonica.ClickHouseClient/):\n```\ndotnet add package Octonica.ClickHouseClient\n```\n\nConnectionString syntax: \n`Host=<host>;Port=<port>;Database=<db>;Password=<pass>`, e.g. `\"Host=127.0.0.1;Password=P@ssw0rd; Database=db` additionally, if you want to build a connection string via code you can use `ClickHouseConnectionStringBuilder`.\n\nEntry point for API is ADO .NET DbConnection Class: `Octonica.ClickHouse.ClickHouseConnection`.\n\n### Extended API\nIn order to provide non-ADO.NET complaint data manipulation functionality, proprietary [ClickHouseColumnWriter](docs/ClickHouseColumnWriter.md) API exists.\nEntry point for API is `ClickHouseConnection.CreateColumnWriter()` method.\n\n#### Simple SELECT async verison\n```csharp\nvar sb = new ClickHouseConnectionStringBuilder();\nsb.Host = \"127.0.0.1\";\nusing var conn = new ClickHouseConnection(sb);\nawait conn.OpenAsync();\nvar currentUser = await conn.CreateCommand(\"select currentUser()\").ExecuteScalarAsync();\n```\n#### Insert data with parameters\n```csharp\nvar sb = new ClickHouseConnectionStringBuilder();\nsb.Host = \"127.0.0.1\";\nusing var conn = new ClickHouseConnection(sb);\nconn.Open();\nusing var cmd = conn.CreateCommand(\"INSERT INTO table_you_just_created SELECT {id}, {dt}\");\ncmd.Parameters.AddWithValue(\"id\", Guid.NewGuid());\ncmd.Parameters.AddWithValue(\"dt\", DateTime.Now, System.Data.DbType.DateTime);\nvar _ = cmd.ExecuteNonQuery();\n```\nFor more information see [Parameters](docs/Parameters.md).\n#### Bulk insert\n```csharp\nusing var conn = new ClickHouseConnection(\"Host=127.0.0.1\");\nconn.Open();\nusing var cmd = conn.CreateCommand(\"CREATE TABLE IF NOT EXISTS table_with_two_fields(id Int32, name String) engine Memory\");\nawait cmd.ExecuteNonQueryAsync();\n\n//generate values\nList<int> ids = Enumerable.Range(1, 10_000).ToList();\nList<string> names = ids.Select(i => $\"Name #{i}\").ToList();\n\n//insert data\nawait using (var writer = await conn.CreateColumnWriterAsync(\"insert into table_with_two_fields(id, name) values\", default))\n{\n\tawait writer.WriteTableAsync(new object[] { ids, names }, ids.Count, default);\n}\n```\n\n### Build requirements\nIn order to build the driver you need to have .NET SDK 5.0 or higher.\n"
  },
  {
    "path": "docs/ClickHouseColumnWriter.md",
    "content": "﻿# ClickHouseColumnWriter\n\n`ClickHouseColumnWriter` is a class dedicated for writing arbitrary large amount of rows to a table.\nIt writes tables in a columnar layout. It means that the table consists of several columns and\neach column contains a list of values (cells) of a particular type. All columns of the table must contain\nthe same number of cells.\n\nTo create a writer call the method `ClickHouseConnection.CreateColumnWriter` (or `ClickHouseConnection.CreateColumnWriterAsync`).\n```C#\nusing var connection = ClickHouseConnection(connectionStr);\nconnection.Open();\n\nusing var writer = connection.CreateColumnWriter(\"INSERT INTO some_table VALUES\");\n```\n\nPlease, note that the `INSERT` query for the writer must end with `VALUES` keyword, but without actual list of values.\n\nSome methods of `ClickHouseColumnWriter` are similar to methods of `ClickHouseDataReader`: `GetName`, `GetOrdinal`, `GetFieldType`,\n`ConfigureColumn` and other methods for manipulating column metadata.\n\nHere is the method for writing tables:\n```C#\nvoid WriteTable(IReadOnlyList<object?> columns, int rowCount)\n```\n\nA column could be of any type which implements one of interfaces:\n* `IReadOnlyList<T>`;\n* `IList<T>`;\n* `IEnumerable<T>`;\n* `IAsyncEnumerable<T>` (supported only by `WriteTableAsync`);\n* `IEnumerable`.\n\nThe number of columns must be equal to the number of columns in the initial `INSERT` query. Columns may have different number of rows,\nbut not less than `rowCount`. Columns must be passed in the order defined by the query.\n\nThere is an overload of `WriteTable` which distinguishes columns by their names:\n```C#\nvoid WriteTable(IReadOnlyDictionary<string, object?> columns, int rowCount)\n```\n\n## Examples\n\nAssume there is a table `some_table`.\n```C#\nusing var connection = new ClickHouseConnection(connectionStr);\nconnection.Open();\nvar cmd = connection.CreateCommand(\"CREATE TABLE some_table(id Int32, str Nullable(String), dt DateTime, val Decimal64(4)) ENGINE = Memory\");\ncmd.ExecuteNonQuery();\n```\n\n### Write ordered columns\n```C#\nvar id = new List<Guid>();\nvar str = new List<string?>();\nvar dt = new List<DateTime>();\nvar val = new List<decimal>();\n\n/*\n * Fill lists id, str, dt and val with actual values\n */\n \nawait using var connection = new ClickHouseConnection(connectionStr);\nawait connection.OpenAsync();\n\nawait using var writer = connection.CreateColumnWriter(\"INSERT INTO some_table VALUES\");\n\nvar columns = new object[writer.FieldCount];\ncolumns[writer.GetOrdinal(\"id\")] = id;\ncolumns[writer.GetOrdinal(\"str\")] = str;\ncolumns[writer.GetOrdinal(\"dt\")] = dt;\ncolumns[writer.GetOrdinal(\"val\")] = val;\n\nvar rowCount = id.Count;\nawait writer.WriteTableAsync(columns, rowCount, CancellationToken.None);\n```\n\n### Write named columns\n```C#\nvar id = new List<Guid>();\nvar dt = new List<DateTime>();\nvar val = new List<decimal>();\n\n/*\n * Fill lists id, dt and val with actual values\n */\n \nawait using var connection = new ClickHouseConnection(connectionStr);\nawait connection.OpenAsync();\n\nawait using var writer = connection.CreateColumnWriter(\"INSERT INTO some_table(id, dt, val) VALUES\");\n\nvar columns = new Dictionary<string, object?>\n{\n\t[\"id\"] = id,\n\t[\"dt\"] = dt,\n\t[\"val\"] = val\n};\n\nvar rowCount = id.Count;\nawait writer.WriteTableAsync(columns, rowCount, CancellationToken.None);\n```"
  },
  {
    "path": "docs/Parameters.md",
    "content": "# Parameters\r\n\r\nClickHouseClient's implementation of parameters is compliant with ADO.NET. API for working with parameters intended to be familiar to users of\r\nother ADO.NET drivers.\r\n\r\nEach command (`Octonica.ClickHouseClient.ClickHouseCommand`) contains a collection of parameters. This collection can be acquired with the\r\nproperty `Parameters`. Parameters from this collection can be referenced in a query.\r\n\r\n## Parameter format\r\n\r\nAs specified in [Queries with Parameters](https://clickhouse.tech/docs/en/interfaces/cli/#cli-queries-with-parameters) the default format\r\nof the parameter is `{<name>:<data type>}`. However, the type can be derived from the parameter's settings, which allows to omit `:<data type>`\r\npart and declare parameter just as `{<name>}`.\r\n\r\nClickHouseClient also supports parameters in MSSQL-like format: `@<name>`.\r\n\r\nHere is an example demonstrating different namestyles of parameters:\r\n```C#\r\nusing var connection = new ClickHouseConnection(connectionStr);\r\nconnection.Open();\r\n\r\nvar cmd = connection.CreateCommand(\"SELECT number/{value:Decimal64(2)} FROM numbers(100000) WHERE number >= @min AND number <= {max}\");\r\ncmd.Parameters.AddWithValue(\"value\", 100);\r\ncmd.Parameters.AddWithValue(\"{min}\", 1000);\r\ncmd.Parameters.AddWithValue(\"@max\", 2000);\r\n\r\nusing var reader = cmd.ExecuteReader();\r\nwhile(reader.Read())\r\n{\r\n  // Reading the data\r\n}\r\n```\r\n\r\n## Parameter settings\r\n\r\nThe settings of the parameter usually can be detected based on the type of the parameter's value. It is possible to override auto-detected settings\r\nin cases when auto-detection fails or when the required type of the parameter doesn't match to the type of the parameter's value. When settings are\r\noverridden ClickHouseClient will try to convert parameter's value to the requested type.\r\n\r\nThere are settings inherited from `System.Data.Common.DbParameter`:\r\n1. `DbType`. The type of the parameter. `System.Data.DbType` is a subset of `Octonica.ClickHouseClient.ClickHouseDbType`. For any value of the property\r\n  `ClickHouseDbType` which can't be mapped to the type `System.Data.DbType` this property returns `DbType.Object`;\r\n2. `IsNullable`. Indicates whether the parameter accept `NULL`;\r\n3. `Precision`. Defines the precision for `Decimal` or `DateTime64`;\r\n4. `Scale`. Defines the scale for `Decimal`;\r\n5. `Size`. Defines the size for `FixedString`.\r\n\r\nAnd there are additional settings supported by `Octonica.ClickHouseClient.ClickHouseParameter`:\r\n1. `ArrayRank`. The number of dimensions in the array. Zero for non-arrays;\r\n2. `ClickHouseDbType`. The type of the parameter;\r\n3. `IsArray`. Indicates whether the parameter is an array, i.e. `ArrayRank > 0`;\r\n4. `StringEncoding`. Defines the encoding which will be used for strings;\r\n5. `TimeZone`. Defines the timezone for `DateTime` or `DateTime64`.\r\n\r\n## Table-valued parameters\r\n\r\nTo be fair, a parameter can't be a table. However, ClickHouse allows to pass arbitrary tables with a query. These tables can be referenced in the query\r\nwithout special syntax.\r\n\r\nThe tables and parameters are stored separately in `Octonica.ClickHouseClient.ClickHouseCommand`. The collection of tables can be acquired with the property\r\n`TableProviders` of the command.\r\n\r\nThe basic interface for client-defined table is `Octonica.ClickHouseClient.IClickHouseTableProvider`. A class implementing this interface should provide\r\na table in a columnar format. There is a default implementation of this interface: `Octonica.ClickHouseClient.ClickHouseTableProvider`. This class allows\r\nto pass a table in a way similar to [ClickHouseColumnWriter](ClickHouseColumnWriter.md).\r\n\r\nHere is a simple example demonstrating how to pass a client-defined table to a query:\r\n```C#\r\nusing var connection = new ClickHouseConnection(connectionStr);\r\nconnection.Open();\r\n\r\nvar cmd = connection.CreateCommand(\"SELECT ptable.id, ptable.user, ptable.ip FROM ptable\");\r\n\r\nvar users = new[] {\"user1\", \"user2\", \"admin1\", \"admin2\"};\r\nvar ips = new[] {\"1.1.1.1\", \"2.2.2.2\", \"127.0.0.1\", \"::ffff:192.0.2.1\"};\r\n\r\nvar pTableProvider = new ClickHouseTableProvider(\"ptable\", users.Length);\r\npTableProvider.AddColumn(\"id\", Enumerable.Range(1, users.Length));\r\npTableProvider.AddColumn(\"user\", users);\r\n\r\n// The settings of the column are similar to the settings of parameter\r\nvar ipColumn = pTableProvider.AddColumn(\"ip\", ips);\r\nipColumn.ClickHouseDbType = ClickHouseDbType.IpV6;\r\nipColumn.IsNullable = false;\r\n\r\ncmd.TableProviders.Add(pTableProvider);\r\n\r\nusing var reader = cmd.ExecuteReader();\r\nwhile(reader.Read())\r\n{\r\n  // Reading the data\r\n}\r\n```\r\n\r\nAnd here is a bit more practical example demonstrating how a temporary table can be used with `IN` clause:\r\n```C#\r\nusing var connection = new ClickHouseConnection(connectionStr);\r\nconnection.Open();\r\n\r\nvar cmd = cn.CreateCommand(\"SELECT toInt32(number) FROM numbers(100000) WHERE number IN param_table\");\r\n\r\nvar tableProvider = new ClickHouseTableProvider(\"param_table\", 100);\r\ntableProvider.AddColumn(Enumerable.Range(500, int.MaxValue / 2));\r\n\r\ncmd.TableProviders.Add(tableProvider);\r\n\r\nusing var reader = cmd.ExecuteReader();\r\nwhile(reader.Read())\r\n{\r\n  // Reading only 100 rows\r\n}\r\n```\r\n\r\n## Implementation details\r\n\r\nUnfortunately, parameters are not supported by the ClickHouse binary protocol. Which means that it's a client-side feature. ClickHouseClient passes\r\nparameters to the server as a table with one row. The name of this table is unique for each query. It is generated based on `Guid` so there should be\r\nno collision with names of existing tables.\r\n\r\nClickHouseClient analyzes the query and substitutes parameters with `SELECT` subquery. For example, the query\r\n```SQL\r\nSELECT * FROM some_table WHERE id = {id:UInt32}\r\n```\r\nwill be transformed before sending to the server to\r\n```SQL\r\nSELECT * FROM some_table WHERE id = (CAST((SELECT _b3dcef95634b4fcfbf67624a39ce2e85.id FROM _b3dcef95634b4fcfbf67624a39ce2e85) AS UInt32))\r\n```\r\nwhere `_b3dcef95634b4fcfbf67624a39ce2e85` is the name of the table with parameters.\r\n"
  },
  {
    "path": "docs/TypeMapping.md",
    "content": "﻿# Type mappings\n**ClickHouse type**. The type of the column.\n\n**Default type**. This is the type returned by `Octonica.ClickHouseClient.ClickHouseDataReader.GetFieldType(int ordinal)`. The method `Octonica.ClickHouseClient.ClickHouseDataReader.GetValue(int ordinal)` returns either a value of the default type or `System.DBNull`.\n\n**Supported types**. The value can be converted to one of this types.\n\n**ClickHouseDataReader's method**. The method dedicated to the default type. \n\nYou can get the value of one of supported types by calling `GetFieldValue<T>(int ordinal)` or `GetFieldValue<T>(int ordinal, T? nullValue)`. The latter doesn't throw an error on NULL value.\n| ClickHouse type | Default type | Supported types | ClickHouseDataReader's method | \n|---|---|---|---|\n| Int8 | sbyte | short, int, long | `GetSByte` |\n| Int16 | short | int, long | `GetInt16` |\n| Int32 | int | long | `GetInt32` |\n| Int64 | long | | `GetInt64` |\n| Int128 | System.Numerics.BigInteger | | `GetBigInteger` |\n| Int256 | System.Numerics.BigInteger | | `GetBigInteger` |\n| UInt8 | byte | ushort, uint, ulong, int, long | `GetByte` |\n| UInt16 | ushort | uint, ulong, int, long | `GetUInt16` |\n| UInt32 | uint | ulong, long | `GetUInt132` |\n| UInt64 | ulong | | `GetUInt64` |\n| UInt128 | System.Numerics.BigInteger | | `GetBigInteger` |\n| UInt256 | System.Numerics.BigInteger | | `GetBigInteger` |\n| Float32 | float | double | `GetFloat` |\n| Float64 | double | | `GetDouble` |\n| Decimal | decimal | | `GetDecimal` |\n| Date\\* | System.DateOnly | System.DateTime | `GetDate` |\n| Date32\\* | System.DateOnly | System.DateTime | `GetDate` |\n| DateTime | System.DateTimeOffset | System.DateTime | `GetDateTimeOffset` |\n| DateTime64 | System.DateTimeOffset | System.DateTime | `GetDateTimeOffset` |\n| String | string | char[], byte[] | `GetString` |\n| FixedString | byte[] | string, char[] | |\n| UUID | System.Guid | | `GetGuid` |\n| IPv4 | System.Net.IPAddress | string, int, uint | `GetIPAddress` |\n| IPv6 | System.Net.IPAddress | string | `GetIPAddress` |\n| Enum8 | string | sbyte, short, int, long | |\n| Enum16 | string | short, int, long | |\n| Nothing | System.DBNull | | `GetValue` |\n| Nullable(T) | T? | | |\n| Array(T) | T[] | | |\n| Tuple(T1, ... Tn) | System.Tuple<T1, ... Tn> | System.ValueTuple<T1, ... Tn> | |\n| LowCardinality<T> | T | | The method for `T` |\n| Map(TKey, TValue) | System.Collections.Generic.KeyValuePair<TKey, TValue>[] | System.Tuple<TKey, TValue>[], System.ValueTuple<TKey, TValue>[] | |\n\n\\* The type `System.DateOnly` is available since .NET 6.0. For previous .NET versions dates are mapped to `System.DateTime`."
  },
  {
    "path": "src/ConnectionSettingsHelper.cs",
    "content": "#region License Apache 2.0\n/* Copyright 2020-2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.IO;\n\nnamespace Octonica.ClickHouseClient\n{\n    internal static class ConnectionSettingsHelper\n    {\n        public static ClickHouseConnectionSettings GetConnectionSettings(Action<ClickHouseConnectionStringBuilder>? updateSettings = null)\n        {\n            return GetConnectionSettingsInternal(updateSettings).settings;\n        }\n\n        public static string GetConnectionString()\n        {\n            return GetConnectionSettingsInternal(null).connectionString;\n        }\n\n        public static string GetConnectionString(Action<ClickHouseConnectionStringBuilder> updateSettings)\n        {\n            var settings = GetConnectionSettings(updateSettings);\n            var builder = new ClickHouseConnectionStringBuilder(settings);\n            return builder.ConnectionString;\n        }\n\n        private static (ClickHouseConnectionSettings settings, string connectionString) GetConnectionSettingsInternal(Action<ClickHouseConnectionStringBuilder>? updateSettings)\n        {\n            const string envVariableName = \"CLICKHOUSE_TEST_CONNECTION\";\n            const string configFileName = \"clickHouse.dbconfig\";\n            const string conStrExample = \"host=clickhouse.example.com; port=9000; user=default;\";\n\n            var configTextFromEnvVar = Environment.GetEnvironmentVariable(envVariableName);\n            if (configTextFromEnvVar != null)\n            {\n                try\n                {\n                    var builder = new ClickHouseConnectionStringBuilder(configTextFromEnvVar);\n                    updateSettings?.Invoke(builder);\n                    return (builder.BuildSettings(), configTextFromEnvVar);\n                }\n                catch (Exception ex)\n                {\n                    throw new InvalidOperationException($\"The connection string from the environment variable '{envVariableName}' is not valid. Connection string example: '{conStrExample}'. {ex.Message}\", ex);\n                }\n            }\n\n            string configPath = Path.Combine(AppContext.BaseDirectory, configFileName);\n            if (!File.Exists(configPath))\n            {\n                throw new InvalidOperationException(\n                    \"The connection string is required. \" +\n                    $\"Please, set the environment variable '{envVariableName}' or write the connection string to the file '{configFileName}'. \" +\n                    $\"Connection string example: '{conStrExample}'.\");\n            }\n            \n            string configText = File.ReadAllText(configPath);\n            try\n            {\n                var builder = new ClickHouseConnectionStringBuilder(configText);\n                updateSettings?.Invoke(builder);\n                return (builder.BuildSettings(), configText);\n            }\n            catch (Exception ex)\n            {\n                throw new InvalidOperationException($\"The connection string from the file '{configFileName}' is not valid. Connection string example: '{conStrExample}'. {ex.Message}\", ex);\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseBinaryProtocolReader.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.IO;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient\n{\n    internal class ClickHouseBinaryProtocolReader: IDisposable\n    {\n        private readonly ReadWriteBuffer _buffer;\n        private readonly Stream _stream;\n        private readonly int _bufferSize;\n\n        private CompressionAlgorithm _currentCompression;\n\n        private CompressionDecoderBase? _compressionDecoder;\n\n        public ClickHouseBinaryProtocolReader(Stream stream, int bufferSize)\n        {\n            _buffer = new ReadWriteBuffer(bufferSize);\n            _stream = stream ?? throw new ArgumentNullException(nameof(stream));\n            _bufferSize = bufferSize;\n        }\n\n        internal void BeginDecompress(CompressionAlgorithm algorithm)\n        {\n            if (algorithm != CompressionAlgorithm.None)\n            {\n                if (_compressionDecoder != null && _compressionDecoder.Algorithm != algorithm)\n                {\n                    _compressionDecoder?.Dispose();\n                    _compressionDecoder = null;\n                }\n                else\n                {\n                    _compressionDecoder?.Reset();\n                }\n            }\n\n            switch (algorithm)\n            {\n                case CompressionAlgorithm.None:\n                    _currentCompression = algorithm;\n                    return;\n                case CompressionAlgorithm.Lz4:\n                    if (_compressionDecoder == null)\n                        _compressionDecoder = new Lz4CompressionDecoder(_bufferSize);\n\n                    _currentCompression = algorithm;\n                    break;\n                default:\n                    throw new ArgumentOutOfRangeException(nameof(algorithm), algorithm, null);\n            }\n        }\n\n        internal void EndDecompress()\n        {\n            _currentCompression = CompressionAlgorithm.None;\n        }\n\n        public async ValueTask<string> ReadString(bool async, CancellationToken cancellationToken)\n        {\n            var size = await ReadSize(async, cancellationToken);\n            if (size == 0)\n                return string.Empty;\n\n            ReadOnlySequence<byte> readResult;\n            do\n            {\n                readResult = await Read(async, cancellationToken);\n                if (readResult.Length >= size)\n                    break;\n\n                AdvanceReader(readResult, 0);\n                await Advance(async, cancellationToken);\n            } while (true);\n\n            string result;\n            var encoding = Encoding.UTF8;\n            var stringSpan = readResult.Slice(readResult.Start, readResult.GetPosition(size));\n            if (stringSpan.IsSingleSegment)\n            {\n                result = encoding.GetString(stringSpan.FirstSpan);\n            }\n            else\n            {\n                var buffer = stringSpan.ToArray();\n                result = encoding.GetString(buffer);\n            }\n\n            AdvanceReader(readResult, (int) stringSpan.Length);\n            return result;\n        }\n\n        public async ValueTask<int> Read7BitInt32(bool async, CancellationToken cancellationToken)\n        {\n            var longValue = await Read7BitInteger(async, cancellationToken);\n            if (longValue > uint.MaxValue)\n                throw new FormatException(); //TODO: exception\n\n            return unchecked((int) longValue);\n        }\n\n        public ValueTask<ulong> Read7BitUInt64(bool async, CancellationToken cancellationToken)\n        {\n            return Read7BitInteger(async, cancellationToken);\n        }\n\n        public async ValueTask<int> ReadInt32(bool async, CancellationToken cancellationToken)\n        {\n            do\n            {\n                var readResult = await Read(async, cancellationToken);\n                if (readResult.Length < sizeof(int))\n                {\n                    AdvanceReader(readResult, 0);\n                    await Advance(async, cancellationToken);\n                    continue;\n                }\n\n                int result;\n                if (readResult.FirstSpan.Length >= sizeof(int))\n                    result = BitConverter.ToInt32(readResult.FirstSpan);\n                else\n                {\n                    var tmpArr = readResult.Slice(0, sizeof(int)).ToArray();\n                    result = BitConverter.ToInt32(tmpArr, 0);\n                }\n\n                AdvanceReader(readResult, sizeof(int));\n                return result;\n\n            } while (true);\n        }\n\n        public async ValueTask<int> ReadSize(bool async, CancellationToken cancellationToken)\n        {\n            var longValue = await Read7BitInteger(async, cancellationToken);\n            if (longValue > int.MaxValue)\n                throw new FormatException(); //TODO: exception\n\n            return (int) longValue;\n        }\n\n        public async ValueTask<bool> ReadBool(bool async, CancellationToken cancellationToken)\n        {\n            return await ReadByte(async, cancellationToken) != 0;\n        }\n\n        public async ValueTask<byte> ReadByte(bool async, CancellationToken cancellationToken)\n        {\n            var readResult = await Read(async, cancellationToken);\n            var result = readResult.FirstSpan[0];\n            AdvanceReader(readResult, 1);\n            return result;\n        }\n\n        private async ValueTask<ulong> Read7BitInteger(bool async, CancellationToken cancellationToken)\n        {\n            do\n            {\n                var readResult = await Read(async, cancellationToken);\n                if (!TryRead7BitInteger(readResult, out var result, out var bytesRead))\n                {\n                    AdvanceReader(readResult, 0);\n                    await Advance(async, cancellationToken);\n                }\n                else\n                {\n                    AdvanceReader(readResult, bytesRead);\n                    return result;\n                }\n            } while (true);\n        }\n\n        public async ValueTask<SequenceSize> ReadRaw(Func<ReadOnlySequence<byte>, SequenceSize> readBytes, bool async, CancellationToken cancellationToken)\n        {\n            if (readBytes == null)\n                throw new ArgumentNullException(nameof(readBytes));\n\n            var readResult = await Read(async, cancellationToken);\n            var size = readBytes(readResult);\n            AdvanceReader(readResult, size.Bytes);\n\n            return size;\n        }\n\n        public async ValueTask SkipBytes(int bytesCount, bool async, CancellationToken cancellationToken)\n        {\n            if (bytesCount < 0)\n                throw new ArgumentException(\"The number of bytes for is negative.\", nameof(bytesCount));\n\n            if (bytesCount == 0)\n                return;\n\n            var c = bytesCount;\n            while (c > 0)\n            {\n                var readResult = await Read(async, cancellationToken);\n                var consumed = Math.Min(c, (int)readResult.Length);\n                AdvanceReader(readResult, consumed);\n                c -= consumed;\n            }\n        }\n\n        internal bool TryPeekByte(out byte value)\n        {\n            if (_currentCompression != CompressionAlgorithm.None)\n                throw new NotImplementedException();\n\n            var readResult = _buffer.Read();\n            if (readResult.IsEmpty)\n            {\n                value = 0;\n                return false;\n            }\n\n            value = readResult.FirstSpan[0];\n            return true;\n        }\n\n        public async ValueTask<IServerMessage> ReadMessage(int protocolRevision, bool throwOnUnknownMessage, bool async, CancellationToken cancellationToken)\n        {\n            var messageCode = (ServerMessageCode) await Read7BitInt32(async, cancellationToken);\n            switch (messageCode)\n            {\n                case ServerMessageCode.Hello:\n                    return await ServerHelloMessage.Read(this, protocolRevision, async, cancellationToken);\n\n                case ServerMessageCode.Data:\n                case ServerMessageCode.Totals:\n                case ServerMessageCode.Extremes:\n                    return await ServerDataMessage.Read(this, messageCode, async, cancellationToken);\n\n                case ServerMessageCode.Error:\n                    return await ServerErrorMessage.Read(this, async, cancellationToken);\n\n                case ServerMessageCode.Progress:\n                    return await ServerProgressMessage.Read(this, protocolRevision, async, cancellationToken);\n\n                case ServerMessageCode.Pong:\n                    return ServerPongMessage.Instance;\n\n                case ServerMessageCode.EndOfStream:\n                    return ServerEndOfStreamMessage.Instance;\n\n                case ServerMessageCode.ProfileInfo:\n                    return await ServerProfileInfoMessage.Read(this, async, cancellationToken);\n\n                case ServerMessageCode.TableColumns:\n                    return await ServerTableColumnsMessage.Read(this, async, cancellationToken);\n\n                case ServerMessageCode.TableStatusResponse:\n                case ServerMessageCode.Log:\n                case ServerMessageCode.PartUuids:\n                case ServerMessageCode.ReadTaskRequest:\n                case ServerMessageCode.MergeTreeAllRangesAnnouncement:\n                case ServerMessageCode.MergeTreeReadTaskRequest:\n                    throw new NotImplementedException($\"A message of type \\\"{messageCode}\\\" is not supported.\");\n\n                case ServerMessageCode.ProfileEvents:\n                    return await ServerDataMessage.Read(this, messageCode, async, cancellationToken);\n\n                case ServerMessageCode.TimezoneUpdate:\n                    return await ServerTimeZoneUpdateMessage.Read(this, async, cancellationToken);\n\n                default:\n                    if (throwOnUnknownMessage)\n                        throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Internal error. Not supported message code (0x{messageCode:X}) received from the server.\");\n\n                    return new UnknownServerMessage(messageCode);\n            }\n        }\n\n        private async ValueTask<ReadOnlySequence<byte>> Read(bool async, CancellationToken cancellationToken)\n        {\n            if (_currentCompression == CompressionAlgorithm.None)\n                return await ReadFromPipe(async, cancellationToken);\n\n            if (_compressionDecoder == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. An encoder is not initialized.\");\n\n            if (!_compressionDecoder.IsCompleted)\n                await Advance(async, cancellationToken);\n\n            var sequence = _compressionDecoder.Read();\n            while (sequence.IsEmpty)\n            {\n                await Advance(async, cancellationToken);\n                sequence = _compressionDecoder.Read();\n            }\n\n            return sequence;\n        }\n\n        private async ValueTask<ReadOnlySequence<byte>> ReadFromPipe(bool async, CancellationToken cancellationToken)\n        {\n            do\n            {\n                var readResult = _buffer.Read();\n                if (!readResult.IsEmpty)\n                    return readResult;\n\n                await AdvanceBuffer(async, cancellationToken);\n            } while (true);\n        }\n\n        private void AdvanceReader(ReadOnlySequence<byte> readResult, int consumedPosition)\n        {\n            if (_currentCompression == CompressionAlgorithm.None)\n            {\n                _buffer.ConfirmRead(consumedPosition);\n            }\n            else\n            {\n                if (_compressionDecoder == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. A decoder is not initialized.\");\n\n                _compressionDecoder.AdvanceReader(readResult.GetPosition(consumedPosition));\n            }\n        }\n\n        internal async ValueTask Advance(bool async, CancellationToken cancellationToken)\n        {\n            if (_currentCompression != CompressionAlgorithm.None)\n            {\n                if (_compressionDecoder == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. A decoder is not initialized.\");\n\n                if (_compressionDecoder.IsCompleted)\n                {\n                    while (true)\n                    {\n                        var buffer = await ReadFromPipe(async, cancellationToken);\n                        var size = _compressionDecoder.ReadHeader(buffer);\n                        if (size >= 0)\n                        {\n                            _buffer.ConfirmRead(size);\n                            break;\n                        }\n\n                        await AdvanceBuffer(async, cancellationToken);\n                    }\n                }\n\n                while (!_compressionDecoder.IsCompleted)\n                {\n                    var sequence = await ReadFromPipe(async, cancellationToken);\n                    var consumed = _compressionDecoder.ConsumeNext(sequence);\n                    _buffer.ConfirmRead(consumed);\n                }\n\n                return;\n            }\n\n            await AdvanceBuffer(async, cancellationToken);\n        }\n\n        private async ValueTask AdvanceBuffer(bool async, CancellationToken cancellationToken)\n        {\n            var buffer = _buffer.GetMemory();\n\n            int bytesRead;\n            if (async)\n            {\n                if (cancellationToken == CancellationToken.None && _stream.ReadTimeout >= 0)\n                {\n                    var timeout = TimeSpan.FromMilliseconds(_stream.ReadTimeout);\n                    using var tokenSource = new CancellationTokenSource(timeout);\n                    try\n                    {\n                        bytesRead = await _stream.ReadAsync(buffer, tokenSource.Token);\n                    }\n                    catch (OperationCanceledException ex)\n                    {\n                        throw new IOException($\"Unable to read data from the transport connection: timeout exceeded ({timeout}).\", ex);\n                    }\n                }\n                else\n                {\n                    bytesRead = await _stream.ReadAsync(buffer, cancellationToken);\n                }\n            }\n            else\n            {\n                bytesRead = _stream.Read(buffer.Span);\n                cancellationToken.ThrowIfCancellationRequested();\n            }\n\n            if (bytesRead == 0)\n                throw new EndOfStreamException($\"Reached an unexpected end of the server's response. {ClickHouseConnectionStringBuilder.DefaultClientName} expected at least one more byte in the response.\");\n\n            _buffer.ConfirmWrite(bytesRead);\n            _buffer.Flush();\n        }\n\n        public static bool TryRead7BitInteger(ReadOnlySequence<byte> sequence, out ulong value, out int bytesRead)\n        {\n            ulong result = 0;\n            int i = 0, shiftSize = 0;\n            foreach (var slice in sequence)\n            {\n                for (int j = 0; j < slice.Length; j++)\n                {\n                    var byteValue = slice.Span[j];\n                    result |= (byteValue & (ulong)0x7F) << shiftSize;\n                    i++;\n\n                    if ((byteValue & 0x80) == 0x80)\n                    {\n                        shiftSize += 7;\n                        if (shiftSize > sizeof(ulong) * 8 - 7)\n                            throw new FormatException(); //TODO: exception\n                    }\n                    else\n                    {\n                        value = result;\n                        bytesRead = i;\n                        return true;\n                    }\n                }\n            }\n\n            value = 0;\n            bytesRead = 0;\n            return false;\n        }\n\n        public void Dispose()\n        {\n            _compressionDecoder?.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseBinaryProtocolWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient\n{\n    internal class ClickHouseBinaryProtocolWriter : IDisposable\n    {\n        private readonly int _bufferSize;\n\n        private readonly ReadWriteBuffer _buffer;\n        private readonly Stream _stream;\n\n        private CompressionAlgorithm _currentCompression;\n\n        private CompressionEncoderBase? _compressionEncoder;\n\n        public ClickHouseBinaryProtocolWriter(Stream stream, int bufferSize)\n        {\n            _buffer = new ReadWriteBuffer(bufferSize);\n            _stream = stream ?? throw new ArgumentNullException(nameof(stream));\n            _bufferSize = bufferSize;\n        }\n\n        public async ValueTask Flush(bool async, CancellationToken cancellationToken)\n        {\n            if (_currentCompression != CompressionAlgorithm.None)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. The stream can't be flushed because it's compression is not completed.\");\n\n            _buffer.Flush();\n\n            var readResult = _buffer.Read();\n            if (readResult.IsEmpty)\n                return;\n\n            foreach (var buffer in readResult)\n            {\n                if (async)\n                    await WriteWithTimeoutAsync((networkStream, ct) => networkStream.WriteAsync(buffer, ct).AsTask(), cancellationToken);\n                else\n                    _stream.Write(buffer.Span);\n            }\n\n            _buffer.ConfirmRead((int) readResult.Length);\n\n            if (async)\n                await WriteWithTimeoutAsync((networkStream, ct) => networkStream.FlushAsync(ct), cancellationToken);\n            else\n                _stream.Flush();\n        }\n\n        public void Discard()\n        {\n            _buffer.Discard();\n\n            var readResult = _buffer.Read();\n            if (!readResult.IsEmpty)\n                _buffer.ConfirmRead((int) readResult.Length);\n\n            _currentCompression = CompressionAlgorithm.None;\n        }\n\n        public void BeginCompress(CompressionAlgorithm algorithm, int compressionBlockSize)\n        {\n            if (_compressionEncoder != null)\n            {\n                if (_compressionEncoder.Algorithm != algorithm)\n                {\n                    _compressionEncoder.Dispose();\n                    _compressionEncoder = null;\n                }\n                else\n                {\n                    _currentCompression = _compressionEncoder.Algorithm;\n                    _compressionEncoder.Reset();\n                    return;\n                }\n            }\n\n            switch (algorithm)\n            {\n                case CompressionAlgorithm.None:\n                    break;\n\n                case CompressionAlgorithm.Lz4:\n                    _currentCompression = algorithm;\n                    _compressionEncoder = new Lz4CompressionEncoder(_bufferSize, compressionBlockSize);\n                    break;\n\n                default:\n                    throw new NotSupportedException($\"Compression algorithm \\\"{algorithm}\\\" is not supported.\");\n            }\n        }\n\n        public void EndCompress()\n        {\n            _compressionEncoder?.Complete(_buffer);\n            _currentCompression = CompressionAlgorithm.None;\n        }\n\n        public void WriteString(string value)\n        {\n            if (value == null)\n                throw new ArgumentNullException(nameof(value));\n\n            var encoding = Encoding.UTF8;\n            var length = encoding.GetByteCount(value);\n            Write7BitInteger((uint) length);\n            if (length == 0)\n                return;\n\n            var charSpan = value.AsSpan();\n            var byteSpan = GetSpan(length);\n            var count = Encoding.UTF8.GetBytes(charSpan, byteSpan);\n            Debug.Assert(count == length);\n\n            Advance(length);\n        }\n\n        public SequenceSize WriteRaw(Func<Memory<byte>, SequenceSize> writeBytes)\n        {\n            return WriteRaw(0, writeBytes);\n        }\n\n        public SequenceSize WriteRaw(int sizeHint, Func<Memory<byte>, SequenceSize> writeBytes)\n        {\n            if (writeBytes == null)\n                throw new ArgumentNullException(nameof(writeBytes));\n\n            SequenceSize size;\n            var memory = sizeHint > 0 ? GetMemory(sizeHint) : GetMemory();\n            if (!memory.IsEmpty)\n            {\n                try\n                {\n                    size = writeBytes(memory);\n                }\n                catch\n                {\n                    Advance(0);\n                    throw;\n                }\n\n                if (size.Bytes > 0 || size.Elements > 0)\n                {\n                    Advance(size.Bytes);\n                    return size;\n                }\n            }\n\n            var bufferSize = _bufferSize;\n            do\n            {\n                Advance(0);\n\n                memory = GetMemory(bufferSize);\n                try\n                {\n                    size = writeBytes(memory);\n                }\n                catch\n                {\n                    Advance(0);\n                    throw;\n                }\n\n                bufferSize *= 2;\n\n            } while (size.Bytes == 0 && size.Elements == 0);\n\n            Advance(size.Bytes);\n            return size;\n        }\n\n        public void WriteInt32(int value)\n        {\n            var span = GetSpan(sizeof(int));\n            var success = BitConverter.TryWriteBytes(span, value);\n            Debug.Assert(success);\n\n            Advance(sizeof(int));\n        }\n\n        public void WriteBool(bool value)\n        {\n            WriteByte(value ? (byte) 1 : (byte) 0);\n        }\n\n        public void WriteByte(byte value)\n        {\n            var buffer = GetSpan(1);\n            buffer[0] = value;\n            Advance(1);\n        }\n\n        public void WriteBytes(ReadOnlySpan<byte> bytes)\n        {\n            var span = GetSpan(bytes.Length);\n            bytes.CopyTo(span);\n            Advance(bytes.Length);\n        }\n\n        private async Task WriteWithTimeoutAsync(Func<Stream, CancellationToken, Task> writeAsync, CancellationToken cancellationToken)\n        {\n            if (cancellationToken == CancellationToken.None && _stream.WriteTimeout >= 0)\n            {\n                var timeout = TimeSpan.FromMilliseconds(_stream.WriteTimeout);\n                using var tokenSource = new CancellationTokenSource(timeout);\n                try\n                {\n                    await writeAsync(_stream, tokenSource.Token);\n                }\n                catch (OperationCanceledException ex)\n                {\n                    throw new IOException($\"Unable to write data to the transport connection: timeout exceeded ({timeout}).\", ex);\n                }\n            }\n            else\n            {\n                await writeAsync(_stream, cancellationToken);\n            }\n        }\n\n        public void Write7BitInt32(int value)\n        {\n            var ulongValue = (ulong) unchecked((uint) value);\n            Write7BitInteger(ulongValue);\n        }\n\n        private void Write7BitInteger(ulong value)\n        {\n            ulong v = value;\n            int totalLength = 0;\n\n            var buffer = GetSpan(10);\n            for (int i = 0; i < buffer.Length; i++)\n            {\n                ++totalLength;\n\n                if (v >= 0x80)\n                {\n                    buffer[i] = (byte) (v | 0x80);\n                    v >>= 7;\n                }\n                else\n                {\n                    buffer[i] = (byte) v;\n                    Advance(totalLength);\n                    return;\n                }\n            }\n\n            Advance(totalLength);\n        }\n\n        private Span<byte> GetSpan(int sizeHint)\n        {\n            if (_currentCompression != CompressionAlgorithm.None)\n            {\n                if (_compressionEncoder == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. An encoder is not initialized.\");\n\n                return _compressionEncoder.GetSpan(sizeHint);\n            }\n\n            return _buffer.GetMemory(sizeHint).Span;\n        }\n\n        private Memory<byte> GetMemory(int sizeHint)\n        {\n            if (_currentCompression == CompressionAlgorithm.None)\n                return _buffer.GetMemory(sizeHint);\n\n            if (_compressionEncoder == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. An encoder is not initialized.\");\n\n            return _compressionEncoder.GetMemory(sizeHint);\n\n        }\n\n        private Memory<byte> GetMemory()\n        {\n            if (_currentCompression == CompressionAlgorithm.None)\n                return _buffer.GetMemory();\n\n            if (_compressionEncoder == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. An encoder is not initialized.\");\n\n            return _compressionEncoder.GetMemory();\n        }\n\n        private void Advance(int bytes)\n        {\n            if (_currentCompression != CompressionAlgorithm.None)\n            {\n                if (_compressionEncoder == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. An encoder is not initialized.\");\n\n                _compressionEncoder.Advance(bytes);\n            }\n            else\n            {\n                _buffer.ConfirmWrite(bytes);\n            }\n        }\n\n        public static int TryWrite7BitInteger(Span<byte> buffer, ulong value)\n        {\n            ulong v = value;\n            int count = 0;\n\n            while (true)\n            {\n                if (buffer.Length == count)\n                    return 0;\n\n                if (v >= 0x80)\n                {\n                    buffer[count++] = (byte) (v | 0x80);\n                    v >>= 7;\n                }\n                else\n                {\n                    buffer[count++] = (byte) v;\n                    break;\n                }\n            }\n\n            return count;\n        }\n\n        public void Dispose()\n        {\n            _compressionEncoder?.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseColumnSettings.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Text;\nusing Octonica.ClickHouseClient.Types;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents additional column settings that affect the behavior of <see cref=\"ClickHouseDataReader\"/> and <see cref=\"ClickHouseColumnWriter\"/>.\n    /// </summary>\n    public class ClickHouseColumnSettings\n    {\n        private ITypeDispatcher? _columnTypeDispatcher;\n\n        /// <summary>\n        /// Gets encoding applied to strings when reading from the database or writing to the database.\n        /// </summary>\n        public Encoding? StringEncoding { get; }\n\n        /// <summary>\n        /// Gets the converter applied to enums.\n        /// </summary>\n        public IClickHouseEnumConverter? EnumConverter { get; }\n\n        /// <summary>\n        /// Gets the explicitly defined type of the column. This value overrides the type of the field for <see cref=\"ClickHouseDataReader\"/>\n        /// and <see cref=\"ClickHouseColumnWriter\"/>. <see cref=\"ClickHouseDataReader\"/> will try to convert a column's value to this type.\n        /// <see cref=\"ClickHouseColumnWriter\"/> will expect a collection of items of this type as input.\n        /// </summary>\n        public Type? ColumnType { get; }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseConnectionSettings\"/> class with the specified encoding.\n        /// </summary>\n        /// <param name=\"stringEncoding\">The encoding applied to strings when reading from the database or writing to the database.</param>\n        public ClickHouseColumnSettings(Encoding stringEncoding)\n        {\n            StringEncoding = stringEncoding ?? throw new ArgumentNullException(nameof(stringEncoding));\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseConnectionSettings\"/> class with the specified enum converter.\n        /// </summary>\n        /// <param name=\"enumConverter\">The converter applied to enums.</param>\n        public ClickHouseColumnSettings(IClickHouseEnumConverter enumConverter)\n        {\n            EnumConverter = enumConverter ?? throw new ArgumentNullException(nameof(enumConverter));\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseConnectionSettings\"/> class with the specified column type.\n        /// </summary>\n        /// <param name=\"columnType\">\n        /// The explicitly defined type of the column. This value overrides the type of the field for <see cref=\"ClickHouseDataReader\"/>\n        /// and <see cref=\"ClickHouseColumnWriter\"/>. <see cref=\"ClickHouseDataReader\"/> will try to convert a column's value to this type.\n        /// <see cref=\"ClickHouseColumnWriter\"/> will expect a collection of items of this type as input.\n        /// </param>\n        public ClickHouseColumnSettings(Type columnType)\n        {\n            ColumnType = columnType ?? throw new ArgumentException(nameof(columnType));\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseConnectionSettings\"/> class with multiple specified parameters.\n        /// </summary>\n        /// <param name=\"stringEncoding\">The encoding applied to strings when reading from the database or writing to the database.</param>\n        /// <param name=\"enumConverter\">The converter applied to enums.</param>\n        /// <param name=\"columnType\">\n        /// The explicitly defined type of the column. This value overrides the type of the field for <see cref=\"ClickHouseDataReader\"/>\n        /// and <see cref=\"ClickHouseColumnWriter\"/>. <see cref=\"ClickHouseDataReader\"/> will try to convert a column's value to this type.\n        /// <see cref=\"ClickHouseColumnWriter\"/> will expect a collection of items of this type as input.\n        /// </param>\n        public ClickHouseColumnSettings(Encoding? stringEncoding = null, IClickHouseEnumConverter? enumConverter = null, Type? columnType = null)\n        {\n            StringEncoding = stringEncoding;\n            EnumConverter = enumConverter;\n            ColumnType = columnType;\n        }\n\n        internal ITypeDispatcher? GetColumnTypeDispatcher()\n        {\n            if (ColumnType == null)\n                return null;\n\n            return _columnTypeDispatcher ??= TypeDispatcher.Create(ColumnType);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseColumnWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Types;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Provides a way of writing set of columns to a ClickHouse database. This class cannot be inherited.\n    /// </summary>\n    public sealed class ClickHouseColumnWriter : IDisposable, IAsyncDisposable\n    {\n        private readonly ClickHouseTcpClient.Session _session;\n        private readonly ClientQueryMessage _query;\n        private readonly ReadOnlyCollection<ColumnInfo> _columns;\n\n        private ClickHouseColumnSettings?[]? _columnSettings;\n\n        private int? _rowsPerBlock;\n        private bool _endOfStream;\n\n        /// <summary>\n        /// Gets the number of fields (columns) in the table.\n        /// </summary>\n        public int FieldCount => _columns.Count;\n\n        /// <summary>\n        /// Gets the value indicating whether the writer is closed.\n        /// </summary>\n        /// <returns><see langword=\"true\"/> if the reader is closed; otherwise <see langword=\"false\"/>.</returns>\n        public bool IsClosed => _session.IsDisposed || _session.IsFailed;\n\n        /// <summary>\n        /// Gets or sets the maximal number of rows in a single block of data.\n        /// </summary>\n        /// <returns>The maximal number of rows in a single block of data. <see langword=\"null\"/> if the size of the block is not limited.</returns>\n        public int? MaxBlockSize\n        {\n            get => _rowsPerBlock;\n            set\n            {\n                if (value <= 0)\n                    throw new ArgumentException(\"A number of rows in a block must be greater than zero.\");\n\n                _rowsPerBlock = value;\n            }\n        }\n\n        /// <summary>\n        /// Gets the query execution progress reported by the server.\n        /// </summary>\n        public ClickHouseQueryExecutionProgress ExecutionProgress { get; private set; }\n\n        internal ClickHouseColumnWriter(ClickHouseTcpClient.Session session, ClientQueryMessage query, ReadOnlyCollection<ColumnInfo> columns)\n        {\n            _session = session ?? throw new ArgumentNullException(nameof(session));\n            _query = query ?? throw new ArgumentNullException(nameof(query));\n            _columns = columns;\n\n            if (columns.Count <= 100)\n                MaxBlockSize = 8000;\n            else if (columns.Count >= 1000)\n                MaxBlockSize = 800;\n            else\n                MaxBlockSize = 8800 - 8 * columns.Count;\n        }\n\n        internal static async ValueTask<ClickHouseTable> ReadTableMetadata(ClickHouseTcpClient.Session session, string queryText, bool async, CancellationToken cancellationToken)\n        {\n            var msg = await session.ReadMessage(async, cancellationToken);\n            switch (msg.MessageCode)\n            {\n                case ServerMessageCode.Error:\n                    throw ((ServerErrorMessage)msg).Exception.CopyWithQuery(queryText);\n\n                case ServerMessageCode.TableColumns:\n                    break;\n\n                default:\n                    throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Unexpected server message. Received the message of type {msg.MessageCode}.\");\n            }\n\n            msg = await session.ReadMessage(async, cancellationToken);\n            ClickHouseTable data;\n            switch (msg.MessageCode)\n            {\n                case ServerMessageCode.Error:\n                    throw ((ServerErrorMessage)msg).Exception.CopyWithQuery(queryText);\n\n                case ServerMessageCode.Data:\n                    data = await session.ReadTable((ServerDataMessage)msg, null, async, cancellationToken);\n                    break;\n\n                default:\n                    throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Unexpected server message. Received the message of type {msg.MessageCode}.\");\n            }\n\n            return data;\n        }\n\n        /// <inheritdoc cref=\"ClickHouseDataReader.ConfigureColumn(string, ClickHouseColumnSettings)\"/>\n        public void ConfigureColumn(string name, ClickHouseColumnSettings columnSettings)\n        {\n            var index = GetOrdinal(name);\n            if (index < 0)\n                throw new ArgumentException($\"A column with the name \\\"{name}\\\" not found.\", nameof(name));\n\n            ConfigureColumn(index, columnSettings);\n        }\n\n        /// <inheritdoc cref=\"ClickHouseDataReader.ConfigureColumn(int, ClickHouseColumnSettings)\"/>\n        public void ConfigureColumn(int ordinal, ClickHouseColumnSettings columnSettings)\n        {\n            if (_columnSettings == null)\n                _columnSettings = new ClickHouseColumnSettings?[_columns.Count];\n\n            _columnSettings[ordinal] = columnSettings;\n        }\n\n        /// <inheritdoc cref=\"ClickHouseDataReader.ConfigureDataReader(ClickHouseColumnSettings)\"/>\n        public void ConfigureColumnWriter(ClickHouseColumnSettings columnSettings)\n        {\n            if (_columnSettings == null)\n                _columnSettings = new ClickHouseColumnSettings?[_columns.Count];\n\n            for (int i = 0; i < _columns.Count; i++)\n                _columnSettings[i] = columnSettings;\n        }\n\n        /// <inheritdoc cref=\"ClickHouseDataReader.GetFieldTypeInfo(int)\"/>\n        public IClickHouseTypeInfo GetFieldTypeInfo(int ordinal)\n        {\n            return _columns[ordinal].TypeInfo;\n        }\n\n        /// <inheritdoc cref=\"ClickHouseDataReader.GetName(int)\"/>\n        public string GetName(int ordinal)\n        {\n            return _columns[ordinal].Name;\n        }\n\n        /// <inheritdoc cref=\"ClickHouseDataReader.GetDataTypeName(int)\"/>\n        public string GetDataTypeName(int ordinal)\n        {\n            return _columns[ordinal].TypeInfo.ComplexTypeName;\n        }\n\n        /// <inheritdoc cref=\"ClickHouseDataReader.GetFieldType(int)\"/>\n        public Type GetFieldType(int ordinal)\n        {\n            // This method should implement the same logic as ClickHouseDataReader.GetFieldType\n\n            var type = _columnSettings?[ordinal]?.ColumnType;\n            type ??= _columns[ordinal].TypeInfo.GetFieldType();\n            return Nullable.GetUnderlyingType(type) ?? type;\n        }\n\n        /// <inheritdoc cref=\"ClickHouseDataReader.GetOrdinal(string)\"/>\n        public int GetOrdinal(string name)\n        {\n            if (name == null)\n                throw new ArgumentNullException(nameof(name));\n\n            return CommonUtils.GetColumnIndex(_columns, name);\n        }\n\n        /// <summary>\n        /// Writes a single row to the table.\n        /// </summary>\n        /// <param name=\"values\">The list of column values.</param> \n        /// <remarks>Please note that the method always commits a transaction. No subsequent call of <see cref=\"Commit\"/> is required.</remarks>\n        public void WriteRow(params object?[] values)\n        {\n            TaskHelper.WaitNonAsyncTask(WriteRow(values, commit: true, async: false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Writes a single row to the table.\n        /// </summary>\n        /// <param name=\"values\">The list of column values.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        /// <remarks>Please note that the method always commits a transaction. No subsequent call of <see cref=\"Commit\"/> is required.</remarks>\n        public void WriteRow(IReadOnlyCollection<object?> values)\n        {\n            TaskHelper.WaitNonAsyncTask(WriteRow(values, commit: true, async: false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Writes a single row to the table.\n        /// </summary>\n        /// <param name=\"values\">The list of column values.</param>\n        /// <param name=\"commit\">\n        /// If <see langword=\"true\"/>, commits the transaction immediately after writing a row (the same mode as <see cref=\"ClickHouseTransactionMode.Block\"/>).\n        /// If <see langword=\"false\"/>, leaves the transaction open (the same mode as <see cref=\"ClickHouseTransactionMode.Manual\"/>).\n        /// </param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        public void WriteRow(IReadOnlyCollection<object?> values, bool commit)\n        {\n            TaskHelper.WaitNonAsyncTask(WriteRow(values, commit, async: false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Asyncronously writes a single row to the table.\n        /// </summary>\n        /// <param name=\"values\">The list of column values.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        /// <remarks>Please note that the method always commits a transaction. No subsequent call of <see cref=\"CommitAsync(CancellationToken)\"/> is required.</remarks>\n        public async Task WriteRowAsync(IReadOnlyCollection<object?> values)\n        {\n            await WriteRow(values, commit: true, async: true, CancellationToken.None);\n        }\n\n\n        /// <summary>\n        /// Asyncronously writes a single row to the table.\n        /// </summary>\n        /// <param name=\"values\">The list of column values.</param>\n        /// <param name=\"commit\">\n        /// If <see langword=\"true\"/>, commits the transaction immediately after writing a row (the same mode as <see cref=\"ClickHouseTransactionMode.Block\"/>).\n        /// If <see langword=\"false\"/>, leaves the transaction open (the same mode as <see cref=\"ClickHouseTransactionMode.Manual\"/>).\n        /// </param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        public async Task WriteRowAsync(IReadOnlyCollection<object?> values, bool commit)\n        {\n            await WriteRow(values, commit, async: true, CancellationToken.None);\n        }\n\n        /// <summary>\n        /// Asyncronously writes a single row to the table.\n        /// </summary>\n        /// <param name=\"values\">The list of column values.</param>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        /// <remarks>Please note that the method always commits a transaction. No subsequent call of <see cref=\"CommitAsync(CancellationToken)\"/> is required.</remarks>\n        public async Task WriteRowAsync(IReadOnlyCollection<object?> values, CancellationToken cancellationToken)\n        {\n            await WriteRow(values, commit: true, async: true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Asyncronously writes a single row to the table.\n        /// </summary>\n        /// <param name=\"values\">The list of column values.</param>\n        /// <param name=\"commit\">\n        /// If <see langword=\"true\"/>, commits the transaction immediately after writing a row (the same mode as <see cref=\"ClickHouseTransactionMode.Block\"/>).\n        /// If <see langword=\"false\"/>, leaves the transaction open (the same mode as <see cref=\"ClickHouseTransactionMode.Manual\"/>).\n        /// </param>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        public async Task WriteRowAsync(IReadOnlyCollection<object?> values, bool commit, CancellationToken cancellationToken)\n        {\n            await WriteRow(values, commit, async: true, cancellationToken);\n        }\n\n        private async ValueTask WriteRow(IReadOnlyCollection<object?> values, bool commit, bool async, CancellationToken cancellationToken)\n        {\n            if (values == null)\n                throw new ArgumentNullException(nameof(values));\n\n            if (values.Count != _columns.Count)\n                throw new ArgumentException(\"The number of values must be equal to the number of columns.\");\n\n            var columnWriters = new List<IClickHouseColumnWriter>(_columns.Count);\n            foreach (var value in values)\n            {\n                int i = columnWriters.Count;\n\n                var columnInfo = _columns[i];\n                var settings = _columnSettings?[i];\n\n                if (settings?.ColumnType == typeof(object))\n                {\n                    throw new ClickHouseException(\n                        ClickHouseErrorCodes.InvalidColumnSettings,\n                        $\"Type \\\"{settings.ColumnType}\\\" should not be used as a type of a column. This type is defined in column settings of the column \\\"{columnInfo.Name}\\\" (position {i}).\");\n                }\n\n                ITypeDispatcher? typeDispatcher;\n                SingleRowColumnWriterDispatcher dispatcher;\n                if (value != null && !(value is DBNull))\n                {\n                    dispatcher = new SingleRowColumnWriterDispatcher(value, columnInfo, _columnSettings?[i]);\n                    var valueType = value.GetType();\n\n                    if (settings?.ColumnType != null)\n                    {\n                        if (!settings.ColumnType.IsAssignableFrom(valueType))\n                        {\n                            throw new ClickHouseException(\n                                ClickHouseErrorCodes.ColumnTypeMismatch,\n                                $\"The value of the row at the position {i} (column \\\"{columnInfo.Name}\\\") can't be converted to the type \\\"{settings.ColumnType}\\\". This type is defined in column settings.\");\n                        }\n\n                        typeDispatcher = settings.GetColumnTypeDispatcher();\n                        Debug.Assert(typeDispatcher != null);\n                    }\n                    else\n                    {\n                        typeDispatcher = TypeDispatcher.Create(valueType);\n                    }\n                }\n                else if (columnInfo.TypeInfo.TypeName != \"Nullable\")\n                {\n                    throw new ClickHouseException(ClickHouseErrorCodes.ColumnTypeMismatch, $\"The column \\\"{columnInfo.Name}\\\" at the position {i} doesn't support nulls.\");\n                }\n                else\n                {\n                    dispatcher = new SingleRowColumnWriterDispatcher(null, columnInfo, _columnSettings?[i]);\n                    if (settings?.ColumnType != null)\n                    {\n                        if (settings.ColumnType.IsValueType && Nullable.GetUnderlyingType(settings.ColumnType) == null)\n                        {\n                            throw new ClickHouseException(\n                                ClickHouseErrorCodes.ColumnTypeMismatch,\n                                $\"The value of the row at the position {i} (column \\\"{columnInfo.Name}\\\") is null. But the type of this column defined in the settings (\\\"{settings.ColumnType}\\\") doesn't allow nulls.\");\n                        }\n\n                        typeDispatcher = settings.GetColumnTypeDispatcher();\n                        Debug.Assert(typeDispatcher != null);\n                    }\n                    else\n                    {\n                        var fieldType = columnInfo.TypeInfo.GetFieldType();\n                        typeDispatcher = TypeDispatcher.Create(fieldType);\n                    }\n                }\n\n                IClickHouseColumnWriter columnWriter;\n                try\n                {\n                    columnWriter = typeDispatcher.Dispatch(dispatcher);\n                }\n                catch (ClickHouseException ex)\n                {\n                    throw new ClickHouseException(ex.ErrorCode, $\"Column \\\"{columnInfo.Name}\\\" (position {i}): {ex.Message}\", ex);\n                }\n\n                columnWriters.Add(columnWriter);\n            }\n\n            var table = new ClickHouseTableWriter(string.Empty, 1, columnWriters);\n            await SendTable(table, commit, async, cancellationToken);\n        }\n\n        /// <summary>\n        /// Writes the specified columns to the table.\n        /// <br/>\n        /// Each column must be an object implementing one of the interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </summary>\n        /// <param name=\"columns\">The <see cref=\"IReadOnlyDictionary{TKey, TValue}\"/> object that provides access to columns by their names.</param>\n        /// <param name=\"rowCount\">The number of rows in columns.</param>\n        public void WriteTable(IReadOnlyDictionary<string, object?> columns, int rowCount)\n        {\n            TaskHelper.WaitNonAsyncTask(WriteTable(columns, rowCount, ClickHouseTransactionMode.Default, false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Writes the specified columns to the table.\n        /// <br/>\n        /// Each column must be an object implementing one of the interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </summary>\n        /// <param name=\"columns\">The <see cref=\"IReadOnlyDictionary{TKey, TValue}\"/> object that provides access to columns by their names.</param>\n        /// <param name=\"rowCount\">The number of rows in columns.</param>\n        /// <param name=\"transactionMode\">The mode of sending write confirmations to the server.See <see cref=\"ClickHouseTransactionMode\"/> for details.</param>\n        public void WriteTable(IReadOnlyDictionary<string, object?> columns, int rowCount, ClickHouseTransactionMode transactionMode)\n        {\n            TaskHelper.WaitNonAsyncTask(WriteTable(columns, rowCount, transactionMode, false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Writes the specified columns to the table.\n        /// <br/>\n        /// Each column must be an object implementing one of the interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </summary>\n        /// <param name=\"columns\">The list of columns.</param>\n        /// <param name=\"rowCount\">The number of rows in columns.</param>\n        public void WriteTable(IReadOnlyList<object?> columns, int rowCount)\n        {\n            TaskHelper.WaitNonAsyncTask(WriteTable(columns, rowCount, ClickHouseTransactionMode.Default, false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Writes the specified columns to the table.\n        /// <br/>\n        /// Each column must be an object implementing one of the interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </summary>\n        /// <param name=\"columns\">The list of columns.</param>\n        /// <param name=\"rowCount\">The number of rows in columns.</param>\n        /// <param name=\"transactionMode\">The mode of sending write confirmations to the server.See <see cref=\"ClickHouseTransactionMode\"/> for details.</param>\n        public void WriteTable(IReadOnlyList<object?> columns, int rowCount, ClickHouseTransactionMode transactionMode)\n        {\n            TaskHelper.WaitNonAsyncTask(WriteTable(columns, rowCount, transactionMode, false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Asyncronously writes the specified columns to the table.\n        /// <br/>\n        /// Each column must be an object implementing one of the interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </summary>\n        /// <param name=\"columns\">The <see cref=\"IReadOnlyDictionary{TKey, TValue}\"/> object that provides access to columns by their names.</param>\n        /// <param name=\"rowCount\">The number of rows in columns.</param>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        public async Task WriteTableAsync(IReadOnlyDictionary<string, object?> columns, int rowCount, CancellationToken cancellationToken)\n        {\n            await WriteTable(columns, rowCount, ClickHouseTransactionMode.Default, true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Asyncronously writes the specified columns to the table.\n        /// <br/>\n        /// Each column must be an object implementing one of the interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </summary>\n        /// <param name=\"columns\">The <see cref=\"IReadOnlyDictionary{TKey, TValue}\"/> object that provides access to columns by their names.</param>\n        /// <param name=\"rowCount\">The number of rows in columns.</param>\n        /// <param name=\"transactionMode\">The mode of sending write confirmations to the server.See <see cref=\"ClickHouseTransactionMode\"/> for details.</param>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        public async Task WriteTableAsync(IReadOnlyDictionary<string, object?> columns, int rowCount, ClickHouseTransactionMode transactionMode, CancellationToken cancellationToken)\n        {\n            await WriteTable(columns, rowCount, transactionMode, true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Asyncronously writes the specified columns to the table.\n        /// <br/>\n        /// Each column must be an object implementing one of the interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </summary>\n        /// <param name=\"columns\">The list of columns.</param>\n        /// <param name=\"rowCount\">The number of rows in columns.</param>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        public async Task WriteTableAsync(IReadOnlyList<object?> columns, int rowCount, CancellationToken cancellationToken)\n        {\n            await WriteTable(columns, rowCount, ClickHouseTransactionMode.Default, true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Asyncronously writes the specified columns to the table.\n        /// <br/>\n        /// Each column must be an object implementing one of the interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </summary>\n        /// <param name=\"columns\">The list of columns.</param>\n        /// <param name=\"rowCount\">The number of rows in columns.</param>\n        /// <param name=\"transactionMode\">The mode of sending write confirmations to the server.See <see cref=\"ClickHouseTransactionMode\"/> for details.</param>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        public async Task WriteTableAsync(IReadOnlyList<object?> columns, int rowCount, ClickHouseTransactionMode transactionMode, CancellationToken cancellationToken)\n        {\n            await WriteTable(columns, rowCount, transactionMode, true, cancellationToken);\n        }\n\n        private async ValueTask WriteTable(IReadOnlyDictionary<string, object?> columns, int rowCount, ClickHouseTransactionMode mode, bool async, CancellationToken cancellationToken)\n        {\n            if (columns == null)\n                throw new ArgumentNullException(nameof(columns));\n\n            var list = new List<object?>(_columns.Count);\n            foreach (var columnInfo in _columns)\n            {\n                if (columns.TryGetValue(columnInfo.Name, out var column))\n                    list.Add(column);\n                else\n                    list.Add(null);\n            }\n\n            await WriteTable(list, rowCount, mode, async, cancellationToken);\n        }\n\n        private async ValueTask WriteTable(IReadOnlyList<object?> columns, int rowCount, ClickHouseTransactionMode mode, bool async, CancellationToken cancellationToken)\n        {\n            if (columns == null)\n                throw new ArgumentNullException(nameof(columns));\n            if (columns.Count != _columns.Count)\n                throw new ArgumentException(\"The number of columns for writing must be equal to the number of columns in the table.\", nameof(columns));\n            if (rowCount < 0)\n                throw new ArgumentOutOfRangeException(nameof(rowCount));\n            if (rowCount == 0)\n                throw new ArgumentException(\"The number of rows must be greater than zero.\", nameof(rowCount));\n\n            if (IsClosed)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"The writer is closed.\");\n\n            var writerFactories = new List<IClickHouseColumnWriterFactory>(_columns.Count);\n            for (int i = 0; i < _columns.Count; i++)\n            {\n                var factory = await CreateColumnWriterFactory(_columns[i], columns[i], i, rowCount, _columnSettings?[i], async, cancellationToken);\n                writerFactories.Add(factory);\n            }\n\n            int offset;\n            var blockSize = MaxBlockSize ?? rowCount;\n            bool commitBlock = mode == ClickHouseTransactionMode.Block;\n            for (offset = 0; offset + blockSize < rowCount; offset += blockSize)\n            {\n                var table = new ClickHouseTableWriter(string.Empty, blockSize, writerFactories.Select(w => w.Create(offset, blockSize)));\n                await SendTable(table, commitBlock, async, cancellationToken);\n            }\n\n            var finalBlockSize = rowCount - offset;\n            var finalTable = new ClickHouseTableWriter(string.Empty, finalBlockSize, writerFactories.Select(w => w.Create(offset, finalBlockSize)));\n            bool commit = commitBlock || mode == ClickHouseTransactionMode.Default || mode == ClickHouseTransactionMode.Auto;\n            await SendTable(finalTable, commit, async, cancellationToken);\n        }\n\n        private async ValueTask SendTable(ClickHouseTableWriter table, bool commit, bool async, CancellationToken cancellationToken)\n        {\n            if (_endOfStream)\n                await RepeatQuery(async, cancellationToken);\n\n            try\n            {\n                await _session.SendTable(table, async, cancellationToken);\n\n                if (commit)\n                    await EndWrite(TerminationMode.Confirm, closeSession: false, async, cancellationToken);\n            }\n            catch (ClickHouseHandledException)\n            {\n                throw;\n            }\n            catch (Exception ex)\n            {\n                var aggrEx = await _session.SetFailed(ex, false, async);\n                if (aggrEx != null)\n                    throw aggrEx;\n\n                throw;\n            }\n        }\n\n        private async ValueTask RepeatQuery(bool async, CancellationToken cancellationToken)\n        {\n            ClickHouseTable data;\n            try\n            {\n                await _session.SendQuery(_query, async, cancellationToken);\n                data = await ReadTableMetadata(_session, _query.Query, async, cancellationToken);\n                _endOfStream = false;\n            }\n            catch (ClickHouseServerException)\n            {\n                await _session.Dispose(async);\n                throw;\n            }\n            catch (ClickHouseHandledException)\n            {\n                await _session.Dispose(async);\n                throw;\n            }\n            catch (Exception ex)\n            {\n                var aggrEx = await _session.SetFailed(ex, false, async);\n                if (aggrEx != null)\n                    throw aggrEx;\n\n                throw;\n            }\n\n            try\n            {\n                // Repeating the query is almost the same as opening a new independent column writer. So we must check that the structure of the table wasn't changed.\n                var newColumns = data.Header.Columns;\n                if (newColumns.Count != _columns.Count)\n                    throw new ClickHouseException(ClickHouseErrorCodes.TableModified, \"The number of columns returned by the query has changed.\");\n\n                for (int i = 0; i < data.Columns.Count; i++)\n                {\n                    var newCol = newColumns[i];\n                    var origCol = _columns[i];\n\n                    if (!string.Equals(origCol.Name, newCol.Name, StringComparison.Ordinal))\n                        throw new ClickHouseException(ClickHouseErrorCodes.TableModified, $\"Unexpected column \\\"{newCol.Name}\\\" at the position {i}. Expected \\\"{origCol.Name}\\\".\");\n\n                    if (!string.Equals(origCol.TypeInfo.ComplexTypeName, newCol.TypeInfo.ComplexTypeName, StringComparison.Ordinal))\n                        throw new ClickHouseException(ClickHouseErrorCodes.TableModified, $\"The type of the column \\\"{origCol.Name}\\\" has changed from \\\"{origCol.TypeInfo.ComplexTypeName}\\\" to \\\"{newCol.TypeInfo.ComplexTypeName}\\\" between queries.\");\n                }\n            }\n            catch (Exception ex)\n            {\n                try\n                {\n                    await EndWrite(TerminationMode.Cancel, closeSession: true, async, cancellationToken);\n                }\n                catch (Exception cancellationEx)\n                {\n                    throw new AggregateException(ex, cancellationEx);\n                }\n\n                var hEx = ClickHouseHandledException.Wrap(ex);\n                if (ReferenceEquals(hEx, ex))\n                    throw;\n\n                throw hEx;\n            }\n        }\n\n        /// <summary>\n        /// Notifies the server that the transaction should be commited.\n        /// This method acts similar to <see cref=\"EndWrite()\"/>, but it doesn't close the writer.\n        /// </summary>\n        /// <remarks>A subsequent writing operation will send a new INSERT query to the server.</remarks>\n        public void Commit()\n        {\n            TaskHelper.WaitNonAsyncTask(EndWrite(TerminationMode.Confirm, closeSession: false, async: false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Asyncronously notifies the server that the transaction should be commited.\n        /// This method acts similar to <see cref=\"EndWriteAsync(CancellationToken)\"/>, but it doesn't close the writer.\n        /// </summary>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        /// <remarks>A subsequent writing operation will send a new INSERT query to the server.</remarks>\n        public async Task CommitAsync(CancellationToken cancellationToken)\n        {\n            await EndWrite(TerminationMode.Confirm, closeSession: false, async: true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Closes the writer and releases all resources associated with it.\n        /// </summary>\n        public void EndWrite()\n        {\n            TaskHelper.WaitNonAsyncTask(EndWrite(TerminationMode.Confirm, closeSession: true, false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Notifies the server that non-commited rows shoud be discarded. This method takes an effect\n        /// only if the pervious operation was made in the <see cref=\"ClickHouseTransactionMode.Manual\"/> mode.\n        /// </summary>\n        public void Rollback()\n        {\n            TaskHelper.WaitNonAsyncTask(EndWrite(TerminationMode.Cancel, closeSession: false, async: false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Asyncronously notifies the server that non-commited rows shoud be discarded. This method takes an effect\n        /// only if the pervious operation was made in the <see cref=\"ClickHouseTransactionMode.Manual\"/> mode.\n        /// </summary>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        /// <remarks>A subsequent writing operation will send a new INSERT query to the server.</remarks>\n        public async Task RollbackAsync(CancellationToken cancellationToken)\n        {\n            await EndWrite(TerminationMode.Cancel, closeSession: false, async: true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Asyncronously closes the writer and releases all resources associated with it.\n        /// </summary>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\n        public async Task EndWriteAsync(CancellationToken cancellationToken)\n        {\n            await EndWrite(TerminationMode.Confirm, closeSession: true, true, cancellationToken);\n        }\n\n        private async ValueTask EndWrite(TerminationMode mode, bool closeSession, bool async, CancellationToken cancellationToken)\n        {\n            // If the writer is dispesed the session should also be disposed\n            Debug.Assert(closeSession || mode != TerminationMode.Dispose);\n\n            if (IsClosed)\n                return;\n\n            if (_endOfStream)\n            {\n                if (closeSession)\n                    await _session.Dispose(async);\n\n                return;\n            }\n\n            try\n            {\n                switch (mode)\n                {\n                    case TerminationMode.None:\n                        break;\n                    case TerminationMode.Cancel:\n                    case TerminationMode.Dispose:\n                        await _session.SendCancel(async);\n                        break;\n                    case TerminationMode.Confirm:\n                        await _session.SendTable(ClickHouseEmptyTableWriter.Instance, async, cancellationToken);\n                        break;\n                    default:\n                        Debug.Fail($\"Unexpected termination mode: {mode}.\");\n                        break;\n                }\n\n                bool isProfileEvents;\n                do\n                {\n                    isProfileEvents = false;\n                    var message = await _session.ReadMessage(async, CancellationToken.None);\n                    switch (message.MessageCode)\n                    {\n                        case ServerMessageCode.EndOfStream:\n                            if (closeSession)\n                                await _session.Dispose(async);\n\n                            _endOfStream = true;\n                            break;\n\n                        case ServerMessageCode.Error:\n                            // Connection state can't be resotred if the server raised an exception.\n                            // This error is probably caused by the wrong formatted data.\n                            var exception = ((ServerErrorMessage)message).Exception;\n                            if (mode == TerminationMode.Dispose)\n                            {\n                                await _session.SetFailed(exception, false, async);\n                                break;\n                            }\n\n                            throw exception;\n\n                        case ServerMessageCode.ProfileEvents:\n                            isProfileEvents = true;\n                            var profileEventsMessage = (ServerDataMessage)message;\n                            await _session.SkipTable(profileEventsMessage, async, cancellationToken);\n                            break;\n\n                        case ServerMessageCode.ProfileInfo:\n                            break;\n\n                        case ServerMessageCode.Progress:\n                            var progressMessage = (ServerProgressMessage)message;\n                            ExecutionProgress = progressMessage.ExecutionProgress;\n                            break;\n\n                        default:\n                            throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Unexpected server message: \\\"{message.MessageCode}\\\".\");\n                    }\n                } while (isProfileEvents);\n            }\n            catch (ClickHouseHandledException ex)\n            {\n                if (mode != TerminationMode.Dispose)\n                    throw;\n\n                // Connection state can't be restored\n                await _session.SetFailed(ex.InnerException, false, async);\n            }\n            catch (Exception ex)\n            {\n                var aggrEx = await _session.SetFailed(ex, false, async);\n                if (aggrEx != null)\n                    throw aggrEx;\n\n                throw;\n            }\n        }\n\n        /// <summary>\n        /// Closes the writer and releases all resources associated with it.\n        /// </summary>\n        public void Dispose()\n        {\n            TaskHelper.WaitNonAsyncTask(Dispose(false));\n        }\n\n        /// <summary>\n        /// Asyncronously closes the writer and releases all resources associated with it.\n        /// </summary>\n        /// <returns>A <see cref=\"ValueTask\"/> representing asyncronous operation.</returns>\n        public ValueTask DisposeAsync()\n        {\n            return Dispose(true);\n        }\n\n        private async ValueTask Dispose(bool async)\n        {\n            await EndWrite(TerminationMode.Dispose, closeSession: true, async, CancellationToken.None);\n        }\n\n        internal static async ValueTask<IClickHouseColumnWriterFactory> CreateColumnWriterFactory(ColumnInfo columnInfo, object? column, int columnIndex, int rowCount, ClickHouseColumnSettings? settings, bool async, CancellationToken cancellationToken)\n        {\n            if (settings?.ColumnType == typeof(object))\n            {\n                throw new ClickHouseException(\n                    ClickHouseErrorCodes.InvalidColumnSettings,\n                    $\"Type \\\"{settings.ColumnType}\\\" should not be used as a type of a column. This type is defined in column settings of the column \\\"{columnInfo.Name}\\\" (position {columnIndex}).\");\n            }\n\n            if (column == null)\n            {\n                if (!columnInfo.TypeInfo.TypeName.StartsWith(\"Nullable\"))\n                    throw new ClickHouseException(ClickHouseErrorCodes.ColumnTypeMismatch, $\"The column \\\"{columnInfo.Name}\\\" at the position {columnIndex} doesn't support nulls.\");\n\n                ITypeDispatcher? typeDispatcher;\n                if (settings?.ColumnType != null)\n                {\n                    if (settings.ColumnType.IsValueType && Nullable.GetUnderlyingType(settings.ColumnType) == null)\n                    {\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.ColumnTypeMismatch,\n                            $\"The column \\\"{columnInfo.Name}\\\" (position {columnIndex}) contains null value. But the type of this column defined in the settings (\\\"{settings.ColumnType}\\\") doesn't allow nulls.\");\n                    }\n\n                    typeDispatcher = settings.GetColumnTypeDispatcher();\n                    Debug.Assert(typeDispatcher != null);\n                }\n                else\n                {\n                    typeDispatcher = TypeDispatcher.Create(columnInfo.TypeInfo.GetFieldType());\n                }\n\n                var constColumn = typeDispatcher.Dispatch(new NullColumnWriterDispatcher(columnInfo, settings, rowCount));\n                return constColumn;\n            }\n\n            var columnType = column.GetType();\n            Type? enumerable = null;\n            Type? altEnumerable = null;\n            Type? asyncEnumerable = null;\n            Type? altAsyncEnumerable = null;\n            Type? readOnlyList = null;\n            Type? altReadOnlyList = null;\n            Type? list = null;\n            Type? altList = null;\n            foreach (var ifs in columnType.GetInterfaces())\n            {\n                if (!ifs.IsGenericType)\n                    continue;\n\n                var ifsDefinition = ifs.GetGenericTypeDefinition();\n                if (ifsDefinition == typeof(IEnumerable<>) && ifs.GetGenericArguments()[0] != typeof(object))\n                {\n                    altEnumerable ??= enumerable;\n                    enumerable = ifs;\n                }\n                else if (ifsDefinition == typeof(IAsyncEnumerable<>) && ifs.GetGenericArguments()[0] != typeof(object))\n                {\n                    altAsyncEnumerable = asyncEnumerable;\n                    asyncEnumerable = ifs;\n                }\n                else if (ifsDefinition == typeof(IReadOnlyList<>) && ifs.GetGenericArguments()[0] != typeof(object))\n                {\n                    altReadOnlyList = readOnlyList;\n                    readOnlyList = ifs;\n                }\n                else if (ifsDefinition == typeof(IList<>) && ifs.GetGenericArguments()[0] != typeof(object))\n                {\n                    altList = list;\n                    list = ifs;\n                }\n            }\n\n            /*\n             * All supported interfaces (sorted by priority):\n             * 1. IReadOnlyList<T>\n             * 2. IList<T>\n             * 3. IAsyncEnumerable<T> (supported only in ascynronuous mode, i.e. async == true)\n             * 4. IEnumerable<T>\n             * 5. IEnumerable\n             */\n\n            var explicitTypeDispatcher = settings?.GetColumnTypeDispatcher();\n            IClickHouseColumnWriterFactory? columnWriter;\n            if (explicitTypeDispatcher != null)\n            {\n                Debug.Assert(settings?.ColumnType != null);\n\n                // The type is explicitly specified in the column settings. Either cast the column to a collection\n                // of this type or throw an exception.\n                columnWriter = explicitTypeDispatcher.Dispatch(new ColumnWriterDispatcher(column, columnInfo, settings, rowCount, columnIndex, async));\n                if (columnWriter != null)\n                    return columnWriter;\n\n                if (async && typeof(IAsyncEnumerable<>).MakeGenericType(settings.ColumnType).IsAssignableFrom(columnType))\n                {\n                    columnWriter = await explicitTypeDispatcher.Dispatch(new AsyncColumnWriterDispatcher(column, columnInfo, settings, rowCount, columnIndex, cancellationToken));\n                    return columnWriter;\n                }\n\n                // There is almost no chance that IEnumerable's IEnumerator returns the value of expected type if at least one of interfaces is implemented by the column's type.\n                bool ignoreNonGenericEnumerable = readOnlyList != null || list != null || asyncEnumerable != null || enumerable != null;\n                columnWriter = explicitTypeDispatcher.Dispatch(new ColumnWriterObjectCollectionDispatcher(column, columnInfo, settings, rowCount, columnIndex, async, ignoreNonGenericEnumerable));\n                if (columnWriter != null)\n                    return columnWriter;\n\n                if (async && column is IAsyncEnumerable<object?> aeCol)\n                {\n                    columnWriter = await explicitTypeDispatcher.Dispatch(new AsyncObjectColumnWriterDispatcher(aeCol, columnInfo, settings, rowCount, columnIndex, cancellationToken));\n                    return columnWriter;\n                }\n\n                if (!async)\n                {\n                    Type? aeInterface = typeof(IAsyncEnumerable<>).MakeGenericType(settings.ColumnType);\n                    if (!aeInterface.IsAssignableFrom(settings.ColumnType))\n                    {\n                        aeInterface = column is IAsyncEnumerable<object?> ? typeof(IAsyncEnumerable<object?>) : null;\n                    }\n\n                    if (aeInterface != null)\n                    {\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.NotSupportedInSyncronousMode,\n                            $\"The column \\\"{columnInfo.Name}\\\" at the position {columnIndex} implements interface \\\"{aeInterface}\\\". Call async method \\\"{nameof(WriteTableAsync)}\\\".\");\n                    }\n                }\n\n                throw new ClickHouseException(\n                    ClickHouseErrorCodes.ColumnTypeMismatch,\n                    $\"The column \\\"{columnInfo.Name}\\\" at the position {columnIndex} is not a collection of type \\\"{settings.ColumnType}\\\". This type is defined in the column's settings.\");\n            }\n\n            // Trying to extract an actual type of column's items from an interface implemented by this column.\n            Type dispatchedElementType;\n            if (readOnlyList != null)\n            {\n                if (altReadOnlyList != null)\n                    throw CreateInterfaceAmbiguousException(readOnlyList, altReadOnlyList, columnInfo.Name, columnIndex);\n\n                dispatchedElementType = readOnlyList.GetGenericArguments()[0];\n            }\n            else if (list != null)\n            {\n                if (altList != null)\n                    throw CreateInterfaceAmbiguousException(list, altList, columnInfo.Name, columnIndex);\n\n                dispatchedElementType = list.GetGenericArguments()[0];\n            }\n            else\n            {\n                if (async && asyncEnumerable != null)\n                {\n                    if (altAsyncEnumerable != null)\n                        throw CreateInterfaceAmbiguousException(asyncEnumerable, altAsyncEnumerable, columnInfo.Name, columnIndex);\n\n                    var genericArg = asyncEnumerable.GetGenericArguments()[0];\n                    var asyncDispatcher = new AsyncColumnWriterDispatcher(column, columnInfo, settings, rowCount, columnIndex, cancellationToken);\n                    var asyncColumn = await TypeDispatcher.Dispatch(genericArg, asyncDispatcher);\n                    return asyncColumn;\n                }\n\n                if (enumerable != null)\n                {\n                    if (altEnumerable != null)\n                        throw CreateInterfaceAmbiguousException(enumerable, altEnumerable, columnInfo.Name, columnIndex);\n\n                    dispatchedElementType = enumerable.GetGenericArguments()[0];\n                }\n                else\n                {\n                    // There is still hope that the column implements one of suported interfaces with typeof(T) == typeof(object).\n                    // In this case assume that the type of the table's field is equal to the type of the column.\n\n                    dispatchedElementType = columnInfo.TypeInfo.GetFieldType();\n                    var typeDispatcher = TypeDispatcher.Create(dispatchedElementType);\n                    var objDispatcher = new ColumnWriterObjectCollectionDispatcher(column, columnInfo, settings, rowCount, columnIndex, async);\n                    var objColumnWriter = typeDispatcher.Dispatch(objDispatcher);\n                    if (async && objColumnWriter == null && column is IAsyncEnumerable<object?> aeCol)\n                    {\n                        var asyncDispatcher = new AsyncObjectColumnWriterDispatcher(aeCol, columnInfo, settings, rowCount, columnIndex, cancellationToken);\n                        objColumnWriter = await typeDispatcher.Dispatch(asyncDispatcher);\n                    }\n\n                    if (objColumnWriter == null)\n                    {\n                        if (!async && (asyncEnumerable != null || column is IAsyncEnumerable<object?>))\n                        {\n                            var aeInterface = asyncEnumerable ?? typeof(IAsyncEnumerable<object?>);\n                            throw new ClickHouseException(\n                                ClickHouseErrorCodes.NotSupportedInSyncronousMode,\n                                $\"The column \\\"{columnInfo.Name}\\\" at the position {columnIndex} implements interface \\\"{aeInterface}\\\". Call async method \\\"{nameof(WriteTableAsync)}\\\".\");\n                        }\n\n                        throw new ClickHouseException(ClickHouseErrorCodes.ColumnTypeMismatch, $\"The column \\\"{columnInfo.Name}\\\" at the position {columnIndex} is not a collection.\");\n                    }\n\n                    return objColumnWriter;\n                }\n            }\n\n            var dispatcher = new ColumnWriterDispatcher(column, columnInfo, settings, rowCount, columnIndex, false);\n            columnWriter = TypeDispatcher.Dispatch(dispatchedElementType, dispatcher);\n\n            if (columnWriter == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.ColumnTypeMismatch, $\"The column \\\"{columnInfo.Name}\\\" at the position {columnIndex} is not a collection.\");\n\n            return columnWriter;\n        }\n\n        private static ClickHouseException CreateInterfaceAmbiguousException(Type itf, Type altItf, string columnName, int columnIndex)\n        {\n            return new ClickHouseException(ClickHouseErrorCodes.ColumnTypeMismatch,\n                $\"A type of the column \\\"{columnName}\\\" at the position {columnIndex} is ambiguous. The column implements interfaces \\\"{itf}\\\" and \\\"{altItf}\\\".\");\n        }\n\n        private class ColumnWriterFactory<T> : IClickHouseColumnWriterFactory\n        {\n            private readonly ColumnInfo _columnInfo;\n            private readonly IReadOnlyList<T> _rows;\n            private readonly ClickHouseColumnSettings? _columnSettings;\n\n            public ColumnWriterFactory(ColumnInfo columnInfo, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n            {\n                _columnInfo = columnInfo;\n                _rows = rows;\n                _columnSettings = columnSettings;\n            }\n\n            public IClickHouseColumnWriter Create(int offset, int length)\n            {\n                var slice = _rows.Slice(offset, length);\n                return _columnInfo.TypeInfo.CreateColumnWriter(_columnInfo.Name, slice, _columnSettings);\n            }\n        }\n\n        private class NullColumnWriterDispatcher : ITypeDispatcher<IClickHouseColumnWriterFactory>\n        {\n            private readonly ColumnInfo _columnInfo;\n            private readonly ClickHouseColumnSettings? _columnSettings;\n            private readonly int _rowCount;\n\n            public NullColumnWriterDispatcher(ColumnInfo columnInfo, ClickHouseColumnSettings? columnSettings, int rowCount)\n            {\n                _columnInfo = columnInfo;\n                _columnSettings = columnSettings;\n                _rowCount = rowCount;\n            }\n\n            public IClickHouseColumnWriterFactory Dispatch<T>()\n            {\n                var rows = new ConstantReadOnlyList<T>(default, _rowCount);\n                return new ColumnWriterFactory<T>(_columnInfo, rows, _columnSettings);\n            }\n        }\n\n        private class AsyncColumnWriterDispatcher : ITypeDispatcher<Task<IClickHouseColumnWriterFactory>>\n        {\n            private readonly object _asyncEnumerable;\n            private readonly ColumnInfo _columnInfo;\n            private readonly ClickHouseColumnSettings? _columnSettings;\n            private readonly int _rowCount;\n            private readonly int _columnIndex;\n            private readonly CancellationToken _cancellationToken;\n\n            public AsyncColumnWriterDispatcher(\n                object asyncEnumerable,\n                ColumnInfo columnInfo,\n                ClickHouseColumnSettings? columnSettings,\n                int rowCount,\n                int columnIndex,\n                CancellationToken cancellationToken)\n            {\n                _asyncEnumerable = asyncEnumerable;\n                _columnInfo = columnInfo;\n                _columnSettings = columnSettings;\n                _rowCount = rowCount;\n                _columnIndex = columnIndex;\n                _cancellationToken = cancellationToken;\n            }\n\n            public async Task<IClickHouseColumnWriterFactory> Dispatch<T>()\n            {\n                if (_rowCount == 0)\n                    return new ColumnWriterFactory<T>(_columnInfo, Array.Empty<T>(), _columnSettings);\n\n                ConfiguredCancelableAsyncEnumerable<T>.Enumerator enumerator = default;\n                bool disposeEnumerator = false;\n                try\n                {\n                    enumerator = ((IAsyncEnumerable<T>)_asyncEnumerable).WithCancellation(_cancellationToken).GetAsyncEnumerator();\n                    disposeEnumerator = true;\n\n                    var rows = new T[_rowCount];\n                    for (int i = 0; i < _rowCount; i++)\n                    {\n                        if (!await enumerator.MoveNextAsync())\n                        {\n                            throw new ClickHouseException(\n                                ClickHouseErrorCodes.InvalidRowCount,\n                                $\"The column \\\"{_columnInfo.Name}\\\" at the position {_columnIndex} has only {i} row(s), but the required number of rows is {_rowCount}.\");\n                        }\n\n                        rows[i] = enumerator.Current;\n                    }\n\n                    return new ColumnWriterFactory<T>(_columnInfo, rows, _columnSettings);\n                }\n                finally\n                {\n                    if (disposeEnumerator)\n                        await enumerator.DisposeAsync();\n                }\n            }\n        }\n\n        private class AsyncObjectColumnWriterDispatcher : ITypeDispatcher<Task<IClickHouseColumnWriterFactory>>\n        {\n            private readonly IAsyncEnumerable<object?> _asyncEnumerable;\n            private readonly ColumnInfo _columnInfo;\n            private readonly ClickHouseColumnSettings? _columnSettings;\n            private readonly int _rowCount;\n            private readonly int _columnIndex;\n            private readonly CancellationToken _cancellationToken;\n\n            public AsyncObjectColumnWriterDispatcher(\n                IAsyncEnumerable<object?> asyncEnumerable,\n                ColumnInfo columnInfo,\n                ClickHouseColumnSettings? columnSettings,\n                int rowCount,\n                int columnIndex,\n                CancellationToken cancellationToken)\n            {\n                _asyncEnumerable = asyncEnumerable;\n                _columnInfo = columnInfo;\n                _columnSettings = columnSettings;\n                _rowCount = rowCount;\n                _columnIndex = columnIndex;\n                _cancellationToken = cancellationToken;\n            }\n\n            public async Task<IClickHouseColumnWriterFactory> Dispatch<T>()\n            {\n                if (_rowCount == 0)\n                    return new ColumnWriterFactory<T>(_columnInfo, Array.Empty<T>(), _columnSettings);\n\n                ConfiguredCancelableAsyncEnumerable<object?>.Enumerator enumerator = default;\n                bool disposeEnumerator = false;\n                try\n                {\n                    enumerator = _asyncEnumerable.WithCancellation(_cancellationToken).GetAsyncEnumerator();\n                    disposeEnumerator = true;\n\n                    var rows = new T[_rowCount];\n                    for (int i = 0; i < _rowCount; i++)\n                    {\n                        if (!await enumerator.MoveNextAsync())\n                        {\n                            throw new ClickHouseException(\n                                ClickHouseErrorCodes.InvalidRowCount,\n                                $\"The column \\\"{_columnInfo.Name}\\\" at the position {_columnIndex} has only {i} row(s), but the required number of rows is {_rowCount}.\");\n                        }\n\n                        rows[i] = ColumnWriterObjectCollectionDispatcher.CastTo<T>(enumerator.Current);\n                    }\n\n                    return new ColumnWriterFactory<T>(_columnInfo, rows, _columnSettings);\n                }\n                finally\n                {\n                    if (disposeEnumerator)\n                        await enumerator.DisposeAsync();\n                }\n            }\n        }\n\n        private class ColumnWriterDispatcher : ITypeDispatcher<IClickHouseColumnWriterFactory?>\n        {\n            private readonly object _collection;\n            private readonly ColumnInfo _columnInfo;\n            private readonly ClickHouseColumnSettings? _columnSettings;\n            private readonly int _rowCount;\n            private readonly int _columnIndex;\n            private readonly bool _checkAsyncEnumerable;\n\n            public ColumnWriterDispatcher(\n                object collection,\n                ColumnInfo columnInfo,\n                ClickHouseColumnSettings? columnSettings,\n                int rowCount,\n                int columnIndex,\n                bool checkAsyncEnumerable)\n            {\n                _collection = collection;\n                _columnInfo = columnInfo;\n                _columnSettings = columnSettings;\n                _rowCount = rowCount;\n                _columnIndex = columnIndex;\n                _checkAsyncEnumerable = checkAsyncEnumerable;\n            }\n\n            public IClickHouseColumnWriterFactory? Dispatch<T>()\n            {\n                if (_collection is IReadOnlyList<T> readOnlyList)\n                {\n                    if (readOnlyList.Count < _rowCount)\n                    {\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.InvalidRowCount,\n                            $\"The column \\\"{_columnInfo.Name}\\\" at the position {_columnIndex} has only {readOnlyList.Count} row(s), but the required number of rows is {_rowCount}.\");\n                    }\n\n                    if (readOnlyList.Count > _rowCount)\n                        readOnlyList = readOnlyList.Slice(0, _rowCount);\n\n                    return new ColumnWriterFactory<T>(_columnInfo, readOnlyList, _columnSettings);\n                }\n\n                if (_collection is IList<T> list)\n                {\n                    if (list.Count < _rowCount)\n                    {\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.InvalidRowCount,\n                            $\"The column \\\"{_columnInfo.Name}\\\" at the position {_columnIndex} has only {list.Count} row(s), but the required number of rows is {_rowCount}.\");\n                    }\n\n                    var listSpan = list.Slice(0, _rowCount);\n                    return new ColumnWriterFactory<T>(_columnInfo, listSpan, _columnSettings);\n                }\n\n                if (_checkAsyncEnumerable && _collection is IAsyncEnumerable<T>)\n                {\n                    // Should be handled in async mode\n                    return null;\n                }\n\n                if (_collection is IEnumerable<T> genericEnumerable)\n                {\n                    using var enumerator = genericEnumerable.GetEnumerator();\n\n                    T[] rows = new T[_rowCount];\n                    for (int i = 0; i < _rowCount; i++)\n                    {\n                        if (!enumerator.MoveNext())\n                        {\n                            throw new ClickHouseException(\n                                ClickHouseErrorCodes.InvalidRowCount,\n                                $\"The column \\\"{_columnInfo.Name}\\\" at the position {_columnIndex} has only {i} row(s), but the required number of rows is {_rowCount}.\");\n                        }\n\n                        rows[i] = enumerator.Current;\n                    }\n\n                    return new ColumnWriterFactory<T>(_columnInfo, rows, _columnSettings);\n                }\n\n                return null;\n            }\n        }\n\n        private class ColumnWriterObjectCollectionDispatcher : ITypeDispatcher<IClickHouseColumnWriterFactory?>\n        {\n            private readonly object _collection;\n            private readonly ColumnInfo _columnInfo;\n            private readonly ClickHouseColumnSettings? _columnSettings;\n            private readonly int _rowCount;\n            private readonly int _columnIndex;\n            private readonly bool _checkAsyncEnumerable;\n            private readonly bool _ignoreNonGenericEnumerable;\n\n            public ColumnWriterObjectCollectionDispatcher(\n                object collection,\n                ColumnInfo columnInfo,\n                ClickHouseColumnSettings? columnSettings,\n                int rowCount,\n                int columnIndex,\n                bool checkAsyncEnumerable,\n                bool ignoreNonGenericEnumerable = false)\n            {\n                _collection = collection;\n                _columnInfo = columnInfo;\n                _columnSettings = columnSettings;\n                _rowCount = rowCount;\n                _columnIndex = columnIndex;\n                _checkAsyncEnumerable = checkAsyncEnumerable;\n                _ignoreNonGenericEnumerable = ignoreNonGenericEnumerable;\n            }\n\n            public IClickHouseColumnWriterFactory? Dispatch<T>()\n            {\n                if (_collection is IReadOnlyList<object?> readOnlyList)\n                {\n                    if (readOnlyList.Count < _rowCount)\n                    {\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.InvalidRowCount,\n                            $\"The column \\\"{_columnInfo.Name}\\\" at the position {_columnIndex} has only {readOnlyList.Count} row(s), but the required number of rows is {_rowCount}.\");\n                    }\n\n                    if (readOnlyList.Count > _rowCount)\n                        readOnlyList = readOnlyList.Slice(0, _rowCount);\n                }\n                else if (_collection is IList<object?> list)\n                {\n                    if (list.Count < _rowCount)\n                    {\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.InvalidRowCount,\n                            $\"The column \\\"{_columnInfo.Name}\\\" at the position {_columnIndex} has only {list.Count} row(s), but the required number of rows is {_rowCount}.\");\n                    }\n\n                    readOnlyList = list.Slice(0, _rowCount);\n                }\n                else if (_checkAsyncEnumerable && _collection is IAsyncEnumerable<object?>)\n                {\n                    // Should be handled in async mode\n                    return null;\n                }\n                else if (_collection is IEnumerable<object?> genericEnumerable)\n                {\n                    using var enumerator = genericEnumerable.GetEnumerator();\n\n                    T[] rows = new T[_rowCount];\n                    for (int i = 0; i < _rowCount; i++)\n                    {\n                        if (!enumerator.MoveNext())\n                        {\n                            throw new ClickHouseException(\n                                ClickHouseErrorCodes.InvalidRowCount,\n                                $\"The column \\\"{_columnInfo.Name}\\\" at the position {_columnIndex} has only {i} row(s), but the required number of rows is {_rowCount}.\");\n                        }\n\n                        rows[i] = CastTo<T>(enumerator.Current);\n                    }\n\n                    return new ColumnWriterFactory<T>(_columnInfo, rows, _columnSettings);\n                }\n                else if (!_ignoreNonGenericEnumerable && _collection is IEnumerable enumerable)\n                {\n                    IEnumerator? enumerator = null;\n                    try\n                    {\n                        enumerator = enumerable.GetEnumerator();\n                        T[] rows = new T[_rowCount];\n                        for (int i = 0; i < _rowCount; i++)\n                        {\n                            if (!enumerator.MoveNext())\n                            {\n                                throw new ClickHouseException(\n                                    ClickHouseErrorCodes.InvalidRowCount,\n                                    $\"The column \\\"{_columnInfo.Name}\\\" at the position {_columnIndex} has only {i} row(s), but the required number of rows is {_rowCount}.\");\n                            }\n\n                            rows[i] = CastTo<T>(enumerator.Current);\n                        }\n\n                        return new ColumnWriterFactory<T>(_columnInfo, rows, _columnSettings);\n                    }\n                    finally\n                    {\n                        (enumerator as IDisposable)?.Dispose();\n                    }\n                }\n                else\n                {\n                    // An object is not a collection\n                    return null;\n                }\n\n                var mappedList = readOnlyList.Map(CastTo<T>);\n                return new ColumnWriterFactory<T>(_columnInfo, mappedList, _columnSettings);\n            }\n\n            [MethodImpl(MethodImplOptions.AggressiveInlining)]\n            public static T CastTo<T>(object? value)\n            {\n                // T may be nullable but there is no way to declare T?\n                if (value == DBNull.Value)\n                    return (T)(default(T) is null ? null! : value);\n\n                return (T)value!;\n            }\n        }\n\n        private class SingleRowColumnWriterDispatcher : ITypeDispatcher<IClickHouseColumnWriter>\n        {\n            [AllowNull] private readonly object _value;\n            private readonly ColumnInfo _columnInfo;\n            private readonly ClickHouseColumnSettings? _columnSettings;\n\n            public SingleRowColumnWriterDispatcher(object? value, ColumnInfo columnInfo, ClickHouseColumnSettings? columnSettings)\n            {\n                _value = value;\n                _columnInfo = columnInfo;\n                _columnSettings = columnSettings;\n            }\n\n            public IClickHouseColumnWriter Dispatch<T>()\n            {\n                var rows = new ConstantReadOnlyList<T>((T) _value, 1);\n                return _columnInfo.TypeInfo.CreateColumnWriter(_columnInfo.Name, rows, _columnSettings);\n            }\n        }\n\n        private enum TerminationMode\n        {\n            /// <summary>\n            /// Send nothing and wait for EndOfStream\n            /// </summary>\n            None = 0,\n\n            /// <summary>\n            /// Send Cancel and wait for EndOfStream\n            /// </summary>\n            Cancel = 1,\n\n            /// <summary>\n            /// Send confirmation message (completely empty talbe) and wait for EndOfStream\n            /// </summary>\n            Confirm = 2,\n\n            /// <summary>\n            /// Send Cancel and then release all resources associated with the writer\n            /// </summary>\n            Dispose = 3\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseCommand.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2026 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Types;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents an SQL statement to execute against a ClickHouse database. This class cannot be inherited.\n    /// </summary>\n    public sealed class ClickHouseCommand : DbCommand\n    {\n        private string? _commandText;\n        private TimeSpan? _commandTimeout;\n\n        /// <summary>\n        /// Gets or sets the SQL statement to execute at the data source.\n        /// </summary>\n        [AllowNull]\n        public override string CommandText\n        {\n            get => _commandText ?? string.Empty;\n            set => _commandText = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the wait time (in seconds) before terminating the attempt to execute a command and generating an error.\n        /// </summary>\n        public override int CommandTimeout\n        {\n            get => (int)CommandTimeoutSpan.TotalSeconds;\n            set => CommandTimeoutSpan = TimeSpan.FromSeconds(value);\n        }\n\n        /// <summary>\n        /// Gets or sets the wait time before terminating the attempt to execute a command and generating an error.\n        /// </summary>\n        public TimeSpan CommandTimeoutSpan\n        {\n            get => GetCommandTimeout(Connection);\n            set => _commandTimeout = value;\n        }\n\n        /// <summary>\n        /// Gets the sets type of the command. The only supported type is <see cref=\"CommandType.Text\"/>.\n        /// </summary>\n        /// <returns>The value <see cref=\"CommandType.Text\"/>.</returns>\n        /// <exception cref=\"NotSupportedException\">The type set is not <see cref=\"CommandType.Text\"/>.</exception>\n        public override CommandType CommandType\n        {\n            get => CommandType.Text;\n            set\n            {\n                if (value != CommandType.Text)\n                    throw new NotSupportedException($\"The type of the command \\\"{value}\\\" is not supported.\");                \n            }\n        }\n\n        /// <summary>\n        /// Gets or sets how command results are applied to the <see cref=\"DataRow\"/> when used by the Update method of the <see cref=\"DbDataAdapter\"/>.\n        /// The value of this property is ignored by the command and therefore doesn't affect it's behavior.\n        /// </summary>\n        /// <returns>One of enumeration values that indicates how command results are applied. The default value is <see cref=\"UpdateRowSource.None\"/>.</returns>\n        public override UpdateRowSource UpdatedRowSource { get; set; }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"ClickHouseConnection\"/> used by this command.\n        /// </summary>\n        public new ClickHouseConnection? Connection { get; set; }\n\n        /// <inheritdoc cref=\"Connection\"/>    \n        protected override DbConnection? DbConnection\n        {\n            get => Connection;\n            set => Connection = (ClickHouseConnection?) value;\n        }\n\n        /// <summary>\n        /// Gets the <see cref=\"ClickHouseParameterCollection\"/>.\n        /// </summary>\n        /// <returns>The parameters of the SQL statement. The default is an empty collection.</returns>\n        public new ClickHouseParameterCollection Parameters { get; } = new ClickHouseParameterCollection();\n\n        /// <inheritdoc cref=\"Parameters\"/>    \n        protected sealed override DbParameterCollection DbParameterCollection => Parameters;\n\n        /// <summary>\n        /// Gets the <see cref=\"ClickHouseTableProviderCollection\"/>.\n        /// </summary>\n        /// <returns>\n        /// The tables which should be sent along with the query. The default is an empty collection.\n        /// </returns>\n        public ClickHouseTableProviderCollection TableProviders { get; } = new ClickHouseTableProviderCollection();\n\n        /// <summary>\n        /// Gets or sets the transaction within which the command executes. Always returns <b>null</b>.\n        /// </summary>\n        /// <returns><b>null</b></returns>\n        /// <exception cref=\"NotSupportedException\">The value set is not <b>null</b>.</exception>\n        protected override DbTransaction? DbTransaction\n        {\n            get => null;\n            set\n            {\n                if (value != null)\n                    throw new NotSupportedException($\"{nameof(DbTransaction)} is read only.'\");\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether the command object should be visible in a customized interface control.\n        /// </summary>\n        /// <returns><see langword=\"true\"/>, if the command object should be visible in a control; otherwise <see langword=\"false\"/>. The default is <see langword=\"true\"/>.</returns>\n        public override bool DesignTimeVisible { get; set; } = true;\n\n        /// <summary>\n        /// Gets or sets value indicating whether the query should be executed with an explicitly defined values of the property 'extremes'.\n        /// </summary>\n        public bool? Extremes { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value indicating whether profile events should be ignored while reading data.\n        /// </summary>\n        /// <returns><see langword=\"true\"/>, if the data reader should skip profile events. <see langword=\"false\"/>,\n        /// if the data reader should return profile events as a recordset. The default value is <see langword=\"true\"/>.</returns>\n        public bool IgnoreProfileEvents { get; set; } = true;\n\n        /// <summary>\n        /// Gets or sets the mode of passing parameters to the query. The value of this property overrides <see cref=\"ClickHouseConnection.ParametersMode\"/>.\n        /// </summary>\n        /// <returns>The mode of passing parameters to the query. The default value is <see cref=\"ClickHouseParameterMode.Inherit\"/>.</returns>\n        public ClickHouseParameterMode ParametersMode { get; set; } = ClickHouseParameterMode.Inherit;\n\n        /// <summary>\n        /// Optional. Gets or sets the identifier passed along with the query as the query ID.\n        /// Usually, a query identifier is generated by the sever, setting it on the client side is not required.\n        /// </summary>\n        public string? QueryId { get; set; }\n\n        /// <summary>\n        /// Creates a new instance of <see cref=\"ClickHouseCommand\"/>.\n        /// </summary>\n        public ClickHouseCommand()\n        {\n        }\n\n        internal ClickHouseCommand(ClickHouseConnection connection)\n        {\n            Connection = connection ?? throw new ArgumentNullException(nameof(connection));\n        }\n\n        /// <summary>\n        /// Not supported. To cancel a command execute it asyncronously with an appropriate cancellation token.\n        /// </summary>\n        /// <exception cref=\"NotImplementedException\">Always throws <see cref=\"NotImplementedException\"/>.</exception>\n        public override void Cancel()\n        {\n            throw new NotImplementedException();\n        }\n\n        /// <summary>\n        /// Executes a SQL statement against a connection object.\n        /// </summary>\n        /// <returns>The number of rows affected. The returned value is negative when the actual number of rows is greater than <see cref=\"int.MaxValue\"/>.</returns>\n        public override int ExecuteNonQuery()\n        {\n            var result = TaskHelper.WaitNonAsyncTask(ExecuteNonQuery(false, CancellationToken.None));\n\n            if (result > int.MaxValue)\n                return int.MinValue;\n\n            return (int) result;\n        }\n\n        /// <summary>\n        /// Executes a SQL statement against a connection object asyncronously.\n        /// </summary>\n        /// <param name=\"cancellationToken\"></param>\n        /// <returns>\n        /// A <see cref=\"Task{TResult}\"/> representing the asynchronous operation. The result (<see cref=\"Task{TResult}.Result\"/>) is the \n        /// number of affected rows. The result is negative when the actual number of rows is greater than <see cref=\"int.MaxValue\"/>.\n        /// </returns>\n        public override async Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken)\n        {\n            var result = await ExecuteNonQuery(true, cancellationToken);\n            \n            if (result > int.MaxValue)\n                return int.MinValue;\n\n            return (int) result;\n        }\n\n        private async ValueTask<ulong> ExecuteNonQuery(bool async, CancellationToken cancellationToken)\n        {\n            ClickHouseTcpClient.Session? session = null;\n\n            bool cancelOnFailure = false;            \n            try\n            {\n                session = await OpenSession(false, async, cancellationToken);\n                var query = await SendQuery(session, CommandBehavior.Default, async, cancellationToken);\n\n                cancelOnFailure = true;\n                (ulong read, ulong written) result = (0, 0), progress = (0, 0);\n                while (true)\n                {\n                    var message = await session.ReadMessage(async, cancellationToken);\n                    switch (message.MessageCode)\n                    {\n                        case ServerMessageCode.Data:\n                        case ServerMessageCode.Totals:\n                        case ServerMessageCode.Extremes:\n                            var dataMessage = (ServerDataMessage) message;\n                            var blockHeader = await session.SkipTable(dataMessage, async, cancellationToken);\n                            if (blockHeader.Columns.Count == 0 && blockHeader.RowCount == 0)\n                            {\n                                result = (result.read + progress.read, result.written + progress.written);\n                                progress = (0, 0);\n                            }\n                            continue;\n\n                        case ServerMessageCode.ProfileEvents:\n                            var profileEventsMessage = (ServerDataMessage)message;\n                            await session.SkipTable(profileEventsMessage, async, cancellationToken);\n                            continue;\n\n                        case ServerMessageCode.Error:\n                            throw ((ServerErrorMessage) message).Exception.CopyWithQuery(query);\n\n                        case ServerMessageCode.EndOfStream:\n                            result = (result.read + progress.read, result.written + progress.written);\n                            await session.Dispose(async);\n\n                            if (result.written > 0)\n                            {\n                                // INSERT command could also return the number of parsed rows. Return only the number of inserted rows.\n                                return result.written;\n                            }\n\n                            return result.read;\n\n                        case ServerMessageCode.Progress:\n                            var progressMessage = (ServerProgressMessage) message;\n                            progress = (progressMessage.ExecutionProgress.Rows, progressMessage.ExecutionProgress.WrittenRows);\n                            continue;\n\n                        case ServerMessageCode.ProfileInfo:\n                            continue;\n\n                        case ServerMessageCode.Pong:\n                        case ServerMessageCode.Hello:\n                        case ServerMessageCode.Log:\n                            throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Unexpected server message: \\\"{message.MessageCode}\\\".\");\n\n                        default:\n                            throw new NotSupportedException($\"Internal error. Message code \\\"{message.MessageCode}\\\" not supported.\");\n                    }\n                }\n            }\n            catch (ClickHouseHandledException ex)\n            {\n                // Exception can't be handled at this level\n                if (session != null)\n                {\n                    var aggrEx = await session.SetFailed(ex.InnerException, cancelOnFailure, async);\n                    if (aggrEx != null)\n                        throw aggrEx;\n                }\n\n                throw;\n            }\n            catch (Exception ex)\n            {\n                if (session != null)\n                {\n                    var aggrEx = await session.SetFailed(ex, cancelOnFailure, async);\n                    if (aggrEx != null)\n                        throw aggrEx;\n                }\n\n                throw;\n            }\n            finally\n            {\n                if (session != null)\n                    await session.Dispose(async);\n            }\n        }\n\n        /// <summary>\n        /// Executes the query and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <returns>\n        /// The first row of the first columns in the result set or <see cref=\"DBNull.Value\"/> if the result set is empty.\n        /// </returns>\n        public override object ExecuteScalar()\n        {\n            return TaskHelper.WaitNonAsyncTask(ExecuteScalar(null, false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Executes the query and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <param name=\"columnSettings\">Optional parameter. Settings for the first column in the result set.</param>\n        /// <returns>\n        /// The first row of the first columns in the result set or <see cref=\"DBNull.Value\"/> if the result set is empty.\n        /// </returns>\n        public object ExecuteScalar(ClickHouseColumnSettings? columnSettings)\n        {\n            return TaskHelper.WaitNonAsyncTask(ExecuteScalar(columnSettings, false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Executes the query and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <typeparam name=\"T\">The expected type of the first column in the result set.</typeparam>\n        /// <returns>\n        /// The first row of the first columns in the result set.\n        /// </returns>\n        public T ExecuteScalar<T>()\n        {\n            return TaskHelper.WaitNonAsyncTask(ExecuteScalar<T>(null, false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Executes the query and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <typeparam name=\"T\">The expected type of the first column in the result set.</typeparam>\n        /// <param name=\"columnSettings\">Optional parameter. Settings for the first column in the result set.</param>\n        /// <returns>\n        /// The first row of the first columns in the result set.\n        /// </returns>\n        public T ExecuteScalar<T>(ClickHouseColumnSettings? columnSettings)\n        {\n            return TaskHelper.WaitNonAsyncTask(ExecuteScalar<T>(columnSettings, false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>\n        /// A <see cref=\"Task{TResult}\"/> representing the asynchronous operation. The result (<see cref=\"Task{TResult}.Result\"/>) is\n        /// the first row of the first columns in the result set or <see cref=\"DBNull.Value\"/> if the result set is empty.\n        /// </returns>\n        public override async Task<object?> ExecuteScalarAsync(CancellationToken cancellationToken)\n        {\n            return await ExecuteScalar(null, true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <param name=\"columnSettings\">Optional parameter. Settings for the first column in the result set.</param>\n        /// <returns>\n        /// A <see cref=\"Task{TResult}\"/> representing the asynchronous operation. The result (<see cref=\"Task{TResult}.Result\"/>) is\n        /// the first row of the first columns in the result set or <see cref=\"DBNull.Value\"/> if the result set is empty.\n        /// </returns>\n        public async Task<object> ExecuteScalarAsync(ClickHouseColumnSettings? columnSettings)\n        {\n            return await ExecuteScalar(columnSettings, true, CancellationToken.None);\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <param name=\"columnSettings\">Optional parameter. Settings for the first column in the result set.</param>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>\n        /// A <see cref=\"Task{TResult}\"/> representing the asynchronous operation. The result (<see cref=\"Task{TResult}.Result\"/>) is\n        /// the first row of the first columns in the result set or <see cref=\"DBNull.Value\"/> if the result set is empty.\n        /// </returns>\n        public async Task<object> ExecuteScalarAsync(ClickHouseColumnSettings? columnSettings, CancellationToken cancellationToken)\n        {\n            return await ExecuteScalar(columnSettings, true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <typeparam name=\"T\">The expected type of the first column in the result set.</typeparam>\n        /// <returns>\n        /// A <see cref=\"Task{TResult}\"/> representing the asynchronous operation. The result (<see cref=\"Task{TResult}.Result\"/>) is\n        /// the first row of the first columns in the result set.\n        /// </returns>\n        public async Task<T> ExecuteScalarAsync<T>()\n        {\n            return await ExecuteScalar<T>(null, true, CancellationToken.None);\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <typeparam name=\"T\">The expected type of the first column in the result set.</typeparam>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>\n        /// A <see cref=\"Task{TResult}\"/> representing the asynchronous operation. The result (<see cref=\"Task{TResult}.Result\"/>) is\n        /// the first row of the first columns in the result set.\n        /// </returns>\n        public async Task<T> ExecuteScalarAsync<T>(CancellationToken cancellationToken)\n        {\n            return await ExecuteScalar<T>(null, true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <typeparam name=\"T\">The expected type of the first column in the result set.</typeparam>\n        /// <param name=\"columnSettings\">Optional parameter. Settings for the first column in the result set.</param>\n        /// <returns>\n        /// A <see cref=\"Task{TResult}\"/> representing the asynchronous operation. The result (<see cref=\"Task{TResult}.Result\"/>) is\n        /// the first row of the first columns in the result set.\n        /// </returns>\n        public async Task<T> ExecuteScalarAsync<T>(ClickHouseColumnSettings? columnSettings)\n        {\n            return await ExecuteScalar<T>(columnSettings, true, CancellationToken.None);\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and returns the first column of the first row in the result set returned by the query.\n        /// All other columns and rows are ignored.\n        /// </summary>\n        /// <typeparam name=\"T\">The expected type of the first column in the result set.</typeparam>\n        /// <param name=\"columnSettings\">Optional parameter. Settings for the first column in the result set.</param>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>\n        /// A <see cref=\"Task{TResult}\"/> representing the asynchronous operation. The result (<see cref=\"Task{TResult}.Result\"/>) is\n        /// the first row of the first columns in the result set.\n        /// </returns>\n        public async Task<T> ExecuteScalarAsync<T>(ClickHouseColumnSettings? columnSettings, CancellationToken cancellationToken)\n        {\n            return await ExecuteScalar<T>(columnSettings, true, cancellationToken);\n        }\n\n        private async ValueTask<T> ExecuteScalar<T>(ClickHouseColumnSettings? columnSettings, bool async, CancellationToken cancellationToken)\n        {\n            var result = await ExecuteScalar(columnSettings, reader => reader.GetFieldValue<T>(0), async, cancellationToken);\n            return (T) result;\n        }\n\n        private ValueTask<object> ExecuteScalar(ClickHouseColumnSettings? columnSettings, bool async, CancellationToken cancellationToken)\n        {\n            return ExecuteScalar(columnSettings, reader => reader.GetValue(0), async, cancellationToken);\n        }\n\n        private async ValueTask<object> ExecuteScalar(ClickHouseColumnSettings? columnSettings, Func<ClickHouseDataReader, object?> valueSelector, bool async, CancellationToken cancellationToken)\n        {\n            ClickHouseDataReader? reader = null;\n            try\n            {\n                reader = await ExecuteDbDataReader(CommandBehavior.Default, true, async, cancellationToken);\n                bool hasAnyColumn = reader.FieldCount > 0;\n                if (!hasAnyColumn)\n                    return DBNull.Value;\n\n                if (columnSettings != null)\n                    reader.ConfigureColumn(0, columnSettings);\n\n                bool hasAnyRow = async ? await reader.ReadAsync(cancellationToken) : reader.Read();\n                if (!hasAnyRow)\n                    return DBNull.Value;\n\n                if (reader.IsDBNull(0))\n                    return DBNull.Value;\n\n                var result = valueSelector(reader);\n\n                if (async)\n                    await reader.CloseAsync();\n                else\n                    reader.Close();\n\n                return result ?? DBNull.Value;\n            }\n            finally\n            {\n                if (async)\n                {\n                    if (reader != null)\n                        await reader.DisposeAsync();\n                }\n                else\n                {\n                    reader?.Dispose();\n                }\n            }\n        }\n\n        /// <summary>\n        /// Not supported. A preparation of the command is not implemented.\n        /// </summary>\n        /// <exception cref=\"NotImplementedException\">Always throws <see cref=\"NotImplementedException\"/>.</exception>\n        public override void Prepare()\n        {\n            throw new NotImplementedException();\n        }\n\n        /// <inheritdoc cref=\"Prepare\"/>\n        public override Task PrepareAsync(CancellationToken cancellationToken = new CancellationToken())\n        {\n            throw new NotImplementedException();\n        }\n\n        /// <summary>\n        /// Creates a new <see cref=\"ClickHouseParameter\"/> object with the default name and adds it to the collection of parameters (<see cref=\"Parameters\"/>).\n        /// </summary>\n        /// <returns>A new <see cref=\"ClickHouseParameter\"/> object.</returns>\n        protected override DbParameter CreateDbParameter()\n        {\n            const string baseParamName = \"param\";\n            int i = 0;\n            string paramName;\n            do\n            {\n                paramName = string.Format(CultureInfo.InvariantCulture, \"{{{0}{1}}}\", baseParamName, ++i);\n            } while (Parameters.Contains(paramName));\n\n            return new ClickHouseParameter(paramName);\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and builds a <see cref=\"ClickHouseDataReader\"/> with the default command behavior.\n        /// </summary>\n        /// <returns>A <see cref=\"Task{TResult}\"/> representing the asynchronous operation.</returns>\n        public new async Task<ClickHouseDataReader> ExecuteReaderAsync()\n        {\n            return await ExecuteDbDataReader(CommandBehavior.Default, IgnoreProfileEvents, true, CancellationToken.None);\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and builds a <see cref=\"ClickHouseDataReader\"/> with the default command behavior.\n        /// </summary>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task{TResult}\"/> representing the asynchronous operation.</returns>\n        public new async Task<ClickHouseDataReader> ExecuteReaderAsync(CancellationToken cancellationToken)\n        {\n            return await ExecuteDbDataReader(CommandBehavior.Default, IgnoreProfileEvents, true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and builds a <see cref=\"ClickHouseDataReader\"/>.\n        /// </summary>\n        /// <param name=\"behavior\">\n        /// The set of flags determining the behavior of the command.\n        /// The flag <see cref=\"CommandBehavior.KeyInfo\"/> is not supported.\n        /// The flag <see cref=\"CommandBehavior.SequentialAccess\"/> is ignored.\n        /// </param>\n        /// <returns>A <see cref=\"Task{TResult}\"/> representing the asynchronous operation.</returns>\n        public new async Task<ClickHouseDataReader> ExecuteReaderAsync(CommandBehavior behavior)\n        {\n            return await ExecuteDbDataReader(behavior, IgnoreProfileEvents, true, CancellationToken.None);\n        }\n\n        /// <summary>\n        /// Executes the query asyncronously and builds a <see cref=\"ClickHouseDataReader\"/>.\n        /// </summary>\n        /// <param name=\"behavior\">\n        /// The set of flags determining the behavior of the command.\n        /// The flag <see cref=\"CommandBehavior.KeyInfo\"/> is not supported.\n        /// The flag <see cref=\"CommandBehavior.SequentialAccess\"/> is ignored.\n        /// </param>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>A <see cref=\"Task{TResult}\"/> representing the asynchronous operation.</returns>\n        public new async Task<ClickHouseDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)\n        {\n            return await ExecuteDbDataReader(behavior, IgnoreProfileEvents, true, cancellationToken);\n        }\n\n        /// <inheritdoc cref=\"ExecuteReaderAsync(CommandBehavior, CancellationToken)\"/>\n        protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)\n        {\n            return await ExecuteDbDataReader(behavior, IgnoreProfileEvents, true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Executes the query and builds a <see cref=\"ClickHouseDataReader\"/> with the default command behavior.\n        /// </summary>\n        /// <returns>A <see cref=\"ClickHouseDataReader\"/> object.</returns>\n        public new ClickHouseDataReader ExecuteReader()\n        {\n            return TaskHelper.WaitNonAsyncTask(ExecuteDbDataReader(CommandBehavior.Default, IgnoreProfileEvents, false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Executes the query and builds a <see cref=\"ClickHouseDataReader\"/>.\n        /// </summary>\n        /// <param name=\"behavior\">\n        /// The set of flags determining the behavior of the command.\n        /// The flag <see cref=\"CommandBehavior.KeyInfo\"/> is not supported.\n        /// The flag <see cref=\"CommandBehavior.SequentialAccess\"/> is ignored.\n        /// </param>\n        /// <returns>A <see cref=\"ClickHouseDataReader\"/> object.</returns>\n        public new ClickHouseDataReader ExecuteReader(CommandBehavior behavior)\n        {\n            return TaskHelper.WaitNonAsyncTask(ExecuteDbDataReader(behavior, IgnoreProfileEvents, false, CancellationToken.None));\n        }\n\n        /// <inheritdoc cref=\"ExecuteReader(CommandBehavior)\"/>\n        protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)\n        {\n            return TaskHelper.WaitNonAsyncTask(ExecuteDbDataReader(behavior, IgnoreProfileEvents, false, CancellationToken.None));\n        }\n\n        private async ValueTask<ClickHouseDataReader> ExecuteDbDataReader(CommandBehavior behavior, bool ignoreProfileEvents, bool async, CancellationToken cancellationToken)\n        {\n            const CommandBehavior knownBehaviorFlags =\n                CommandBehavior.CloseConnection |\n                CommandBehavior.KeyInfo |\n                CommandBehavior.SchemaOnly |\n                CommandBehavior.SequentialAccess |\n                CommandBehavior.SingleResult |\n                CommandBehavior.SingleRow;\n\n            var unknownBehaviorFlags = behavior & ~knownBehaviorFlags;\n            if (unknownBehaviorFlags != 0)\n                throw new ArgumentException($\"Command behavior has unknown flags ({unknownBehaviorFlags}).\", nameof(behavior));\n\n            if (behavior.HasFlag(CommandBehavior.KeyInfo))\n            {\n                throw new ArgumentException(\n                    $\"Command behavior has unsupported flag {nameof(CommandBehavior.KeyInfo)}.\" + Environment.NewLine +\n                    \"Please, report an issue if you have any idea of how this flag should affect the result (https://github.com/Octonica/ClickHouseClient/issues).\",\n                    nameof(behavior));\n            }\n\n            ClickHouseDataReaderRowLimit rowLimit;\n            if (behavior.HasFlag(CommandBehavior.SchemaOnly))\n            {\n                if (behavior.HasFlag(CommandBehavior.SingleRow))\n                    throw new ArgumentException($\"Command behavior's flags {nameof(CommandBehavior.SchemaOnly)} and {nameof(CommandBehavior.SingleRow)} are mutualy exclusive.\", nameof(behavior));\n\n                rowLimit = ClickHouseDataReaderRowLimit.Zero;\n            }\n            else if (behavior.HasFlag(CommandBehavior.SingleRow))\n            {\n                rowLimit = ClickHouseDataReaderRowLimit.OneRow;\n            }\n            else if (behavior.HasFlag(CommandBehavior.SingleResult))\n            {\n                rowLimit = ClickHouseDataReaderRowLimit.OneResult;\n            }\n            else\n            {\n                rowLimit = ClickHouseDataReaderRowLimit.Infinite;\n            }\n\n            ClickHouseTcpClient.Session? session = null;\n            bool cancelOnFailure = false;\n            try\n            {\n                session = await OpenSession(behavior.HasFlag(CommandBehavior.CloseConnection), async, cancellationToken);\n                var query = await SendQuery(session, behavior, async, cancellationToken);\n\n                cancelOnFailure = true;\n                bool isProfileEvents;\n                IServerMessage message;\n                var executionProgress = new ClickHouseQueryExecutionProgress();\n                do\n                {\n                    isProfileEvents = false;\n                    message = await session.ReadMessage(async, cancellationToken);\n                    switch (message.MessageCode)\n                    {\n                        case ServerMessageCode.Data:\n                            break;\n\n                        case ServerMessageCode.Error:\n                            throw ((ServerErrorMessage)message).Exception.CopyWithQuery(query);\n\n                        case ServerMessageCode.ProfileEvents:\n                            isProfileEvents = true;\n                            var dataMessage = (ServerDataMessage)message;\n                            await session.SkipTable(dataMessage, async, CancellationToken.None);\n                            break;\n\n                        case ServerMessageCode.EndOfStream:\n                            // The query was executed without errors but returned no data. Highly likely it was a DDL query. Closing the session.\n                            await session.Dispose(async);\n                            return new ClickHouseDataReader(session);\n\n                        case ServerMessageCode.Progress:\n                            var progressMessage = (ServerProgressMessage)message;\n                            executionProgress = progressMessage.ExecutionProgress;\n                            break;\n\n                        case ServerMessageCode.ProfileInfo:\n                        case ServerMessageCode.Pong:\n                            continue;\n\n                        case ServerMessageCode.Totals:\n                        case ServerMessageCode.Extremes:\n                            throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, \"Received unexpected totals or extremes before the main dataset.\");\n\n                        default:\n                            throw new ClickHouseException(ClickHouseErrorCodes.QueryTypeMismatch, \"There is no table in the server's response.\");\n                    }\n                }\n                while (!(message is ServerDataMessage) || isProfileEvents);\n\n                var firstTable = await session.ReadTable((ServerDataMessage) message, null, async, cancellationToken);\n                if (rowLimit == ClickHouseDataReaderRowLimit.Zero)\n                    await session.SendCancel(async);\n\n                return new ClickHouseDataReader(firstTable, session, executionProgress, rowLimit, ignoreProfileEvents);\n            }\n            catch (ClickHouseHandledException)\n            {\n                if (session != null)\n                    await session.Dispose(async);\n\n                throw;\n            }\n            catch (ClickHouseServerException)\n            {\n                if (session != null)\n                    await session.Dispose(async);\n\n                throw;\n            }\n            catch (Exception ex)\n            {\n                Exception? aggrEx = null;\n                if (session != null)\n                    aggrEx = await session.SetFailed(ex, cancelOnFailure, async);\n\n                if (aggrEx != null)\n                    throw aggrEx;\n\n                throw;\n            }\n        }\n\n        private async ValueTask<string> SendQuery(ClickHouseTcpClient.Session session, CommandBehavior behavior, bool async, CancellationToken cancellationToken)\n        {\n            string commandText;\n            List<IClickHouseTableWriter>? tableWriters = null;\n            Dictionary<string, ClickHouseParameterWriter>? parameterWriters;\n\n            try\n            {\n                var parametersTable = $\"_{Guid.NewGuid():N}\";\n                commandText = PrepareCommandText(session.TypeInfoProvider, parametersTable, out var binaryParameters, out parameterWriters);\n\n                if (binaryParameters != null && binaryParameters.Count > 0)\n                {\n                    var paramTableWriter = CreateParameterTableWriter(session.TypeInfoProvider, parametersTable, binaryParameters);\n                    tableWriters = new List<IClickHouseTableWriter>(TableProviders.Count + 1) { paramTableWriter };\n                }\n                if (TableProviders.Count > 0)\n                {\n                    tableWriters ??= new List<IClickHouseTableWriter>(TableProviders.Count);\n                    foreach (var tableProvider in TableProviders)\n                    {\n                        if (tableProvider.ColumnCount == 0)\n                            continue;\n\n                        var tableWriter = await CreateTableWriter(tableProvider, session.TypeInfoProvider, async, cancellationToken);\n                        tableWriters.Add(tableWriter);\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                throw ClickHouseHandledException.Wrap(ex);\n            }\n\n            List<KeyValuePair<string, string>>? setting = null;\n            if (Extremes != null)\n            {\n                setting = new List<KeyValuePair<string, string>>(1) {new KeyValuePair<string, string>(\"extremes\", Extremes.Value ? \"1\" : \"0\")};\n            }\n\n            if (session.ServerInfo.Revision >= ClickHouseProtocolRevisions.MinRevisionWithSettingsSerializedAsStrings)\n            {\n                if (behavior.HasFlag(CommandBehavior.SchemaOnly) || behavior.HasFlag(CommandBehavior.SingleRow))\n                {\n                    // https://github.com/ClickHouse/ClickHouse/blob/master/src/Core/Settings.h\n                    // This settings are hints for the server. The result may contain more than one row.\n\n                    setting ??= new List<KeyValuePair<string, string>>(2);\n                    setting.Add(new KeyValuePair<string, string>(\"max_result_rows\", \"1\"));\n                    setting.Add(new KeyValuePair<string, string>(\"result_overflow_mode\", \"break\"));\n                }\n            }\n\n            var messageBuilder = new ClientQueryMessage.Builder { QueryKind = QueryKind.InitialQuery, QueryId = QueryId, Query = commandText, Settings = setting, Parameters = parameterWriters };\n            await session.SendQuery(messageBuilder, tableWriters, async, cancellationToken);\n\n            return commandText;\n        }\n\n        private async ValueTask<ClickHouseTcpClient.Session> OpenSession(bool closeConnection, bool async, CancellationToken cancellationToken)\n        {\n            var connection = Connection;\n            if (connection == null)\n                throw new InvalidOperationException(\"The connection is not set. The command can't be executed without a connection.\");\n\n            SessionResources? resources = null;\n            try\n            {\n                var timeout = GetCommandTimeout(connection);\n                CancellationTokenSource? sessionTokenSource = null;\n                if (timeout > TimeSpan.Zero)\n                    sessionTokenSource = new CancellationTokenSource(timeout);\n\n                if (closeConnection || sessionTokenSource != null)\n                    resources = new SessionResources(closeConnection ? connection : null, sessionTokenSource);\n                \n                return await connection.OpenSession(async, resources, sessionTokenSource?.Token ?? CancellationToken.None, cancellationToken);\n            }\n            catch\n            {\n                if (resources != null)\n                    await resources.Release(async);\n\n                throw;\n            }\n        }\n\n        private TimeSpan GetCommandTimeout(ClickHouseConnection? connection)\n        {\n            return _commandTimeout ?? connection?.CommandTimeSpan ?? TimeSpan.FromSeconds(ClickHouseConnectionStringBuilder.DefaultCommandTimeout);\n        }\n\n        private ClickHouseParameterMode GetParametersMode(ClickHouseConnection? connection)\n        {\n            var mode = ParametersMode;\n            if (mode != ClickHouseParameterMode.Inherit)\n                return mode;\n\n            return connection?.ParametersMode ?? ClickHouseParameterMode.Default;\n        }\n\n        private IClickHouseTableWriter CreateParameterTableWriter(IClickHouseTypeInfoProvider typeInfoProvider, string tableName, HashSet<string> parameters)\n        {\n            return new ClickHouseTableWriter(tableName, 1, Parameters.Where(p => parameters.Contains(p.Id)).Select(p => p.CreateParameterColumnWriter(typeInfoProvider)));\n        }\n\n        private static async ValueTask<IClickHouseTableWriter> CreateTableWriter(IClickHouseTableProvider tableProvider, IClickHouseTypeInfoProvider typeInfoProvider, bool async, CancellationToken cancellationToken)\n        {\n            var factories = new List<IClickHouseColumnWriterFactory>(tableProvider.ColumnCount);\n\n            var rowCount = tableProvider.RowCount;\n            for (int i = 0; i < tableProvider.ColumnCount; i++)\n            {\n                var columnDescriptor = tableProvider.GetColumnDescriptor(i);\n                var typeInfo = typeInfoProvider.GetTypeInfo(columnDescriptor);\n                var columnInfo = new ColumnInfo(columnDescriptor.ColumnName, typeInfo);\n                var column = tableProvider.GetColumn(i);\n\n                var factory = await ClickHouseColumnWriter.CreateColumnWriterFactory(columnInfo, column, i, rowCount, columnDescriptor.Settings, async, cancellationToken);\n                factories.Add(factory);\n            }\n\n            return new ClickHouseTableWriter(tableProvider.TableName, rowCount, factories.Select(f => f.Create(0, rowCount)));\n        }\n\n        private string PrepareCommandText(IClickHouseTypeInfoProvider typeInfoProvider, string parametersTable, out HashSet<string>? binaryParameters, out Dictionary<string, ClickHouseParameterWriter>? parameterWriters)\n        {\n            var query = CommandText;\n            if (string.IsNullOrEmpty(query))\n                throw new InvalidOperationException(\"Command text is not defined.\");\n\n            var parameterPositions = GetParameterPositions(query);\n            binaryParameters = null;\n            parameterWriters = null;\n            if (parameterPositions.Count == 0)\n                return query;\n\n            var inheritParameterMode = GetParametersMode(Connection);\n            var queryStringBuilder = new StringBuilder(query.Length);\n            for (int i = 0; i < parameterPositions.Count; i++)\n            {\n                var (offset, length, typeSeparatorIdx) = parameterPositions[i];\n\n                var start = i > 0 ? parameterPositions[i - 1].offset + parameterPositions[i - 1].length : 0;\n                queryStringBuilder.Append(query, start, parameterPositions[i].offset - start);\n\n                var parameterName = typeSeparatorIdx < 0 ? query.Substring(offset, length) : query.Substring(offset + 1, typeSeparatorIdx - 1);\n                if (!Parameters.TryGetValue(parameterName, out var parameter))\n                    throw new ClickHouseException(ClickHouseErrorCodes.QueryParameterNotFound, $\"Parameter \\\"{parameterName}\\\" not found.\");\n\n                if (typeSeparatorIdx >= 0)\n                    queryStringBuilder.Append(\"(CAST(\");\n\n                var parameterMode = parameter.GetParameterMode(inheritParameterMode);\n                switch (parameterMode)\n                {\n                    case ClickHouseParameterMode.Interpolate:\n                    {\n                        var parameterWriter = parameter.CreateParameterWriter(typeInfoProvider);\n                        parameterWriter.Interpolate(queryStringBuilder.Append(' ')).Append(' ');\n                        break;\n                    }\n\n                    case ClickHouseParameterMode.Default:\n                    case ClickHouseParameterMode.Binary:\n                        binaryParameters ??= new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n                        binaryParameters.Add(parameter.Id);\n                        queryStringBuilder.Append(\"(SELECT \").Append(parametersTable).Append('.').Append(parameter.Id).Append(\" FROM \").Append(parametersTable).Append(')');\n                        break;\n\n                    case ClickHouseParameterMode.Serialize:\n                    {\n                        parameterWriters ??= new Dictionary<string, ClickHouseParameterWriter>(StringComparer.OrdinalIgnoreCase);\n                        if (!parameterWriters.TryGetValue(parameter.Id, out var parameterWriter))\n                        {\n                            parameterWriter = parameter.CreateParameterWriter(typeInfoProvider);\n                            parameterWriters.Add(parameter.Id, parameterWriter);\n                        }\n\n                        parameterWriter.Interpolate(\n                            queryStringBuilder,\n                            typeInfoProvider,\n                            (qb, tp) => qb.Append('{').Append(parameter.Id).Append(':').Append(tp.ComplexTypeName).Append('}'));\n\n                        break;\n                    }\n\n                    default:\n                        throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. Unexpected parameter mode: {parameterMode}.\");\n                }\n\n                if (typeSeparatorIdx >= 0)\n                    queryStringBuilder.Append(\" AS \").Append(query, offset + typeSeparatorIdx + 1, length - typeSeparatorIdx - 2).Append(\"))\");\n            }\n\n            var lastPartStart = parameterPositions[^1].offset + parameterPositions[^1].length;\n            queryStringBuilder.Append(query, lastPartStart, query.Length - lastPartStart);\n\n            return queryStringBuilder.ToString();\n        }\n\n        private static List<(int offset, int length, int typeSeparatorIdx)> GetParameterPositions(string query)\n        {\n            // Searching parameters outside of comments, string literals and escaped identifiers\n            // https://github.com/ClickHouse/ClickHouse/blob/master/docs/en/query_language/syntax.md\n\n            var identifierRegex = new Regex(@\"^[a-zA-Z_][0-9a-zA-Z_]*(\\:.+)?$\");\n            var simpleIdentifierRegex = new Regex(\"^[a-zA-Z_][0-9a-zA-Z_]*\");\n            ReadOnlySpan<char> significantChars = stackalloc char[7] { '-', '\\'', '\"', '`', '/', '{' , '@' };\n            ReadOnlySpan<char> querySlice = query;\n\n            var parameterPositions = new List<(int offset, int length, int typeSeparatorIdx)>();\n\n            int position = 0;\n            while (!querySlice.IsEmpty)\n            {\n                var idx = querySlice.IndexOfAny(significantChars);\n                if (idx < 0)\n                    break;\n\n                var ch = querySlice[idx];\n\n                position += idx + 1;\n                querySlice = querySlice.Slice(idx + 1);\n                if (querySlice.IsEmpty)\n                    break;\n\n                switch (ch)\n                {\n                    case '-':\n                        if (querySlice[0] != '-')\n                            break;\n\n                        var endOfCommentIdx = querySlice.IndexOfAny('\\r', '\\n');\n                        if (endOfCommentIdx < 0)\n                        {\n                            position += querySlice.Length;\n                            querySlice = ReadOnlySpan<char>.Empty;\n                        }\n                        else\n                        {\n                            position += endOfCommentIdx + 1;\n                            querySlice = querySlice.Slice(endOfCommentIdx + 1);\n                        }\n\n                        break;\n\n                    case '\\'':\n                    case '\"':\n                    case '`':\n                        var tokenLen = ClickHouseSyntaxHelper.GetQuotedTokenLength(((ReadOnlySpan<char>) query).Slice(position - 1), ch);\n                        if (tokenLen < 0)\n                        {\n                            position += querySlice.Length;\n                            querySlice = ReadOnlySpan<char>.Empty;\n                        }\n                        else\n                        {\n                            position += tokenLen - 1;\n                            querySlice = querySlice.Slice(tokenLen - 1);\n                        }\n\n                        break;\n\n                    case '/':\n                        if (querySlice[0] != '*')\n                            break;\n\n                        ++position;\n                        querySlice = querySlice.Slice(1);\n                        while (!querySlice.IsEmpty)\n                        {\n                            var endOfMultilineCommentIdx = querySlice.IndexOf('*');\n                            if (endOfMultilineCommentIdx < 0)\n                            {\n                                position += querySlice.Length;\n                                querySlice = ReadOnlySpan<char>.Empty;\n                            }\n                            else\n                            {\n                                position += endOfMultilineCommentIdx + 1;\n                                querySlice = querySlice.Slice(endOfMultilineCommentIdx + 1);\n                                if (querySlice.Length > 0 && querySlice[0] == '/')\n                                {\n                                    ++position;\n                                    querySlice = querySlice.Slice(1);\n                                    break;\n                                }\n                            }\n                        }\n\n                        break;\n\n                    case '{':\n                        var closeIdx = querySlice.IndexOf('}');\n                        if (closeIdx < 0)\n                        {\n                            position += querySlice.Length;\n                            querySlice = ReadOnlySpan<char>.Empty;\n                            break;\n                        }\n\n                        var match = identifierRegex.Match(query, position, closeIdx);\n                        if (match.Success)\n                        {\n                            if (match.Groups[1].Success)\n                                parameterPositions.Add((position - 1, closeIdx + 2, match.Groups[1].Index - position + 1));\n                            else\n                                parameterPositions.Add((position - 1, closeIdx + 2, -1));\n                        }\n\n                        position += closeIdx + 1;\n                        querySlice = querySlice.Slice(closeIdx + 1);\n                        break;\n\n                    case '@':\n                        var simpleMatch = simpleIdentifierRegex.Match(query, position, querySlice.Length);\n                        if (simpleMatch.Success)\n                        {\n                            var len = simpleMatch.Groups[0].Length;\n                            parameterPositions.Add((position - 1, len + 1, -1));\n                            \n                            position += len;\n                            querySlice = querySlice.Slice(len);\n                        }\n\n                        break;                        \n\n                    default:\n                        throw new NotSupportedException($\"Internal error. Unexpected character \\\"{ch}\\\".\");\n                }\n            }\n\n            return parameterPositions;\n        }\n\n        /// <inheritdoc/>\n        public override ValueTask DisposeAsync()\n        {\n            return base.DisposeAsync();\n        }\n\n        /// <inheritdoc/>\n        protected override void Dispose(bool disposing)\n        {\n            base.Dispose(disposing);\n        }\n\n        internal sealed class SessionResources : IClickHouseSessionExternalResources\n        {\n            private readonly ClickHouseConnection? _connection;\n            private readonly CancellationTokenSource? _tokenSource;\n\n            public SessionResources(ClickHouseConnection? connection, CancellationTokenSource? tokenSource)\n            {\n                _connection = connection;\n                _tokenSource = tokenSource;\n            }\n\n            public ValueTask Release(bool async)\n            {\n                _tokenSource?.Dispose();\n                return _connection?.Close(async) ?? default;\n            }\n\n            public async ValueTask<Exception?> ReleaseOnFailure(Exception? exception, bool async)\n            {\n                await Release(async);\n                return null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseConnection.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2019-2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing System;\r\nusing System.Data;\r\nusing System.Data.Common;\r\nusing System.Diagnostics;\r\nusing System.Diagnostics.CodeAnalysis;\r\nusing System.IO;\r\nusing System.Net.Security;\r\nusing System.Net.Sockets;\r\nusing System.Security.Authentication;\r\nusing System.Security.Cryptography.X509Certificates;\r\nusing System.Threading;\r\nusing System.Threading.Tasks;\r\nusing System.Transactions;\r\nusing Octonica.ClickHouseClient.Exceptions;\r\nusing Octonica.ClickHouseClient.Protocol;\r\nusing Octonica.ClickHouseClient.Types;\r\nusing Octonica.ClickHouseClient.Utils;\r\n\r\nnamespace Octonica.ClickHouseClient\r\n{\r\n    /// <summary>\r\n    /// Represents a connection to a ClickHouse database. This class cannot be inherited.\r\n    /// </summary>\r\n    public sealed class ClickHouseConnection : DbConnection\r\n    {\r\n        private const int MinBufferSize = 32;\r\n\r\n        private readonly IClickHouseTypeInfoProvider? _typeInfoProvider;\r\n\r\n        private ClickHouseConnectionState _connectionState;\r\n\r\n        /// <summary>\r\n        /// Gets or sets the string used to open a connection to a ClickHouse database server.\r\n        /// </summary>\r\n        [AllowNull]\r\n        public override string ConnectionString\r\n        {\r\n            get\r\n            {\r\n                var state = _connectionState;\r\n                if (state.Settings == null)\r\n                    return string.Empty;\r\n\r\n                return new ClickHouseConnectionStringBuilder(state.Settings).ConnectionString;\r\n            }\r\n            set\r\n            {\r\n                var newSettings = value == null ? null : new ClickHouseConnectionStringBuilder(value).BuildSettings();\r\n                var state = _connectionState;\r\n                while (true)\r\n                {\r\n                    if (ReferenceEquals(state.Settings, newSettings))\r\n                        break;\r\n\r\n                    if (state.State != ConnectionState.Closed && state.State != ConnectionState.Broken)\r\n                        throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"The connection string can not be modified because the connection is active.\");\r\n\r\n                    var newState = new ClickHouseConnectionState(state.State, state.TcpClient, newSettings, unchecked(state.Counter + 1));\r\n                    if (TryChangeConnectionState(state, newState, out state))\r\n                        break;\r\n                }\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// The connection doesn't support a timeout. This property always returns <see cref=\"Timeout.Infinite\"/>.\r\n        /// </summary>\r\n        /// <returns><see cref=\"Timeout.Infinite\"/>.</returns>\r\n        public override int ConnectionTimeout => Timeout.Infinite;\r\n\r\n        /// <summary>\r\n        /// Gets the name of the database specified in the connection settings.\r\n        /// </summary>\r\n        /// <returns>The name of the database specified in the connection settings. The default value is an empty string.</returns>\r\n        public override string Database => _connectionState.Settings?.Database ?? string.Empty;\r\n\r\n        /// <summary>\r\n        /// Gets the name of the database server specified in the connection settings.\r\n        /// The name of the server contains the hostname and the port. If the port is equal to the default ClickHouse server port (9000) the\r\n        /// name of the server will conatin only hostname.\r\n        /// </summary>\r\n        /// <returns>The name of the database server specified in the connection settings. The default value is an empty string.</returns>\r\n        public override string DataSource\r\n        {\r\n            get\r\n            {\r\n                var state = _connectionState;\r\n                if (state.Settings == null)\r\n                    return string.Empty;\r\n\r\n                return state.Settings.Host + (state.Settings.Port != ClickHouseConnectionStringBuilder.DefaultPort ? \":\" + state.Settings.Port : string.Empty);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// When the connection is open gets the version of the ClickHouse database server.\r\n        /// </summary>\r\n        /// <returns>The version of the ClickHouse database server.  The default value is an empty string.</returns>\r\n        public override string ServerVersion => _connectionState.TcpClient?.ServerInfo.Version.ToString() ?? string.Empty;\r\n\r\n        /// <summary>\r\n        /// Gets the state of the connection.\r\n        /// </summary>\r\n        /// <returns>The state of the connection.</returns>\r\n        public override ConnectionState State => _connectionState.State;\r\n\r\n        /// <summary>\r\n        /// Gets or sets the callback for custom validation of the server's certificate. When the callback is set\r\n        /// other TLS certificate validation options are ignored.\r\n        /// </summary>\r\n        public RemoteCertificateValidationCallback? RemoteCertificateValidationCallback { get; set; }\r\n\r\n        internal TimeSpan? CommandTimeSpan\r\n        {\r\n            get\r\n            {\r\n                var commandTimeout = _connectionState.Settings?.CommandTimeout;\r\n                if (commandTimeout == null)\r\n                    return null;\r\n\r\n                return TimeSpan.FromSeconds(commandTimeout.Value);\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// Gets the default mode of passing parameters to the query for the connection.\r\n        /// </summary>\r\n        /// <returns>The default mode of passing parameters to the query for the connection. The default value is <see cref=\"ClickHouseParameterMode.Default\"/>.</returns>\r\n        public ClickHouseParameterMode ParametersMode\r\n        {\r\n            get\r\n            {\r\n                var mode = _connectionState.Settings?.ParametersMode;\r\n                if (mode == null || mode.Value == ClickHouseParameterMode.Inherit)\r\n                    return ClickHouseParameterMode.Default;\r\n\r\n                return mode.Value;\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// When the connection is open gets the server info.\r\n        /// </summary>\r\n        public ClickHouseServerInfo? ServerInfo => _connectionState.TcpClient?.ServerInfo;\r\n\r\n        /// <summary>\r\n        /// Initializes a new instance of <see cref=\"ClickHouseConnection\"/> class.\r\n        /// </summary>\r\n        public ClickHouseConnection()\r\n        {\r\n            _connectionState = new ClickHouseConnectionState();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Initializes a new instance of <see cref=\"ClickHouseConnection\"/> with the settings and the default type provider.\r\n        /// </summary>\r\n        /// <param name=\"connectionString\">The connection string.</param>\r\n        public ClickHouseConnection(string connectionString)\r\n            : this(connectionString, null)\r\n        {\r\n        }\r\n\r\n        /// <summary>\r\n        /// Initializes a new instance of <see cref=\"ClickHouseConnection\"/> with the settings.\r\n        /// </summary>\r\n        /// <param name=\"connectionString\">The connection string.</param>\r\n        /// <param name=\"typeInfoProvider\">Optional parameter. The provider of types for the connection. If the value is not specified the default type provider (<see cref=\"DefaultTypeInfoProvider.Instance\"/>) will be used.</param>\r\n        public ClickHouseConnection(string connectionString, IClickHouseTypeInfoProvider? typeInfoProvider)\r\n            : this(new ClickHouseConnectionStringBuilder(connectionString), typeInfoProvider)\r\n        {\r\n        }\r\n\r\n        /// <summary>\r\n        /// Initializes a new instance of <see cref=\"ClickHouseConnection\"/> with the settings and the default type provider.\r\n        /// </summary>\r\n        /// <param name=\"stringBuilder\">The connection string builder which will be used for building the connection settings.</param>\r\n        public ClickHouseConnection(ClickHouseConnectionStringBuilder stringBuilder)\r\n            : this(stringBuilder, null)\r\n        {\r\n        }\r\n\r\n        /// <summary>\r\n        /// Initializes a new instance of <see cref=\"ClickHouseConnection\"/> with the settings.\r\n        /// </summary>\r\n        /// <param name=\"stringBuilder\">The connection string builder which will be used for building the connection settings.</param>\r\n        /// <param name=\"typeInfoProvider\">Optional parameter. The provider of types for the connection. If the value is not specified the default type provider (<see cref=\"DefaultTypeInfoProvider.Instance\"/>) will be used.</param>\r\n        public ClickHouseConnection(ClickHouseConnectionStringBuilder stringBuilder, IClickHouseTypeInfoProvider? typeInfoProvider)\r\n        {\r\n            if (stringBuilder == null)\r\n                throw new ArgumentNullException(nameof(stringBuilder));\r\n\r\n            var connectionSettings = stringBuilder.BuildSettings();\r\n\r\n            _connectionState = new ClickHouseConnectionState(ConnectionState.Closed, null, connectionSettings, 0);\r\n            _typeInfoProvider = typeInfoProvider;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Initializes a new instance of <see cref=\"ClickHouseConnection\"/> with the settings and the default type provider.\r\n        /// </summary>\r\n        /// <param name=\"connectionSettings\">The connection settings.</param>\r\n        public ClickHouseConnection(ClickHouseConnectionSettings connectionSettings)\r\n            : this(connectionSettings, null)\r\n        {\r\n        }\r\n\r\n        /// <summary>\r\n        /// Initializes a new instance of <see cref=\"ClickHouseConnection\"/> with the settings.\r\n        /// </summary>\r\n        /// <param name=\"connectionSettings\">The connection settings.</param>\r\n        /// <param name=\"typeInfoProvider\">Optional parameter. The provider of types for the connection. If the value is not specified the default type provider (<see cref=\"DefaultTypeInfoProvider.Instance\"/>) will be used.</param>\r\n        public ClickHouseConnection(ClickHouseConnectionSettings connectionSettings, IClickHouseTypeInfoProvider? typeInfoProvider)\r\n        {\r\n            if (connectionSettings == null)\r\n                throw new ArgumentNullException(nameof(connectionSettings));\r\n\r\n            _connectionState = new ClickHouseConnectionState(ConnectionState.Closed, null, connectionSettings, 0);\r\n            _typeInfoProvider = typeInfoProvider;\r\n        }\r\n\r\n        /// <summary>\r\n        /// Not supported. The database cannot be changed while the connection is open.\r\n        /// </summary>\r\n        /// <exception cref=\"NotSupportedException\">Always throws <see cref=\"NotSupportedException\"/>.</exception>\r\n        public override void ChangeDatabase(string databaseName)\r\n        {\r\n            throw new NotSupportedException();\r\n        }\r\n\r\n        /// <inheritdoc cref=\"ChangeDatabase(string)\"/>\r\n        public override Task ChangeDatabaseAsync(string databaseName, CancellationToken cancellationToken = default)\r\n        {\r\n            throw new NotSupportedException();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Closes the connection to the database.\r\n        /// </summary>\r\n        public override void Close()\r\n        {\r\n            TaskHelper.WaitNonAsyncTask(Close(false));\r\n        }\r\n\r\n        /// <inheritdoc/>\r\n        public override async Task CloseAsync()\r\n        {\r\n            await Close(true);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Not supported. Transactions are not supported by the ClickHouse server.\r\n        /// </summary>\r\n        /// <exception cref=\"NotSupportedException\">Always throws <see cref=\"NotSupportedException\"/>.</exception>\r\n        public override void EnlistTransaction(Transaction? transaction)\r\n        {\r\n            throw new NotSupportedException();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Not supported. Schema information is not implemented.\r\n        /// </summary>\r\n        /// <exception cref=\"NotImplementedException\">Always throws <see cref=\"NotImplementedException\"/>.</exception>\r\n        public override DataTable GetSchema()\r\n        {\r\n            throw new NotImplementedException();\r\n        }\r\n\r\n        /// <inheritdoc cref=\"GetSchema()\"/>\r\n        public override DataTable GetSchema(string collectionName)\r\n        {\r\n            throw new NotImplementedException();\r\n        }\r\n\r\n        /// <inheritdoc cref=\"GetSchema()\"/>\r\n        public override DataTable GetSchema(string collectionName, string?[] restrictionValues)\r\n        {\r\n            throw new NotImplementedException();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Opens a database connection.\r\n        /// </summary>\r\n        public override void Open()\r\n        {\r\n            TaskHelper.WaitNonAsyncTask(Open(false, CancellationToken.None));\r\n        }\r\n\r\n        /// <summary>\r\n        /// Opens a database connection asyncronously.\r\n        /// </summary>\r\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\r\n        /// <returns>A <see cref=\"Task\"/> representing asyncronous operation.</returns>\r\n        public override async Task OpenAsync(CancellationToken cancellationToken)\r\n        {\r\n            await Open(true, cancellationToken);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Not supported. Transactions are not supported by the ClickHouse server.\r\n        /// </summary>\r\n        /// <exception cref=\"NotSupportedException\">Always throws <see cref=\"NotSupportedException\"/>.</exception>\r\n        protected override DbTransaction BeginDbTransaction(System.Data.IsolationLevel isolationLevel)\r\n        {\r\n            throw new NotSupportedException();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Not supported. Transactions are not supported by the ClickHouse server.\r\n        /// </summary>\r\n        /// <exception cref=\"NotSupportedException\">Always throws <see cref=\"NotSupportedException\"/>.</exception>\r\n        protected override ValueTask<DbTransaction> BeginDbTransactionAsync(System.Data.IsolationLevel isolationLevel, CancellationToken cancellationToken)\r\n        {\r\n            throw new NotSupportedException();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Creates and returns a <see cref=\"ClickHouseCommand\"/> object associated with the connection.\r\n        /// </summary>\r\n        /// <returns>A <see cref=\"ClickHouseCommand\"/> object.</returns>\r\n        public new ClickHouseCommand CreateCommand()\r\n        {\r\n            return new ClickHouseCommand(this);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Creates and returns a <see cref=\"ClickHouseCommand\"/> object associated with the connection.\r\n        /// </summary>\r\n        /// <param name=\"commandText\">The text for a new command.</param>\r\n        /// <returns>A <see cref=\"ClickHouseCommand\"/> object.</returns>\r\n        public ClickHouseCommand CreateCommand(string commandText)\r\n        {\r\n            return new ClickHouseCommand(this) {CommandText = commandText};\r\n        }\r\n\r\n        /// <inheritdoc cref=\"CreateCommand()\"/>\r\n        protected override DbCommand CreateDbCommand()\r\n        {\r\n            return CreateCommand();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Creates and returns a <see cref=\"ClickHouseColumnWriter\"/> object.\r\n        /// </summary>\r\n        /// <param name=\"insertFormatCommand\">The INSERT statement.</param>\r\n        /// <returns>A <see cref=\"ClickHouseColumnWriter\"/> object.</returns>\r\n        /// <remarks>\r\n        /// The command (<paramref name=\"insertFormatCommand\"/>) must be a valid INSERT statement ending with VALUES. For example,\r\n        /// <code>INSERT INTO table(field1, ... fieldN) VALUES</code>\r\n        /// </remarks>\r\n        public ClickHouseColumnWriter CreateColumnWriter(string insertFormatCommand)\r\n        {\r\n            return TaskHelper.WaitNonAsyncTask(CreateColumnWriter(insertFormatCommand, false, CancellationToken.None));\r\n        }\r\n\r\n        /// <summary>\r\n        /// Asyncronously creates and returns a <see cref=\"ClickHouseColumnWriter\"/> object.\r\n        /// </summary>\r\n        /// <param name=\"insertFormatCommand\">The INSERT statement.</param>\r\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\r\n        /// <returns>A <see cref=\"Task{ClickHouseColumnWriter}\"/> representing asyncronous operation.</returns>\r\n        /// <remarks>\r\n        /// The command (<paramref name=\"insertFormatCommand\"/>) must be a valid INSERT statement ending with VALUES. For example,\r\n        /// <code>INSERT INTO table(field1, ... fieldN) VALUES</code>\r\n        /// </remarks>\r\n        public async Task<ClickHouseColumnWriter> CreateColumnWriterAsync(string insertFormatCommand, CancellationToken cancellationToken)\r\n        {\r\n            return await CreateColumnWriter(insertFormatCommand, true, cancellationToken);\r\n        }\r\n\r\n        private async ValueTask<ClickHouseColumnWriter> CreateColumnWriter(string insertFormatCommand, bool async, CancellationToken cancellationToken)\r\n        {\r\n            var connectionState = _connectionState;\r\n            if (connectionState.TcpClient == null)\r\n            {\r\n                Debug.Assert(connectionState.State != ConnectionState.Open);\r\n                throw new ClickHouseException(ClickHouseErrorCodes.ConnectionClosed, \"The connection is closed.\");\r\n            }\r\n\r\n            ClickHouseTcpClient.Session? session = null;\r\n            bool cancelOnFailure = false;\r\n            try\r\n            {\r\n                session = await connectionState.TcpClient.OpenSession(async, null, CancellationToken.None, cancellationToken);\r\n\r\n                var messageBuilder = new ClientQueryMessage.Builder {QueryKind = QueryKind.InitialQuery, Query = insertFormatCommand};\r\n                var query = await session.SendQuery(messageBuilder, null, async, cancellationToken);\r\n\r\n                cancelOnFailure = true;\r\n                var data = await ClickHouseColumnWriter.ReadTableMetadata(session, query.Query, async, cancellationToken);\r\n\r\n                return new ClickHouseColumnWriter(session, query, data.Header.Columns);\r\n            }\r\n            catch (ClickHouseServerException)\r\n            {\r\n                if (session != null)\r\n                    await session.Dispose(async);\r\n                \r\n                throw;\r\n            }\r\n            catch(ClickHouseHandledException)\r\n            {\r\n                if (session != null)\r\n                    await session.Dispose(async);\r\n\r\n                throw;\r\n            }\r\n            catch(Exception ex)\r\n            {\r\n                if (session != null)\r\n                {\r\n                    var aggrEx = await session.SetFailed(ex, cancelOnFailure, async);\r\n                    if (aggrEx != null)\r\n                        throw aggrEx;\r\n                }\r\n\r\n                throw;\r\n            }\r\n        }\r\n\r\n        /// <summary>\r\n        /// For an open connection gets the default timezone of the ClickHouse server.\r\n        /// </summary>\r\n        /// <returns>The default timezone of the ClickHouse server.</returns>\r\n        /// <exception cref=\"ClickHouseException\">Throws <see cref=\"ClickHouseException\"/> if the connection is not open.</exception>\r\n        public TimeZoneInfo GetServerTimeZone()\r\n        {\r\n            var connectionState = _connectionState;\r\n            var serverInfo = connectionState.TcpClient?.ServerInfo;\r\n            if (serverInfo == null || connectionState.State != ConnectionState.Open)\r\n                throw new ClickHouseException(ClickHouseErrorCodes.ConnectionClosed, \"The connection is closed.\");\r\n\r\n            return TimeZoneHelper.GetTimeZoneInfo(serverInfo.Timezone);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Closes the connection and releases resources associated with it.\r\n        /// </summary>\r\n        protected override void Dispose(bool disposing)\r\n        {\r\n            var connectionState = _connectionState;\r\n            if (connectionState == null)\r\n            {\r\n                // This is possible when GC calls finalizer for System.ComponentModel.Component, but the connection\r\n                // object was not created properly (an error occured in the constructor).\r\n                return;\r\n            }\r\n\r\n            var counter = connectionState.Counter;\r\n            while (connectionState.Counter == counter)\r\n            {\r\n                var targetState = connectionState.State == ConnectionState.Closed ? ConnectionState.Closed : ConnectionState.Broken;\r\n                if (connectionState.State == targetState && connectionState.TcpClient == null)\r\n                    break;\r\n\r\n                var tcpClient = connectionState.TcpClient;\r\n                if (!TryChangeConnectionState(connectionState, targetState, null, out connectionState, out _))\r\n                    continue;\r\n\r\n                tcpClient?.Dispose();\r\n                break;\r\n            }\r\n            \r\n            base.Dispose(disposing);\r\n        }\r\n\r\n        private async ValueTask Open(bool async, CancellationToken cancellationToken)\r\n        {\r\n            if (!BitConverter.IsLittleEndian)\r\n            {\r\n                throw new NotSupportedException(\r\n                    \"An architecture of the processor is not supported. Only little-endian architectures are supported.\" + Environment.NewLine +\r\n                    \"Please, report an issue if you see this message (https://github.com/Octonica/ClickHouseClient/issues).\");\r\n            }\r\n\r\n            var connectionState = _connectionState;\r\n            switch (connectionState.State)\r\n            {\r\n                case ConnectionState.Closed:\r\n                    break;\r\n                case ConnectionState.Open:\r\n                    return; // Re-entrance is allowed\r\n                case ConnectionState.Connecting:\r\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"The connection is already opening.\");\r\n                case ConnectionState.Broken:\r\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"The connection is broken.\");\r\n                default:\r\n                    throw new NotSupportedException($\"Internal error. The state {_connectionState} is not supported.\");\r\n            }\r\n\r\n            if (!TryChangeConnectionState(connectionState, ConnectionState.Connecting, out connectionState, out var onStateChanged))\r\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"The state of the connection was modified.\");\r\n\r\n            var stateChangeEx = onStateChanged(this);\r\n            var connectionSettings = connectionState.Settings;\r\n            if (stateChangeEx != null || connectionSettings == null)\r\n            {\r\n                var initialEx = stateChangeEx ?? new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"The connection is not initialized.\");\r\n                if (!TryChangeConnectionState(connectionState, ConnectionState.Closed, out _, out onStateChanged))\r\n                    throw new AggregateException(initialEx, new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"The state of the connection was modified.\"));\r\n\r\n                var stateChangeEx2 = onStateChanged(this);\r\n                if (stateChangeEx2 != null)\r\n                    throw new AggregateException(initialEx, stateChangeEx2);\r\n\r\n                if (stateChangeEx != null)\r\n                    throw new ClickHouseException(ClickHouseErrorCodes.CallbackError, \"External callback error. See the inner exception for details.\", stateChangeEx);\r\n\r\n                throw initialEx;\r\n            }\r\n\r\n            const int defaultHttpPort = 8123;\r\n            TcpClient? client = null;\r\n            SslStream? sslStream = null;\r\n            ClickHouseBinaryProtocolWriter? writer = null;\r\n            ClickHouseBinaryProtocolReader? reader = null;\r\n\r\n            try\r\n            {\r\n                try\r\n                {\r\n                    client = new TcpClient {SendTimeout = connectionSettings.ReadWriteTimeout, ReceiveTimeout = connectionSettings.ReadWriteTimeout};\r\n\r\n                    if (async)\r\n                        await client.ConnectAsync(connectionSettings.Host, connectionSettings.Port);\r\n                    else\r\n                        client.Connect(connectionSettings.Host, connectionSettings.Port);\r\n                }\r\n                catch\r\n                {\r\n                    client?.Client?.Close(0);\r\n                    client?.Dispose();\r\n                    client = null;\r\n                    throw;\r\n                }\r\n\r\n                if (connectionSettings.TlsMode == ClickHouseTlsMode.Require)\r\n                {\r\n                    var certValidationCallback = RemoteCertificateValidationCallback;\r\n                    if (certValidationCallback == null && (connectionSettings.RootCertificate != null || !connectionSettings.ServerCertificateHash.IsEmpty))\r\n                        certValidationCallback = (_, cert, chain, errors) => ValidateServerCertificate(connectionSettings, cert, chain, errors);\r\n\r\n                    sslStream = new SslStream(client.GetStream(), true, certValidationCallback);\r\n\r\n                    try\r\n                    {\r\n                        if (async)\r\n                            await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions { TargetHost = connectionSettings.Host }, cancellationToken);\r\n                        else\r\n                            sslStream.AuthenticateAsClient(connectionSettings.Host);\r\n                    }\r\n                    catch(AuthenticationException authEx)\r\n                    {\r\n                        throw new ClickHouseException(ClickHouseErrorCodes.TlsError, $\"TLS handshake error.\", authEx);\r\n                    }\r\n                }\r\n\r\n                var stream = sslStream ?? (Stream)client.GetStream();\r\n                writer = new ClickHouseBinaryProtocolWriter(stream, Math.Max(connectionSettings.BufferSize, MinBufferSize));\r\n\r\n                var clientHello = new ClientHelloMessage.Builder\r\n                {\r\n                    ClientName = connectionSettings.ClientName,\r\n                    ClientVersion = connectionSettings.ClientVersion,\r\n                    User = connectionSettings.User,\r\n                    Database = connectionSettings.Database,\r\n                    Password = connectionSettings.Password,\r\n                    ProtocolRevision = ClickHouseProtocolRevisions.CurrentRevision,\r\n                    QuotaKey = connectionSettings.QuotaKey\r\n                }.Build();\r\n\r\n                clientHello.Write(writer);\r\n\r\n                await writer.Flush(async, cancellationToken);\r\n\r\n                reader = new ClickHouseBinaryProtocolReader(stream, Math.Max(connectionSettings.BufferSize, MinBufferSize));\r\n                var message = await reader.ReadMessage(clientHello.ProtocolRevision, false, async, cancellationToken);\r\n\r\n                switch (message.MessageCode)\r\n                {\r\n                    case ServerMessageCode.Hello:\r\n                        var helloMessage = (ServerHelloMessage) message;\r\n\r\n                        var serverInfo = helloMessage.ServerInfo;\r\n                        if (serverInfo.Revision >= ClickHouseProtocolRevisions.MinRevisionWithAddendum)\r\n                        {\r\n                            // Despite receiving the server hello message, we don't know yet if there was an error on the server side.\r\n                            // The server could reply with an error after receiveng an addendum. If there is no error, the server will not reply at all.\r\n                            // We can't rely on checking the state of the network channel, because the absense of bytes doesn't guarantee the success,\r\n                            // an error message could be delayed. Instead, we are going to add the ping message after the addendum, forcing the server\r\n                            // to send a reply in any case.\r\n\r\n                            clientHello.WriteAddendum(writer);\r\n                            writer.Write7BitInt32((int)ClientMessageCode.Ping);\r\n                            await writer.Flush(async, cancellationToken);\r\n\r\n                            var extraMessage = await reader.ReadMessage(Math.Min(clientHello.ProtocolRevision, serverInfo.Revision), false, async, cancellationToken);\r\n                            if (extraMessage.MessageCode != ServerMessageCode.Pong)\r\n                            {\r\n                                if (extraMessage.MessageCode == ServerMessageCode.Error)\r\n                                    throw ((ServerErrorMessage)extraMessage).Exception;\r\n\r\n                                throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Internal error. Unexpected message code (0x{extraMessage.MessageCode:X}) received from the server.\");\r\n                            }\r\n                        }\r\n\r\n                        bool hasExtraByte = reader.TryPeekByte(out var extraByte);\r\n                        if (!hasExtraByte && client.Available > 0)\r\n                        {\r\n                            hasExtraByte = true;\r\n                            extraByte = await reader.ReadByte(async, cancellationToken);\r\n                        }\r\n\r\n                        if (hasExtraByte)\r\n                        {\r\n                            throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Expected the end of the data. Unexpected byte (0x{extraByte:X}) received from the server.\");\r\n                        }\r\n\r\n                        var configuredTypeInfoProvider = (_typeInfoProvider ?? ClickHouseTypeInfoProvider.Instance).Configure(serverInfo);\r\n                        var tcpClient = new ClickHouseTcpClient(client, reader, writer, connectionSettings, serverInfo, configuredTypeInfoProvider, sslStream);\r\n                        \r\n                        if (!TryChangeConnectionState(connectionState, ConnectionState.Open, tcpClient, out _, out onStateChanged))\r\n                            throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"The state of the connection was modified.\");\r\n\r\n                        break;\r\n\r\n                    case ServerMessageCode.Error:\r\n                        throw ((ServerErrorMessage) message).Exception;\r\n\r\n                    default:\r\n                        if ((int) message.MessageCode == 'H')\r\n                        {\r\n                            // It looks like HTTP\r\n                            string httpDetectedMessage;\r\n                            if (connectionSettings.Port == defaultHttpPort)\r\n                            {\r\n                                // It's definitely HTTP\r\n                                httpDetectedMessage = $\"Detected an attempt to connect by HTTP protocol with the default port {defaultHttpPort}. \";\r\n                            }\r\n                            else\r\n                            {\r\n                                httpDetectedMessage =\r\n                                    $\"Internal error. Unexpected message code (0x{message.MessageCode:X}) received from the server. \" +\r\n                                    \"This error may by caused by an attempt to connect with HTTP protocol. \";\r\n                            }\r\n\r\n                            httpDetectedMessage +=\r\n                                $\"{ClickHouseConnectionStringBuilder.DefaultClientName} supports only ClickHouse native protocol. \" +\r\n                                $\"The default port for the native protocol is {ClickHouseConnectionStringBuilder.DefaultPort}.\";\r\n\r\n                            throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, httpDetectedMessage);\r\n                        }\r\n\r\n                        if ((int) message.MessageCode == 0x15)\r\n                        {\r\n                            // 0x15 stands for TLS alert message\r\n                            var sslAlertMessage =\r\n                                $\"Unexpected message code (0x{message.MessageCode:X}) received from the server. \" +\r\n                                \"This code may indicate that the server requires establishing a connection over TLS.\";\r\n\r\n                            throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, sslAlertMessage);\r\n                        }\r\n\r\n                        throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Internal error. Unexpected message code (0x{message.MessageCode:X}) received from the server.\");\r\n                }\r\n            }\r\n            catch (Exception ex)\r\n            {\r\n                reader?.Dispose();\r\n                writer?.Dispose();\r\n                sslStream?.Dispose();\r\n                client?.Client?.Close(0);\r\n                client?.Dispose();\r\n\r\n                if (TryChangeConnectionState(connectionState, ConnectionState.Closed, out _, out onStateChanged))\r\n                    stateChangeEx = onStateChanged(this);\r\n\r\n                if (connectionSettings.Port == defaultHttpPort && ex is IOException)\r\n                {\r\n                    var extraMessage =\r\n                        $\"{ex.Message} This error may be caused by an attempt to connect to the default HTTP port ({defaultHttpPort}). \" +\r\n                        $\"{ClickHouseConnectionStringBuilder.DefaultClientName} supports only ClickHouse native protocol. \" +\r\n                        $\"The default port for the native protocol is {ClickHouseConnectionStringBuilder.DefaultPort}.\";\r\n\r\n                    var extraEx = new IOException(extraMessage, ex);\r\n                    if (stateChangeEx != null)\r\n                        throw new AggregateException(extraEx, stateChangeEx);\r\n\r\n                    throw extraEx;\r\n                }\r\n\r\n                if (stateChangeEx != null)\r\n                    throw new AggregateException(ex, stateChangeEx);\r\n\r\n                throw;\r\n            }\r\n\r\n            stateChangeEx = onStateChanged.Invoke(this);\r\n            if (stateChangeEx != null)\r\n                throw new ClickHouseException(ClickHouseErrorCodes.CallbackError, \"External callback error. See the inner exception for details.\", stateChangeEx);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Send ping to the server and wait for response.\r\n        /// </summary>\r\n        /// <returns>\r\n        /// Returns <b>true</b> if ping was successful.\r\n        /// Returns <b>false</b> if the connection is busy with a command execution.\r\n        /// </returns>\r\n        public bool TryPing()\r\n        {\r\n            return TaskHelper.WaitNonAsyncTask(TryPing(false, CancellationToken.None));\r\n        }\r\n\r\n        /// <inheritdoc cref=\"TryPing()\"/>\r\n        public Task<bool> TryPingAsync()\r\n        {\r\n            return TryPingAsync(CancellationToken.None);\r\n        }\r\n\r\n        /// <inheritdoc cref=\"TryPing()\"/>\r\n        public async Task<bool> TryPingAsync(CancellationToken cancellationToken)\r\n        {\r\n            return await TryPing(true, cancellationToken);\r\n        }\r\n\r\n        private async ValueTask<bool> TryPing(bool async, CancellationToken cancellationToken)\r\n        {\r\n            if (_connectionState.TcpClient?.State == ClickHouseTcpClientState.Active)\r\n                return false;\r\n\r\n            ClickHouseTcpClient.Session? session = null;\r\n            try\r\n            {\r\n                using (var ts = new CancellationTokenSource(TimeSpan.FromMilliseconds(5)))\r\n                {\r\n                    try\r\n                    {\r\n                        session = await OpenSession(async, null, CancellationToken.None, ts.Token);\r\n                    }\r\n                    catch (OperationCanceledException ex)\r\n                    {\r\n                        if (ex.CancellationToken == ts.Token)\r\n                            return false;\r\n\r\n                        throw;\r\n                    }\r\n                }\r\n\r\n                await session.SendPing(async, cancellationToken);\r\n                var responseMsg = await session.ReadMessage(async, cancellationToken);\r\n\r\n                switch (responseMsg.MessageCode)\r\n                {\r\n                    case ServerMessageCode.Pong:\r\n                        return true;\r\n\r\n                    case ServerMessageCode.Error:\r\n                        // Something else caused this error. Keep it in InnerException for debug.\r\n                        throw new ClickHouseException(\r\n                            ClickHouseErrorCodes.ProtocolUnexpectedResponse,\r\n                            $\"Internal error. Unexpected message code (0x{responseMsg.MessageCode:X}) received from the server as a response to ping.\",\r\n                            ((ServerErrorMessage)responseMsg).Exception);\r\n\r\n                    default:\r\n                        throw new ClickHouseException(\r\n                            ClickHouseErrorCodes.ProtocolUnexpectedResponse,\r\n                            $\"Internal error. Unexpected message code (0x{responseMsg.MessageCode:X}) received from the server as a response to ping.\");\r\n                }\r\n            }\r\n            catch (ClickHouseHandledException)\r\n            {\r\n                throw;\r\n            }\r\n            catch (Exception ex)\r\n            {\r\n                if (session != null)\r\n                {\r\n                    await session.SetFailed(ex, false, async);\r\n                    session = null;\r\n                }\r\n\r\n                throw;\r\n            }\r\n            finally\r\n            {\r\n                if (session != null)\r\n                {\r\n                    if (async)\r\n                        await session.DisposeAsync();\r\n                    else\r\n                        session.Dispose();\r\n                }\r\n            }\r\n        }\r\n\r\n        internal ValueTask<ClickHouseTcpClient.Session> OpenSession(bool async, IClickHouseSessionExternalResources? externalResources, CancellationToken sessionCancellationToken, CancellationToken cancellationToken)\r\n        {\r\n            var connectionSession = new ConnectionSession(this, externalResources);\r\n            return connectionSession.OpenSession(async, sessionCancellationToken, cancellationToken);\r\n        }\r\n\r\n        internal async ValueTask Close(bool async)\r\n        {\r\n            var connectionState = _connectionState;\r\n            var counter = connectionState.Counter;\r\n            while (connectionState.Counter == counter)\r\n            {\r\n                var tcpClient = connectionState.TcpClient;\r\n                Func<ClickHouseConnection, Exception?>? onStateChanged;\r\n                Exception? stateChangedEx;\r\n                switch (connectionState.State)\r\n                {\r\n                    case ConnectionState.Closed:\r\n                        return; // Re-entrance is allowed\r\n\r\n                    case ConnectionState.Open:\r\n                        ClickHouseTcpClient.Session? session = null;\r\n                        try\r\n                        {\r\n                            // Acquire session for preventing access to the communication object\r\n                            var sessionTask = tcpClient?.OpenSession(async, null, CancellationToken.None, CancellationToken.None);\r\n                            if (sessionTask != null)\r\n                                session = await sessionTask.Value;\r\n                        }\r\n                        catch (ObjectDisposedException)\r\n                        {\r\n                            if (!TryChangeConnectionState(connectionState, ConnectionState.Closed, null, out connectionState, out onStateChanged))\r\n                                continue;\r\n\r\n                            stateChangedEx = onStateChanged(this);\r\n                            if (stateChangedEx != null)\r\n                                throw new ClickHouseException(ClickHouseErrorCodes.CallbackError, \"External callback error. See the inner exception for details.\", stateChangedEx);\r\n\r\n                            return;\r\n                        }\r\n                        catch\r\n                        {\r\n                            if (session != null)\r\n                                await session.Dispose(async);\r\n\r\n                            throw;\r\n                        }\r\n\r\n                        if (!TryChangeConnectionState(connectionState, ConnectionState.Closed, null, out connectionState, out onStateChanged))\r\n                        {\r\n                            if (session != null)\r\n                                await session.Dispose(async);\r\n\r\n                            continue;\r\n                        }\r\n\r\n                        tcpClient?.Dispose();\r\n\r\n                        stateChangedEx = onStateChanged(this);\r\n                        if (stateChangedEx != null)\r\n                            throw new ClickHouseException(ClickHouseErrorCodes.CallbackError, \"External callback error. See the inner exception for details.\", stateChangedEx);\r\n\r\n                        return;\r\n\r\n                    case ConnectionState.Broken:\r\n                        if (!TryChangeConnectionState(connectionState, ConnectionState.Closed, null, out connectionState, out onStateChanged))\r\n                            continue;\r\n\r\n                        tcpClient?.Dispose();\r\n\r\n                        stateChangedEx = onStateChanged(this);\r\n                        if (stateChangedEx != null)\r\n                            throw new ClickHouseException(ClickHouseErrorCodes.CallbackError, \"External callback error. See the inner exception for details.\", stateChangedEx);\r\n\r\n                        break;\r\n\r\n                    case ConnectionState.Connecting:\r\n                        throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"The connection is opening. It can't be closed.\");\r\n\r\n                    default:\r\n                        throw new NotSupportedException($\"Internal error. The state {connectionState.State} is not supported.\");\r\n                }\r\n            }\r\n        }\r\n\r\n        private bool TryChangeConnectionState(ClickHouseConnectionState from, ClickHouseConnectionState to, out ClickHouseConnectionState actualState)\r\n        {\r\n            actualState = Interlocked.CompareExchange(ref _connectionState, to, from);\r\n            if (ReferenceEquals(actualState, from))\r\n            {\r\n                actualState = to;\r\n                return true;\r\n            }\r\n\r\n            return false;\r\n        }\r\n\r\n        private bool TryChangeConnectionState(\r\n            ClickHouseConnectionState state,\r\n            ConnectionState newState,\r\n            ClickHouseTcpClient? client,\r\n            out ClickHouseConnectionState actualState,\r\n            [NotNullWhen(true)] out Func<ClickHouseConnection, Exception?>? onStateChanged)\r\n        {\r\n            var counter = state.State != ConnectionState.Connecting && newState == ConnectionState.Connecting ? unchecked(state.Counter + 1) : state.Counter;\r\n            var nextState = new ClickHouseConnectionState(newState, client, state.Settings, counter);\r\n            if (TryChangeConnectionState(state, nextState, out actualState))\r\n            {\r\n                onStateChanged = CreateConnectionStateChangedCallback(state.State, actualState.State);\r\n                return true;\r\n            }\r\n\r\n            onStateChanged = null;\r\n            return false;\r\n        }\r\n\r\n        private bool TryChangeConnectionState(\r\n            ClickHouseConnectionState state,\r\n            ConnectionState newState,\r\n            out ClickHouseConnectionState actualState,\r\n            [NotNullWhen(true)] out Func<ClickHouseConnection, Exception?>? onStateChanged)\r\n        {\r\n            return TryChangeConnectionState(state, newState, state.TcpClient, out actualState, out onStateChanged);\r\n        }\r\n\r\n        private static Func<ClickHouseConnection, Exception?> CreateConnectionStateChangedCallback(ConnectionState originalState, ConnectionState currentState)\r\n        {\r\n            if (originalState == currentState)\r\n                return _ => null;\r\n\r\n            return FireEvent;\r\n\r\n            Exception? FireEvent(ClickHouseConnection connection)\r\n            {\r\n                try\r\n                {\r\n                    connection.OnStateChange(new StateChangeEventArgs(originalState, currentState));\r\n                }\r\n                catch (Exception ex)\r\n                {\r\n                    return ex;\r\n                }\r\n\r\n                return null;\r\n            }\r\n        }\r\n\r\n        private static bool ValidateServerCertificate(ClickHouseConnectionSettings connectionSettings, X509Certificate? cert, X509Chain? chain, SslPolicyErrors errors)\r\n        {\r\n            if (errors == SslPolicyErrors.None)\r\n                return true;\r\n\r\n            if (cert == null)\r\n                return false;\r\n\r\n            if (!connectionSettings.ServerCertificateHash.IsEmpty)\r\n            {\r\n                var certHash = cert.GetCertHash();\r\n                if (connectionSettings.ServerCertificateHash.Span.SequenceEqual(certHash))\r\n                    return true;\r\n            }\r\n\r\n            if (chain != null && connectionSettings.RootCertificate != null)\r\n            {\r\n                if ((errors & ~SslPolicyErrors.RemoteCertificateChainErrors) != SslPolicyErrors.None)\r\n                    return false;\r\n\r\n                var collection = CertificateHelper.LoadFromFile(connectionSettings.RootCertificate);\r\n#if NET5_0_OR_GREATER\r\n                chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;\r\n                chain.ChainPolicy.CustomTrustStore.AddRange(collection);\r\n                var isValid = chain.Build(cert as X509Certificate2 ?? new X509Certificate2(cert));\r\n                return isValid;\r\n#else\r\n                foreach (var chainElement in chain.ChainElements)\r\n                {\r\n                    if (chainElement.ChainElementStatus.Length != 0)\r\n                    {\r\n                        bool ignoreError = true;\r\n                        foreach (var status in chainElement.ChainElementStatus)\r\n                        {\r\n                            if (status.Status == X509ChainStatusFlags.UntrustedRoot)\r\n                                continue;\r\n\r\n                            ignoreError = false;\r\n                            break;\r\n                        }\r\n\r\n                        if (!ignoreError)\r\n                            break;\r\n                    }\r\n\r\n                    if (collection.Contains(chainElement.Certificate))\r\n                        return true;\r\n                }\r\n#endif\r\n            }\r\n\r\n            return false;\r\n        }\r\n\r\n        private class ConnectionSession : IClickHouseSessionExternalResources\r\n        {\r\n            private readonly ClickHouseConnection _connection;\r\n            private readonly ClickHouseConnectionState _state;\r\n            private readonly IClickHouseSessionExternalResources? _externalResources;\r\n\r\n            public ConnectionSession(ClickHouseConnection connection, IClickHouseSessionExternalResources? externalResources)\r\n            {\r\n                _connection = connection;\r\n                _state = _connection._connectionState;\r\n                _externalResources = externalResources;\r\n\r\n                var tcpClient = _state.TcpClient;\r\n                if (tcpClient == null)\r\n                {\r\n                    Debug.Assert(_state.State != ConnectionState.Open);\r\n                    throw new ClickHouseException(ClickHouseErrorCodes.ConnectionClosed, \"The connection is closed.\");\r\n                }\r\n\r\n                if (_state.State != ConnectionState.Open)\r\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"The connection is closed.\");                \r\n            }\r\n\r\n            public ValueTask<ClickHouseTcpClient.Session> OpenSession(bool async, CancellationToken sessionCancellationToken, CancellationToken cancellationToken)\r\n            {\r\n                Debug.Assert(_state.TcpClient != null);\r\n                return _state.TcpClient.OpenSession(async, this, sessionCancellationToken, cancellationToken);\r\n            }\r\n\r\n            public ValueTask Release(bool async)\r\n            {\r\n                return _externalResources?.Release(async) ?? default;\r\n            }\r\n\r\n            public async ValueTask<Exception?> ReleaseOnFailure(Exception? exception, bool async)\r\n            {\r\n                Exception? ex = null;\r\n                if (_connection.TryChangeConnectionState(_state, ConnectionState.Broken, null, out _, out var onStateChanged))\r\n                    ex = onStateChanged(_connection);\r\n\r\n                Exception? externalEx = null;\r\n                if (_externalResources != null)\r\n                    externalEx = await _externalResources.ReleaseOnFailure(exception, async);\r\n\r\n                if (ex != null && externalEx != null)\r\n                    return new AggregateException(ex, externalEx);\r\n\r\n                return externalEx ?? ex;\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseConnectionSettings.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents an immutable set of properties applied to the connection. This class can't be inherited.\n    /// </summary>\n    public sealed class ClickHouseConnectionSettings\n    {\n        /// <summary>\n        /// Gets the name or the IP address of the host.\n        /// </summary>\n        public string Host { get; }\n\n        /// <summary>\n        /// Gets the IP port of the server.\n        /// </summary>\n        public ushort Port { get; }\n\n        /// <summary>\n        /// Gets the name of the user.\n        /// </summary>\n        public string User { get; }\n\n        /// <summary>\n        /// Gets the password.\n        /// </summary>\n        public string? Password { get; }\n\n        /// <summary>\n        /// Get the name of the default database.\n        /// </summary>\n        public string? Database { get; }\n\n        /// <summary>\n        /// Gets the network socket timeout in <b>milliseconds</b>. This timeout will be used to initialize properties <see cref= \"System.Net.Sockets.TcpClient.SendTimeout\"/> and \n        /// <see cref= \"System.Net.Sockets.TcpClient.ReceiveTimeout\"/>.\n        /// </summary>\n        public int ReadWriteTimeout { get; }\n\n        /// <summary>\n        /// Gets the name of the client. The name of the client is passed to the ClickHouse server as a part of the client's identifier.\n        /// </summary>\n        public string ClientName { get; }\n\n        /// <summary>\n        /// Gets the version of the client. The first two parts of the version (<see cref=\"ClickHouseVersion.Major\"/> and <see cref=\"ClickHouseVersion.Minor\"/>)\n        /// are passed to the ClickHouse server as a part of the client's identifier.\n        /// </summary>\n        public ClickHouseVersion ClientVersion { get; }\n\n        /// <summary>\n        /// Gets the preferred size of the internal buffer in bytes.\n        /// </summary>\n        public int BufferSize { get; }\n\n        /// <summary>\n        /// Gets the value indicating whether the compression (LZ4) of data is enabled.\n        /// </summary>\n        public bool Compress { get; }\n\n        /// <summary>\n        /// Gets the command timeout in <b>seconds</b>. This timeout will be used to initialize the property <see cref= \"ClickHouseCommand.CommandTimeout\"/> of the command.\n        /// </summary>\n        public int CommandTimeout { get; }\n\n        /// <summary>\n        /// Gets the TLS mode for the connection. See <see cref=\"ClickHouseTlsMode\"/> for details.\n        /// </summary>\n        public ClickHouseTlsMode TlsMode { get; }\n\n        /// <summary>\n        /// Gets the path to the file that contains a certificate (*.crt) or a list of certificates (*.pem).\n        /// When performing TLS hanshake any of these certificates will be treated as a valid root for the certificate chain.\n        /// </summary>\n        public string? RootCertificate { get; }\n\n        /// <summary>\n        /// Gets the hash of the server's certificate. When performing TLS handshake the remote certificate with the specified\n        /// hash will be treated as a valid certificate despite any other certificate chain validation errors (e.g. invalid hostname).\n        /// </summary>\n        public ReadOnlyMemory<byte> ServerCertificateHash { get; }\n\n        /// <summary>\n        /// Gets the default mode of passing parameters to the query for the connection.\n        /// </summary>\n        public ClickHouseParameterMode ParametersMode { get; }\n\n        /// <summary>\n        /// Gets the 'quota key' passed with the query. This key is used by the ClickHouse server for tracking quotas.\n        /// </summary>\n        public string? QuotaKey { get; }\n\n        internal readonly int CompressionBlockSize = 1024 * 8; // Maybe it should be configurable\n\n        internal ClickHouseConnectionSettings(ClickHouseConnectionStringBuilder builder)\n        {\n            if (string.IsNullOrWhiteSpace(builder.Host))\n                throw new ArgumentException(\"The host is not defined.\", nameof(builder));\n\n            if (builder.BufferSize <= 0)\n                throw new ArgumentException(\"The size of the buffer must be a positive number.\", nameof(builder));\n\n            Host = builder.Host;\n            Port = builder.Port;\n            User = builder.User;\n            Password = string.IsNullOrEmpty(builder.Password) ? null : builder.Password;\n            Database = string.IsNullOrEmpty(builder.Database) ? null : builder.Database;\n            ReadWriteTimeout = builder.ReadWriteTimeout;\n            BufferSize = builder.BufferSize;\n            ClientName = builder.ClientName;\n            ClientVersion = builder.ClientVersion;\n            Compress = builder.Compress;\n            CommandTimeout = builder.CommandTimeout;\n            TlsMode = builder.TlsMode;\n            RootCertificate = builder.RootCertificate;\n            ServerCertificateHash = ParseHashString(builder.ServerCertificateHash);\n            ParametersMode = builder.ParametersMode;\n            QuotaKey = string.IsNullOrEmpty(builder.QuotaKey) ? null : builder.QuotaKey;\n        }\n\n        private static byte[]? ParseHashString(string? hashString)\n        {\n            if (string.IsNullOrEmpty(hashString))\n                return null;\n\n            int resultPos = 0;\n            var result = new byte[hashString.Length / 2];\n            for (int i = 0; i < hashString.Length; i++)\n            {\n                var ch = hashString[i];\n                if (char.IsWhiteSpace(ch) || ch == '-')\n                    continue;\n\n                if (i + 1 == hashString.Length)\n                    throw new ArgumentException(\"Unexpected end of the hash string. Expected at least one more significant character.\", nameof(hashString));\n\n                byte byteVal;\n                if (ch >= '0' && ch <= '9')\n                    byteVal = (byte)(ch - '0');\n                else if (ch >= 'a' && ch <= 'f')\n                    byteVal = (byte)(ch - 'a' + 0xA);\n                else if (ch >= 'A' && ch <= 'F')\n                    byteVal = (byte)(ch - 'A' + 0xA);\n                else\n                    throw new ArgumentException($\"Unexpected character '{ch}' at the position {i} in the hash string.\", nameof(hashString));\n\n                byteVal <<= 4;\n                ch = hashString[++i];\n                if (ch >= '0' && ch <= '9')\n                    byteVal |= (byte)(ch - '0');\n                else if (ch >= 'a' && ch <= 'f')\n                    byteVal |= (byte)(ch - 'a' + 0xA);\n                else if (ch >= 'A' && ch <= 'F')\n                    byteVal |= (byte)(ch - 'A' + 0xA);\n                else\n                    throw new ArgumentException($\"Unexpected character '{ch}' at the position {i} in the hash string.\", nameof(hashString));\n\n                result[resultPos++] = byteVal;\n            }\n\n            if (resultPos == 0)\n                return null;\n\n            if (result.Length != resultPos)\n                Array.Resize(ref result, resultPos);\n\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseConnectionState.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Data;\n\nnamespace Octonica.ClickHouseClient\n{\n    internal sealed class ClickHouseConnectionState\n    {\n        public ConnectionState State { get; }\n\n        public ClickHouseConnectionSettings? Settings { get; }\n\n        public int Counter { get; }\n\n        public ClickHouseTcpClient? TcpClient { get; }\n\n        public ClickHouseConnectionState()\n            : this(ConnectionState.Closed, null, null, 0)\n        {\n        }\n\n        public ClickHouseConnectionState(ConnectionState state, ClickHouseTcpClient? tcpClient, ClickHouseConnectionSettings? settings, int counter)\n        {\n            State = state;\n            TcpClient = tcpClient;\n            Settings = settings;\n            Counter = counter;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseConnectionStringBuilder.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data.Common;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Provides a set of methods for working with connection settings and connection strings.\n    /// </summary>\n    public class ClickHouseConnectionStringBuilder : DbConnectionStringBuilder\n    {\n        private static readonly HashSet<string> AllProperties;\n\n        /// <summary>\n        /// The default IP port of the server (9000).\n        /// </summary>\n        public const ushort DefaultPort = 9000;\n\n        /// <summary>\n        /// The default name of the user ('default').\n        /// </summary>\n        public const string DefaultUser = \"default\";\n\n        /// <summary>\n        /// The default network socket timeout in <b>milliseconds</b> (10000).\n        /// </summary>\n        public const int DefaultReadWriteTimeout = 10000;\n\n        /// <summary>\n        /// The default command timeout in <b>seconds</b> (15).\n        /// </summary>\n        public const int DefaultCommandTimeout = 15;\n\n        /// <summary>\n        /// The default size of the internal buffer in bytes (4096).\n        /// </summary>\n        public const int DefaultBufferSize = 4096;\n\n        /// <summary>\n        /// The default name of the client ('Octonica.ClickHouseClient'). \n        /// </summary>\n        public const string DefaultClientName = \"Octonica.ClickHouseClient\";\n\n        /// <summary>\n        /// The default value (<see langword=\"true\"/>). indicating whether the compression (LZ4) is enabled.\n        /// </summary>\n        public const bool DefaultCompress = true;\n\n        /// <summary>\n        /// The default version of the client. This value is equal to the version of the assembly Octonica.ClickHouseClient.\n        /// </summary>\n        public static readonly ClickHouseVersion DefaultClientVersion;\n\n        /// <summary>\n        /// The default value for the TLS mode is <see cref=\"ClickHouseTlsMode.Disable\"/>.\n        /// </summary>\n        public const ClickHouseTlsMode DefaultTlsMode = ClickHouseTlsMode.Disable;\n\n        /// <summary>\n        /// The default value for the mode of passing parameters to the query is <see cref=\"ClickHouseParameterMode.Default\"/>.\n        /// </summary>\n        public const ClickHouseParameterMode DefaultParametersMode = ClickHouseParameterMode.Default;\n\n        /// <summary>\n        /// Gets or sets the name or the IP address of the host.\n        /// </summary>\n        /// <returns>The name or the IP address of the host.</returns>\n        public string? Host\n        {\n            get => GetString(nameof(Host));\n            set => this[nameof(Host)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the IP port of the server.\n        /// </summary>\n        /// <returns>The IP port of the server. The default value is <see cref=\"DefaultPort\"/>.</returns>\n        public ushort Port\n        {\n            get => (ushort) GetInt32OrDefault(nameof(Port), DefaultPort);\n            set => this[nameof(Port)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the name of the user.\n        /// </summary>\n        /// <returns>The name of the user. The default value is <see cref=\"DefaultUser\"/>.</returns>\n        public string User\n        {\n            get => GetStringOrDefault(nameof(User), DefaultUser);\n            set => this[nameof(User)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the password.\n        /// </summary>\n        /// <returns>The password.</returns>\n        public string? Password\n        {\n            get => GetString(nameof(Password));\n            set => this[nameof(Password)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the name of the default database.\n        /// </summary>\n        /// <returns>The name of the default database. <see langword=\"null\"/> if the default database in not specified.</returns>\n        public string? Database\n        {\n            get => GetString(nameof(Database));\n            set => this[nameof(Database)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the network socket timeout in <b>milliseconds</b>. This timeout will be used to initialize properties <see cref= \"System.Net.Sockets.TcpClient.SendTimeout\"/> and \n        /// <see cref= \"System.Net.Sockets.TcpClient.ReceiveTimeout\"/>.\n        /// </summary>\n        /// <returns>The network socket timeout in <b>milliseconds</b>. The default value is <see cref=\"DefaultReadWriteTimeout\"/>.</returns>\n        public int ReadWriteTimeout\n        {\n            get => GetInt32OrDefault(nameof(ReadWriteTimeout), DefaultReadWriteTimeout);\n            set => this[nameof(ReadWriteTimeout)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the preferred size of the internal buffer in bytes.\n        /// </summary>\n        /// <returns>the preferred size of the internal buffer in bytes. The default value is <see cref=\"DefaultBufferSize\"/>.</returns>\n        public int BufferSize\n        {\n            get => GetInt32OrDefault(nameof(BufferSize), DefaultBufferSize);\n            set => this[nameof(BufferSize)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the name of the client. The name of the client is passed to the ClickHouse server as a part of the client's identifier.\n        /// </summary>\n        /// <returns>The name of the client. The default value is <see cref=\"DefaultClientName\"/>.</returns>\n        public string ClientName\n        {\n            get => GetStringOrDefault(nameof(ClientName), DefaultClientName);\n            set => this[nameof(ClientName)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the value indicating whether the compression (LZ4) of data is enabled.\n        /// </summary>\n        /// <returns><see langword=\"true\"/> if compression is enabled; otherwise <see langword=\"true\"/>. The default value is <see cref=\"DefaultCompress\"/>.</returns>\n        public bool Compress\n        {\n            get => GetBoolOrDefault(nameof(Compress), DefaultCompress);\n            set => this[nameof(Compress)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the command timeout in <b>seconds</b>. This timeout will be used to initialize the property <see cref= \"ClickHouseCommand.CommandTimeout\"/> of the command.\n        /// </summary>\n        /// <returns>The command timeout in seconds. The default value is <see cref=\"DefaultCommandTimeout\"/>.</returns>\n        public int CommandTimeout\n        {\n            get => GetInt32OrDefault(nameof(CommandTimeout), DefaultCommandTimeout);\n            set => this[nameof(CommandTimeout)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the version of the client. The first two parts of the version (<see cref=\"ClickHouseVersion.Major\"/> and <see cref=\"ClickHouseVersion.Minor\"/>)\n        /// are passed to the ClickHouse server as a part of the client's identifier.\n        /// </summary>\n        /// <returns>The version of the client. The default value is <see cref=\"DefaultClientVersion\"/>.</returns>\n        public ClickHouseVersion ClientVersion\n        {\n            get\n            {\n                var value = GetString(nameof(ClientVersion));\n                if (value == null)\n                    return DefaultClientVersion;\n\n                return ClickHouseVersion.Parse(value);\n            }\n            set => this[nameof(ClientVersion)] = value.ToString();\n        }\n\n        /// <summary>\n        /// Gets or sets the TLS mode for the connection. See <see cref=\"ClickHouseTlsMode\"/> for details.\n        /// </summary>\n        /// <returns>The TLS mode for the connection. The default value is <see cref=\"DefaultTlsMode\"/>.</returns>\n        public ClickHouseTlsMode TlsMode\n        {\n            get => GetEnumOrDefault(nameof(TlsMode), DefaultTlsMode);\n            set => this[nameof(TlsMode)] = value == DefaultTlsMode ? null : value.ToString(\"G\");\n        }\n\n        /// <summary>\n        /// Gets or sets the path to the file that contains a certificate (*.crt) or a list of certificates (*.pem).\n        /// When performing TLS hanshake any of these certificates will be treated as a valid root for the certificate chain.\n        /// </summary>\n        /// <returns>The path to the file that contains a certificate (*.crt) or a list of certificates (*.pem). The default value is <see langword=\"null\"/>.</returns>\n        public string? RootCertificate\n        {\n            get\n            {\n                var value = GetString(nameof(RootCertificate));\n                if (string.IsNullOrWhiteSpace(value))\n                    return null;\n\n                return value;\n            }\n            set => this[nameof(RootCertificate)] = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the hash of the server's certificate in the hexadecimal format.\n        /// When performing TLS handshake the remote certificate with the specified hash will be treated as a valid certificate\n        /// despite any other certificate chain validation errors (e.g. invalid hostname).\n        /// </summary>\n        /// <returns>The hash of the server's certificate in the hexadecimal format. The default value is <see langword=\"null\"/>.</returns>\n        public string? ServerCertificateHash\n        {\n            get\n            {\n                var value = GetString(nameof(ServerCertificateHash));\n                if (string.IsNullOrWhiteSpace(value))\n                    return null;\n\n                return value;\n            }\n            set => this[nameof(ServerCertificateHash)] = value;\n        }\n\n        /// <summary>\n        /// Gets the default mode of passing parameters to the query for the connection.\n        /// </summary>\n        /// <returns>The default mode of passing parameters to the query for the connection. The default value is <see cref=\"DefaultParametersMode\"/>.</returns>\n        public ClickHouseParameterMode ParametersMode\n        {\n            get => GetEnumOrDefault(nameof(ParametersMode), DefaultParametersMode);\n            set => this[nameof(ParametersMode)] = value == DefaultParametersMode ? null : value.ToString(\"G\");\n        }\n\n        /// <summary>\n        /// Gets the 'quota key' passed with the query. This key is used by the ClickHouse server for tracking quotas.\n        /// </summary>\n        /// <returns>The value of 'quota key' passed with the query.</returns>\n        public string? QuotaKey\n        {\n            get => GetString(nameof(QuotaKey));\n            set => this[nameof(QuotaKey)] = value;\n        }\n\n        static ClickHouseConnectionStringBuilder()\n        {\n            var asm = typeof(ClickHouseConnectionStringBuilder).Assembly;\n            var version = asm.GetName().Version;\n            DefaultClientVersion = new ClickHouseVersion(version?.Major ?? 1, version?.Minor ?? 0, version?.Build ?? 0);\n\n            AllProperties = new HashSet<string>(StringComparer.OrdinalIgnoreCase)\n            {\n                nameof(BufferSize),\n                nameof(ClientName),\n                nameof(ClientVersion),\n                nameof(CommandTimeout),\n                nameof(Compress),\n                nameof(Database),\n                nameof(Host),\n                nameof(Password),\n                nameof(ReadWriteTimeout),\n                nameof(Port),\n                nameof(User),\n                nameof(TlsMode),\n                nameof(RootCertificate),\n                nameof(ServerCertificateHash),\n                nameof(ParametersMode),\n                nameof(QuotaKey)\n            };\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseConnectionStringBuilder\"/> with the default settings.\n        /// </summary>\n        public ClickHouseConnectionStringBuilder()\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseConnectionStringBuilder\"/> with the settings specified in the connection string.\n        /// </summary>\n        /// <param name=\"connectionString\">The connection string.</param>\n        public ClickHouseConnectionStringBuilder(string connectionString)\n        {\n            ConnectionString = connectionString;\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseConnectionStringBuilder\"/> with the specified.\n        /// </summary>\n        /// <param name=\"settings\">The settings.</param>\n        public ClickHouseConnectionStringBuilder(ClickHouseConnectionSettings settings)\n        {\n            if (settings == null)\n                throw new ArgumentNullException(nameof(settings));\n\n            Host = settings.Host;\n            Port = settings.Port;\n            User = settings.User;\n            Password = settings.Password;\n            Database = settings.Database;\n            ReadWriteTimeout = settings.ReadWriteTimeout;\n            BufferSize = settings.BufferSize;\n            Compress = settings.Compress;\n            CommandTimeout = settings.CommandTimeout;\n            TlsMode = settings.TlsMode;\n            RootCertificate = settings.RootCertificate;\n            ServerCertificateHash = HashToString(settings.ServerCertificateHash);\n            ParametersMode = settings.ParametersMode;\n            QuotaKey = settings.QuotaKey;\n\n            if (settings.ClientName != DefaultClientName)\n                ClientName = settings.ClientName;\n\n            if (settings.ClientVersion != DefaultClientVersion)\n                ClientVersion = settings.ClientVersion;\n        }\n\n        /// <inheritdoc/>\n        [AllowNull]\n        public override object this[string keyword]\n        {\n            get => base[keyword];\n            set\n            {\n                if (!AllProperties.Contains(keyword))\n                    throw new ArgumentException($\"\\\"{keyword}\\\" is not a valid connection parameter name.\", nameof(keyword));\n\n                base[keyword] = value;\n            }\n        }\n\n        /// <summary>\n        /// Creates and returns a new instance of the <see cref=\"ClickHouseConnectionSettings\"/>.\n        /// </summary>\n        /// <returns>A new instance of the <see cref=\"ClickHouseConnectionSettings\"/>.</returns>\n        public ClickHouseConnectionSettings BuildSettings()\n        {\n            return new ClickHouseConnectionSettings(this);\n        }\n\n        private string? GetString(string key)\n        {\n            return TryGetValue(key, out var value) ? (string) value : null;\n        }\n\n        private string GetStringOrDefault(string key, string defaultValue)\n        {\n            if (!TryGetValue(key, out var value))\n                return defaultValue;\n\n            return (string) value ?? defaultValue;\n        }\n\n        private int GetInt32OrDefault(string key, int defaultValue)\n        {\n            if (!TryGetValue(key, out var value))\n                return defaultValue;\n\n            if (value is string strValue)\n            {\n                if (!int.TryParse(strValue.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))\n                    throw new InvalidOperationException($\"The value of the property \\\"{key}\\\" must be an integer value.\");\n\n                return result;\n            }\n\n            return (int?) value ?? defaultValue;\n        }\n\n        private bool GetBoolOrDefault(string key, bool defaultValue)\n        {\n            if (!TryGetValue(key, out var value))\n                return defaultValue;\n\n            if (value is string strValue)\n            {\n                switch (strValue.Trim().ToLowerInvariant())\n                {\n                    case \"on\":\n                    case \"true\":\n                    case \"1\":\n                        return true;\n\n                    case \"off\":\n                    case \"false\":\n                    case \"0\":\n                        return false;\n\n                    default:\n                        throw new InvalidOperationException($\"The value of the property \\\"{key}\\\" is not a valid boolean value.\");\n                }\n            }\n\n            return (bool?) value ?? defaultValue;\n        }\n\n        private TEnum GetEnumOrDefault<TEnum>(string key, TEnum defaultValue)\n            where TEnum : struct\n        {\n            if (!TryGetValue(key, out var value) || value == null)\n                return defaultValue;\n\n            if(value is string strValue)\n            {\n                if (string.IsNullOrWhiteSpace(strValue))\n                    return defaultValue;\n\n                // Enum.TryParse parses an integer value and casts it into enum without additional check.\n                // Check that the value is not an integer before performing an actual enum parsing.\n                if (int.TryParse(strValue.Trim(), out _) || !Enum.TryParse<TEnum>(strValue, true, out var result))\n                    throw new InvalidOperationException($\"The value \\\"{strValue}\\\" is not a valid value for the property \\\"{key}\\\".\");\n\n                return result;\n            }\n\n            return (TEnum)value;\n        }\n\n        private static string? HashToString(ReadOnlyMemory<byte> hashBytes)\n        {\n            if (hashBytes.Length == 0)\n                return null;\n\n            return string.Create(hashBytes.Length * 2, hashBytes, HashToString);\n        }\n\n        static void HashToString(Span<char> span, ReadOnlyMemory<byte> hashBytes)\n        {\n            var byteSpan = hashBytes.Span;\n            for (int i = 0; i < hashBytes.Length; i++)\n            {\n                var val = byteSpan[i] >> 4;\n                span[i * 2] = (char)(val + (val >= 0xA ? 'A' - 0xA : '0'));\n\n                val = byteSpan[i] & 0x0F;\n                span[i * 2 + 1] = (char)(val + (val >= 0xA ? 'A' - 0xA : '0'));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseDataReader.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024, 2026 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Net;\nusing System.Numerics;\nusing System.Runtime.CompilerServices;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Types;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Provides a way of reading a forward-only stream of rows from a ClickHouse database. This class cannot be inherited.\n    /// </summary>\n    public sealed class ClickHouseDataReader : ClickHouseDataReaderBase\n    {\n        private readonly ClickHouseTcpClient.Session _session;\n        private readonly ClickHouseDataReaderRowLimit _rowLimit;\n        private readonly bool _ignoreProfileEvents;\n        private ulong _recordsAffected;\n\n        private int _rowIndex = -1;\n\n        private IServerMessage? _nextResultMessage;\n        private ClickHouseTable _currentTable;\n\n        private IClickHouseTableColumn[] _reinterpretedColumnsCache;\n        private ClickHouseReaderColumnSettings[]? _columnSettings;\n\n        /// <summary>\n        /// Gets the current state of the reader.\n        /// </summary>\n        public ClickHouseDataReaderState State { get; private set; }\n\n        /// <summary>\r\n        /// Gets the query execution progress reported by the server.\r\n        /// </summary>\n        public ClickHouseQueryExecutionProgress ExecutionProgress { get; private set; }\n\n        /// <summary>\n        /// Gets the number of affected rows.\n        /// </summary>\n        /// <returns>A non-negative number of rows affected by the query.</returns>\n        /// <exception cref=\"OverflowException\">The number of affected rows is greater than <see cref=\"int.MaxValue\"/>.</exception>\n        /// <remarks>Use the property <see cref=\"RecordsAffectedLong\"/> if the query can affect more than <see cref=\"int.MaxValue\"/> rows .</remarks>\n        public override int RecordsAffected\n        {\n            get\n            {\n                if (_recordsAffected <= int.MaxValue)\n                    return (int) _recordsAffected;\n\n                throw new OverflowException($\"The number of affected records is too large. Use the property \\\"{nameof(RecordsAffectedLong)}\\\" to get this number.\");\n            }\n        }\n\n        /// <summary>\n        /// Gets the number of affected rows.\n        /// </summary>\n        /// <returns>A number of rows affected by the query.</returns>\n        public ulong RecordsAffectedLong => _recordsAffected;\n\n        /// <summary>\n        /// Gets a value that indicates whether the reader contains one or more rows.\n        /// </summary>\n        /// /// <returns><see langword=\"true\"/> if the reader contains one or more rows; otherwise <see langword=\"false\"/>.</returns>\n        public override bool HasRows => _rowIndex < 0 || _recordsAffected > 0;\n\n        /// <summary>\n        /// Gets the value indicating whether the reader is closed.\n        /// </summary>\n        /// <returns><see langword=\"true\"/> if the reader is closed; otherwise <see langword=\"false\"/>.</returns>\n        public override bool IsClosed => State == ClickHouseDataReaderState.Closed || State == ClickHouseDataReaderState.Broken;\n\n        /// <summary>\n        /// Gets the number of columns.\n        /// </summary>\n        public override int FieldCount => _currentTable.Header.Columns.Count;\n\n        /// <summary>\n        /// Gets a value that indicates the depth of nesting for the current row.\n        /// </summary>\n        /// <returns>Always returns 0.</returns>\n        public override int Depth => 0;\n\n        internal ClickHouseDataReader(ClickHouseTable table, ClickHouseTcpClient.Session session, ClickHouseQueryExecutionProgress executionProgress, ClickHouseDataReaderRowLimit rowLimit, bool ignoreProfileEvents)\n        {\n            _currentTable = table.Header == null || table.Columns == null ? throw new ArgumentNullException(nameof(table)) : table;\n            _session = session ?? throw new ArgumentNullException(nameof(session));\n            _rowLimit = rowLimit;\n            _ignoreProfileEvents = ignoreProfileEvents;\n            _reinterpretedColumnsCache = new IClickHouseTableColumn[_currentTable.Columns.Count];\n            _recordsAffected = checked((ulong) _currentTable.Header.RowCount);\n            ExecutionProgress = executionProgress;\n            State = _rowLimit == ClickHouseDataReaderRowLimit.Zero ? ClickHouseDataReaderState.ClosePending : ClickHouseDataReaderState.Data;\n        }\n\n        /// <summary>\n        /// An empty, closed reader. Only for a disposed session.\n        /// </summary>\n        internal ClickHouseDataReader(ClickHouseTcpClient.Session session)\n        {\n            _currentTable = new ClickHouseTable(new BlockHeader(null, new List<ColumnInfo>(0).AsReadOnly(), 0), new List<IClickHouseTableColumn>(0).AsReadOnly());\n            _session = session ?? throw new ArgumentNullException(nameof(session));\n            _rowLimit = 0;\n            _ignoreProfileEvents = true;\n            _reinterpretedColumnsCache = Array.Empty<IClickHouseTableColumn>();\n            _recordsAffected = 0;\n\n            Debug.Assert(session.IsDisposed);\n            State = ClickHouseDataReaderState.Closed;\n        }\n\n        // Note that this xml comment is inherited by ClickHouseColumnWriter.ConfigureColumn\n        /// <summary>\n        /// Applies the settings to the specified column.\n        /// </summary>\n        /// <param name=\"name\">The name of the column.</param>\n        /// <param name=\"columnSettings\">The settings.</param>\n        public void ConfigureColumn(string name, ClickHouseColumnSettings columnSettings)\n        {\n            var index = GetOrdinal(name);\n            if (index < 0)\n                throw new ArgumentException($\"A column with the name \\\"{name}\\\" not found.\", nameof(name));\n\n            ConfigureColumn(index, columnSettings);\n        }\n\n        // Note that this xml comment is inherited by ClickHouseColumnWriter.ConfigureColumn\n        /// <summary>\n        /// Applies the settings to the specified column.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <param name=\"columnSettings\">The settings.</param>\n        public void ConfigureColumn(int ordinal, ClickHouseColumnSettings columnSettings)\n        {\n            if (_rowIndex >= 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The column can't be reconfigured during reading.\");\n\n            if (State == ClickHouseDataReaderState.ProfileEvents)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The column can't be configured when reading profile events.\");\n\n            IClickHouseColumnReinterpreter? reinterpreter = null;\n            if (columnSettings.ColumnType != null)\n                reinterpreter = ClickHouseColumnReinterpreter.Create(columnSettings.ColumnType);\n\n            var columnName = _currentTable.Header.Columns[ordinal].Name;\n            if (_columnSettings != null)\n            {\n                _columnSettings[ordinal] = _columnSettings[ordinal].WithColumnSettings(columnName, columnSettings, reinterpreter);\n            }\n            else\n            {\n                _columnSettings = new ClickHouseReaderColumnSettings[_currentTable.Columns.Count];\n                _columnSettings[ordinal] = new ClickHouseReaderColumnSettings(columnSettings, reinterpreter);\n            }\n        }\n\n        /// <summary>\n        /// Configures the reader to invoke an arbitrary type conversion callback function when reading a value of a column.\n        /// </summary>\n        /// <typeparam name=\"T\">The type supported by ClickHouseDataReader. The column is expected to be convertible to this type.</typeparam>\n        /// <typeparam name=\"TResult\">The type to which column values must be converted.</typeparam>\n        /// <param name=\"name\">The name of the column.</param>\n        /// <param name=\"readAs\">The callback function converting a value of type <typeparamref name=\"T\"/> to type <typeparamref name=\"TResult\"/>.</param>\n        /// <remarks>The callback function (<paramref name=\"readAs\"/>) must never return <see langword=\"null\"/> when its argument is not null.</remarks>\n        public void ConfigureColumnReader<T, TResult>(string name, Func<T, TResult>? readAs)\n        {\n            var index = GetOrdinal(name);\n            if (index < 0)\n                throw new ArgumentException($\"A column with the name \\\"{name}\\\" not found.\", nameof(name));\n\n            ConfigureColumnReader(index, readAs);\n        }\n\n        /// <summary>\n        /// Configures the reader to invoke an arbitrary type conversion callback function when reading a value of a column.\n        /// </summary>\n        /// <typeparam name=\"T\">The type supported by ClickHouseDataReader. The column is expected to be convertible to this type.</typeparam>\n        /// <typeparam name=\"TResult\">The type to which column values must be converted.</typeparam>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <param name=\"readAs\">The callback function converting a value of type <typeparamref name=\"T\"/> to type <typeparamref name=\"TResult\"/>.</param>\n        /// <remarks>The callback function (<paramref name=\"readAs\"/>) must never return <see langword=\"null\"/> when its argument is not null.</remarks>\n        public void ConfigureColumnReader<T, TResult>(int ordinal, Func<T, TResult>? readAs)\n        {\n            if (_rowIndex >= 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The column can't be reconfigured during reading.\");\n\n            if (State == ClickHouseDataReaderState.ProfileEvents)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The column can't be configured when reading profile events.\");\n\n            if (readAs == null && _columnSettings == null)\n                return;\n\n            IClickHouseColumnReinterpreter? reinterpreter = null;\n            if (readAs != null)\n            {\n                if (typeof(T) == typeof(object))\n                    reinterpreter = new ClickHouseObjectColumnReinterpreter<TResult>((Func<object, TResult>)(object)readAs);\n                else\n                    reinterpreter = ClickHouseColumnReinterpreter.Create(readAs);\n\n                // Dry run. The interpreter could invoke the function 'readAs' and it could fail.\n                reinterpreter.TryReinterpret(_currentTable.Columns[ordinal]);\n            }\n\n            if (_columnSettings == null)\n                _columnSettings = new ClickHouseReaderColumnSettings[_currentTable.Columns.Count];\n\n            _columnSettings[ordinal] = _columnSettings[ordinal].WithUserDefinedReader(_currentTable.Header.Columns[ordinal].Name, reinterpreter);\n        }\n\n        /// <summary>\n        /// Configures the reader to invoke an arbitrary type converter callback function provided by the dispatcher when reading a value of a column.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <param name=\"readAsDispatcher\">The instance of a dispatcher that provides a type converter callback function or <see langword=\"null\"/> to reset the dispatcher for the column.</param>\n        /// <remarks>A callback function provided by the dispathcer must never return <see langword=\"null\"/> when its argument is not null.</remarks>\n        public void ConfigureColumnReader(int ordinal, IConverterDispatcher? readAsDispatcher)\n        {\n            if (_rowIndex >= 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The column can't be reconfigured during reading.\");\n\n            if (State == ClickHouseDataReaderState.ProfileEvents)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The column can't be configured when reading profile events.\");\n\n            if (readAsDispatcher == null && _columnSettings == null)\n                return;\n\n            IClickHouseColumnReinterpreter? reinterpreter = null;\n            if (readAsDispatcher != null)\n            {\n                var dispatcher = ClickHouseColumnReinterpreter.CreateDispatcher(readAsDispatcher);\n                if (!_currentTable.Columns[ordinal].TryDipatch(dispatcher, out reinterpreter))\n                    reinterpreter = dispatcher.Dispatch<object>();\n\n                // Dry run. The interpreter could invoke the function 'readAs' and it could fail.\n                reinterpreter?.TryReinterpret(_currentTable.Columns[ordinal]);\n            }\n\n            if (reinterpreter == null && _columnSettings == null)\n                return;\n\n            if (_columnSettings == null)\n                _columnSettings = new ClickHouseReaderColumnSettings[_currentTable.Columns.Count];\n\n            _columnSettings[ordinal] = _columnSettings[ordinal].WithUserDefinedReader(_currentTable.Header.Columns[ordinal].Name, reinterpreter);\n        }\n\n        // Note that this xml comment is inherited by ClickHouseColumnWriter.ConfigureColumnWriter\n        /// <summary>\n        /// Applies the settings to all columns. All previously applied settings are discarded.\n        /// </summary>\n        /// <param name=\"columnSettings\">The settings.</param>\n        public void ConfigureDataReader(ClickHouseColumnSettings columnSettings)\n        {\n            if (_rowIndex >= 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The reader can't be reconfigured during reading.\");\n\n            IClickHouseColumnReinterpreter? reinterpreter = null;\n            if (columnSettings.ColumnType != null)\n                reinterpreter = ClickHouseColumnReinterpreter.Create(columnSettings.ColumnType);\n\n            if (_columnSettings == null)\n            {\n                var settings = new ClickHouseReaderColumnSettings(columnSettings, reinterpreter);\n                _columnSettings = new ClickHouseReaderColumnSettings[_currentTable.Columns.Count];\n                for (int i = 0; i < _columnSettings.Length; i++)\n                    _columnSettings[i] = settings;\n            }\n            else\n            {\n                var settingsCopy = new ClickHouseReaderColumnSettings[_currentTable.Columns.Count];\n                for (int i = 0; i < settingsCopy.Length; i++)\n                    settingsCopy[i] = _columnSettings[i].WithColumnSettings(_currentTable.Header.Columns[i].Name, columnSettings, reinterpreter);\n\n                _columnSettings = settingsCopy;\n            }\n        }\n\n        /// <summary>\n        /// Configures the reader to invoke an arbitrary type converter callback function provided by the dispatcher when reading values.\n        /// </summary>\n        /// <param name=\"readAsDispatcher\">The instance of a dispatcher that provides a type converter callback function.</param>\n        /// <remarks>A callback function provided by the dispathcer must never return <see langword=\"null\"/> when its argument is not null.</remarks>\n        public void ConfigureDataReader(IConverterDispatcher readAsDispatcher)\n        {\n            if (readAsDispatcher == null)\n                throw new ArgumentNullException(nameof(readAsDispatcher));\n\n            if (_rowIndex >= 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The column can't be reconfigured during reading.\");\n\n            if (State == ClickHouseDataReaderState.ProfileEvents)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The column can't be configured when reading profile events.\");\n\n            ClickHouseReaderColumnSettings[] columnSettings;\n            if (_columnSettings == null)\n                columnSettings = new ClickHouseReaderColumnSettings[_currentTable.Columns.Count];\n            else\n                columnSettings = _columnSettings.ToArray();\n\n            Debug.Assert(columnSettings.Length == FieldCount);\n            var dispatcher = ClickHouseColumnReinterpreter.CreateDispatcher(readAsDispatcher);\n            bool affected = false;\n            for (int ordinal = 0; ordinal < columnSettings.Length; ordinal++)\n            {\n                if (!_currentTable.Columns[ordinal].TryDipatch(dispatcher, out var reinterpreter))\n                    reinterpreter = dispatcher.Dispatch<object>();\n\n                if (!ReferenceEquals(reinterpreter, columnSettings[ordinal].Reinterpreter))\n                    continue;\n\n                // Dry run. The interpreter could invoke the function 'readAs' and it could fail.\n                reinterpreter?.TryReinterpret(_currentTable.Columns[ordinal]);\n\n                columnSettings[ordinal] = columnSettings[ordinal].WithUserDefinedReader(_currentTable.Header.Columns[ordinal].Name, reinterpreter);\n                affected = true;\n            }\n\n            if (affected)\n                _columnSettings = columnSettings;\n        }\n\n        // Note that this xml comment is inherited by ClickHouseColumnWriter.GetFieldTypeInfo\n        /// <summary>\n        /// Gets the <see cref=\"IClickHouseTypeInfo\"/> which represents information about the type of the column.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The <see cref=\"IClickHouseTypeInfo\"/> which represents information about the type of the column.</returns>\n        public IClickHouseTypeInfo GetFieldTypeInfo(int ordinal)\n        {\n            return _currentTable.Header.Columns[ordinal].TypeInfo;\n        }\n\n        // Note that this xml comment is inherited by ClickHouseColumnWriter.GetName\n        /// <summary>\n        /// Gets the name of the specified column.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The name of the specified column.</returns>\n        public sealed override string GetName(int ordinal)\n        {\n            return _currentTable.Header.Columns[ordinal].Name;\n        }\n\n        // Note that this xml comment is inherited by ClickHouseColumnWriter.GetDataTypeName\n        /// <summary>\n        /// Gets the name of the data type of the specified column.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The string representing the data type of the specified column.</returns>\n        public sealed override string GetDataTypeName(int ordinal)\n        {\n            return _currentTable.Header.Columns[ordinal].TypeInfo.ComplexTypeName;\n        }\n\n        // Note that this xml comment is inherited by ClickHouseColumnWriter.GetFieldType\n        /// <summary>\n        /// Gets the <see cref=\"Type\"/> that is the data type of the object.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The <see cref=\"Type\"/> that is the data type of the object.</returns>\n        public override Type GetFieldType(int ordinal)\n        {\n            // This method must return the type of a value returned by GetValue.\n            // GetValue should always return DBNull.Value instead of null.\n            // So an actual field type should be unboxed from Nullable<T>.\n\n            var reinterpreter = _columnSettings?[ordinal].Reinterpreter;\n            var type = reinterpreter?.ExternalConvertToType ?? reinterpreter?.BuiltInConvertToType;\n            type ??= _currentTable.Header.Columns[ordinal].TypeInfo.GetFieldType();\n            return Nullable.GetUnderlyingType(type) ?? type;\n        }\n\n        // Note that this xml comment is inherited by ClickHouseColumnWriter.GetOrdinal\n        /// <summary>\n        /// Gets the column ordinal, given the name of the column.\n        /// </summary>\n        /// <param name=\"name\">The name of the column.</param>\n        /// <returns>The zero-based column ordinal.</returns>\n        public sealed override int GetOrdinal(string name)\n        {\n            if (name == null)\n                throw new ArgumentNullException(nameof(name));\n\n            return CommonUtils.GetColumnIndex(_currentTable.Header.Columns, name);\n        }\n\n        /// <summary>\n        /// Reads the value as an array of <see cref=\"byte\"/> and copies values from it to the buffer.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <param name=\"dataOffset\">The index within the field from which to begin the read operation.</param>\n        /// <param name=\"buffer\">The buffer into which to copy bytes.</param>\n        /// <param name=\"bufferOffset\">The index within the <paramref name=\"buffer\"/> where the write operation is to start.</param>\n        /// <param name=\"length\">The maximum length to copy into the buffer.</param>\n        /// <returns>The actual number of bytes copied.</returns>\n        public sealed override long GetBytes(int ordinal, long dataOffset, byte[]? buffer, int bufferOffset, int length)\n        {\n            var arrayColumn = _reinterpretedColumnsCache[ordinal] as IClickHouseArrayTableColumn<byte>;\n            if (arrayColumn == null)\n            {\n                arrayColumn = _currentTable.Columns[ordinal].TryReinterpretAsArray<byte>();\n                if (arrayColumn != null)\n                    _reinterpretedColumnsCache[ordinal] = arrayColumn;\n            }\n\n            if (arrayColumn != null)\n            {\n                CheckRowIndex();\n                return arrayColumn.CopyTo(_rowIndex, new Span<byte>(buffer, bufferOffset, Math.Min(buffer?.Length ?? 0, length)), checked((int)dataOffset));\n            }\n\n            var value = GetFieldValue<byte[]>(ordinal, null);\n            if (value == null)\n            {\n                if (dataOffset == 0)\n                    return 0;\n\n                throw new ArgumentOutOfRangeException(nameof(dataOffset));\n            }\n\n            ReadOnlySpan<byte> source = value;\n            Span<byte> target = buffer;\n\n            int resultLength = Math.Min(value.Length - (int) dataOffset, length);\n\n            source = source.Slice((int) dataOffset, resultLength);\n            target = target.Slice(bufferOffset, resultLength);\n\n            source.CopyTo(target);\n            return resultLength;\n        }\n\n        /// <summary>\n        /// Reads the value as a <see cref=\"string\"/> and copies characters from it to the buffer.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <param name=\"dataOffset\">The index within the field from which to begin the read operation.</param>\n        /// <param name=\"buffer\">The buffer into which to copy characters of the string.</param>\n        /// <param name=\"bufferOffset\">The index within the <paramref name=\"buffer\"/> where the write operation is to start.</param>\n        /// <param name=\"length\">The maximum length to copy into the buffer.</param>\n        /// <returns>The actual number of characters copied.</returns>\n        public sealed override long GetChars(int ordinal, long dataOffset, char[]? buffer, int bufferOffset, int length)\n        {\n            var arrayColumn = _reinterpretedColumnsCache[ordinal] as IClickHouseArrayTableColumn<char>;\n            if (arrayColumn == null)\n            {\n                arrayColumn = _currentTable.Columns[ordinal].TryReinterpretAsArray<char>();\n                if (arrayColumn != null)\n                    _reinterpretedColumnsCache[ordinal] = arrayColumn;\n            }\n\n            if (arrayColumn != null)\n            {\n                CheckRowIndex();\n                return arrayColumn.CopyTo(_rowIndex, new Span<char>(buffer, bufferOffset, Math.Min(buffer?.Length ?? 0, length)), checked((int)dataOffset));\n            }\n\n            var value = GetFieldValue<string>(ordinal, null);\n            if (value == null)\n            {\n                if (dataOffset == 0)\n                    return 0;\n\n                throw new ArgumentOutOfRangeException(nameof(dataOffset));\n            }\n\n            ReadOnlySpan<char> source = value;\n            Span<char> target = buffer;\n\n            int resultLength = Math.Min(value.Length - (int) dataOffset, length);\n\n            source = source.Slice((int) dataOffset, resultLength);\n            target = target.Slice(bufferOffset, resultLength);\n\n            source.CopyTo(target);\n            return resultLength;\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"BigInteger\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public BigInteger GetBigInteger(int ordinal)\n        {\n            return GetFieldValue<BigInteger>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"bool\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override bool GetBoolean(int ordinal)\n        {\n            return GetFieldValue<bool>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"byte\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override byte GetByte(int ordinal)\n        {\n            return GetFieldValue<byte>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"sbyte\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sbyte GetSByte(int ordinal)\n        {\n            return GetFieldValue<sbyte>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"char\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override char GetChar(int ordinal)\n        {\n            return GetFieldValue<char>(ordinal);\n        }\n\n#if NET6_0_OR_GREATER\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"DateOnly\"/> object.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public DateOnly GetDate(int ordinal)\n        {\n            return GetFieldValue<DateOnly>(ordinal);\n        }\n#endif\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"DateTime\"/> object.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override DateTime GetDateTime(int ordinal)\n        {\n            return GetFieldValue<DateTime>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"decimal\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override decimal GetDecimal(int ordinal)\n        {\n            return GetFieldValue<decimal>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"double\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override double GetDouble(int ordinal)\n        {\n            return GetFieldValue<double>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"float\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override float GetFloat(int ordinal)\n        {\n            return GetFieldValue<float>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a globally unique identifier (<see cref=\"Guid\"/>).\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override Guid GetGuid(int ordinal)\n        {\n            return GetFieldValue<Guid>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"short\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override short GetInt16(int ordinal)\n        {\n            return GetFieldValue<short>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"ushort\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public ushort GetUInt16(int ordinal)\n        {\n            return GetFieldValue<ushort>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as an <see cref=\"int\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override int GetInt32(int ordinal)\n        {\n            return GetFieldValue<int>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as an <see cref=\"uint\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public uint GetUInt32(int ordinal)\n        {\n            return GetFieldValue<uint>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"long\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override long GetInt64(int ordinal)\n        {\n            return GetFieldValue<long>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"ulong\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public ulong GetUInt64(int ordinal)\n        {\n            return GetFieldValue<ulong>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as an instance of <see cref=\"IPAddress\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public IPAddress GetIPAddress(int ordinal)\n        {\n            return GetFieldValue<IPAddress>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as an instance of <see cref=\"string\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override string GetString(int ordinal)\n        {\n            return GetFieldValue<string>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as an instance of <see cref=\"string\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <param name=\"nullValue\">The default value which should be returned if the value of the specified column is <see cref=\"DBNull.Value\"/>.</param>\n        /// <returns>The value of the specified column or <paramref name=\"nullValue\"/> if the value of the column is <see cref=\"DBNull.Value\"/>.</returns>\n        [return: NotNullIfNotNull(\"nullValue\")]\n        public string? GetString(int ordinal, string? nullValue)\n        {\n            return GetFieldValue(ordinal, nullValue);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as a <see cref=\"DateTimeOffset\"/> object.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public DateTimeOffset GetDateTimeOffset(int ordinal)\n        {\n            return GetFieldValue<DateTimeOffset>(ordinal);\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as an object of the type <typeparamref name=\"T\"/>.\n        /// </summary>\n        /// <typeparam name=\"T\">The expected type of the column's value.</typeparam>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <param name=\"nullValue\">The default value which should be returned if the value of the specified column is <see cref=\"DBNull.Value\"/>.</param>\n        /// <returns>The value of the specified column or <paramref name=\"nullValue\"/> if the value of the column is <see cref=\"DBNull.Value\"/>.</returns>\n        [return: NotNullIfNotNull(\"nullValue\")]\n        public T? GetFieldValue<T>(int ordinal, T? nullValue)\n            where T : class\n        {\n            CheckRowIndex();\n            var column = _currentTable.Columns[ordinal];\n            if (column is IClickHouseTableColumn<T> typedColumn)\n                return typedColumn.GetValue(_rowIndex) ?? nullValue;\n\n            if (_reinterpretedColumnsCache[ordinal] is IClickHouseTableColumn<T> reinterpretedColumn)\n                return reinterpretedColumn.GetValue(_rowIndex) ?? nullValue;\n\n            var rc = column.TryReinterpret<T>();\n            if (rc != null)\n            {\n                _reinterpretedColumnsCache[ordinal] = rc;\n                return rc.GetValue(_rowIndex) ?? nullValue;\n            }\n\n            if (column.IsNull(_rowIndex))\n                return nullValue;\n\n            var value = column.GetValue(_rowIndex);\n            return (T) value;\n        }\n\n        /// <inheritdoc cref=\"GetFieldValue{T}(int, T)\"/>\n        [return: NotNullIfNotNull(\"nullValue\")]\n        public T? GetFieldValue<T>(int ordinal, T? nullValue)\n            where T : struct\n        {\n            CheckRowIndex();\n            var column = _currentTable.Columns[ordinal];\n            if (column is IClickHouseTableColumn<T?> nullableTypedColumn)\n                return nullableTypedColumn.GetValue(_rowIndex) ?? nullValue;\n\n            var rc = _reinterpretedColumnsCache[ordinal] as IClickHouseTableColumn<T?>;\n            if (rc != null)\n                return rc.GetValue(_rowIndex) ?? nullValue;\n\n            rc = column.TryReinterpret<T?>();\n            if (rc == null)\n            {\n                var structRc = column.TryReinterpret<T>();\n                if (structRc != null)\n                    rc = new NullableStructTableColumn<T>(null, structRc);\n            }\n\n            if (rc == null && column is IClickHouseTableColumn<T> typedColumn)\n                rc = new NullableStructTableColumn<T>(null, typedColumn);\n\n            if (rc != null)\n            {\n                _reinterpretedColumnsCache[ordinal] = rc;\n                return rc.GetValue(_rowIndex) ?? nullValue;\n            }\n\n            if (column.IsNull(_rowIndex))\n                return nullValue;\n\n            var value = column.GetValue(_rowIndex);\n            return (T) value;\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as an object of the type <typeparamref name=\"T\"/>.\n        /// </summary>\n        /// <typeparam name=\"T\">The expected type of the column's value.</typeparam>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column.</returns>\n        public sealed override T GetFieldValue<T>(int ordinal)\n        {\n            CheckRowIndex();\n            var column = _currentTable.Columns[ordinal];\n            if (column.IsNull(_rowIndex))\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The value is null.\");\n\n            if (column is IClickHouseTableColumn<T> typedColumn)\n                return typedColumn.GetValue(_rowIndex);\n\n            if (_reinterpretedColumnsCache[ordinal] is IClickHouseTableColumn<T> reinterpretedColumn)\n                return reinterpretedColumn.GetValue(_rowIndex);\n\n            var rc = column.TryReinterpret<T>();\n            if (rc != null)\n            {\n                _reinterpretedColumnsCache[ordinal] = rc;\n                return rc.GetValue(_rowIndex);\n            }\n\n            var value = column.GetValue(_rowIndex);\n            return (T) value;\n        }\n\n        /// <summary>\n        /// Gets the value of the specified column as an <see cref=\"object\"/>.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns>The value of the specified column or <see cref=\"DBNull.Value\"/>.</returns>\n        public sealed override object GetValue(int ordinal)\n        {\n            CheckRowIndex();\n            var column = _currentTable.Columns[ordinal];\n            return column.GetValue(_rowIndex);\n        }\n\n        /// <summary>\n        /// Populates an array of objects with the column values of the current row.\n        /// </summary>\n        /// <param name=\"values\">An array of <see cref=\"object\"/> into which to copy the attribute columns.</param>\n        /// <returns>The number of instances of <see cref=\"object\"/> in the array.</returns>\n        public sealed override int GetValues(object[] values)\n        {\n            CheckRowIndex();\n            var count = Math.Min(_currentTable.Columns.Count, values.Length);\n            for (int i = 0; i < count; i++)\n            {\n                var column = _currentTable.Columns[i];\n                values[i] = column.GetValue(_rowIndex);\n            }\n\n            return count;\n        }\n\n        /// <summary>\n        /// Gets a value that indicates whether the column contains non-existent or missing values.\n        /// </summary>\n        /// <param name=\"ordinal\">The zero-based column ordinal.</param>\n        /// <returns><see langword=\"true\"/> if the specified column is equivalent to <see cref=\"DBNull.Value\"/>. Otherwise <see langword=\"false\"/>.</returns>\n        public sealed override bool IsDBNull(int ordinal)\n        {\n            CheckRowIndex();\n            var column = _currentTable.Columns[ordinal];\n            return column.IsNull(_rowIndex);\n        }\n\n        /// <inheritdoc cref=\"GetValue(int)\"/>\n        public sealed override object this[int ordinal] => GetValue(ordinal);\n\n        /// <summary>\n        /// Gets the value of the specified column as an <see cref=\"object\"/>.\n        /// </summary>\n        /// <param name=\"name\">The name of the column.</param>\n        /// <returns>The value of the specified column or <see cref=\"DBNull.Value\"/>.</returns>\n        public sealed override object this[string name]\n        {\n            get\n            {\n                var ordinal = GetOrdinal(name);\n                if (ordinal < 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, $\"Column \\\"{name}\\\" not found.\");\n\n                return GetValue(ordinal);\n            }\n        }\n\n        /// <summary>\n        /// Advances the reader to the next record in a result set.\n        /// </summary>\n        /// <returns><see langword=\"true\"/> if there are more rows or <see langword=\"false\"/> if there aren't.</returns>\n        public sealed override bool Read()\n        {\n            return TaskHelper.WaitNonAsyncTask(Read(false, CancellationToken.None));\n        }\n\n        /// <summary>\n        /// Asyncronously advances the reader to the next record in a result set.\n        /// </summary>\n        /// <returns>\n        /// A <see cref=\"ValueTask{TResult}\"/> whose <see cref=\"ValueTask{TResult}.Result\"/> is\n        /// <see langword=\"true\"/> if there are more rows or <see langword=\"false\"/> if there aren't.\n        /// </returns>\n        public new ValueTask<bool> ReadAsync()\n        {\n            return Read(true, CancellationToken.None);\n        }\n\n        /// <summary>\n        /// Asyncronously advances the reader to the next record in a result set.\n        /// </summary>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>\n        /// A <see cref=\"ValueTask{TResult}\"/> whose <see cref=\"ValueTask{TResult}.Result\"/> is\n        /// <see langword=\"true\"/> if there are more rows or <see langword=\"false\"/> if there aren't.\n        /// </returns>\n        public new ValueTask<bool> ReadAsync(CancellationToken cancellationToken)\n        {\n            return Read(true, cancellationToken);\n        }\n\n        /// <inheritdoc cref=\"ClickHouseDataReaderBase.ReadAsync(CancellationToken)\"/>\n        protected sealed override async Task<bool> ReadAsyncInternal(CancellationToken cancellationToken)\n        {\n            return await Read(true, cancellationToken);\n        }\n\n        private async ValueTask<bool> Read(bool async, CancellationToken cancellationToken)\n        {\n            if (State == ClickHouseDataReaderState.Closed || State == ClickHouseDataReaderState.Broken || State == ClickHouseDataReaderState.ClosePending)\n                return false;\n\n            bool result;\n            if (++_rowIndex >= _currentTable.Header.RowCount)\n            {\n                _rowIndex = _currentTable.Header.RowCount;\n\n                if (State == ClickHouseDataReaderState.Data || State == ClickHouseDataReaderState.Totals || State == ClickHouseDataReaderState.Extremes)\n                {\n                    result = await Read(false, async, cancellationToken);\n\n                    if (!result && _rowLimit == ClickHouseDataReaderRowLimit.OneResult && State == ClickHouseDataReaderState.NextResultPending)\n                        await Cancel(false, async);\n                }\n                else\n                {\n                    result = false;\n                }\n            }\n            else\n            {                \n                result = true;\n            }\n\n            if (_rowLimit == ClickHouseDataReaderRowLimit.OneRow && result)\n                await Cancel(false, async);\n\n            return result;\n        }\n\n        private async ValueTask<bool> Read(bool nextResult, bool async, CancellationToken cancellationToken)\n        {\n            while (true)\n            {\n                ClickHouseTable nextTable;\n\n                try\n                {\n                    var message = _nextResultMessage;\n                    if (message == null)\n                        message = await _session.ReadMessage(async, cancellationToken);\n                    else\n                        _nextResultMessage = null;\n\n                    switch (message.MessageCode)\n                    {\n                        case ServerMessageCode.Data:\n                            switch (State)\n                            {\n                                case ClickHouseDataReaderState.NextResultPending:\n                                    State = ClickHouseDataReaderState.Data;\n                                    goto case ClickHouseDataReaderState.Data;\n\n                                case ClickHouseDataReaderState.Data:\n                                {\n                                    var dataMessage = (ServerDataMessage) message;\n                                    nextTable = await _session.ReadTable(dataMessage, _columnSettings, async, cancellationToken);\n                                    break;\n                                }\n\n                                case ClickHouseDataReaderState.Totals:\n                                case ClickHouseDataReaderState.Extremes:\n                                {\n                                    var dataMessage = (ServerDataMessage)message;\n                                    var table = await _session.SkipTable(dataMessage, async, cancellationToken);\n                                    if (table.RowCount != 0 || table.Columns.Count != 0)\n                                        throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, \"Unexpected data block after totals or extremes.\");\n\n                                    continue;\n                                }\n\n                                case ClickHouseDataReaderState.ProfileEvents:\n                                    _nextResultMessage = message;\n                                    State = ClickHouseDataReaderState.NextResultPending;\n                                    return false;\n\n                                default:\n                                    goto UNEXPECTED_RESPONSE;\n                            }\n\n                            break;\n\n                        case ServerMessageCode.Error:\n                            State = ClickHouseDataReaderState.Closed;\n                            await _session.Dispose(async);\n                            throw ((ServerErrorMessage) message).Exception;\n\n                        case ServerMessageCode.Progress:\n                            var progressMessage = (ServerProgressMessage) message;\n                            ExecutionProgress = progressMessage.ExecutionProgress;\n                            _recordsAffected = ExecutionProgress.Rows;\n                            continue;\n\n                        case ServerMessageCode.EndOfStream:\n                            State = ClickHouseDataReaderState.Closed;\n                            await _session.Dispose(async);\n                            return false;\n\n                        case ServerMessageCode.ProfileInfo:\n                            continue;\n\n                        case ServerMessageCode.Totals:\n                            switch (State)\n                            {\n                                case ClickHouseDataReaderState.NextResultPending:\n                                    State = ClickHouseDataReaderState.Totals;\n                                    goto case ClickHouseDataReaderState.Totals;\n\n                                case ClickHouseDataReaderState.Totals:\n                                    var totalsMessage = (ServerDataMessage) message;\n                                    nextTable = await _session.ReadTable(totalsMessage, _columnSettings, async, cancellationToken);\n                                    break;\n\n                                case ClickHouseDataReaderState.Data:\n                                case ClickHouseDataReaderState.Extremes:\n                                case ClickHouseDataReaderState.ProfileEvents:\n                                    _nextResultMessage = message;\n                                    State = ClickHouseDataReaderState.NextResultPending;\n                                    return false;\n\n                                default:\n                                    goto UNEXPECTED_RESPONSE;\n                            }\n\n                            break;\n\n                        case ServerMessageCode.Extremes:\n                            switch (State)\n                            {\n                                case ClickHouseDataReaderState.NextResultPending:\n                                    State = ClickHouseDataReaderState.Extremes;\n                                    goto case ClickHouseDataReaderState.Extremes;\n\n                                case ClickHouseDataReaderState.Extremes:\n                                    var extremesMessage = (ServerDataMessage) message;\n                                    nextTable = await _session.ReadTable(extremesMessage, _columnSettings, async, cancellationToken);\n                                    break;\n\n                                case ClickHouseDataReaderState.Data:\n                                case ClickHouseDataReaderState.Totals:\n                                case ClickHouseDataReaderState.ProfileEvents:\n                                    _nextResultMessage = message;\n                                    State = ClickHouseDataReaderState.NextResultPending;\n                                    return false;\n\n                                default:\n                                    goto UNEXPECTED_RESPONSE;\n                            }\n\n                            break;\n\n                        case ServerMessageCode.ProfileEvents:\n                            if (_ignoreProfileEvents)\n                            {\n                                await _session.SkipTable((ServerDataMessage)message, async, cancellationToken);\n                                continue;\n                            }\n\n                            switch (State)\n                            {\n                                case ClickHouseDataReaderState.NextResultPending:\n                                    State = ClickHouseDataReaderState.ProfileEvents;\n                                    goto case ClickHouseDataReaderState.ProfileEvents;\n\n                                case ClickHouseDataReaderState.ProfileEvents:\n                                    var profileEventsMessage = (ServerDataMessage) message;\n                                    nextTable = await _session.ReadTable(profileEventsMessage, null, async, cancellationToken);\n                                    break;\n\n                                case ClickHouseDataReaderState.Data:\n                                case ClickHouseDataReaderState.Extremes:\n                                case ClickHouseDataReaderState.Totals:\n                                    _nextResultMessage = message;\n                                    State = ClickHouseDataReaderState.NextResultPending;\n                                    return false;\n\n                                default:\n                                    goto UNEXPECTED_RESPONSE;\n                            }\n\n                            break;\n\n                        case ServerMessageCode.Pong:\n                        case ServerMessageCode.Hello:\n                        case ServerMessageCode.Log:\n                            UNEXPECTED_RESPONSE:\n                            throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Unexpected server message: \\\"{message.MessageCode}\\\".\");\n\n                        default:\n                            throw new NotSupportedException($\"Internal error. Message code \\\"{message.MessageCode}\\\" not supported.\");\n                    }\n                }\n                catch (ClickHouseHandledException)\n                {\n                    throw;\n                }\n                catch (ClickHouseServerException)\n                {\n                    throw;\n                }\n                catch (Exception ex)\n                {\n                    State = ClickHouseDataReaderState.Broken;\n                    var aggrEx = await _session.SetFailed(ex, true, async);\n                    if (aggrEx != null)\n                        throw aggrEx;\n\n                    throw;\n                }\n\n                if (nextTable.Header.RowCount == 0)\n                    continue;\n\n                _currentTable = nextTable;\n                _reinterpretedColumnsCache = new IClickHouseTableColumn[_currentTable.Columns.Count];\n                _recordsAffected = checked(_recordsAffected + (ulong) nextTable.Header.RowCount);\n                _rowIndex = nextResult ? -1 : 0;\n                return true;\n            }\n        }\n\n        /// <summary>\n        /// Asyncronously advances the reader to the next result set. The ClickHouse server can send totals or extremes as additional result sets.\n        /// </summary>\n        /// <returns>A <see cref=\"Task{T}\"/> representing asyncronous operation. The result (<see cref=\"Task{TResult}.Result\"/>) is\n        /// <see langword=\"true\"/> if there are more result sets; otherwise <see langword=\"false\"/>\n        /// </returns>\n        public override async Task<bool> NextResultAsync(CancellationToken cancellationToken)\n        {\n            return await NextResult(true, cancellationToken);\n        }\n\n        /// <summary>\n        /// Advances the reader to the next result set. The ClickHouse server can send totals or extremes as additional result sets.\n        /// </summary>\n        /// <returns><see langword=\"true\"/> if there are more result sets; otherwise <see langword=\"false\"/></returns>\n        public override bool NextResult()\n        {\n            return TaskHelper.WaitNonAsyncTask(NextResult(false, CancellationToken.None));\n        }\n\n        private async ValueTask<bool> NextResult(bool async, CancellationToken cancellationToken)\n        {\n            if (State == ClickHouseDataReaderState.Data || State == ClickHouseDataReaderState.Totals || State == ClickHouseDataReaderState.Extremes || State == ClickHouseDataReaderState.ProfileEvents)\n            {\n                bool canReadNext;\n                do\n                {\n                    _rowIndex = Math.Max(_rowIndex, _currentTable.Header.RowCount - 1);\n                    // TODO: skip without actual reading\n                    canReadNext = await Read(false, async, cancellationToken);\n                } while (canReadNext);\n            }\n\n            if (State != ClickHouseDataReaderState.NextResultPending)\n                return false;\n\n            return await Read(true, async, cancellationToken);\n        }\n\n        private async ValueTask Cancel(bool disposing, bool async)\n        {\n            if (State == ClickHouseDataReaderState.Closed || State == ClickHouseDataReaderState.Broken || State == ClickHouseDataReaderState.ClosePending)\n                return;\n\n            try\n            {\n                await _session.SendCancel(async);\n                State = ClickHouseDataReaderState.ClosePending;\n            }\n            catch (Exception ex)\n            {\n                State = ClickHouseDataReaderState.Broken;\n                await _session.SetFailed(ex, false, async);\n\n                if (!disposing)\n                    throw;\n            }\n        }\n\n        /// <summary>\n        /// Closes the reader.\n        /// </summary>\n        public override void Close()\n        {\n            TaskHelper.WaitNonAsyncTask(Close(false, false));\n        }\n\n        /// <summary>\n        /// Asyncronously closes the reader.\n        /// </summary>\n        /// <returns>A <see cref=\"Task\"/> representing the asyncronous operation.</returns>\n        public override async Task CloseAsync()\n        {\n            await Close(false, true);\n        }\n\n        private async ValueTask Close(bool disposing, bool async)\n        {\n            if (_session.IsDisposed || _session.IsFailed)\n                return;\n\n            if (!(State == ClickHouseDataReaderState.Closed || State == ClickHouseDataReaderState.Broken))\n            {\n                await Cancel(disposing, async);\n\n                try\n                {\n                    while (true)\n                    {\n\n                        var message = _nextResultMessage ?? await _session.ReadMessage(async, CancellationToken.None);\n                        _nextResultMessage = null;\n\n                        switch (message.MessageCode)\n                        {\n                            case ServerMessageCode.Data:\n                            case ServerMessageCode.Totals:\n                            case ServerMessageCode.Extremes:\n                            case ServerMessageCode.ProfileEvents:\n                                var dataMessage = (ServerDataMessage) message;\n                                await _session.SkipTable(dataMessage, async, CancellationToken.None);\n                                continue;\n\n                            case ServerMessageCode.Error:\n                                State = ClickHouseDataReaderState.Closed;\n                                if (disposing)\n                                    break;\n\n                                throw ((ServerErrorMessage) message).Exception;\n\n                            case ServerMessageCode.Progress:\n                                var progressMessage = (ServerProgressMessage) message;\n                                ExecutionProgress = progressMessage.ExecutionProgress;\n                                _recordsAffected = ExecutionProgress.Rows;\n                                continue;\n\n                            case ServerMessageCode.EndOfStream:\n                                State = ClickHouseDataReaderState.Closed;\n                                break;\n\n                            case ServerMessageCode.ProfileInfo:\n                                continue;\n\n                            case ServerMessageCode.Pong:\n                            case ServerMessageCode.Hello:\n                            case ServerMessageCode.Log:\n                                throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Unexpected server message: \\\"{message.MessageCode}\\\".\");\n\n                            default:\n                                throw new NotSupportedException($\"Internal error. Message code \\\"{message.MessageCode}\\\" not supported.\");\n                        }\n\n                        break;\n                    }\n                }\n                catch (ClickHouseHandledException ex)\n                {\n                    if (!disposing)\n                        throw;\n\n                    State = ClickHouseDataReaderState.Broken;\n                    await _session.SetFailed(ex.InnerException, false, async);\n                    return;\n                }\n                catch (Exception ex)\n                {\n                    State = ClickHouseDataReaderState.Broken;\n                    var aggrEx = await _session.SetFailed(ex, false, async);\n\n                    if (disposing)\n                        return;\n\n                    if (aggrEx != null)\n                        throw aggrEx;\n\n                    throw;\n                }\n            }\n\n            await _session.Dispose(async);\n        }\n\n        /// <inheritdoc/>\n        protected override void Dispose(bool disposing)\n        {\n            if (!disposing)\n                return;\n\n            TaskHelper.WaitNonAsyncTask(Close(true, false));\n        }\n\n        /// <inheritdoc/>\n        public override ValueTask DisposeAsync()\n        {\n            return Close(true, true);\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        private void CheckRowIndex()\n        {\n            if (_rowIndex >= _currentTable.Header.RowCount)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"The reader is reached the end of the table.\");\n            if (_rowIndex < 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, $\"There are no rows to read. The call of the method {nameof(Read)} is required.\");\n        }\n\n        /// <summary>\n        /// Not supported. An enumerator iterating through the rows of the reader is not implemented.\n        /// </summary>\n        /// <exception cref=\"NotImplementedException\">Always throws <see cref=\"NotImplementedException\"/>.</exception>\n        public override IEnumerator GetEnumerator()\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseDataReaderBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Data.Common;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// This is an infrastracture class. It allows it's descendants to overload the method <see cref=\"ReadAsync(CancellationToken)\"/>.    \n    /// </summary>\n    public abstract class ClickHouseDataReaderBase : DbDataReader\n    {\n        private protected ClickHouseDataReaderBase()\n        {\n        }\n\n        /// <inheritdoc /> \n        public sealed override Task<bool> ReadAsync(CancellationToken cancellationToken)\n        {\n            return ReadAsyncInternal(cancellationToken);\n        }\n\n        /// <summary>\n        /// When overriden in a derived class should asyncronously advance the reader to the next record in a result set.\n        /// </summary>\n        /// <param name=\"cancellationToken\">The cancellation instruction.</param>\n        /// <returns>\n        /// A <see cref=\"Task{T}\"/> representing asyncronous operation. The result (<see cref=\"Task{TResult}.Result\"/>) is <see langword=\"true\"/>\n        /// if there are more rows or <see langword=\"false\"/> if there aren't.\n        /// </returns>\n        protected abstract Task<bool> ReadAsyncInternal(CancellationToken cancellationToken);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseDataReaderRowLimit.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient\n{\n    internal enum ClickHouseDataReaderRowLimit\n    {\n        Zero = 0,\n        OneRow = 1,\n        OneResult = 2,\n        Infinite = 3\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseDataReaderState.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Describes the current state of the <see cref=\"ClickHouseDataReader\"/>.\n    /// </summary>\n    public enum ClickHouseDataReaderState\n    {\n        /// <summary>\n        /// The reader was broken.\n        /// This usualy indicates that there were a network error while fetching data.\n        /// </summary>\n        Broken = 0,\n\n        /// <summary>\n        /// The reader is closed. Which means that the end of the data stream was reached.\n        /// </summary>\n        Closed = 1,\n\n        /// <summary>\n        /// The reader is currently reading the main result set.\n        /// </summary>\n        Data = 2,\n\n        /// <summary>\n        /// The reader reached to the end of the current result set and there possible are the next result set.\n        /// <br/>\n        /// Use one of methods <see cref=\"ClickHouseDataReader.NextResult()\"/> or <see cref=\"ClickHouseDataReader.NextResultAsync(System.Threading.CancellationToken)\"/>\n        /// to check if there are the next result set.\n        /// </summary>\n        NextResultPending = 3,\n\n        /// <summary>\n        /// The reader is currently reading the TOTALS result set.\n        /// </summary>\n        Totals = 4,\n\n        /// <summary>\n        /// The reader is currently reading the EXTREMES result set.\n        /// </summary>\n        Extremes = 5,\n\n        /// <summary>\n        /// The reader can no longer read the data and waiting for closing.\n        /// <br/>\n        /// Use one of methods <see cref=\"ClickHouseDataReader.Close()\"/> <see cref=\"ClickHouseDataReader.CloseAsync()\"/>\n        /// to close the reader.\n        /// </summary>\n        /// <remarks>\n        /// This state usualy indicates that the reader was created with a <see cref=\"System.Data.CommandBehavior\"/> that\n        /// forbids further reading even if there are more rows in the current result set or there are more result sets.\n        /// </remarks>\n        ClosePending = 6,\n\n        /// <summary>\r\n        /// The reader is currently reading profile events.\r\n        /// </summary>\n        ProfileEvents = 7\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseDbProviderFactory.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Data.Common;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents a set of methods for creating instances of ClickHouseClient's implementation of the data source classes.\n    /// </summary>\n    public class ClickHouseDbProviderFactory : DbProviderFactory\n    {\n        /// <summary>\n        /// Returns a new instance of <see cref=\"ClickHouseConnectionStringBuilder\"/>.\n        /// </summary>\n        /// <returns>A new instance of <see cref=\"ClickHouseConnectionStringBuilder\"/>.</returns>\n        public override DbConnectionStringBuilder CreateConnectionStringBuilder()\n        {\n            return new ClickHouseConnectionStringBuilder();\n        }\n\n        /// <summary>\n        /// Returns a new instance of <see cref=\"ClickHouseConnection\"/>.\n        /// </summary>\n        /// <returns>A new instance of <see cref=\"ClickHouseConnection\"/>.</returns>\n        public override DbConnection CreateConnection()\n        {\n            return new ClickHouseConnection();\n        }\n\n        /// <summary>\n        /// Returns a new instance of <see cref=\"ClickHouseCommand\"/>.\n        /// </summary>\n        /// <returns>A new instance of <see cref=\"ClickHouseCommand\"/>.</returns>\n        public override DbCommand CreateCommand()\n        {\n            return new ClickHouseCommand();\n        }\n\n        /// <summary>\n        /// Returns a new instance of <see cref=\"ClickHouseParameter\"/>.\n        /// </summary>\n        /// <returns>A new instance of <see cref=\"ClickHouseParameter\"/>.</returns>\n        public override DbParameter CreateParameter()\n        {\n            return new ClickHouseParameter();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseDbType.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Data;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Specifies the data type of a field (column) or a <see cref=\"ClickHouseParameter\"/> object.\n    /// </summary>\n    public enum ClickHouseDbType\n    {\n        /// <summary>\n        /// The type is not supported by the client. An encoding should be defined explicitly via <see cref=\"ClickHouseParameter.StringEncoding\"/>\n        /// or <see cref=\"ClickHouseColumnSettings.StringEncoding\"/>.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.AnsiString\"/>.</remarks>\n        AnsiString = DbType.AnsiString,\n\n        /// <summary>\n        /// An array of bytes.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Binary\"/>.</remarks>\n        Binary = DbType.Binary,\n\n        /// <summary>\n        /// An 8-bit unsigned integer ranging in value from 0 to 255.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Byte\"/>.</remarks>\n        Byte = DbType.Byte,\n\n        /// <summary>\n        /// A simple type representing Boolean values of <see langword=\"true\"/> or <see langword=\"false\"/>.        \n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Boolean\"/>.</remarks>\n        Boolean = DbType.Boolean,\n\n        /// <summary>\n        /// The ClickHouse type Decimal(18, 4).\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Currency\"/>.</remarks>\n        Currency = DbType.Currency,\n\n        /// <summary>\n        /// A type representing a date value without a time.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Date\"/>.</remarks>\n        Date = DbType.Date,\n\n        /// <summary>\n        /// A type representing a date and time value.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.DateTime\"/>.</remarks>\n        DateTime = DbType.DateTime,\n\n        /// <summary>\n        /// The ClickHouse type Decimal(38, 9).\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Decimal\"/>.</remarks>\n        Decimal = DbType.Decimal,\n\n        /// <inheritdoc cref=\"DbType.Double\"/>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Double\"/>.</remarks>\n        Double = DbType.Double,\n\n        /// <summary>\n        /// The ClickHouse type UUID.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Guid\"/>.</remarks>\n        Guid = DbType.Guid,\n\n        /// <inheritdoc cref=\"DbType.Int16\"/>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Int16\"/>.</remarks>\n        Int16 = DbType.Int16,\n\n        /// <inheritdoc cref=\"DbType.Int32\"/>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Int32\"/>.</remarks>\n        Int32 = DbType.Int32,\n\n        /// <inheritdoc cref=\"DbType.Int64\"/>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Int64\"/>.</remarks>\n        Int64 = DbType.Int64,\n\n        /// <summary>\n        /// A general type representing a value of either an unknown type or the ClickHouse type Nothing.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Object\"/>.</remarks>\n        Object = DbType.Object,\n\n        /// <inheritdoc cref=\"DbType.SByte\"/>\n        /// <remarks>This value corresponds to <see cref=\"DbType.SByte\"/>.</remarks>\n        SByte = DbType.SByte,\n\n        /// <inheritdoc cref=\"DbType.Single\"/>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Single\"/>.</remarks>\n        Single = DbType.Single,\n\n        /// <summary>\n        /// A variable-length string.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.String\"/>.</remarks>\n        String = DbType.String,\n\n        /// <summary>\n        /// The type is not supported by the client.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Time\"/>.</remarks>\n        Time = DbType.Time,\n\n        /// <inheritdoc cref=\"DbType.UInt16\"/>\n        /// <remarks>This value corresponds to <see cref=\"DbType.UInt16\"/>.</remarks>\n        UInt16 = DbType.UInt16,\n\n        /// <inheritdoc cref=\"DbType.UInt32\"/>\n        /// <remarks>This value corresponds to <see cref=\"DbType.UInt32\"/>.</remarks>\n        UInt32 = DbType.UInt32,\n\n        /// <inheritdoc cref=\"DbType.UInt64\"/>\n        /// <remarks>This value corresponds to <see cref=\"DbType.UInt64\"/>.</remarks>\n        UInt64 = DbType.UInt64,\n\n        /// <summary>\n        /// A type representing numeric values with the specified precision (from 1 to 76) and scale (from 0 to the precision).\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.VarNumeric\"/>.</remarks>\n        VarNumeric = DbType.VarNumeric,\n\n        /// <summary>\n        /// The type is not supported by the client.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.AnsiStringFixedLength\"/>.</remarks>\n        AnsiStringFixedLength = DbType.AnsiStringFixedLength,\n\n        /// <summary>\n        /// A fixed-length string.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.StringFixedLength\"/>.</remarks>\n        StringFixedLength = DbType.StringFixedLength,\n\n        /// <summary>\n        /// The type is not supported by the client.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.Xml\"/>.</remarks>\n        Xml = DbType.Xml,\n\n        /// <summary>\n        /// <seealso cref=\"DateTime64\"/> with an accuracy of 100 nanoseconds.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.DateTime2\"/>.</remarks>\n        DateTime2 = DbType.DateTime2,\n\n        /// <summary>\n        /// A type representing a date and time value with time zone awareness.\n        /// </summary>\n        /// <remarks>This value corresponds to <see cref=\"DbType.DateTimeOffset\"/>.</remarks>\n        DateTimeOffset = DbType.DateTimeOffset,\n\n        /// <summary>\n        /// It's not a valid code for any type. This value is used as the delimiter between <see cref=\"DbType\"/> and ClickHouse-specific type codes.\n        /// </summary>\n        ClickHouseSpecificTypeDelimiterCode = 0x3FFF,\n\n        /// <summary>\n        /// A type representing an IP v4 address.\n        /// </summary>\n        IpV4 = ClickHouseSpecificTypeDelimiterCode + 1,\n\n        /// <summary>\n        /// A type representing an IP v6 address.\n        /// </summary>\n        IpV6 = ClickHouseSpecificTypeDelimiterCode + 2,\n\n        /// <summary>\n        /// A type representing a variable-length array of values.\n        /// </summary>\n        Array = ClickHouseSpecificTypeDelimiterCode + 3,\n\n        /// <summary>\n        /// A type representing a fixed-length set of values.\n        /// </summary>\n        Tuple = ClickHouseSpecificTypeDelimiterCode + 4,\n\n        /// <summary>\n        /// A type representing NULL value.\n        /// </summary>\n        Nothing = ClickHouseSpecificTypeDelimiterCode + 5,\n\n        /// <summary>\n        /// A type representing Enum value.\n        /// </summary>\n        Enum = ClickHouseSpecificTypeDelimiterCode + 6,\n\n        /// <summary>\n        /// A type representing a date and time value with defined sub-second precision.\n        /// Supported range of values: [1900-01-01 00:00:00, 2299-12-31 23:59:59.99999999]\n        /// </summary>\n        DateTime64 = ClickHouseSpecificTypeDelimiterCode + 7,\n\n        /// <summary>\n        /// The ClickHouse type Map(key, value). This type is not supported by <see cref=\"ClickHouseParameter\"/>.\n        /// </summary>\n        Map = ClickHouseSpecificTypeDelimiterCode + 8,\n\n        /// <summary>\n        /// An integral type representing signed 128-bit integers with values between -170141183460469231731687303715884105728 and 170141183460469231731687303715884105727.\n        /// </summary>\n        Int128 = ClickHouseSpecificTypeDelimiterCode + 9,\n\n        /// <summary>\n        /// An integral type representing unsigned 128-bit integers with values between 0 and 340282366920938463463374607431768211455.\n        /// </summary>\n        UInt128 = ClickHouseSpecificTypeDelimiterCode + 10,\n\n        /// <summary>\n        /// An integral type representing signed 256-bit integers with values between -57896044618658097711785492504343953926634992332820282019728792003956564819968 and 57896044618658097711785492504343953926634992332820282019728792003956564819967.\n        /// </summary>\n        Int256 = ClickHouseSpecificTypeDelimiterCode + 11,\n\n        /// <summary>\n        /// An integral type representing unsigned 256-bit integers with values between 0 and 115792089237316195423570985008687907853269984665640564039457584007913129639935.\n        /// </summary>\n        UInt256 = ClickHouseSpecificTypeDelimiterCode + 12,\n\n        /// <summary>\n        /// A type representing a date value without a time. Supports the date range same with <see cref=\"DateTime64\"/>.\n        /// Stored in four bytes as the number of days since 1970-01-01.\n        /// </summary>\n        Date32 = ClickHouseSpecificTypeDelimiterCode + 13,\n\n        /// <summary>\n        /// This type represents a union of other data types. Type Variant(T1, T2, ..., TN) means that each row of this type\n        /// has a value of either type T1 or T2 or ... or TN or none of them (NULL value).\n        /// </summary>\n        Variant = ClickHouseSpecificTypeDelimiterCode + 14,\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseFlushMode.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Threading;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Describes a set of strategies used by <see cref=\"ClickHouseColumnWriter\"/> for commiting a transaction.\n    /// </summary>\n    public enum ClickHouseTransactionMode\n    {\n        /// <summary>\n        /// The default strategy. This strategy is used if no strategy is specified. The same as <see cref=\"Auto\"/>.\n        /// </summary>\n        Default = 0,\n\n        /// <summary>\n        /// <see cref=\"ClickHouseColumnWriter\"/> will send a table and finish the query (commits transaction).\n        /// Next table (if any) will be sent with the new INSERT query (in a new transaction).\n        /// </summary>\n        Auto = 1,\n\n        /// <summary>\n        /// <see cref=\"ClickHouseColumnWriter\"/> will <b>not</b> send confirmation after writing a table.\n        /// Next table (if any) will be sent with the current INSERT query (in the same transaction).\n        /// </summary>\n        /// <remarks>Call the method <see cref=\"ClickHouseColumnWriter.Commit\"/> (or <see cref=\"ClickHouseColumnWriter.CommitAsync(CancellationToken)\"/>) after writing tables.</remarks>\n        Manual = 2,\n\n        /// <summary>\n        /// <see cref=\"ClickHouseColumnWriter\"/> will send each block of data in a separate query (one transaction per block).\n        /// </summary>\n        Block = 3\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseParameter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Data;\nusing System.Data.Common;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Net;\nusing System.Net.Sockets;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Types;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents a parameter to a <see cref=\"ClickHouseCommand\"/>. This class cannot be inherited.\n    /// </summary>\n    public sealed class ClickHouseParameter : DbParameter, ICloneable\n    {\n        // https://github.com/ClickHouse/ClickHouse/blob/master/docs/en/query_language/syntax.md\n        private static readonly Regex ParameterNameRegex = new Regex(\"^[a-zA-Z_][0-9a-zA-Z_]*$\");\n\n        private string _parameterName;\n\n        private object? _value;\n        private int _size;\n        private TimeZoneInfo? _timeZone;\n\n        private bool? _forcedNullable;\n        private ClickHouseDbType? _forcedType;\n        private byte? _forcedScale;\n        private byte? _forcedPrecision;\n        private int? _forcedArrayRank;\n\n        private IntermediateClickHouseTypeInfo? _valueTypeInfo;\n\n        private string? _sourceColumn;\n\n        internal string Id { get; private set; }\n\n        /// <summary>\n        /// Gets the collection to which this parameter is attached.\n        /// </summary>\n        public ClickHouseParameterCollection? Collection { get; internal set; }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"ClickHouseDbType\"/> of the parameter.\n        /// </summary>\n        /// <returns>One of the <see cref=\"ClickHouseDbType\"/> values. The default value is defined based on the type of the parameter's value.</returns>\n        public ClickHouseDbType ClickHouseDbType\n        {\n            get => _forcedType ?? GetTypeFromValue().DbType;\n            set => _forcedType = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the <see cref=\"DbType\"/> of the parameter.\n        /// </summary>\n        /// <returns>\n        /// One of the <see cref=\"DbType\"/> values. The default value is defined based on the type of the parameter's value.\n        /// For ClickHouse-specific types returns <see cref=\"DbType.Object\"/>.\n        /// </returns>        \n        public override DbType DbType\n        {\n            get\n            {\n                var chType = ClickHouseDbType;\n                if (chType > ClickHouseDbType.ClickHouseSpecificTypeDelimiterCode)\n                    return DbType.Object;\n\n                return (DbType) chType;\n            }\n            set => ClickHouseDbType = (ClickHouseDbType) value;\n        }\n\n        /// <summary>\n        /// Gets the direction of the parameter. Always returns <see cref=\"ParameterDirection.Input\"/>.\n        /// </summary>\n        /// <returns>Always returns <see cref=\"ParameterDirection.Input\"/>.</returns>\n        /// <exception cref=\"NotSupportedException\">Throws <see cref=\"NotSupportedException\"/> on attempt to set the value different from <see cref=\"ParameterDirection.Input\"/>.</exception>\n        public override ParameterDirection Direction\n        {\n            get => ParameterDirection.Input;\n            set\n            {\n                if (value != ParameterDirection.Input)\n                    throw new NotSupportedException(\"Only input parameters are supported.\");\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the value indicating whether the type of the parameter is nullable.\n        /// </summary>\n        /// <returns><see langword=\"true\"/> if the parameter's value can be NULL; otherwise <see langword=\"false\"/>. The default value is defined based on the type of the parameter's value.</returns>\n        public override bool IsNullable\n        {\n            get => _forcedNullable ?? GetTypeFromValue().IsNullable;\n            set => _forcedNullable = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the name of the parameter.\n        /// </summary>\n        [AllowNull]\n        public sealed override string ParameterName\n        {\n            get => _parameterName;\n            set\n            {\n                var id = GetId(value);\n                Debug.Assert(value != null);\n\n                if (StringComparer.Ordinal.Equals(id, Id))\n                {\n                    _parameterName = value;\n                    return;\n                }\n\n                if (Collection == null)\n                {\n                    Id = id;\n                    _parameterName = value;\n                    return;\n                }\n\n                var oldId = Id;\n                var oldParameterName = _parameterName;\n                Id = id;\n                _parameterName = value;\n\n                try\n                {\n                    Collection.OnParameterIdChanged(oldId, this);\n                }\n                catch\n                {\n                    Id = oldId;\n                    _parameterName = oldParameterName;\n                    throw;\n                }\n            }\n        }\n\n        /// <inheritdoc/>\n        /// <remarks>This property is ignored by ClickHouseClient.</remarks>\n        [AllowNull]\n        public override string SourceColumn\n        {\n            get => _sourceColumn ?? string.Empty;\n            set => _sourceColumn = value;\n        }\n\n        /// <inheritdoc/>\n        public override object? Value\n        {\n            get => _value;\n            set\n            {\n                _value = value;\n                _valueTypeInfo = null;\n            }\n        }\n\n        /// <inheritdoc/>\n        /// <remarks>This property is ignored by ClickHouseClient.</remarks>\n        public override bool SourceColumnNullMapping { get; set; }\n\n        /// <summary>\n        /// Gets or sets the size. This value is applied to the ClickHouse type FixedString.\n        /// </summary>\n        public override int Size\n        {\n            get => _size;\n            set\n            {\n                _size = value;\n                _valueTypeInfo = null;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the precision. This value is applied to ClickHouse types Decimal and DateTime64.\n        /// </summary>\n        public override byte Precision\n        {\n            get => _forcedPrecision ?? (ClickHouseDbType == ClickHouseDbType.DateTime64 ? (byte) DateTime64TypeInfo.DefaultPrecision : DecimalTypeInfoBase.DefaultPrecision);\n            set => _forcedPrecision = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the scale. This value is applied to the ClickHouse type Decimal.\n        /// </summary>\n        public override byte Scale\n        {\n            get => _forcedScale ?? DecimalTypeInfoBase.DefaultScale;\n            set => _forcedScale = value;\n        }\n\n        /// <summary>\n        /// Gets or sets the encoding that will be used when writing a string value to the database.\n        /// </summary>\n        public Encoding? StringEncoding { get; set; }\n\n        /// <summary>\n        /// This property allows to specify the timezone for datetime types (<see cref=\"ClickHouseClient.ClickHouseDbType.DateTime\"/>,\n        /// <see cref=\"ClickHouseClient.ClickHouseDbType.DateTimeOffset\"/>, <see cref=\"ClickHouseClient.ClickHouseDbType.DateTime2\"/>\n        /// and <see cref=\"ClickHouseClient.ClickHouseDbType.DateTime64\"/>).\n        /// </summary>\n        public TimeZoneInfo? TimeZone\n        {\n            get => _timeZone;\n            set\n            {\n                _timeZone = value;\n                _valueTypeInfo = null;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the value indicating whether the type is an array.\n        /// </summary>\n        /// <returns><see langword=\"true\"/> if the value is an array; otherwise <see langword=\"false\"/>. The default value is defined based on the <see cref=\"ArrayRank\"/>.</returns>\n        public bool IsArray\n        {\n            get => ArrayRank > 0;\n            set\n            {\n                if (value)\n                {\n                    if (_forcedArrayRank == null || ArrayRank == 0)\n                        ArrayRank = 1;\n                }\n                else\n                {\n                    if (_forcedArrayRank == null || ArrayRank > 0)\n                        ArrayRank = 0;\n                }\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the rank (a number of dimensions) of an array.\n        /// </summary>\n        /// <returns>The rank of an array. 0 if the type is not an array. The default value is defined based on the type of the parameter's value.</returns>\n        public int ArrayRank\n        {\n            get\n            {\n                if (_forcedArrayRank != null)\n                    return _forcedArrayRank.Value;\n\n                var typeInfo = GetTypeFromValue();\n                if (typeInfo.ArrayRank > 0 && typeInfo.DbType == ClickHouseDbType.Byte && (_forcedType == ClickHouseDbType.String || _forcedType == ClickHouseDbType.StringFixedLength))\n                    return typeInfo.ArrayRank - 1;\n\n                return typeInfo.ArrayRank;\n            }\n            set\n            {\n                if (value < 0)\n                    throw new ArgumentException(\"The rank of an array must be a non-negative number.\", nameof(value));\n\n                _forcedArrayRank = value;\n            }\n        }\n\n        /// <summary>\n        /// Gets or sets the mode of passing this parameter to the query. The value of this property overrides <see cref=\"ClickHouseCommand.ParametersMode\"/>.\n        /// </summary>\n        /// <returns>The mode of passing this parameter to the query. The default value is <see cref=\"ClickHouseParameterMode.Inherit\"/>.</returns>\n        public ClickHouseParameterMode ParameterMode { get; set; } = ClickHouseParameterMode.Inherit;\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseParameter\"/> with the default name.\n        /// </summary>\n        public ClickHouseParameter()\n            : this(\"parameter\")\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseParameter\"/> with the specified name.\n        /// </summary>\n        /// <param name=\"parameterName\">The name of the parameter.</param>\n        public ClickHouseParameter(string parameterName)\n        {\n            if (parameterName == null)\n                throw new ArgumentNullException(nameof(parameterName));\n\n            Id = GetId(parameterName);\n            _parameterName = parameterName;\n        }\n\n        /// <inheritdoc/>\n        public override void ResetDbType()\n        {\n            _forcedType = null;\n            _forcedNullable = null;\n            _forcedPrecision = null;\n            _forcedScale = null;\n            _forcedArrayRank = null;\n\n            _size = 0;\n            StringEncoding = null;\n            _timeZone = null;\n            _valueTypeInfo = null;\n        }\n\n        object ICloneable.Clone()\n        {\n            return Clone();\n        }\n\n        /// <summary>\n        /// Creates a new <see cref=\"ClickHouseParameter\"/> that is a copy of this instance. The new parameter is not attached to any <see cref=\"ClickHouseParameterCollection\"/>.\n        /// </summary>\n        /// <returns>A new <see cref=\"ClickHouseParameter\"/> that is a copy of this instance.</returns>\n        public ClickHouseParameter Clone()\n        {\n            var result = new ClickHouseParameter(ParameterName);\n            CopyTo(result);\n            return result;\n        }\n\n        /// <summary>\n        /// Copies all properties except <see cref=\"ParameterName\"/> of this parameter to the target parameter.\n        /// </summary>\n        /// <param name=\"parameter\">The parameter to which the properties of this parameters should be copied.</param>\n        public void CopyTo(ClickHouseParameter parameter)\n        {\n            parameter._forcedType = _forcedType;\n            parameter._forcedArrayRank = _forcedArrayRank;\n            parameter._forcedNullable = _forcedNullable;\n            parameter._forcedPrecision = _forcedPrecision;\n            parameter._forcedScale = _forcedScale;\n            parameter._forcedArrayRank = _forcedArrayRank;\n\n            parameter._size = _size;\n            parameter.StringEncoding = StringEncoding;\n            parameter._timeZone = _timeZone;\n            parameter._value = _value;\n\n            parameter.SourceColumn = SourceColumn;\n            parameter.SourceColumnNullMapping = SourceColumnNullMapping;\n            parameter.SourceVersion = SourceVersion;\n\n            parameter._valueTypeInfo = null;\n            parameter.ParameterMode = ParameterMode;\n        }\n\n        internal ClickHouseParameterWriter CreateParameterWriter(IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            return CreateParameterWriter(typeInfoProvider, Create);\n\n            static ClickHouseParameterWriter Create(string id, object? value, ClickHouseColumnSettings? columnSettings, IClickHouseColumnTypeInfo typeInfo, Type clrType)\n            {\n                return ClickHouseParameterWriter.Dispatch(typeInfo, value);\n            }\n        }\n\n        internal IClickHouseColumnWriter CreateParameterColumnWriter(IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            return CreateParameterWriter(typeInfoProvider, Create);\n\n            static IClickHouseColumnWriter Create(string id, object? value, ClickHouseColumnSettings? columnSettings, IClickHouseColumnTypeInfo typeInfo, Type clrType)\n            {\n                var columnBuilder = new ParameterColumnWriterBuilder(id, value, columnSettings, typeInfo);\n                var column = TypeDispatcher.Dispatch(clrType, columnBuilder);\n                return column;\n            }\n        }\n\n        private T CreateParameterWriter<T>(IClickHouseTypeInfoProvider typeInfoProvider, Func<string, object?, ClickHouseColumnSettings?, IClickHouseColumnTypeInfo, Type, T> createWriter)\n        {\n            bool isNull = Value == DBNull.Value || Value == null;\n            if (isNull && _forcedNullable == false)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidQueryParameterConfiguration, $\"The parameter \\\"{ParameterName}\\\" is declared as non-nullable but it's value is null.\");\n\n            var typeInfo = GetTypeInfo(typeInfoProvider);\n            object? preparedValue = null;\n            if (_forcedType == ClickHouseDbType.StringFixedLength)\n            {\n                Debug.Assert(typeInfo.TypeName == \"FixedString\");\n\n                ReadOnlySpan<char> strSpan = default;\n                bool isStr = false;\n                if (Value is string strValue)\n                {\n                    strSpan = strValue;\n                    isStr = true;\n                }\n                else if (Value is Memory<char> mem)\n                {\n                    strSpan = mem.Span;\n                    isStr = true;\n                }\n                else if (Value is ReadOnlyMemory<char> roMem)\n                {\n                    strSpan = roMem.Span;\n                    isStr = true;\n                }\n\n                if (isStr)\n                {\n                    var encoding = StringEncoding ?? Encoding.UTF8;\n                    var size = encoding.GetByteCount(strSpan);\n                    if (size > Size)\n                    {\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.InvalidQueryParameterConfiguration,\n                            $\"Parameter \\\"{ParameterName}\\\". The length of the string in bytes with encoding \\\"{encoding.EncodingName}\\\" is greater than the size of the parameter.\");\n                    }\n\n                    var bytes = new byte[size];\n                    encoding.GetBytes(strSpan, bytes);\n                    preparedValue = bytes;\n                }\n            }\n\n            var clrType = isNull ? typeInfo.GetFieldType() : (preparedValue ?? Value)!.GetType();\n            var columnSettings = StringEncoding == null ? null : new ClickHouseColumnSettings(StringEncoding);\n            return createWriter(Id, isNull ? null : preparedValue ?? Value, columnSettings, typeInfo, clrType);\n        }\n\n        internal IClickHouseColumnTypeInfo GetTypeInfo(IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            var adapter = new ParameterColumnTypeDescriptorAdapter(this);\n            try\n            {\n                return typeInfoProvider.GetTypeInfo(adapter);\n            }\n            catch (ClickHouseException ex)\n            {\n                throw new ClickHouseException(ex.ErrorCode, $\"Parameter \\\"{ParameterName}\\\". {ex.Message}\", ex);\n            }\n        }\n\n        private IntermediateClickHouseTypeInfo GetTypeFromValue()\n        {\n            if (_valueTypeInfo != null)\n                return _valueTypeInfo.Value;\n\n            var result = GetValueDependentType();\n\n            if (result == null)\n            {\n                try\n                {\n                    result = ClickHouseTypeInfoProvider.GetTypeFromValue(Value?.GetType() ?? typeof(DBNull), Value == null, TimeZone);\n                }\n                catch (ClickHouseException ex)\n                {\n                    throw new ClickHouseException(ex.ErrorCode, $\"Parameter \\\"{ParameterName}\\\". {ex.Message}\", ex);\n                }\n            }\n\n            _valueTypeInfo = result;\n            return result.Value;\n        }\n\n        private IntermediateClickHouseTypeInfo? GetValueDependentType()\n        {\n            if (Value is IPAddress ipAddress)\n            {\n                if (ipAddress.AddressFamily == AddressFamily.InterNetwork)\n                    return new IntermediateClickHouseTypeInfo(ClickHouseDbType.IpV4, \"IPv4\", false, 0);\n\n                if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)\n                    return new IntermediateClickHouseTypeInfo(ClickHouseDbType.IpV6, \"IPv6\", false, 0);\n\n                throw new ClickHouseException(\n                        ClickHouseErrorCodes.InvalidQueryParameterConfiguration,\n                        $\"Parameter \\\"{ParameterName}\\\". The type \\\"{ipAddress.AddressFamily}\\\" of the network address is not supported.\");\n            }\n\n            return null;\n        }\n\n        /// <summary>\n        /// Checks if the provided string is a valid name for a parameter.\n        /// </summary>\n        /// <param name=\"parameterName\">The string to check.</param>\n        /// <returns><see langword=\"true\"/> if the string is a valid parameter name; otherwise <see langword=\"false\"/>.</returns>\n        public static bool IsValidParameterName(string? parameterName)\n        {\n            return ValidateParameterName(parameterName, out _);\n        }\n\n        private static string GetId(string? parameterName)\n        {\n            if (!ValidateParameterName(parameterName, out var id))\n                throw new ArgumentException(\"The name of the parameter must be a valid ClickHouse identifier.\", nameof(parameterName));\n\n            return id;\n        }\n\n        private static bool ValidateParameterName(string? parameterName, [MaybeNullWhen(false)] out string id)\n        {\n            if (string.IsNullOrWhiteSpace(parameterName))\n            {\n                id = null;\n                return false;\n            }\n\n            id = TrimParameterName(parameterName);\n            return ParameterNameRegex.IsMatch(id);\n        }\n\n        internal ClickHouseParameterMode GetParameterMode(ClickHouseParameterMode inheritParameterMode)\n        {\n            var mode = ParameterMode;\n            if (mode == ClickHouseParameterMode.Inherit)\n                return inheritParameterMode;\n\n            return mode;\n        }\n\n        internal static string TrimParameterName(string parameterName)\n        {\n            if (parameterName.Length > 0)\n            {\n                if (parameterName[0] == '{' && parameterName[^1] == '}')\n                    return parameterName[1..^1];\n\n                // MSSQL-style parameter name\n                if (parameterName[0] == '@')\n                    return parameterName[1..];\n            }\n\n            return parameterName;\n        }\n\n        private class ParameterColumnWriterBuilder : ITypeDispatcher<IClickHouseColumnWriter>\n        {\n            private readonly string _parameterId;\n            [AllowNull] private readonly object _value;\n            private readonly ClickHouseColumnSettings? _columnSettings;\n            private readonly IClickHouseColumnTypeInfo _typeInfo;\n\n            public ParameterColumnWriterBuilder(string parameterId, object? value, ClickHouseColumnSettings? columnSettings, IClickHouseColumnTypeInfo typeInfo)\n            {\n                _parameterId = parameterId;\n                _value = value;\n                _columnSettings = columnSettings;\n                _typeInfo = typeInfo ?? throw new ArgumentNullException(nameof(typeInfo));\n            }\n\n            public IClickHouseColumnWriter Dispatch<T>()\n            {\n                var singleElementColumn = new ConstantReadOnlyList<T>((T) _value, 1);\n                return _typeInfo.CreateColumnWriter(_parameterId, singleElementColumn, _columnSettings);\n            }\n        }\n\n        private class ParameterColumnTypeDescriptorAdapter : IClickHouseColumnTypeDescriptor\n        {\n            private readonly ClickHouseParameter _parameter;\n\n            public ClickHouseDbType? ClickHouseDbType => _parameter._forcedType ?? _parameter.GetValueDependentType()?.DbType;\n\n            public Type ValueType => _parameter?.Value?.GetType() ?? typeof(DBNull);\n\n            public bool? IsNullable => _parameter._forcedNullable;\n\n            public int Size => _parameter._size;\n\n            public byte? Precision => _parameter._forcedPrecision;\n\n            public byte? Scale => _parameter._forcedScale;\n\n            public TimeZoneInfo? TimeZone => _parameter.TimeZone;\n\n            public int? ArrayRank => _parameter._forcedArrayRank;\n\n            public ParameterColumnTypeDescriptorAdapter(ClickHouseParameter parameter)\n            {\n                _parameter = parameter;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseParameterCollection.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents a collection of parameters associated with a <see cref=\"ClickHouseCommand\"/>. This class cannot be inherited.\n    /// </summary>\n    public sealed class ClickHouseParameterCollection : DbParameterCollection, IList<ClickHouseParameter>, IReadOnlyList<ClickHouseParameter>\n    {\n        private readonly List<string> _parameterNames = new List<string>();\n\n        private readonly Dictionary<string, ClickHouseParameter> _parameters = new Dictionary<string, ClickHouseParameter>(StringComparer.OrdinalIgnoreCase);\n\n        /// <inheritdoc/>\n        public override int Count => _parameters.Count;\n\n        /// <inheritdoc/>\n        public override object SyncRoot => ((ICollection) _parameters).SyncRoot;\n\n        /// <inheritdoc/>\n        public override int Add(object value)\n        {\n            if (value == null)\n                throw new ArgumentNullException(nameof(value));\n\n            var parameter = (ClickHouseParameter) value;\n            return Add(parameter);\n        }\n\n        /// <summary>\n        /// Creates, adds to the collection and returns a new parameter with specified name and value.\n        /// </summary>\n        /// <param name=\"parameterName\">The name of the parameter.</param>\n        /// <param name=\"value\">The value of the paramter.</param>\n        /// <returns>A new <see cref=\"ClickHouseParameter\"/> added to the collection.</returns>\n        public ClickHouseParameter AddWithValue(string parameterName, object? value)\n        {\n            var parameter = new ClickHouseParameter(parameterName) {Value = value};\n            Add(parameter);\n            return parameter;\n        }\n\n        /// <summary>\n        /// Creates, adds to the collection and returns a new parameter with specified name, value and type.\n        /// </summary>\n        /// <param name=\"parameterName\">The name of the parameter.</param>\n        /// <param name=\"value\">The value of the paramter.</param>\n        /// <param name=\"dbType\">The type of the paramter</param>\n        /// <returns>A new <see cref=\"ClickHouseParameter\"/> added to the collection.</returns>\n        public ClickHouseParameter AddWithValue(string parameterName, object? value, DbType dbType)\n        {\n            return AddWithValue(parameterName, value, (ClickHouseDbType)dbType);\n        }\n\n        /// <summary>\n        /// Creates, adds to the collection and returns a new parameter with specified name, value and type.\n        /// </summary>\n        /// <param name=\"parameterName\">The name of the parameter.</param>\n        /// <param name=\"value\">The value of the paramter.</param>\n        /// <param name=\"dbType\">The type of the paramter</param>\n        /// <returns>A new <see cref=\"ClickHouseParameter\"/> added to the collection.</returns>\n        public ClickHouseParameter AddWithValue(string parameterName, object? value, ClickHouseDbType dbType)\n        {\n            var parameter = new ClickHouseParameter(parameterName) { Value = value, ClickHouseDbType = dbType };\n            Add(parameter);\n            return parameter;\n        }\n\n        void ICollection<ClickHouseParameter>.Add(ClickHouseParameter item)\n        {\n            Add(item);\n        }\n\n        /// <summary>\n        /// Adds an existing parameter to the collection.\n        /// </summary>\n        /// <param name=\"item\">The parameter.</param>\n        /// <returns>The zero-based index of the parameter in the collection.</returns>\n        public int Add(ClickHouseParameter item)\n        {\n            if (item == null)\n                throw new ArgumentNullException(nameof(item));\n\n            if (item.Collection != null)\n            {\n                var errorText = ReferenceEquals(item.Collection, this)\n                    ? $\"The parameter \\\"{item.ParameterName}\\\" already belongs to the collection. It can't be added to the same connection twice.\"\n                    : $\"The parameter \\\"{item.ParameterName}\\\" already belongs to a collection. It can't be added to different collections.\";\n\n                throw new ArgumentException(errorText, nameof(item));\n            }\n\n            if (_parameters.ContainsKey(item.Id))\n                throw new ArgumentException($\"A parameter with the name \\\"{item.ParameterName}\\\" already exists in the collection.\", nameof(item));\n\n            _parameters.Add(item.Id, item);\n            var result = _parameterNames.Count;\n            _parameterNames.Add(item.Id);\n            item.Collection = this;\n\n            return result;\n        }\n\n        /// <inheritdoc/>\n        public override void Clear()\n        {\n            foreach (var parameter in _parameters.Values)\n                parameter.Collection = null;\n\n            _parameters.Clear();\n            _parameterNames.Clear();\n        }\n\n        /// <inheritdoc/>\n        public bool Contains(ClickHouseParameter item)\n        {\n            return item != null && _parameters.TryGetValue(item.Id, out var parameter) && ReferenceEquals(item, parameter);\n        }\n\n        /// <inheritdoc/>\n        public void CopyTo(ClickHouseParameter[] array, int arrayIndex)\n        {\n            int i = arrayIndex;\n            foreach (var key in _parameterNames)\n                array[i++] = _parameters[key];\n        }\n\n        /// <inheritdoc/>\n        public override bool Contains(object value)\n        {\n            if (!(value is ClickHouseParameter parameter))\n                return false;\n\n            return Contains(parameter);\n        }\n\n        /// <inheritdoc/>\n        public override int IndexOf(object value)\n        {\n            if (!(value is ClickHouseParameter parameter))\n                return -1;\n\n            return IndexOf(parameter);\n        }\n\n        /// <inheritdoc/>\n        public override void Insert(int index, object value)\n        {\n            if (value == null)\n                throw new ArgumentNullException(nameof(value));\n\n            var parameter = (ClickHouseParameter) value;\n            Insert(index, parameter);\n        }\n\n        /// <inheritdoc/>\n        public bool Remove(ClickHouseParameter item)\n        {\n            if (item == null)\n                throw new ArgumentNullException(nameof(item));\n\n            if (!_parameters.TryGetValue(item.Id, out var existingParameter) || !ReferenceEquals(item, existingParameter))\n                return false;\n\n            var comparer = _parameters.Comparer;\n            var name = item.Id;\n            var index = _parameterNames.FindIndex(n => comparer.Equals(n, name));\n\n            _parameterNames.RemoveAt(index);\n            var result = _parameters.Remove(name);\n            item.Collection = null;\n\n            Debug.Assert(result);\n            return true;\n        }\n\n        /// <summary>\n        /// Removes the parameter with the specified name from the collection.\n        /// </summary>\n        /// <param name=\"parameterName\">The name of the parameter.</param>\n        /// <returns><see langword=\"true\"/> if the parameter was removed; <see langword=\"false\"/> if a parameter with the specified name is not present in the collection.</returns>\n        public bool Remove(string parameterName)\n        {\n            return Remove(parameterName, out _);\n        }\n\n        /// <summary>\n        /// Removes the parameter with the specified name from the collection.\n        /// </summary>\n        /// <param name=\"parameterName\">The name of the parameter.</param>\n        /// <param name=\"parameter\">When this method returns, contains the removed parameter or <see langword=\"null\"/> if a parameter was not removed.</param>\n        /// <returns> if the parameter was removed; <see langword=\"false\"/> if a parameter with the specified name is not present in the collection.</returns>\n        public bool Remove(string parameterName, [MaybeNullWhen(false)] out ClickHouseParameter parameter)\n        {\n            if (parameterName == null)\n                throw new ArgumentNullException(nameof(parameterName));\n\n            var name = ClickHouseParameter.TrimParameterName(parameterName);\n            if (!_parameters.Remove(name, out parameter))\n                return false;\n\n            parameter.Collection = null;\n            var comparer = _parameters.Comparer;\n            var index = _parameterNames.FindIndex(n => comparer.Equals(n, name));\n            _parameterNames.RemoveAt(index);\n            return true;\n        }\n\n        /// <inheritdoc/>\n        public override void Remove(object value)\n        {\n            if (!(value is ClickHouseParameter parameter))\n                return;\n\n            Remove(parameter);\n        }\n\n        /// <inheritdoc/>\n        public int IndexOf(ClickHouseParameter item)\n        {\n            if (item == null)\n                return -1;\n\n            if (!_parameters.TryGetValue(item.Id, out var existingParameter) || !ReferenceEquals(item, existingParameter))\n                return -1;\n\n            var comparer = _parameters.Comparer;\n            var name = item.Id;\n            var index = _parameterNames.FindIndex(n => comparer.Equals(n, name));\n\n            return index;\n        }\n\n        /// <inheritdoc/>\n        public void Insert(int index, ClickHouseParameter item)\n        {\n            if (item == null)\n                throw new ArgumentNullException(nameof(item));\n\n            if (item.Collection != null)\n            {\n                var errorText = ReferenceEquals(item.Collection, this)\n                    ? $\"The parameter \\\"{item.ParameterName}\\\" already belongs to the collection. It can't be added to the same connection twice.\"\n                    : $\"The parameter \\\"{item.ParameterName}\\\" already belongs to a collection. It can't be added to different collections.\";\n\n                throw new ArgumentException(errorText, nameof(item));\n            }\n\n            if (_parameters.ContainsKey(item.Id))\n                throw new ArgumentException($\"A parameter with the name \\\"{item.ParameterName}\\\" already exists in the collection.\", nameof(item));\n\n            _parameterNames.Insert(index, item.Id);\n            _parameters.Add(item.Id, item);\n            item.Collection = this;\n        }\n\n        /// <inheritdoc/>\n        public override void RemoveAt(int index)\n        {\n            var name = _parameterNames[index];\n            if (_parameters.Remove(name, out var parameter))\n                parameter.Collection = null;\n\n            _parameterNames.RemoveAt(index);\n        }\n\n        /// <inheritdoc/>\n        public override void RemoveAt(string parameterName)\n        {\n            Remove(parameterName, out _);\n        }\n\n        /// <inheritdoc/>\n        protected override void SetParameter(int index, DbParameter value)\n        {\n            if (value == null)\n                throw new ArgumentNullException(nameof(value));\n\n            var name = _parameterNames[index];\n            var parameter = (ClickHouseParameter) value;\n\n            var comparer = _parameters.Comparer;\n            if (comparer.Equals(name, parameter.Id))\n            {\n                var existingParameter = _parameters[name];\n                if (!ReferenceEquals(parameter, existingParameter))\n                {\n                    if (parameter.Collection != null)\n                    {\n                        var errorText = ReferenceEquals(parameter.Collection, this)\n                            ? $\"The parameter \\\"{parameter.ParameterName}\\\" already belongs to the collection. It can't be added to the same connection twice.\"\n                            : $\"The parameter \\\"{parameter.ParameterName}\\\" already belongs to a collection. It can't be added to different collections.\";\n\n                        throw new ArgumentException(errorText, nameof(value));\n                    }\n\n                    _parameters[name] = parameter;\n                    existingParameter.Collection = null;\n                    parameter.Collection = this;\n                }\n            }\n            else\n            {\n                if (parameter.Collection != null)\n                {\n                    var errorText = ReferenceEquals(parameter.Collection, this)\n                        ? $\"The parameter \\\"{parameter.ParameterName}\\\" already belongs to the collection. It can't be added to the same connection twice.\"\n                        : $\"The parameter \\\"{parameter.ParameterName}\\\" already belongs to a collection. It can't be added to different collections.\";\n\n                    throw new ArgumentException(errorText, nameof(value));\n                }\n\n                if (_parameters.ContainsKey(parameter.Id))\n                    throw new ArgumentException($\"A parameter with the name \\\"{parameter.ParameterName}\\\" already exists in the collection.\", nameof(value));\n\n                if(_parameters.Remove(name, out var existingParameter))\n                    existingParameter.Collection = null;\n\n                _parameters.Add(parameter.Id, parameter);\n                _parameterNames[index] = parameter.Id;\n                parameter.Collection = this;\n            }\n        }\n\n        /// <inheritdoc/>\n        protected override void SetParameter(string parameterName, DbParameter value)\n        {\n            if (parameterName == null)\n                throw new ArgumentNullException(nameof(parameterName));\n            if (value == null)\n                throw new ArgumentNullException(nameof(value));\n\n            var name = ClickHouseParameter.TrimParameterName(parameterName);\n            var parameter = (ClickHouseParameter) value;\n\n            var comparer = _parameters.Comparer;\n            if (_parameters.TryGetValue(name, out var existingParameter))\n            {\n                if (!ReferenceEquals(parameter, existingParameter))\n                {\n                    if (parameter.Collection != null)\n                    {\n                        var errorText = ReferenceEquals(parameter.Collection, this)\n                            ? $\"The parameter \\\"{parameter.ParameterName}\\\" already belongs to the collection. It can't be added to the same connection twice.\"\n                            : $\"The parameter \\\"{parameter.ParameterName}\\\" already belongs to a collection. It can't be added to different collections.\";\n\n                        throw new ArgumentException(errorText, nameof(value));\n                    }\n                }\n\n                if (comparer.Equals(name, parameter.Id))\n                {\n                    _parameters[name] = parameter;\n                }\n                else\n                {\n                    if (_parameters.ContainsKey(parameter.Id))\n                        throw new ArgumentException($\"A parameter with the name \\\"{parameter.ParameterName}\\\" already exists in the collection.\", nameof(value));\n\n                    var index = _parameterNames.FindIndex(n => comparer.Equals(n, name));\n                    _parameterNames[index] = parameter.Id;\n\n                    _parameters.Remove(name);\n                    _parameters.Add(parameter.Id, parameter);\n                }\n\n                if (!ReferenceEquals(parameter, existingParameter))\n                {\n                    existingParameter.Collection = null;\n                    parameter.Collection = this;\n                }\n            }\n            else if (comparer.Equals(name, parameter.Id))\n            {\n                Add(parameter);\n            }\n            else\n            {\n                throw new ArgumentException(\n                    $\"A parameter with the name \\\"{parameterName}\\\" is not present in the collection. It can't be replaced with the parameter \\\"{parameter.ParameterName}\\\".\",\n                    nameof(parameterName));\n            }\n        }\n\n        /// <inheritdoc/>\n        public override int IndexOf(string parameterName)\n        {\n            if (parameterName == null)\n                throw new ArgumentNullException(nameof(parameterName));\n\n            var name = ClickHouseParameter.TrimParameterName(parameterName);\n            var comparer = _parameters.Comparer;\n\n            return _parameterNames.FindIndex(n => comparer.Equals(n, name));\n        }\n\n        /// <inheritdoc/>\n        public override bool Contains(string value)\n        {\n            if (value == null)\n                throw new ArgumentNullException(nameof(value));\n\n            var parameterName = ClickHouseParameter.TrimParameterName(value);\n            return _parameters.ContainsKey(parameterName);\n        }\n\n        /// <inheritdoc/>\n        public override void CopyTo(Array array, int index)\n        {\n            if (array == null)\n                throw new ArgumentNullException(nameof(array));\n\n            var i = index;\n            foreach (var name in _parameterNames)\n                array.SetValue(_parameters[name], i++);\n        }\n\n        IEnumerator<ClickHouseParameter> IEnumerable<ClickHouseParameter>.GetEnumerator()\n        {\n            return _parameterNames.Select(n => _parameters[n]).GetEnumerator();\n        }\n\n        /// <inheritdoc/>\n        public override IEnumerator GetEnumerator()\n        {\n            return _parameterNames.Select(n => _parameters[n]).GetEnumerator();\n        }\n\n        /// <inheritdoc/>\n        protected override DbParameter GetParameter(int index)\n        {\n            return _parameters[_parameterNames[index]];\n        }\n\n        /// <inheritdoc/>\n        protected override DbParameter GetParameter(string parameterName)\n        {\n            if (!TryGetValue(parameterName, out var parameter))\n                throw new ArgumentException($\"Parameter \\\"{parameterName}\\\" not found.\", nameof(parameterName));\n\n            return parameter;\n        }\n\n        /// <inheritdoc/>\n        public override void AddRange(Array values)\n        {\n            if (values == null)\n                throw new ArgumentNullException(nameof(values));\n\n            foreach (var parameter in values.Cast<ClickHouseParameter>())\n                Add(parameter);\n        }\n\n        /// <summary>\n        /// Adds the specified parameters to the collection.\n        /// </summary>\n        /// <param name=\"parameters\">The set of parameters that should be added to this collection.</param>\n        /// <remarks>This operation is not atomic, it calls <see cref=\"Add(ClickHouseParameter)\"/> for each parameter in <paramref name=\"parameters\"/>.</remarks>\n        public void AddRange(IEnumerable<ClickHouseParameter> parameters)\n        {\n            if (parameters == null)\n                throw new ArgumentNullException(nameof(parameters));\n\n            foreach (var parameter in parameters)\n                Add(parameter);\n        }\n\n        /// <summary>\n        /// Gets the <see cref=\"ClickHouseParameter\"/> with the specified name.\n        /// </summary>\n        /// <param name=\"parameterName\">The name of the parameter.</param>\n        /// <param name=\"parameter\">\n        /// When this method returns, contains the <see cref=\"ClickHouseParameter\"/> with the specified name or\n        /// <see langword=\"null\"/> if a parameter is not present in the collection.\n        /// </param>\n        /// <returns><see langword=\"true\"/> if the parameter with the specified name was found in the collection; otherwise <see langword=\"false\"/></returns>\n        public bool TryGetValue(string parameterName, [NotNullWhen(true)] out ClickHouseParameter? parameter)\n        {\n            if (parameterName == null)\n                throw new ArgumentNullException(nameof(parameterName));\n\n            var name = ClickHouseParameter.TrimParameterName(parameterName);\n            return _parameters.TryGetValue(name, out parameter);\n        }\n\n        internal void OnParameterIdChanged(string originalId, ClickHouseParameter parameter)\n        {\n            Debug.Assert(ReferenceEquals(parameter.Collection, this));\n            if (_parameters.Comparer.Equals(originalId, parameter.Id))\n                return;\n\n            SetParameter(originalId, parameter);\n        }\n\n        /// <inheritdoc/>\n        public new ClickHouseParameter this[int index]\n        {\n            get => (ClickHouseParameter) base[index];\n            set => base[index] = value;\n        }\n\n        /// <summary>Gets or sets the <see cref=\"ClickHouseParameter\"/> with the specified name.</summary>\n        /// <param name=\"parameterName\">The name of the <see cref=\"ClickHouseParameter\"/> in the collection.</param>\n        /// <returns>The <see cref=\"ClickHouseParameter\"/> with the specified name.</returns>\n        public new ClickHouseParameter this[string parameterName]\n        {\n            get => (ClickHouseParameter) base[parameterName];\n            set => base[parameterName] = value;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseParameterMode.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2022-2023 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nnamespace Octonica.ClickHouseClient\r\n{\r\n    /// <summary>\r\n    /// Specifies the list of available modes of passing parameters to the query.\r\n    /// </summary>\r\n    public enum ClickHouseParameterMode\r\n    {\r\n        /// <summary>\r\n        /// The default mode. Currently the default mode is <see cref=\"Binary\"/>.\r\n        /// </summary>\r\n        Default = 0,\r\n\r\n        /// <summary>\r\n        /// This value indicates that the mode should be inherited.\r\n        /// <see cref=\"ClickHouseParameter\">Parameters</see> inherit the mode from a <see cref=\"ClickHouseCommand\">command</see>.\r\n        /// A <see cref=\"ClickHouseCommand\">command</see> inherits the mode from a <see cref=\"ClickHouseConnection\">connection</see>.\r\n        /// For a <see cref=\"ClickHouseConnection\">connection</see> this value is equivalent to <see cref=\"Default\"/>.\r\n        /// </summary>\r\n        Inherit = 1,\r\n\r\n        /// <summary>\r\n        /// This value indicates that parameters should be passed to the query in the binary format. This is the default mode.\r\n        /// </summary>\r\n        /// <remarks>\r\n        /// In this mode parameters will be passed to the query as a table with the single row. Each parameter will be replaced by SELECT subquery.\r\n        /// This mode doesn't allow to pass parameters in parts of the query where scalar subqueries are not allowed.\r\n        /// </remarks>\r\n        Binary = 2,\r\n\r\n        /// <summary>\r\n        /// This value indicates that parameters should be passed to the query as constant literals.\r\n        /// </summary>\r\n        /// <remarks>\r\n        /// In this mode parameters' values will be interpolated to the query string as constant literals.\r\n        /// This mode allows to use parameters in any part of the query where a constant is allowed.\r\n        /// For sending parameters without modifying the query use the mode <see cref=\"Serialize\"/>.\r\n        /// </remarks>\r\n        Interpolate = 3,\r\n\r\n        /// <summary>\r\n        /// This value indicates that parameters should be passed as key-value pairs where values are literals.\r\n        /// Parameters will not be inlined to the query.\r\n        /// </summary>\r\n        /// <remarks>\r\n        /// Unlike other modes this one relies on the server-side support. An error will be raised if the server doesn't support this mode.\r\n        /// </remarks>\r\n        Serialize = 4,\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHousePasswordComplexityRule.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Describes a password complexity rule provided by the ClickHouse server.\n    /// </summary>\n    public sealed class ClickHousePasswordComplexityRule\n    {\n        /// <summary>\n        /// Gets the rule pattern, provided by the server.\n        /// </summary>\n        public string OriginalPattern { get; }\n\n        /// <summary>\n        /// Gets the rule message.\n        /// </summary>\n        public string ExceptionMessage { get; }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHousePasswordComplexityRule\"/> with specified arguments.\n        /// </summary>\n        /// <param name=\"originalPattern\">The rule pattern.</param>\n        /// <param name=\"exceptionMessage\">Ther rule message.</param>\n        public ClickHousePasswordComplexityRule(string originalPattern, string exceptionMessage)\n        {\n            OriginalPattern = originalPattern;\n            ExceptionMessage = exceptionMessage;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseQueryExecutionProgress.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2026 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nnamespace Octonica.ClickHouseClient\r\n{\r\n    /// <summary>\r\n    /// Represents information about a query execution progress.\r\n    /// </summary>\r\n    public readonly struct ClickHouseQueryExecutionProgress\r\n    {\r\n        /// <summary>\r\n        /// The number of rows processed by a query.\r\n        /// </summary>\r\n        public ulong Rows { get; }\r\n\r\n        /// <summary>\r\n        /// The number of bytes processed by a query.\r\n        /// </summary>\r\n        public ulong Bytes { get; }\r\n\r\n        /// <summary>\r\n        /// The total number of rows processed by a query.\r\n        /// </summary>\r\n        public ulong TotalRows { get; }\r\n\r\n        /// <summary>\r\n        /// The total number of rows processed by a query.\r\n        /// </summary>\r\n        public ulong TotalBytes { get; }\r\n\r\n        /// <summary>\r\n        /// The number of rows written by a query.\r\n        /// </summary>\r\n        public ulong WrittenRows { get; }\r\n\r\n        /// <summary>\r\n        /// The number of bytes processed by a query.\r\n        /// </summary>\r\n        public ulong WrittenBytes { get; }\r\n\r\n        /// <summary>\r\n        /// The time elapsed from the start of a query execution in nanoseconds.\r\n        /// </summary>\r\n        public ulong ElapsedNanoseconds { get; }\r\n\r\n        /// <summary>\r\n        /// Creates an instance of a query execution process.\r\n        /// </summary>\r\n        public ClickHouseQueryExecutionProgress(ulong rows, ulong bytes, ulong totalRows, ulong totalBytes, ulong writtenRows, ulong writtenBytes, ulong elapsedNanoseconds)\r\n        {\r\n            Rows = rows;\r\n            Bytes = bytes;\r\n            TotalRows = totalRows;\r\n            TotalBytes = totalBytes;\r\n            WrittenRows = writtenRows;\r\n            WrittenBytes = writtenBytes;\r\n            ElapsedNanoseconds = elapsedNanoseconds;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseReaderColumnSettings.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing Octonica.ClickHouseClient.Exceptions;\r\nusing Octonica.ClickHouseClient.Types;\r\nusing System.Diagnostics;\r\n\r\nnamespace Octonica.ClickHouseClient\r\n{\r\n    internal readonly struct ClickHouseReaderColumnSettings\r\n    {\r\n        public ClickHouseColumnSettings? Column { get; }\r\n\r\n        public IClickHouseColumnReinterpreter? Reinterpreter { get; }\r\n\r\n        public ClickHouseReaderColumnSettings(ClickHouseColumnSettings? column, IClickHouseColumnReinterpreter? reinterpreter)\r\n        {\r\n            Column = column;\r\n            Reinterpreter = reinterpreter;\r\n        }\r\n\r\n        public ClickHouseReaderColumnSettings WithColumnSettings(string columnName, ClickHouseColumnSettings column, IClickHouseColumnReinterpreter? reinterpreter)\r\n        {\r\n            if (Reinterpreter != null && Column?.ColumnType == null)\r\n            {\r\n                if (reinterpreter != null)\r\n                {\r\n                    Debug.Assert(column.ColumnType != null);\r\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidColumnSettings, $\"An external callback function for converting values was defined for the column \\\"{columnName}\\\". The type of the column was implicityly defined and can't be redefined in the column settings.\");\r\n                }\r\n\r\n                return new ClickHouseReaderColumnSettings(column, Reinterpreter);\r\n            }\r\n\r\n            Debug.Assert((column.ColumnType == null) == (reinterpreter == null));\r\n            return new ClickHouseReaderColumnSettings(column, reinterpreter);\r\n        }\r\n\r\n        public ClickHouseReaderColumnSettings WithUserDefinedReader(string columnName, IClickHouseColumnReinterpreter? reinterpreter)\r\n        {\r\n            if (reinterpreter == null)\r\n            {\r\n                if (Column?.ColumnType != null || Reinterpreter == null)\r\n                    return this;\r\n\r\n                return new ClickHouseReaderColumnSettings(Column, null);\r\n            }\r\n\r\n            if(Column?.ColumnType != null)\r\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidColumnSettings, $\"An external callback function for converting values can't be set for the column \\\"{columnName}\\\" because the type of the column was already defined in the column settings.\");\r\n\r\n            return new ClickHouseReaderColumnSettings(Column, reinterpreter);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseServerInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.ObjectModel;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Describes a ClickHouse server. This class can't be inherited.\n    /// </summary>\n    public sealed class ClickHouseServerInfo\n    {\n        /// <summary>\n        /// Gets the name of the server provided by the server when opening a connection.\n        /// </summary>\n        public string Name { get; }\n\n        /// <summary>\n        /// Gets the version of the server.\n        /// </summary>\n        public ClickHouseVersion Version { get; }\n\n        /// <summary>\n        /// Gets the revision of the ClickHouse binary protocol negotiated between the client and the server.\n        /// </summary>\n        public int Revision { get; }\n\n        /// <summary>\n        /// Gets the revision of the ClickHouse binary protocol supported by the server. This value can't be less than the negotiated revision (<see cref=\"Revision\"/>).\n        /// </summary>\n        public int ServerRevision { get; }\n\n        /// <summary>\n        /// Gets the default timezone of the server.\n        /// </summary>\n        public string Timezone { get; }\n\n        /// <summary>\n        /// Gets the display name of the server provided by the server when opening a connection.\n        /// </summary>\n        public string DisplayName { get; }\n\n        /// <summary>\n        /// Gets password complexity rules provided by the server when opening a connection.\n        /// </summary>\n        public ReadOnlyCollection<ClickHousePasswordComplexityRule>? PasswordComplexityRules { get; }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseServerInfo\"/> with specified arguments.\n        /// </summary>\n        /// <param name=\"name\">The name of the server provided by the server when opening a connection.</param>\n        /// <param name=\"version\">The version of the server.</param>\n        /// <param name=\"serverRevision\">The revision of the ClickHouse binary protocol supported by the server.</param>\n        /// <param name=\"revision\">The revision of the ClickHouse binary protocol negotiated between the client and the server.</param>\n        /// <param name=\"timezone\">The default timezone of the server.</param>\n        /// <param name=\"displayName\">The display name of the server provided by the server when opening a connection.</param>\n        /// <param name=\"passwordComplexityRules\">Password complexity rules provided by the server when opening a connection.</param>\n        public ClickHouseServerInfo(string name, ClickHouseVersion version, int serverRevision, int revision, string timezone, string displayName, ReadOnlyCollection<ClickHousePasswordComplexityRule>? passwordComplexityRules)\n        {\n            Name = name ?? throw new ArgumentNullException(nameof(name));\n            Version = version;\n            ServerRevision = serverRevision;\n            Revision = revision;\n            Timezone = timezone;\n            DisplayName = displayName;\n            PasswordComplexityRules = passwordComplexityRules;\n        }\n\n        /// <summary>\n        /// Creates a copy of the server info with the specified timezone.\n        /// </summary>\n        /// <param name=\"timezone\">The default timezone of the server.</param>\n        /// <returns>A new instance of <see cref=\"ClickHouseServerInfo\"/> with the specified timezone.</returns>\n        public ClickHouseServerInfo WithTimezone(string timezone)\n        {\n            return new ClickHouseServerInfo(\n                name: Name,\n                version: Version,\n                serverRevision: ServerRevision,\n                revision: Revision,\n                timezone: timezone,\n                displayName: DisplayName,\n                passwordComplexityRules: PasswordComplexityRules);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseTable.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.ObjectModel;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient\n{\n    internal struct ClickHouseTable\n    {\n        public BlockHeader Header { get; }\n\n        public ReadOnlyCollection<IClickHouseTableColumn> Columns { get; }\n\n        public ClickHouseTable(BlockHeader header, ReadOnlyCollection<IClickHouseTableColumn> columns)\n        {\n            Header = header ?? throw new ArgumentNullException(nameof(header));\n            Columns = columns ?? throw new ArgumentNullException(nameof(columns));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents a configurable column descriptor.\n    /// </summary>\n    public class ClickHouseTableColumn : IClickHouseColumnDescriptor\n    {\n        /// <inheritdoc/>\n        public string ColumnName { get; }\n\n        /// <summary>\n        /// Gets or sets the settings that should be applied when writing the column.\n        /// </summary>\n        public ClickHouseColumnSettings? Settings { get; set; }\n\n        /// <summary>\n        /// Gets or sets the type of the column.\n        /// </summary>\n        public ClickHouseDbType? ClickHouseDbType { get; set; }\n\n        /// <summary>\n        /// Gets or sets the type of the column's value (i.e. the type of column's cells).\n        /// </summary>\n        public Type ValueType { get; }\n\n        /// <summary>\n        /// Gets or sets the value indicating whether the column can contain NULLs.\n        /// </summary>\n        public bool? IsNullable { get; set; }\n\n        /// <summary>\n        /// Gets or sets the size. This value is applied to the ClickHouse type FixedString.\n        /// </summary>\n        public int Size { get; set; }\n\n        /// <summary>\n        /// Gets or sets the precision. This value is applied to ClickHouse types Decimal and DateTime64.\n        /// </summary>\n        public byte? Precision { get; set; }\n\n        /// <summary>\n        /// Gets or sets the scale. This value is applied to the ClickHouse type Decimal.\n        /// </summary>\n        public byte? Scale { get; set; }\n\n        /// <summary>\n        /// Gets or sets the time zone. This value is applied to ClickHouse types DateTime and DateTime64.\n        /// </summary>\n        public TimeZoneInfo? TimeZone { get; set; }\n\n        /// <summary>\n        /// Gets or sets the rank (a number of dimensions) of an array.\n        /// </summary>\n        public int? ArrayRank { get; set; }\n\n        /// <summary>\n        /// Gets the object representing a column. It must implement one of interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </summary>\n        public object Value { get; }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseTableColumn\"/> class with the specified name.\n        /// </summary>\n        /// <param name=\"columnName\">The name of the column.</param>\n        /// <param name=\"value\">\n        /// The object representing a column. It must implement one of interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </param>\n        /// <param name=\"valueType\">The type of the column's values.</param>\n        public ClickHouseTableColumn(string columnName, object value, Type valueType)\n        {\n            if (string.IsNullOrWhiteSpace(columnName))\n                throw new ArgumentException(\"The name of a column must be a non-empty string\", nameof(columnName));\n\n            ColumnName = columnName;\n            Value = value ?? throw new ArgumentNullException(nameof(value));\n            ValueType = valueType ?? throw new ArgumentNullException(nameof(valueType));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseTableColumnCollection.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Globalization;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents a collection of columns associated with a <see cref=\"ClickHouseTableProvider\"/>. This class cannot be inherited.\n    /// </summary>\n    public sealed class ClickHouseTableColumnCollection : IndexedCollectionBase<string, ClickHouseTableColumn>\n    {\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseTableColumnCollection\"/> with the default capacity.\n        /// </summary>\n        public ClickHouseTableColumnCollection()\n            : base(StringComparer.OrdinalIgnoreCase)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseTableColumnCollection\"/> with the specified capacity capacity.\n        /// </summary>\n        /// <param name=\"capacity\">The initial number of elements that the collection can contain.</param>\n        public ClickHouseTableColumnCollection(int capacity)\n            : base(capacity, StringComparer.OrdinalIgnoreCase)\n        {\n        }\n\n        /// <inheritdoc/>\n        protected sealed override string GetKey(ClickHouseTableColumn item)\n        {\n            return item.ColumnName;\n        }\n\n        private string GetUniqueColumnName(string baseName)\n        {\n            int i = 0;\n            string name;\n            do\n            {\n                name = string.Format(CultureInfo.InvariantCulture, \"{0}{1}\", baseName, i++);\n            } while (ContainsKey(name));\n\n            return name;\n        }\n\n        /// <summary>\n        /// Creates a new column with the default name and adds it to the collection.\n        /// </summary>\n        /// <typeparam name=\"T\">The type of the column's values.</typeparam>\n        /// <param name=\"column\">The list of column's values.</param>\n        /// <returns>A new column.</returns>\n        public ClickHouseTableColumn AddColumn<T>(IReadOnlyList<T> column)\n        {\n            return AddColumn(GetUniqueColumnName(\"column\"), column);\n        }\n\n        /// <summary>\n        /// Creates a new column with the specified name and adds it to the collection.\n        /// </summary>\n        /// <typeparam name=\"T\">The type of the column's values.</typeparam>\n        /// <param name=\"columnName\">The name of the column.</param>\n        /// <param name=\"column\">The list of column's values.</param>\n        /// <returns>A new column.</returns>\n        public ClickHouseTableColumn AddColumn<T>(string columnName, IReadOnlyList<T> column)\n        {\n            var result = AddColumn(columnName, column, typeof(T));\n            if (result.Settings?.ColumnType == null)\n                result.Settings = new ClickHouseColumnSettings(result.Settings?.StringEncoding, result.Settings?.EnumConverter, typeof(T));\n\n            return result;\n        }\n\n        /// <summary>\n        /// Creates a new column with the default name and adds it to the collection.\n        /// </summary>\n        /// <param name=\"column\">\n        /// The object representing a column. It must implement one of interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </param>\n        /// <returns>A new column.</returns>\n        public ClickHouseTableColumn AddColumn(object column)\n        {\n            return AddColumn(GetUniqueColumnName(\"column\"), column);\n        }\n\n        /// <summary>\n        /// Creates a new column with the specified name and adds it to the collection.\n        /// </summary>\n        /// <param name=\"columnName\">The name of the column.</param>\n        /// <param name=\"column\">\n        /// The object representing a column. It must implement one of interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </param>\n        /// <returns>A new column.</returns>\n        public ClickHouseTableColumn AddColumn(string columnName, object column)\n        {\n            if (column == null)\n                throw new ArgumentNullException(nameof(column));\n\n            var columnType = column.GetType();\n            Type? enumerable = null;\n            Type? altEnumerable = null;\n            Type? asyncEnumerable = null;\n            Type? altAsyncEnumerable = null;\n            Type? readOnlyList = null;\n            Type? altReadOnlyList = null;\n            Type? list = null;\n            Type? altList = null;\n            foreach (var ifs in columnType.GetInterfaces())\n            {\n                if (!ifs.IsGenericType)\n                    continue;\n\n                var ifsDefinition = ifs.GetGenericTypeDefinition();\n                if (ifsDefinition == typeof(IEnumerable<>) && ifs.GetGenericArguments()[0] != typeof(object))\n                {\n                    altEnumerable ??= enumerable;\n                    enumerable = ifs;\n                }\n                else if (ifsDefinition == typeof(IAsyncEnumerable<>) && ifs.GetGenericArguments()[0] != typeof(object))\n                {\n                    altAsyncEnumerable = asyncEnumerable;\n                    asyncEnumerable = ifs;\n                }\n                else if (ifsDefinition == typeof(IReadOnlyList<>) && ifs.GetGenericArguments()[0] != typeof(object))\n                {\n                    altReadOnlyList = readOnlyList;\n                    readOnlyList = ifs;\n                }\n                else if (ifsDefinition == typeof(IList<>) && ifs.GetGenericArguments()[0] != typeof(object))\n                {\n                    altList = list;\n                    list = ifs;\n                }\n            }\n\n            Type genericCollectionType;\n            if (readOnlyList != null)\n            {\n                if (altReadOnlyList != null)\n                    throw CreateInterfaceAmbiguousException(readOnlyList, altReadOnlyList);\n\n                genericCollectionType = readOnlyList;\n            }\n            else if (list != null)\n            {\n                if (altList != null)\n                    throw CreateInterfaceAmbiguousException(list, altList);\n\n                genericCollectionType = list;\n            }\n            else if (asyncEnumerable != null)\n            {\n                if (altAsyncEnumerable != null)\n                    throw CreateInterfaceAmbiguousException(asyncEnumerable, altAsyncEnumerable);\n\n                genericCollectionType = asyncEnumerable;\n            }\n            else if (enumerable != null)\n            {\n                if (altEnumerable != null)\n                    throw CreateInterfaceAmbiguousException(enumerable, altEnumerable);\n\n                genericCollectionType = enumerable;\n            }\n            else\n            {\n                throw new ClickHouseException(ClickHouseErrorCodes.ColumnTypeMismatch, \"The column is not a generic collection. A type of the column can't be detected.\");\n            }\n\n            var elementType = genericCollectionType.GetGenericArguments()[0];\n\n            var result = AddColumn(columnName, column, elementType);\n            if (result.Settings?.ColumnType == null)\n                result.Settings = new ClickHouseColumnSettings(result.Settings?.StringEncoding, result.Settings?.EnumConverter, elementType);\n\n            return result;\n        }\n\n        /// <summary>\n        /// Creates a new column with the default name and adds it to the collection.\n        /// </summary>\n        /// <param name=\"column\">\n        /// The object representing a column. It must implement one of interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </param>\n        /// <param name=\"columnType\">The type of the column's values.</param>\n        /// <returns>A new column.</returns>\n        public ClickHouseTableColumn AddColumn(object column, Type columnType)\n        {\n            return AddColumn(GetUniqueColumnName(\"column\"), column, columnType);\n        }\n\n        /// <summary>\n        /// Creates a new column with the specified name and adds it to the collection.\n        /// </summary>\n        /// <param name=\"columnName\">The name of the column.</param>\n        /// <param name=\"column\">\n        /// The object representing a column. It must implement one of interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </param>\n        /// <param name=\"columnType\">The type of the column's values.</param>\n        /// <returns>A new column.</returns>\n        public ClickHouseTableColumn AddColumn(string columnName, object column, Type columnType)\n        {\n            var result = new ClickHouseTableColumn(columnName, column, columnType);\n            Add(result);\n            return result;\n        }\n\n        private static ClickHouseException CreateInterfaceAmbiguousException(Type itf, Type altItf)\n        {\n            return new ClickHouseException(ClickHouseErrorCodes.ColumnTypeMismatch, $\"A type of the column is ambiguous. The column implements interfaces \\\"{itf}\\\" and \\\"{altItf}\\\".\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseTableProvider.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// The default implementation of <see cref=\"IClickHouseTableProvider\"/>. This class can't be inherited.\n    /// </summary>\n    public sealed class ClickHouseTableProvider : IClickHouseTableProvider\n    {\n        /// <inheritdoc/>\n        public string TableName { get; }\n\n        /// <inheritdoc/>\n        public int ColumnCount => Columns.Count;\n\n        /// <inheritdoc/>\n        public int RowCount { get; }\n\n        /// <summary>\n        /// Gets the collection of table's columns.\n        /// </summary>\n        public ClickHouseTableColumnCollection Columns { get; } = new ClickHouseTableColumnCollection();\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseTableProvider\"/> class with specified table name and number of rows.\n        /// </summary>\n        /// <param name=\"tableName\">The name of the table.</param>\n        /// <param name=\"rowCount\">The number of rows in the table.</param>\n        public ClickHouseTableProvider(string tableName, int rowCount)\n        {\n            if (string.IsNullOrWhiteSpace(tableName))\n                throw new ArgumentNullException(\"The name of a table must be a non-empty string.\", nameof(tableName));\n            if (rowCount < 0)\n                throw new ArgumentException(\"The number of rows must be a positive number.\", nameof(rowCount));\n\n            TableName = tableName;\n            RowCount = rowCount;\n        }\n\n        /// <inheritdoc cref=\"ClickHouseTableColumnCollection.AddColumn(object)\"/>\n        public ClickHouseTableColumn AddColumn(object column)\n        {\n            return Columns.AddColumn(column);\n        }\n\n        /// <inheritdoc cref=\"ClickHouseTableColumnCollection.AddColumn(object, Type)\"/>\n        public ClickHouseTableColumn AddColumn(object column, Type columnType)\n        {\n            return Columns.AddColumn(column, columnType);\n        }\n\n        /// <inheritdoc cref=\"ClickHouseTableColumnCollection.AddColumn(string, object)\"/>\n        public ClickHouseTableColumn AddColumn(string columnName, object column)\n        {\n            return Columns.AddColumn(columnName, column);\n        }\n\n        /// <inheritdoc cref=\"ClickHouseTableColumnCollection.AddColumn(string, object, Type)\"/>\n        public ClickHouseTableColumn AddColumn(string columnName, object column, Type columnType)\n        {\n            return Columns.AddColumn(columnName, column, columnType);\n        }\n\n        /// <inheritdoc cref=\"ClickHouseTableColumnCollection.AddColumn{T}(IReadOnlyList{T})\"/>\n        public ClickHouseTableColumn AddColumn<T>(IReadOnlyList<T> column)\n        {\n            return Columns.AddColumn(column);\n        }\n\n        /// <inheritdoc cref=\"ClickHouseTableColumnCollection.AddColumn{T}(string, IReadOnlyList{T})\"/>\n        public ClickHouseTableColumn AddColumn<T>(string columnName, IReadOnlyList<T> column)\n        {\n            return Columns.AddColumn(columnName, column);\n        }\n\n        object IClickHouseTableProvider.GetColumn(int index)\n        {\n            return Columns[index].Value;\n        }\n\n        IClickHouseColumnDescriptor IClickHouseTableProvider.GetColumnDescriptor(int index)\n        {\n            return Columns[index];\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseTableProviderCollection.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Utils;\nusing System;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents a collection of table providers associated with a <see cref=\"ClickHouseCommand\"/>. This class cannot be inherited.\n    /// </summary>\n    public sealed class ClickHouseTableProviderCollection : IndexedCollectionBase<string, IClickHouseTableProvider>\n    {\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseTableProviderCollection\"/> with the default capacity.\n        /// </summary>\n        public ClickHouseTableProviderCollection()\n            : base(StringComparer.OrdinalIgnoreCase)\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseTableProviderCollection\"/> with the specified capacity capacity.\n        /// </summary>\n        /// <param name=\"capacity\">The initial number of elements that the collection can contain.</param>\n        public ClickHouseTableProviderCollection(int capacity)\n            : base(capacity, StringComparer.OrdinalIgnoreCase)\n        {\n        }\n\n        /// <inheritdoc/>\n        protected sealed override string GetKey(IClickHouseTableProvider item)\n        {\n            return item.TableName;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseTableWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient\n{\n    internal sealed class ClickHouseTableWriter : IClickHouseTableWriter\n    {\n        public string TableName { get; }\n\n        public int RowCount { get; }\n\n        public IReadOnlyList<IClickHouseColumnWriter> Columns { get; }\n\n        public ClickHouseTableWriter(string tableName, int rowCount, IEnumerable<IClickHouseColumnWriter> columns)\n        {\n            if (columns == null)\n                throw new ArgumentNullException(nameof(columns));\n\n            TableName = tableName ?? throw new ArgumentNullException(nameof(tableName));\n            RowCount = rowCount;\n            Columns = columns.ToList().AsReadOnly();\n        }\n\n        public ClickHouseTableWriter(string tableName, int rowCount, IReadOnlyList<IClickHouseColumnWriter> columns)\n        {\n            TableName = tableName;\n            RowCount = rowCount;\n            Columns = columns;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseTcpClient.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Net;\nusing System.Net.Security;\nusing System.Net.Sockets;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Types;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient\n{\n    internal sealed class ClickHouseTcpClient : IDisposable\n    {\n        private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);\n\n        private readonly TcpClient _client;\n        private readonly ClickHouseConnectionSettings _settings;\n        private IClickHouseTypeInfoProvider _typeInfoProvider;\n        private readonly ClickHouseBinaryProtocolReader _reader;\n        private readonly ClickHouseBinaryProtocolWriter _writer;\n        private readonly SslStream? _sslStream;\n\n        private Exception? _unhandledException;\n        private int _state;\n\n        public ClickHouseTcpClientState State => (ClickHouseTcpClientState)_state;\n\n        public ClickHouseServerInfo ServerInfo { get; private set; }\n\n        public ClickHouseTcpClient(\n            TcpClient client,\n            ClickHouseBinaryProtocolReader reader,\n            ClickHouseBinaryProtocolWriter writer,\n            ClickHouseConnectionSettings settings,\n            ClickHouseServerInfo serverInfo,\n            IClickHouseTypeInfoProvider typeInfoProvider,\n            SslStream? sslStream)\n        {\n            _reader = reader ?? throw new ArgumentNullException(nameof(reader));\n            _writer = writer ?? throw new ArgumentNullException(nameof(writer));\n            _client = client ?? throw new ArgumentNullException(nameof(client));\n            _settings = settings ?? throw new ArgumentNullException(nameof(settings));\n            ServerInfo = serverInfo;\n            _typeInfoProvider = typeInfoProvider;\n            _sslStream = sslStream;\n        }\n\n        public async ValueTask<Session> OpenSession(bool async, IClickHouseSessionExternalResources? externalResources, CancellationToken sessionCancellationToken, CancellationToken cancellationToken)\n        {\n            var state = (ClickHouseTcpClientState)_state;\n            if (state != ClickHouseTcpClientState.Failed)\n            {\n                try\n                {\n                    if (async)\n                        await _semaphore.WaitAsync(cancellationToken);\n                    else\n                        _semaphore.Wait(cancellationToken);\n\n                    var previousState = (ClickHouseTcpClientState)Interlocked.CompareExchange(ref _state, (int)ClickHouseTcpClientState.Active, (int)ClickHouseTcpClientState.Ready);\n                    Debug.Assert(previousState != ClickHouseTcpClientState.Active);\n                    state = previousState == ClickHouseTcpClientState.Ready ? ClickHouseTcpClientState.Active : previousState;\n                }\n                catch (ObjectDisposedException)\n                {\n                    // Reading an actual state without modification of field _state\n                    state = (ClickHouseTcpClientState)Interlocked.CompareExchange(ref _state, (int)state, (int)state);\n                    if (state != ClickHouseTcpClientState.Failed)\n                        throw;\n                }\n            }\n\n            if (state == ClickHouseTcpClientState.Failed)\n            {\n                if (_unhandledException != null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"Connection is broken.\", _unhandledException);\n\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"Connection is broken.\");\n            }\n\n            try\n            {\n                return new Session(this, externalResources, sessionCancellationToken);\n            }\n            catch\n            {\n                _semaphore.Release();\n                throw;\n            }\n        }\n\n        private bool TryInterceptMessage(IServerMessage message)\n        {\n            switch (message.MessageCode)\n            {\n                case ServerMessageCode.TimezoneUpdate:\n                    var timezone = ((ServerTimeZoneUpdateMessage)message).Timezone;\n                    if (string.Equals(timezone, ServerInfo.Timezone, StringComparison.Ordinal))\n                        return true;\n\n                    var updServerInfo = ServerInfo.WithTimezone(timezone);\n                    _typeInfoProvider = _typeInfoProvider.Configure(updServerInfo);\n                    ServerInfo = updServerInfo;\n                    return true;\n\n                default:\n                    return false;\n            }\n        }\n\n        private void SetFailed(Exception? unhandledException)\n        {\n            Dispose(false);\n            _unhandledException = unhandledException;\n            // 'Failed' is the terminal state. Plain assignment should work just as well as interlocked operations.\n            _state = (int)ClickHouseTcpClientState.Failed;\n        }\n\n        public void Dispose()\n        {\n            Dispose(true);\n        }\n\n        private void Dispose(bool disposing)\n        {\n            _semaphore.Dispose();\n            _reader.Dispose();\n            _writer.Dispose();\n            _sslStream?.Dispose();\n\n            // The disposed TcpClient returns null for Client\n            _client.Client?.Close(disposing ? 1 : 0);\n            _client.Dispose();\n        }\n\n        public sealed class Session : IDisposable, IAsyncDisposable\n        {\n            private readonly ClickHouseTcpClient _client;\n            private readonly IClickHouseSessionExternalResources? _externalResources;\n            private readonly CancellationToken _sessionCancellationToken;\n\n            public IClickHouseTypeInfoProvider TypeInfoProvider => _client._typeInfoProvider;\n\n            public ClickHouseServerInfo ServerInfo => _client.ServerInfo;\n\n            public bool IsDisposed { get; private set; }\n\n            public bool IsFailed => _client.State == ClickHouseTcpClientState.Failed;\n\n            public Session(ClickHouseTcpClient client, IClickHouseSessionExternalResources? externalResources, CancellationToken sessionCancellationToken)\n            {\n                _client = client;\n                _externalResources = externalResources;\n                _sessionCancellationToken = sessionCancellationToken;\n            }\n\n            public async ValueTask<ClientQueryMessage> SendQuery(\n                ClientQueryMessage.Builder messageBuilder,\n                IReadOnlyCollection<IClickHouseTableWriter>? tables,\n                bool async,\n                CancellationToken cancellationToken)\n            {\n                CheckDisposed();\n\n                var writer = _client._writer;\n                ClientQueryMessage queryMessage;\n                try\n                {\n                    var settings = _client._settings;\n\n                    messageBuilder.ClientName = settings.ClientName;\n                    messageBuilder.ClientVersion = settings.ClientVersion;\n                    messageBuilder.Host = settings.Host;\n                    messageBuilder.RemoteAddress = ((IPEndPoint?) _client._client.Client.RemoteEndPoint)?.ToString();\n                    messageBuilder.ProtocolRevision = _client.ServerInfo.Revision;\n                    messageBuilder.CompressionEnabled = _client._settings.Compress;\n\n                    queryMessage = messageBuilder.Build();\n                    if (queryMessage.Settings != null)\n                    {\n                        if (_client.ServerInfo.Revision < ClickHouseProtocolRevisions.MinRevisionWithSettingsSerializedAsStrings)\n                        {\n                            throw new ClickHouseException(\n                                ClickHouseErrorCodes.ProtocolRevisionNotSupported,\n                                $\"Query settings are not supported. Current protocol revision is {_client.ServerInfo.Revision}. Minimal required protocol revision is {ClickHouseProtocolRevisions.MinRevisionWithSettingsSerializedAsStrings}.\");\n                        }\n                    }\n\n                    queryMessage.Write(writer);\n\n                    if (tables != null)\n                    {\n                        foreach (var table in tables)\n                            WriteTable(table);\n                    }\n\n                    WriteTable(ClickHouseEmptyTableWriter.Instance);\n                }\n                catch (Exception ex)\n                {\n                    writer.Discard();\n                    throw ClickHouseHandledException.Wrap(ex);\n                }\n\n                await WithCancellationToken(cancellationToken, ct => writer.Flush(async, ct));\n\n                return queryMessage;\n            }\n\n            public async ValueTask SendQuery(ClientQueryMessage queryMessage, bool async, CancellationToken cancellationToken)\n            {\n                CheckDisposed();\n\n                var writer = _client._writer;\n                try\n                {\n                    queryMessage.Write(writer);\n                    WriteTable(ClickHouseEmptyTableWriter.Instance);\n                }\n                catch (Exception ex)\n                {\n                    writer.Discard();\n                    throw ClickHouseHandledException.Wrap(ex);\n                }\n\n                await WithCancellationToken(cancellationToken, ct => writer.Flush(async, ct));\n            }\n\n            public async ValueTask SendCancel(bool async)\n            {\n                CheckDisposed();\n\n                var writer = _client._writer;\n                writer.Write7BitInt32((int) ClientMessageCode.Cancel);\n\n                await writer.Flush(async, CancellationToken.None);\n            }\n\n            public async ValueTask SendPing(bool async, CancellationToken cancellationToken)\n            {\n                CheckDisposed();\n\n                var writer = _client._writer;\n                writer.Write7BitInt32((int) ClientMessageCode.Ping);\n\n                await WithCancellationToken(cancellationToken, ct => writer.Flush(async, ct));\n            }\n\n            public async ValueTask SendTable(IClickHouseTableWriter table, bool async, CancellationToken cancellationToken)\n            {\n                CheckDisposed();\n\n                try\n                {\n                    WriteTable(table);\n                }\n                catch (Exception ex)\n                {\n                    _client._writer.Discard();\n                    throw ClickHouseHandledException.Wrap(ex);\n                }\n\n                await WithCancellationToken(cancellationToken, ct => _client._writer.Flush(async, ct));\n            }\n\n            private void WriteTable(IClickHouseTableWriter table)\n            {\n                var writer = _client._writer;\n\n                writer.Write7BitInt32((int) ClientMessageCode.Data);\n                writer.WriteString(table.TableName);\n\n                var compression = _client._settings.Compress ? CompressionAlgorithm.Lz4 : CompressionAlgorithm.None;\n                writer.BeginCompress(compression, _client._settings.CompressionBlockSize);\n\n                writer.WriteByte(BlockFieldCodes.IsOverflows);\n                writer.WriteBool(false); // is overflow\n                writer.WriteByte(BlockFieldCodes.BucketNum);\n                writer.WriteInt32(-1); // data size in block. -1 for null\n                writer.WriteByte(BlockFieldCodes.End);\n\n                writer.Write7BitInt32(table.Columns.Count);\n                writer.Write7BitInt32(table.RowCount);\n\n                foreach (var column in table.Columns)\n                {\n                    writer.WriteString(column.ColumnName);\n                    writer.WriteString(column.ColumnType);\n\n                    if (_client.ServerInfo.Revision >= ClickHouseProtocolRevisions.MinRevisionWithCustomSerialization)\n                        writer.WriteBool(false); // has_custom\n\n                    if (table.RowCount == 0)\n                        continue;\n\n                    while (true)\n                    {\n                        var size = writer.WriteRaw(mem => column.WritePrefix(mem.Span));\n                        if (size.Elements == 1)\n                            break;\n\n                        if (size.Elements != 0)\n                            throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. A column writer returned an unexpected number of prefixes: {size.Elements}.\");\n                    }\n\n                    int rowCount = table.RowCount;\n                    while (rowCount > 0)\n                    {\n                        var size = writer.WriteRaw(mem => column.WriteNext(mem.Span));\n                        rowCount -= size.Elements;\n                    }\n                }\n\n                writer.EndCompress();\n            }\n\n            public async ValueTask<IServerMessage> ReadMessage(bool async, CancellationToken cancellationToken)\n            {\n                return await WithCancellationToken(cancellationToken, ct => ReadMessageInternal(async, ct));\n            }\n\n            private async ValueTask<IServerMessage> ReadMessageInternal(bool async, CancellationToken cancellationToken)\n            {\n                IServerMessage message;\n                do\n                {\n                    message = await _client._reader.ReadMessage(_client.ServerInfo.Revision, true, async, cancellationToken);\n                } while (_client.TryInterceptMessage(message));\n\n                return message;\n            }\n\n            public async ValueTask<ClickHouseTable> ReadTable(ServerDataMessage dataMessage, IReadOnlyList<ClickHouseReaderColumnSettings>? columnSettings, bool async, CancellationToken cancellationToken)\n            {\n                var withDecompression = dataMessage.MessageCode != ServerMessageCode.ProfileEvents;\n\n                var result = await WithCancellationToken(\n                    cancellationToken,\n                    ct =>\n                        ReadTable(\n                            withDecompression,\n                            (typeInfo, rowCount, mode) => typeInfo.CreateColumnReader(rowCount, mode),\n                            (columnInfo, reader, index) => ReadTableColumn(columnInfo, reader, columnSettings == null || columnSettings.Count <= index ? default : columnSettings[index]),\n                            async,\n                            ct)\n                );\n\n                Debug.Assert(result.columns != null);\n                var blockHeader = new BlockHeader(dataMessage.TempTableName, result.columnInfos.AsReadOnly(), result.rowCount);\n                return new ClickHouseTable(blockHeader, result.columns.AsReadOnly());\n            }\n\n            public async ValueTask<BlockHeader> SkipTable(ServerDataMessage dataMessage, bool async, CancellationToken cancellationToken)\n            {\n                var withDecompression = dataMessage.MessageCode != ServerMessageCode.ProfileEvents;\n                var result = await WithCancellationToken(cancellationToken, ct => ReadTable(withDecompression, (typeInfo, rowCount, mode) => typeInfo.CreateSkippingColumnReader(rowCount, mode), null, async, ct));\n                return new BlockHeader(dataMessage.TempTableName, result.columnInfos.AsReadOnly(), result.rowCount);\n            }\n\n            private async ValueTask<(List<ColumnInfo> columnInfos, List<IClickHouseTableColumn>? columns, int rowCount)> ReadTable<TReader>(\n                bool withDecompression,\n                Func<IClickHouseColumnTypeInfo, int, ClickHouseColumnSerializationMode, TReader> createColumnReader,\n                Func<ColumnInfo, TReader, int, IClickHouseTableColumn>? readTableColumn,\n                bool async,\n                CancellationToken cancellationToken)\n                where TReader : IClickHouseColumnReaderBase\n            {\n                CheckDisposed();\n\n                var compression = withDecompression && _client._settings.Compress ? CompressionAlgorithm.Lz4 : CompressionAlgorithm.None;\n                var reader = _client._reader;\n                reader.BeginDecompress(compression);\n\n                int blockFieldCode;\n                bool isOverflows = false;\n                // It seems that this value is used only for internal purposes and does not affect the format of the block\n                int bucketNum = -1;\n                do\n                {\n                    blockFieldCode = await reader.Read7BitInt32(async, cancellationToken);\n                    switch (blockFieldCode)\n                    {\n                        case BlockFieldCodes.IsOverflows:\n                            isOverflows = await reader.ReadBool(async, cancellationToken);\n                            break;\n                        case BlockFieldCodes.BucketNum:\n                            bucketNum = await reader.ReadInt32(async, cancellationToken);\n                            break;\n                        case BlockFieldCodes.End:\n                            break;\n                        default:\n                            throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Internal error. Unexpected block field code (0x{blockFieldCode:X}) received from the server.\");\n                    }\n                } while (blockFieldCode != BlockFieldCodes.End);\n\n                var columnCount = await reader.Read7BitInt32(async, cancellationToken);\n                var rowCount = await reader.Read7BitInt32(async, cancellationToken);\n\n                if (isOverflows)\n                    throw new NotImplementedException(\"TODO: implement support for is_overflows.\");\n\n                var columnInfos = new List<ColumnInfo>(columnCount);\n                var columns = readTableColumn == null ? null : new List<IClickHouseTableColumn>(columnCount);\n                for (int i = 0; i < columnCount; i++)\n                {\n                    var columnName = await reader.ReadString(async, cancellationToken);\n                    var columnTypeName = await reader.ReadString(async, cancellationToken);\n\n                    var serializationMode = ClickHouseColumnSerializationMode.Default;\n                    if (_client.ServerInfo.Revision >= ClickHouseProtocolRevisions.MinRevisionWithCustomSerialization)\n                    {\n                        var hasCustom = await reader.ReadBool(async, cancellationToken);\n                        if (hasCustom)\n                            serializationMode = ClickHouseColumnSerializationMode.Custom;\n                    }\n\n                    var columnType = _client._typeInfoProvider.GetTypeInfo(columnTypeName);\n                    var columnInfo = new ColumnInfo(columnName, columnType);\n                    columnInfos.Add(columnInfo);\n\n                    var columnReader = createColumnReader(columnType, rowCount, serializationMode);\n                    if (rowCount > 0)\n                    {\n                        while (true)\n                        {\n                            var sequenceSize = await reader.ReadRaw(columnReader.ReadPrefix, async, cancellationToken);\n                            if (sequenceSize.Elements == 1)\n                                break;\n\n                            if (sequenceSize.Elements != 0)\n                                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. Received an unexpected number of column prefixes: {sequenceSize.Elements}.\");\n                        }\n                    }\n\n                    var columnRowCount = rowCount;\n                    while (columnRowCount > 0)\n                    {\n                        var sequenceSize = await reader.ReadRaw(columnReader.ReadNext, async, cancellationToken);\n\n                        if (sequenceSize.Elements < 0)\n                            throw new InvalidOperationException(\"The number of elements must be greater than zero.\");\n                        if (sequenceSize.Elements > columnRowCount)\n                            throw new InvalidOperationException($\"The number of rows in the column \\\"{columnName}\\\" is greater than the number of rows in the table.\");\n                        if (sequenceSize.Elements < columnRowCount)\n                            await reader.Advance(async, cancellationToken);\n\n                        columnRowCount -= sequenceSize.Elements;\n                    }\n\n                    if (columns != null)\n                    {\n                        Debug.Assert(readTableColumn != null);\n                        var column = readTableColumn(columnInfo, columnReader, i);\n                        columns.Add(column);\n                    }\n                }\n\n                reader.EndDecompress();\n                return (columnInfos, columns, rowCount);                \n            }\n\n            private static IClickHouseTableColumn ReadTableColumn(ColumnInfo columnInfo, IClickHouseColumnReader columnReader, ClickHouseReaderColumnSettings settings)\n            {\n                var column = columnReader.EndRead(settings.Column);\n\n                var reinterpreter = settings.Reinterpreter;\n                if (reinterpreter != null)\n                {\n                    var reinterpretedColumn = reinterpreter.TryReinterpret(column);\n                    if (reinterpretedColumn != null)\n                        return reinterpretedColumn;\n\n                    return new ReinterpretedTableColumn(column, CreateCastFunc(columnInfo, reinterpreter.BuiltInConvertToType, reinterpreter.ExternalConvertToType));\n                }\n\n                return column;\n            }\n\n            private static Func<object, object> CreateCastFunc(ColumnInfo columnInfo, Type? builtInConvertToType, Type? externalConvertToType)\n            {\n                if ((externalConvertToType ?? builtInConvertToType) == typeof(object))\n                    return value => value; // This is fine, everything is an object\n\n                return CastFailed;\n\n                object CastFailed(object value)\n                {\n                    if (value == DBNull.Value)\n                        return value;\n\n                    if (builtInConvertToType != null)\n                    {\n                        if (externalConvertToType == null)\n                        {\n                            throw new ClickHouseException(\n                                ClickHouseErrorCodes.ColumnTypeMismatch,\n                                $\"A value from the column \\\"{columnInfo.Name}\\\" of type \\\"{columnInfo.TypeInfo.GetFieldType()}\\\" can't be converted to type \\\"{builtInConvertToType}\\\". \"\n                                + \"This type is defined in the column settings.\");\n                        }\n\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.ColumnTypeMismatch,\n                            $\"A value from the column \\\"{columnInfo.Name}\\\" of type \\\"{columnInfo.TypeInfo.GetFieldType()}\\\" can't be converted to type \\\"{builtInConvertToType}\\\". \"\n                            + $\"This type is implicitly defined by the callback function for a value conversion. The callback function returns a value of type \\\"{externalConvertToType}\\\".\");\n                    }\n\n                    throw new ClickHouseException(\n                        ClickHouseErrorCodes.InternalError,\n                        $\"ClickHouseClient didn't find a suitable type conversion for the column \\\"{columnInfo.Name}\\\" of type \\\"{columnInfo.TypeInfo.GetFieldType()}\\\". \"\n                        + \"This is an internal error. Please, report a bug if you see this message.\");\n                }\n            }\n\n            private async ValueTask<T> WithCancellationToken<T>(CancellationToken token, Func<CancellationToken, ValueTask<T>> execute)\n            {\n                if (_sessionCancellationToken == CancellationToken.None)\n                    return await execute(token);\n\n                if (token == CancellationToken.None)\n                    return await execute(_sessionCancellationToken);\n\n                using var linkedTs = CancellationTokenSource.CreateLinkedTokenSource(token, _sessionCancellationToken);\n                try\n                {\n                    return await execute(linkedTs.Token);\n                }\n                catch (TaskCanceledException taskCanceledEx)\n                {\n                    if (token.IsCancellationRequested)\n                        throw new TaskCanceledException(taskCanceledEx.Message, taskCanceledEx, token);\n\n                    throw;\n                }\n                catch (OperationCanceledException operationCanceledEx)\n                {\n                    if (token.IsCancellationRequested)\n                        throw new OperationCanceledException(operationCanceledEx.Message, operationCanceledEx, token);\n\n                    throw;\n                }\n            }\n\n            private async ValueTask WithCancellationToken(CancellationToken token, Func<CancellationToken, ValueTask> execute)\n            {\n                if (_sessionCancellationToken == CancellationToken.None)\n                {\n                    await execute(token);\n                    return;\n                }\n\n                if (token == CancellationToken.None)\n                {\n                    await execute(_sessionCancellationToken);\n                    return;\n                }\n\n                using var linkedTs = CancellationTokenSource.CreateLinkedTokenSource(token, _sessionCancellationToken);\n                try\n                {\n                    await execute(linkedTs.Token);\n                }\n                catch (TaskCanceledException taskCanceledEx)\n                {\n                    if (token.IsCancellationRequested)\n                        throw new TaskCanceledException(taskCanceledEx.Message, taskCanceledEx, token);\n\n                    throw;\n                }\n                catch (OperationCanceledException operationCanceledEx)\n                {\n                    if (token.IsCancellationRequested)\n                        throw new OperationCanceledException(operationCanceledEx.Message, operationCanceledEx, token);\n\n                    throw;\n                }\n            }\n\n            private void CheckDisposed()\n            {\n                if (IsDisposed)\n                    throw new ObjectDisposedException(\"Internal error. This object was disposed and no more has an exclusive access to the network stream.\");\n\n                if (_client.State == ClickHouseTcpClientState.Failed)\n                {\n                    if (_client._unhandledException != null)\n                        throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"Connection is broken.\", _client._unhandledException);\n\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidConnectionState, \"Connection is broken.\");\n                }                \n            }\n\n            public async ValueTask<Exception?> SetFailed(Exception? unhandledException, bool sendCancel, bool async)\n            {\n                if (IsDisposed)\n                    return null;\n\n                Exception? networkException = null;\n                if (sendCancel)\n                {\n                    try\n                    {\n                        await SendCancel(async);\n                    }\n                    catch (Exception ex)\n                    {\n                        networkException = new ClickHouseException(ClickHouseErrorCodes.NetworkError, \"Network error. Operation was not canceled properly.\", ex);                        \n                    }\n                }\n\n                _client.SetFailed(unhandledException);\n\n                Exception? externalException = null;\n                if (_externalResources != null)\n                {\n                    externalException = await _externalResources.ReleaseOnFailure(unhandledException, async);\n\n                    if (ReferenceEquals(unhandledException, externalException))\n                        externalException = null;\n                }\n\n                var exceptions = new List<Exception>(3);\n                if (unhandledException != null)\n                    exceptions.Add(unhandledException);\n\n                if (networkException != null)\n                    exceptions.Add(networkException);\n\n                if (externalException != null)\n                    exceptions.Add(externalException);\n\n                switch (exceptions.Count)\n                {\n                    case 0:\n                        return null;\n                    case 1:\n                        return exceptions[0];\n                    default:\n                        return new AggregateException(exceptions);\n                }\n            }\n\n            public void Dispose()\n            {\n                TaskHelper.WaitNonAsyncTask(Dispose(false));\n            }\n\n            public ValueTask DisposeAsync()\n            {\n                return Dispose(true);\n            }\n\n            public ValueTask Dispose(bool async)\n            {\n                if (IsDisposed || IsFailed)\n                    return default;\n\n                Interlocked.CompareExchange(ref _client._state, (int)ClickHouseTcpClientState.Ready, (int)ClickHouseTcpClientState.Active);\n                _client._semaphore.Release();\n                IsDisposed = true;\n\n                return _externalResources?.Release(async) ?? default;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseTcpClientState.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient\n{\n    internal enum ClickHouseTcpClientState\n    {\n        /// <summary>\n        /// TCP client is ready to open a session\n        /// </summary>\n        Ready = 0,\n\n        /// <summary>\n        /// There is an active session associated with the client\n        /// </summary>\n        Active = 1,\n\n        /// <summary>\n        /// A session was failed. TCP client was forced to close\n        /// </summary>\n        Failed = 2\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseTlsMode.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Specifies the list of available options for establishing a secure connection with the TLS protocol.\n    /// </summary>\n    public enum ClickHouseTlsMode\n    {\n        /// <summary>\n        /// TLS is disabled. Data exchange between the client and the server will be performed without encryption.\n        /// The connection will fail to open if the server requires TLS.\n        /// </summary>\n        Disable = 0,\n\n        /// <summary>\n        /// TLS is required. The connection will fail to open if the server doesn't support TLS.\n        /// </summary>\n        Require = 1\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/ClickHouseVersion.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Globalization;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents a version of the ClickHouse client or of the ClickHouse server.\n    /// </summary>\n    public readonly struct ClickHouseVersion : IEquatable<ClickHouseVersion>\n    {\n        /// <summary>\n        /// Gets the value of the major component of the version.\n        /// </summary>\n        public int Major { get; }\n\n        /// <summary>\n        /// Gets the value of the minor component of the version.\n        /// </summary>\n        public int Minor { get; }\n\n        /// <summary>\n        /// Gets the value of the build component of the version.\n        /// </summary>\n        public int Build { get; }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseVersion\"/> with the specified components.\n        /// </summary>\n        /// <param name=\"major\">The value of the major component of the version.</param>\n        /// <param name=\"minor\">the value of the minor component of the version.</param>\n        /// <param name=\"build\">the value of the build component of the version.</param>\n        public ClickHouseVersion(int major, int minor, int build)\n        {\n            Major = major;\n            Minor = minor;\n            Build = build;\n        }\n\n        /// <summary>\n        /// Returns the string representation of the version in the format {Major}.{Minor}.{Build}.\n        /// </summary>\n        /// <returns>The string representation of the version.</returns>\n        public override string ToString()\n        {\n            return string.Format(CultureInfo.InvariantCulture, \"{0}.{1}.{2}\", Major, Minor, Build);\n        }\n\n        /// <inheritdoc/>\n        public bool Equals(ClickHouseVersion other)\n        {\n            return Major == other.Major && Minor == other.Minor && Build == other.Build;\n        }\n\n        /// <inheritdoc/>\n        public override bool Equals(object? obj)\n        {\n            return obj is ClickHouseVersion other && Equals(other);\n        }\n\n        /// <inheritdoc/>\n        public override int GetHashCode()\n        {\n            unchecked\n            {\n                int hashCode = Major;\n                hashCode = (hashCode * 397) ^ Minor;\n                hashCode = (hashCode * 397) ^ Build;\n                return hashCode;\n            }\n        }\n\n        /// <summary>\n        /// Parses the string in the format \"{Major}.{Minor}.{Build}\".\n        /// </summary>\n        /// <param name=\"value\">The string to parse.</param>\n        /// <returns>The parsed <see cref=\"ClickHouseVersion\"/>.</returns>\n        /// <exception cref=\"ArgumentNullException\">Throws an <see cref=\"ArgumentNullException\"/> when the value is null.</exception>\n        /// <exception cref=\"ArgumentException\">Throws an <see cref=\"ArgumentException\"/> when the value is not a valid string representation of the <see cref=\"ClickHouseVersion\"/>.</exception>\n        public static ClickHouseVersion Parse(string value)\n        {\n            if (value == null)\n                throw new ArgumentNullException(nameof(value));\n\n            var firstIndex = value.IndexOf('.');\n            var lastIndex = firstIndex < 0 ? -1 : value.IndexOf('.', firstIndex + 1);\n            var nextIndex = lastIndex < 0 ? -1 : value.IndexOf('.', lastIndex + 1);\n\n            const NumberStyles numberStyle = NumberStyles.Integer & ~NumberStyles.AllowLeadingSign;\n            if (firstIndex < 0)\n            {\n                if (int.TryParse(value, numberStyle, CultureInfo.InvariantCulture, out var major))\n                    return new ClickHouseVersion(major, 0, 0);\n            }\n            else if (nextIndex < 0)\n            {\n                var span = value.AsSpan();\n                if (int.TryParse(span.Slice(0, firstIndex), numberStyle, CultureInfo.InvariantCulture, out var major))\n                {\n                    if (lastIndex < 0)\n                    {\n                        if (int.TryParse(span.Slice(firstIndex + 1), numberStyle, CultureInfo.InvariantCulture, out var minor))\n                            return new ClickHouseVersion(major, minor, 0);\n                    }\n                    else\n                    {\n                        if (int.TryParse(span.Slice(firstIndex + 1, lastIndex - firstIndex - 1), numberStyle, CultureInfo.InvariantCulture, out var minor) &&\n                            int.TryParse(span.Slice(lastIndex + 1), numberStyle, CultureInfo.InvariantCulture, out var build))\n                        {\n                            return new ClickHouseVersion(major, minor, build);\n                        }\n                    }\n                }\n            }\n\n            throw new ArgumentException(\"The value is not a valid version number.\", nameof(value));\n        }\n\n        /// <summary>\n        /// Compares two objects of type <see cref=\"ClickHouseVersion\"/>.\n        /// </summary>\n        /// <param name=\"left\">The left operand.</param>\n        /// <param name=\"right\">The right operand.</param>\n        /// <returns><see langword=\"true\"/> if the two objects are equal; oterwise <see langword=\"false\"/>.</returns>\n        public static bool operator ==(ClickHouseVersion left, ClickHouseVersion right)\n        {\n            return left.Equals(right);\n        }\n\n        /// <summary>\n        /// Compares two objects of type <see cref=\"ClickHouseVersion\"/>.\n        /// </summary>\n        /// <param name=\"left\">The left operand.</param>\n        /// <param name=\"right\">The right operand.</param>\n        /// <returns><see langword=\"false\"/> if the two objects are equal; oterwise <see langword=\"true\"/>.</returns>\n        public static bool operator !=(ClickHouseVersion left, ClickHouseVersion right)\n        {\n            return !left.Equals(right);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Exceptions/ClickHouseErrorCodes.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Exceptions\n{\n    /// <summary>\n    /// The static class that provides access to error codes.\n    /// </summary>\n    public static class ClickHouseErrorCodes\n    {\n        /// <summary>\n        /// The code for an unspecified error. This code means that an error of an unknown origin was wrapped in <see cref=\"ClickHouseException\"/>.\n        /// </summary>\n        public const int Unspecified = 0;\n        \n        /// <summary>\n        /// The code for an error provided by the ClickHouse server.\n        /// </summary>\n        public const int ServerError = 1;\n\n        /// <summary>\n        /// The code for an error caused by an invalid state of the connection.\n        /// </summary>\n        public const int InvalidConnectionState = 2;\n\n        /// <summary>\n        /// The code for an error caused by an attempt to execute an operation when the connection is closed.\n        /// </summary>\n        public const int ConnectionClosed = 3;\n\n        /// <summary>\n        /// The code for an error caused by an unexpected response from the server.\n        /// </summary>\n        public const int ProtocolUnexpectedResponse = 4;\n\n        /// <summary>\n        /// The code for an error caused by an attempt to use a feature that is not supported by the revision negotiated between the client and the server.\n        /// </summary>\n        public const int ProtocolRevisionNotSupported = 5;\n\n        /// <summary>\n        /// The code for an error caused by an attempt to read or write a column of an unknown type.\n        /// </summary>\n        public const int TypeNotSupported = 6;\n\n        /// <summary>\n        /// The code for an error caused by an invalid full name of the type. This code usually means that the type was specified with invalid parameters.\n        /// </summary>\n        public const int InvalidTypeName = 7;\n\n        /// <summary>\n        /// The code for an error caused by an attempt to use a parametrizable type without parameters.\n        /// </summary>\n        public const int TypeNotFullySpecified = 8;\n\n        /// <summary>\n        /// The code for an error caused by a lack of an expected response from the server.\n        /// </summary>\n        public const int QueryTypeMismatch = 9;\n\n        /// <summary>\n        /// The code for a data reading error.\n        /// </summary>\n        public const int DataReaderError = 10;\n\n        /// <summary>\n        /// The code for an error caused by a lack of required query parameter.\n        /// </summary>\n        public const int QueryParameterNotFound = 11;\n\n        /// <summary>\n        /// The code for an error caused by a configuration of the query parameter (<see cref=\"ClickHouseParameter\"/>).\n        /// </summary>\n        public const int InvalidQueryParameterConfiguration = 12;\n\n        /// <summary>\n        /// An obsolete error code. It was replaced with <see cref=\"ColumnTypeMismatch\"/>, <see cref=\"NotSupportedInSyncronousMode\"/>, and <see cref=\"InvalidRowCount\"/>.\n        /// </summary>\n        [Obsolete(\"ColumnMismatch was replaced with \" + nameof(ColumnTypeMismatch) + \", \" + nameof(NotSupportedInSyncronousMode) + \" and \" + nameof(InvalidRowCount))]\n        public const int ColumnMismatch = 13;\n\n        /// <summary>\n        /// The common code for unexpected situations. This code indicates errors (bugs) in the client's code.\n        /// </summary>\n        public const int InternalError = 14;\n\n        /// <summary>\n        /// The code for an error caused by a compression decoder.\n        /// </summary>\n        public const int CompressionDecoderError = 15;\n\n        /// <summary>\n        /// The code for an error caused by a network error.\n        /// </summary>\n        public const int NetworkError = 16;\n        \n        /// <summary>\n        /// The code for an error caused by invalid settings (<see cref=\"ClickHouseColumnSettings\"/>).\n        /// </summary>\n        public const int InvalidColumnSettings = 17;\n\n        /// <summary>\n        /// The code for a column type error. This code means that there is no mapping between the ClickHouse column's type and the provided type.\n        /// </summary>\n        public const int ColumnTypeMismatch = 18;\n\n        /// <summary>\n        /// The code for an error caused by a wrong number of rows in a column. Usually this error code means that there are less rows in the colum than expected.\n        /// </summary>\n        public const int InvalidRowCount = 19;\n\n        /// <summary>\n        /// The code for an error caused by an attempt to execute an asyncronous operation in a syncronous method.\n        /// </summary>\n        public const int NotSupportedInSyncronousMode = 20;\n        \n        /// <summary>\n        /// The code for an error caused by the callback to an external code.\n        /// </summary>\n        public const int CallbackError = 21;\n\n        /// <summary>\n        /// The code for an error caused by a violation of the TLS protocol.\n        /// </summary>\n        public const int TlsError = 22;\n\n        /// <summary>\n        /// The code for an error caused by unexpected changes in the table's structure.\n        /// </summary>\n        public const int TableModified = 23;\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Exceptions/ClickHouseException.cs",
    "content": "#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Exceptions\n{\n    /// <summary>\n    /// Represents an exception specific to ClickHouse client or server.\n    /// </summary>\n    public class ClickHouseException : Exception\n    {\n        /// <summary>\n        /// The numeric code of the error. For the full list of errors see <see cref=\"ClickHouseErrorCodes\"/>.\n        /// </summary>\n        public int ErrorCode { get; }\n\n        /// <summary>\n        /// Initializes a new instance of the exception with the specified error code.\n        /// </summary>\n        /// <param name=\"errorCode\">The code of the error. It should be one of the values from <see cref=\"ClickHouseErrorCodes\"/>.</param>\n        public ClickHouseException(int errorCode)\n        {\n            ErrorCode = errorCode;\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the exception with specified error code and message.\n        /// </summary>\n        /// <param name=\"errorCode\">The code of the error. It should be one of the values from <see cref=\"ClickHouseErrorCodes\"/>.</param>\n        /// <param name=\"message\">The error message that explains the reason for the exception.</param>\n        public ClickHouseException(int errorCode, string? message)\n            : base(message)\n        {\n            ErrorCode = errorCode;\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the exception with specified error code, message, and inner exception.\n        /// </summary>\n        /// <param name=\"errorCode\">The code of the error. It should be one of the values from <see cref=\"ClickHouseErrorCodes\"/>.</param>\n        /// <param name=\"message\">The error message that explains the reason for the exception.</param>\n        /// <param name=\"innerException\">The exception that is the cause of the current exception.</param>\n        public ClickHouseException(int errorCode, string? message, Exception? innerException)\n            : base(message, innerException)\n        {\n            ErrorCode = errorCode;\n        }\n    }\n}\n                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    "
  },
  {
    "path": "src/Octonica.ClickHouseClient/Exceptions/ClickHouseHandledException.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Exceptions\n{\n    /// <summary>\n    /// An exception which doesn't break the connection. It always has <see cref=\"Exception.InnerException\"/>. This class can't be inherited.\n    /// </summary>\n    public sealed class ClickHouseHandledException : ClickHouseException\n    {\n        private ClickHouseHandledException(int errorCode, string message, Exception innerException)\n            : base(errorCode, message, innerException)\n        {\n        }\n\n        internal static ClickHouseHandledException Wrap(Exception exception)\n        {\n            if (exception is ClickHouseHandledException nfException)\n                return nfException;\n            \n            if (exception is ClickHouseException chException)\n                return new ClickHouseHandledException(chException.ErrorCode, chException.Message, chException);\n\n            return new ClickHouseHandledException(ClickHouseErrorCodes.Unspecified, exception.Message, exception);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Exceptions/ClickHouseServerException.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Exceptions\n{\n    /// <summary>\n    /// Represents an exception generated by the ClickHouse server. The <see cref=\"ClickHouseException.ErrorCode\"/> of an instance of this class\n    /// is always <see cref=\"ClickHouseErrorCodes.ServerError\"/>. This class can not be inherited.\n    /// </summary>\n    public sealed class ClickHouseServerException : ClickHouseException\n    {\n        /// <summary>\n        /// The code of the error provided by the server.\n        /// </summary>\n        public int ServerErrorCode { get; }\n\n        /// <summary>\n        /// The type of the error provided by the server.\n        /// </summary>\n        public string ServerErrorType { get; }\n\n        /// <summary>\n        /// The server-side stack trace.\n        /// </summary>\n        public string ServerStackTrace { get; }\n        \n        /// <summary>\n        /// The query passed to the server or <see langword=\"null\"/> if the error is not related to the query.\n        /// </summary>\n        public string? Query { get; }\n\n        /// <summary>\n        /// Initializes a new instance of the ClickHouse server exception with the provided parameters and the <see cref=\"ClickHouseException.ErrorCode\"/>\n        /// equal to <see cref=\"ClickHouseErrorCodes.ServerError\"/>.\n        /// </summary>\n        /// <param name=\"serverErrorCode\">The code of the error provided by the server.</param>\n        /// <param name=\"serverErrorType\">The type of the error provided by the server.</param>\n        /// <param name=\"message\">The error message that explains the reason for the exception.</param>\n        /// <param name=\"serverStackTrace\">The server-side stack trace.</param>\n        public ClickHouseServerException(int serverErrorCode, string serverErrorType, string? message, string serverStackTrace)\n            : base(ClickHouseErrorCodes.ServerError, message)\n        {\n            ServerErrorCode = serverErrorCode;\n            ServerErrorType = serverErrorType;\n            ServerStackTrace = serverStackTrace;\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the ClickHouse server exception with the provided parameters and the <see cref=\"ClickHouseException.ErrorCode\"/>\n        /// equal to <see cref=\"ClickHouseErrorCodes.ServerError\"/>.\n        /// </summary>\n        /// <param name=\"serverErrorCode\">The code of the error provided by the server.</param>\n        /// <param name=\"serverErrorType\">The type of the error provided by the server.</param>\n        /// <param name=\"message\">The error message that explains the reason for the exception.</param>\n        /// <param name=\"serverStackTrace\">The server-side stack trace.</param>\n        /// <param name=\"innerException\">The exception that is the cause of the current exception.</param>\n        public ClickHouseServerException(int serverErrorCode, string serverErrorType, string? message, string serverStackTrace, Exception? innerException)\n            : base(ClickHouseErrorCodes.ServerError, message, innerException)\n        {\n            ServerErrorCode = serverErrorCode;\n            ServerErrorType = serverErrorType;\n            ServerStackTrace = serverStackTrace;\n        }\n\n        private ClickHouseServerException(int serverErrorCode, string serverErrorType, string? message, string serverStackTrace, string? query)\n            : base(ClickHouseErrorCodes.ServerError, message)\n        {\n            ServerErrorCode = serverErrorCode;\n            ServerErrorType = serverErrorType;\n            ServerStackTrace = serverStackTrace;\n            Query = query;\n        }\n\n        private ClickHouseServerException(int serverErrorCode, string serverErrorType, string? message, string serverStackTrace, string? query, Exception? innerException)\n            : base(ClickHouseErrorCodes.ServerError, message, innerException)\n        {\n            ServerErrorCode = serverErrorCode;\n            ServerErrorType = serverErrorType;\n            ServerStackTrace = serverStackTrace;\n            Query = query;\n        }\n\n        /// <summary>\n        /// Creates and returns a new instance of <see cref=\"ClickHouseServerException\"/> with the provided query.\n        /// </summary>\n        /// <param name=\"query\">The query that should be added to the exception.</param>\n        /// <returns>The new instance of <see cref=\"ClickHouseServerException\"/> with the provided query</returns>\n        public ClickHouseServerException CopyWithQuery(string query)\n        {\n            if (InnerException == null)\n                return new ClickHouseServerException(ServerErrorCode, ServerErrorType, Message, ServerStackTrace, query);\n\n            return new ClickHouseServerException(ServerErrorCode, ServerErrorType, Message, ServerStackTrace, query, InnerException);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/IClickHouseArrayTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// The interface for an object representing a column with arrays.\n    /// </summary>\n    /// <typeparam name=\"TElement\">The type of the array's element.</typeparam>\n    public interface IClickHouseArrayTableColumn<TElement> : IClickHouseTableColumn\n    {\n        /// <summary>\n        /// Copies elements from the array to the specified buffer.\n        /// </summary>\n        /// <param name=\"index\">The zero-based index of the row.</param>\n        /// <param name=\"buffer\">The buffer into which to copy data.</param>\n        /// <param name=\"dataOffset\">The index within the row from which to begin the copy operation.</param>\n        /// <returns>The actual number of copied elements.</returns>\n        int CopyTo(int index, Span<TElement> buffer, int dataOffset);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/IClickHouseColumnDescriptor.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021-2022 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Types;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// Represents a set of properties describing a column and its type.\n    /// </summary>\n    public interface IClickHouseColumnDescriptor : IClickHouseColumnTypeDescriptor\n    {\n        /// <summary>\n        /// Gets the name of the column.\n        /// </summary>\n        string ColumnName { get; }\n\n        /// <summary>\n        /// Gets the settings that should be applied when writing the column.\n        /// </summary>\n        ClickHouseColumnSettings? Settings { get; }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/IClickHouseSessionExternalResources.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Threading.Tasks;\n\nnamespace Octonica.ClickHouseClient\n{\n    internal interface IClickHouseSessionExternalResources\n    {\n        ValueTask Release(bool async);\n\n        ValueTask<Exception?> ReleaseOnFailure(Exception? exception, bool async);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/IClickHouseTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// The basic interface for <see cref=\"ClickHouseDataReader\"/>'s internal columns.\n    /// </summary>\n    public interface IClickHouseTableColumn\n    {\n        /// <summary>\n        /// Gets the number of rows in the column.\n        /// </summary>\n        int RowCount { get; }\n\n        /// <summary>\n        /// Gets the value indicating whether an actual value at the specified index is NULL.\n        /// </summary>\n        /// <param name=\"index\">The zero-based index of row.</param>\n        /// <returns><see langword=\"true\"/> if an actual value at the specified index is NULL; otherwise <see langword=\"false\"/>.</returns>\n        bool IsNull(int index);\n\n        /// <summary>\n        /// Gets the value at the specified index.\n        /// </summary>\n        /// <param name=\"index\">The zero-based index of row.</param>\n        /// <returns>The value at the specified index or <see cref=\"System.DBNull.Value\"/> if the value is NULL.</returns>\n        object GetValue(int index);\n\n        /// <summary>\n        /// Makes an attempt to convert this column to a column with the values of the type <typeparamref name=\"T\"/>.\n        /// </summary>\n        /// <typeparam name=\"T\">The desired type of column's values.</typeparam>\n        /// <returns>The column converted to the type <see cref=\"IClickHouseTableColumn{T}\"/> or <see langword=\"null\"/> if such conversion is not supported.</returns>\n        /// <remarks>This method may or may not return <see langword=\"null\"/> when the column itself implements the interface <see cref=\"IClickHouseTableColumn{T}\"/>.</remarks>\n        IClickHouseTableColumn<T>? TryReinterpret<T>();\n\n        /// <summary>\n        /// Makes an attempt to convert this column to an array column with the type of array's element <typeparamref name=\"T\"/>.\n        /// </summary>\n        /// <typeparam name=\"T\">The desired type of array elements.</typeparam>\n        /// <returns>The column converted to the type <see cref=\"IClickHouseArrayTableColumn{T}\"/> or <see langword=\"null\"/> if such conversion is not supported.</returns>\n        /// <remarks>This method may or may not return <see langword=\"null\"/> when the column itself implements the interface <see cref=\"IClickHouseArrayTableColumn{T}\"/>.</remarks>\n        IClickHouseArrayTableColumn<T>? TryReinterpretAsArray<T>() => null;\n\n        /// <summary>\n        /// If possible, performs double dispatch and provides this object as an instance of <see cref=\"IClickHouseTableColumn{TValue}\"/>.\n        /// </summary>\n        /// <typeparam name=\"T\">The type of the dispatched value.</typeparam>\n        /// <param name=\"dispatcher\">The dispatcher that receives an instance of <see cref=\"IClickHouseTableColumn{TValue}\"/>.</param>\n        /// <param name=\"dispatchedValue\">When this method returns, the result of the dispatch operation, if the dispatcher was called; otherwise the default value of <typeparamref name=\"T\"/>.</param>\n        /// <returns><see langword=\"true\"/> if this object is an instance of <see cref=\"IClickHouseTableColumn{TValue}\"/> and the dispatch operation was performed; otherwise <see langword=\"false\"/>.</returns>\n        bool TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue);\n    }\n\n    /// <summary>\n    /// The generic interface for <see cref=\"ClickHouseDataReader\"/>'s internal columns.\n    /// </summary>\n    public interface IClickHouseTableColumn<out T> : IClickHouseTableColumn\n    {\n        /// <summary>\n        /// Gets the default value of the column for the sparse serialization.\n        /// </summary>\n        T DefaultValue { get; }\n\n        /// <summary>\n        /// Gets the value at the specified index.\n        /// </summary>\n        /// <param name=\"index\">The zero-based index of row.</param>\n        /// <returns>The value at the specified index.</returns>\n        /// <remarks>This method should never return <see langword=\"null\"/>.</remarks>\n        new T GetValue(int index);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/IClickHouseTableColumnDispatcher.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// The interface for objects performing an arbitrary operation over the generic column.\n    /// </summary>\n    /// <typeparam name=\"TRes\">The type of a value returned by the dispatcher.</typeparam>\n    public interface IClickHouseTableColumnDispatcher<out TRes>\n    {\n        /// <summary>\n        /// When implemented in a derived class performs an arbitrary operation over the column.\n        /// </summary>\n        /// <typeparam name=\"T\">The type of the column's values.</typeparam>\n        /// <param name=\"column\">The column.</param>\n        /// <returns>The result of the operation.</returns>\n        TRes Dispatch<T>(IClickHouseTableColumn<T> column);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/IClickHouseTableProvider.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient\n{\n    /// <summary>\n    /// The basic interface for an object that provides access to a table along with metadata.\n    /// </summary>\n    public interface IClickHouseTableProvider\n    {\n        /// <summary>\n        /// Gets the name of the table.\n        /// </summary>\n        public string TableName { get; }\n\n        /// <summary>\n        /// Gets the number of columns in the table.\n        /// </summary>\n        public int ColumnCount { get; }\n\n        /// <summary>\n        /// Gets the number of rows in the table.\n        /// </summary>\n        public int RowCount { get; }\n\n        /// <summary>\n        /// Gets the descriptor of the column at the specified index.\n        /// </summary>\n        /// <param name=\"index\">The zero-based index of the column.</param>\n        /// <returns>The descriptor of the column.</returns>\n        IClickHouseColumnDescriptor GetColumnDescriptor(int index);\n\n        /// <summary>\n        /// Returns the object that represents the column at the specified index. It must implement one of interfaces:\n        /// <see cref=\"IReadOnlyList{T}\"/>,\n        /// <see cref=\"IList{T}\"/>,\n        /// <see cref=\"IAsyncEnumerable{T}\"/>,\n        /// <see cref=\"IEnumerable{T}\"/> or\n        /// <see cref=\"IEnumerable\"/>.\n        /// </summary>\n        /// <param name=\"index\">The zero-based index of the column.</param>\n        /// <returns>The object that represents the column at the specified index.</returns>\n        object GetColumn(int index);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Octonica.ClickHouseClient.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netcoreapp3.1;net6.0;net8.0</TargetFrameworks>\n    <Nullable>enable</Nullable>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <Version Condition=\"'$(ClickHouseClientVersion)' != ''\">$(ClickHouseClientVersion)</Version>\n    <Version Condition=\"'$(Version)' == ''\">2.2.10</Version>\n    <AssemblyVersion Condition=\"'$(AssemblyVersion)' == ''\">$(Version).0</AssemblyVersion>\n    <Version Condition=\"'$(ClickHouseClientVersionSuffix)' != ''\">$(Version)$(ClickHouseClientVersionSuffix)</Version>\n\n    <Company>Octonica</Company>\n    <Copyright>© 2019 – 2026 Octonica</Copyright>\n    <Product>Octonica.ClickHouseClient</Product>\n    <PackageProjectUrl>https://github.com/Octonica/ClickHouseClient</PackageProjectUrl>\n    <Description>ClickHouse .NET Core driver</Description>\n    <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>\n    <Authors>Octonica</Authors>\n    <PackageTags>ClickHouse</PackageTags>\n    <RepositoryUrl>https://github.com/Octonica/ClickHouseClient.git</RepositoryUrl>\n    <RepositoryType>git</RepositoryType>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"K4os.Compression.LZ4\" Version=\"1.3.6\" />\n    <PackageReference Include=\"TimeZoneConverter\" Version=\"6.1.0\" Condition=\"'$(TargetFramework)' == 'netcoreapp3.1'\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Properties/AssemblyInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Runtime.CompilerServices;\n\n[assembly: InternalsVisibleTo(\"Octonica.ClickHouseClient.Tests\")]"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/BlockFieldCodes.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    /// <summary>\n    /// https://github.com/ClickHouse/ClickHouse/blob/master/src/Core/BlockInfo.h\n    /// </summary>\n    internal static class BlockFieldCodes\n    {\n        public const int End = 0;\n\n        /// <summary>\n        /// * After running GROUP BY ... WITH TOTALS with the max_rows_to_group_by and group_by_overflow_mode = 'any' settings,\n        /// * a row is inserted in the separate block with aggregated values that have not passed max_rows_to_group_by.\n        /// * If it is such a block, then is_overflows is set to true for it.\n        /// </summary>\n        public const int IsOverflows = 1;\n\n        /// <summary>\n        ///* When using the two-level aggregation method, data with different key groups are scattered across different buckets.\n        ///* In this case, the bucket number is indicated here. It is used to optimize the merge for distributed aggregation.\n        ///* Otherwise -1.\n        /// </summary>\n        public const int BucketNum = 2;\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/BlockHeader.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Collections.ObjectModel;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal class BlockHeader\n    {\n        private static readonly ReadOnlyCollection<ColumnInfo> EmptyColumns = new ReadOnlyCollection<ColumnInfo>(new ColumnInfo[0]);\n\n        public string? TableName { get; }\n\n        public ReadOnlyCollection<ColumnInfo> Columns { get; }\n\n        public int RowCount { get; }\n\n        public BlockHeader(string? tableName, ReadOnlyCollection<ColumnInfo>? columns, int rowCount)\n        {\n            TableName = tableName;\n            Columns = columns ?? EmptyColumns;\n            RowCount = rowCount;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/CityHash.cs",
    "content": "﻿#region License\n// Copyright (c) 2011 Google, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// CityHash, by Geoff Pike and Jyrki Alakuijala\n//\n// This file provides CityHash128() and related functions.\n//\n// It's probably possible to create even faster hash functions by\n// writing a program that systematically explores some of the space of\n// possible hash functions, by using SIMD instructions, or by\n// compromising on hash quality.\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Runtime.CompilerServices;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    // CityHash v1.0.2 implementation from ClickHouse\n    internal static class CityHash\n    {\n        private static UInt64 UNALIGNED_LOAD64(ReadOnlySequence<byte> p)\n        {\n            if (p.FirstSpan.Length > sizeof(UInt64))\n                return BitConverter.ToUInt64(p.FirstSpan);\n\n            Span<byte> tmpBuffer = stackalloc byte[sizeof(UInt64)];\n            p.Slice(0, sizeof(UInt64)).CopyTo(tmpBuffer);\n            return BitConverter.ToUInt64(tmpBuffer);\n        }\n\n        private static UInt32 UNALIGNED_LOAD32(ReadOnlySequence<byte> p)\n        {\n            if (p.FirstSpan.Length > sizeof(UInt32))\n                return BitConverter.ToUInt32(p.FirstSpan);\n\n            Span<byte> tmpBuffer = stackalloc byte[sizeof(UInt32)];\n            p.Slice(0, sizeof(UInt32)).CopyTo(tmpBuffer);\n            return BitConverter.ToUInt32(tmpBuffer);\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        private static UInt64 Fetch64(ReadOnlySequence<byte> p)\n        {\n            return UNALIGNED_LOAD64(p);\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        private static UInt32 Fetch32(ReadOnlySequence<byte> p)\n        {\n            return UNALIGNED_LOAD32(p);\n        }\n\n        // Some primes between 2^63 and 2^64 for various uses.\n        private const UInt64 k0 = 0xc3a5c85c97cb3127UL;\n        private const UInt64 k1 = 0xb492b66fbe98f273UL;\n        private const UInt64 k2 = 0x9ae16a3b2f90404fUL;\n        private const UInt64 k3 = 0xc949d7c7509e6557UL;\n\n        // Bitwise right rotate.  Normally this will compile to a single\n        // instruction, especially if the shift is a manifest constant.\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        private static UInt64 Rotate(UInt64 val, int shift)\n        {\n            // Avoid shifting by 64: doing so yields an undefined result.\n            return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));\n        }\n\n        // Equivalent to Rotate(), but requires the second arg to be non-zero.\n        // On x86-64, and probably others, it's possible for this to compile\n        // to a single instruction if both args are already in registers.\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        private static UInt64 RotateByAtLeast1(UInt64 val, int shift)\n        {\n            return (val >> shift) | (val << (64 - shift));\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        private static UInt64 ShiftMix(UInt64 val)\n        {\n            return val ^ (val >> 47);\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        private static UInt64 HashLen16(UInt64 u, UInt64 v)\n        {\n            return Hash128to64(new UInt128(v, u));\n        }\n\n        // Hash 128 input bits down to 64 bits of output.\n        // This is intended to be a reasonably good hash function.\n        private static UInt64 Hash128to64(UInt128 x)\n        {\n            // Murmur-inspired hashing.\n            const UInt64 kMul = 0x9ddfea08eb382d69UL;\n\n            unchecked\n            {\n#if NET8_0_OR_GREATER\n                var low = (UInt64)x;\n                var high = (UInt64)(x >> 64);\n                UInt64 a = (low ^ high) * kMul;\n                a ^= (a >> 47);\n                UInt64 b = (high ^ a) * kMul;\n#else\n                UInt64 a = (x.Low ^ x.High) * kMul;\n                a ^= (a >> 47);\n                UInt64 b = (x.High ^ a) * kMul;\n#endif\n                b ^= (b >> 47);\n                b *= kMul;\n                return b;\n            }\n        }\n\n        private static UInt64 HashLen0to16(ReadOnlySequence<byte> s)\n        {\n            var len = (ulong) s.Length;\n\n            unchecked\n            {\n                if (len > 8)\n                {\n                    UInt64 a = Fetch64(s);\n                    UInt64 b = Fetch64(s.Slice((int) len - 8));\n                    return HashLen16(a, RotateByAtLeast1(b + len, (int) len)) ^ b;\n                }\n\n                if (len >= 4)\n                {\n                    UInt64 a = Fetch32(s);\n                    return HashLen16(len + (a << 3), Fetch32(s.Slice((int) len - 4)));\n                }\n\n                if (len > 0)\n                {\n                    byte a = s.FirstSpan[0];\n                    byte b = s.Slice((int) len >> 1).FirstSpan[0];\n                    byte c = s.Slice((int) len - 1).FirstSpan[0];\n\n                    UInt32 y = a + ((uint) b << 8);\n                    UInt32 z = (uint) (len + ((uint) c << 2));\n                    return ShiftMix(y * k2 ^ z * k3) * k2;\n                }\n\n                return k2;\n            }\n        }\n\n        // Return a 16-byte hash for 48 bytes.  Quick and dirty.\n        // Callers do best to use \"random-looking\" values for a and b.\n        private static (UInt64 first, UInt64 second) WeakHashLen32WithSeeds(\n            UInt64 w,\n            UInt64 x,\n            UInt64 y,\n            UInt64 z,\n            UInt64 a,\n            UInt64 b)\n        {\n            a += w;\n            b = Rotate(b + a + z, 21);\n            UInt64 c = a;\n            a += x;\n            a += y;\n            b += Rotate(a, 44);\n            return (a + z, b + c);\n        }\n\n        // Return a 16-byte hash for s[0] ... s[31], a, and b.  Quick and dirty.\n        private static (UInt64 first, UInt64 second) WeakHashLen32WithSeeds(\n            ReadOnlySequence<byte> s,\n            UInt64 a,\n            UInt64 b)\n        {\n            return WeakHashLen32WithSeeds(\n                Fetch64(s),\n                Fetch64(s.Slice(8)),\n                Fetch64(s.Slice(16)),\n                Fetch64(s.Slice(24)),\n                a,\n                b);\n        }\n\n        // A subroutine for CityHash128().  Returns a decent 128-bit hash for strings\n        // of any length representable in ssize_t.  Based on City and Murmur.\n        private static UInt128 CityMurmur(ReadOnlySequence<byte> s, UInt128 seed)\n        {\n            var len = (ulong) s.Length;\n\n            unchecked\n            {\n#if NET8_0_OR_GREATER\n                UInt64 a = (UInt64)seed;\n                UInt64 b = (UInt64)(seed >> 64);\n#else\n                UInt64 a = seed.Low;\n                UInt64 b = seed.High;\n#endif\n                UInt64 c = 0;\n                UInt64 d = 0;\n                int l = (int) len - 16;\n                if (l <= 0)\n                {\n                    // len <= 16\n                    a = ShiftMix(a * k1) * k1;\n                    c = b * k1 + HashLen0to16(s);\n                    d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c));\n                }\n                else\n                {\n                    // len > 16\n                    c = HashLen16(Fetch64(s.Slice((int)len - 8)) + k1, a);\n                    d = HashLen16(b + len, c + Fetch64(s.Slice((int) len - 16)));\n                    a += d;\n                    do\n                    {\n                        a ^= ShiftMix(Fetch64(s) * k1) * k1;\n                        a *= k1;\n                        b ^= a;\n                        c ^= ShiftMix(Fetch64(s.Slice(8)) * k1) * k1;\n                        c *= k1;\n                        d ^= c;\n                        s = s.Slice(16);\n                        l -= 16;\n                    } while (l > 0);\n                }\n\n                a = HashLen16(a, c);\n                b = HashLen16(d, b);\n                return new UInt128(HashLen16(b, a), a ^ b);\n            }\n        }\n\n        public static UInt128 CityHash128WithSeed(ReadOnlySequence<byte> s, UInt128 seed)\n        {\n            var len = (ulong) s.Length;\n\n            if (len < 128)\n            {\n                return CityMurmur(s, seed);\n            }\n\n            // We expect len >= 128 to be the common case.  Keep 56 bytes of state:\n            // v, w, x, y, and z.\n            unchecked\n            {\n                (UInt64 first, UInt64 second) v, w;\n#if NET8_0_OR_GREATER\n                UInt64 x = (UInt64)seed;\n                UInt64 y = (UInt64)(seed>>64);\n#else\n                UInt64 x = seed.Low;\n                UInt64 y = seed.High;\n#endif\n                UInt64 z = len * k1;\n                v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);\n                v.second = Rotate(v.first, 42) * k1 + Fetch64(s.Slice(8));\n                w.first = Rotate(y + z, 35) * k1 + x;\n                w.second = Rotate(x + Fetch64(s.Slice(88)), 53) * k1;\n\n                // This is the same inner loop as CityHash64(), manually unrolled.\n                var sOrig = s;\n                do\n                {\n                    x = Rotate(x + y + v.first + Fetch64(s.Slice(16)), 37) * k1;\n                    y = Rotate(y + v.second + Fetch64(s.Slice(48)), 42) * k1;\n                    x ^= w.second;\n                    y ^= v.first;\n                    z = Rotate(z ^ w.first, 33);\n                    v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);\n                    w = WeakHashLen32WithSeeds(s.Slice(32), z + w.second, y);\n\n                    var tmp = x;\n                    x = z;\n                    z = tmp;\n\n                    s = s.Slice(64);\n                    x = Rotate(x + y + v.first + Fetch64(s.Slice(16)), 37) * k1;\n                    y = Rotate(y + v.second + Fetch64(s.Slice(48)), 42) * k1;\n                    x ^= w.second;\n                    y ^= v.first;\n                    z = Rotate(z ^ w.first, 33);\n                    v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);\n                    w = WeakHashLen32WithSeeds(s.Slice(32), z + w.second, y);\n\n                    tmp = x;\n                    x = z;\n                    z = tmp;\n\n                    s = s.Slice(64);\n                    len -= 128;\n                } while (len >= 128);\n\n                var offset = sOrig.Length - s.Length;\n                y += Rotate(w.first, 37) * k0 + z;\n                x += Rotate(v.first + z, 49) * k0;\n                // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.\n                for (int tail_done = 0; tail_done < (int) len;)\n                {\n                    tail_done += 32;\n                    y = Rotate(y - x, 42) * k0 + v.second;\n                    w.first += Fetch64(sOrig.Slice((int) len - tail_done + 16 + offset));\n                    x = Rotate(x, 49) * k0 + w.first;\n                    w.first += v.first;\n                    v = WeakHashLen32WithSeeds(sOrig.Slice((int) len - tail_done + offset), v.first, v.second);\n                }\n\n                // At this point our 48 bytes of state should contain more than\n                // enough information for a strong 128-bit hash.  We use two\n                // different 48-byte-to-8-byte hashes to get a 16-byte final result.\n                x = HashLen16(x, v.first);\n                y = HashLen16(y, w.first);\n                return new UInt128(\n                    HashLen16(x + w.second, y + v.second),\n                    HashLen16(x + v.second, w.second) + y);\n            }\n        }\n\n        public static UInt128 CityHash128(ReadOnlySequence<byte> s)\n        {\n            var len = (ulong) s.Length;\n\n            if (len >= 16)\n            {\n                return CityHash128WithSeed(\n                    s.Slice(16),\n                    new UInt128(\n                        Fetch64(s.Slice(8)),\n                        Fetch64(s) ^ k3));\n            }\n            else if (len >= 8)\n            {\n                return CityHash128WithSeed(\n                    ReadOnlySequence<byte>.Empty,\n                    new UInt128(\n                        Fetch64(s.Slice((int)len - 8)) ^ k1,\n                        Fetch64(s) ^ unchecked(len * k0)));\n            }\n            else\n            {\n                return CityHash128WithSeed(s, new UInt128(k1, k0));\n            }\n        }\n    }\n\n#if !NET8_0_OR_GREATER\n    internal readonly struct UInt128\n    {\n        public ulong Low { get; }\n        public ulong High { get; }\n\n        public UInt128(ulong high, ulong low)\n        {\n            Low = low;\n            High = high;\n        }\n    }\n#endif\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ClickHouseEmptyTableWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal class ClickHouseEmptyTableWriter : IClickHouseTableWriter\n    {\n        public static readonly ClickHouseEmptyTableWriter Instance = new ClickHouseEmptyTableWriter();\n\n        public string TableName { get; }\n\n        public int RowCount => 0;\n\n        public IReadOnlyList<IClickHouseColumnWriter> Columns { get; }\n\n        private ClickHouseEmptyTableWriter()\n        {\n            TableName = string.Empty;\n            Columns = new IClickHouseColumnWriter[0];\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ClickHouseParameterWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Types;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal abstract class ClickHouseParameterWriter : IClickHouseParameterValueWriter\n    {\n        public abstract int Length { get; }\n\n        public abstract int Write(Memory<byte> buffer);\n\n        public abstract StringBuilder Interpolate(StringBuilder queryBuilder);\n\n        public abstract StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeProvider, Func<StringBuilder, IClickHouseTypeInfo, StringBuilder> writeValue);\n\n        public static ClickHouseParameterWriter Dispatch(IClickHouseColumnTypeInfo typeInfo, object? value)\n        {\n            var dispatcher = new Dispatcher(typeInfo, value);\n            return dispatcher.Dispatch();\n        }\n\n        private sealed class Dispatcher : ITypeDispatcher<ClickHouseParameterWriter>\n        {\n            private readonly IClickHouseColumnTypeInfo _typeInfo;\n            private readonly object _value;\n\n            public Dispatcher(IClickHouseColumnTypeInfo typeInfo, object? value)\n            {\n                _typeInfo = typeInfo;\n                _value = value ?? DBNull.Value;\n            }\n\n            public ClickHouseParameterWriter Dispatch<T>()\n            {\n                var value = (T)_value;\n                var writer = _typeInfo.CreateParameterWriter<T>();\n                if (!writer.TryCreateParameterValueWriter(value, isNested: false, out var valueWriter))\n                    valueWriter = null;\n\n                return new ClickHouseParameterWriter<T>(writer, value, valueWriter);\n            }\n\n            public ClickHouseParameterWriter Dispatch()\n            {\n                return TypeDispatcher.Dispatch(_value.GetType(), this);\n            }\n        }\n    }\n\n    internal sealed class ClickHouseParameterWriter<T> : ClickHouseParameterWriter\n    {\n        private readonly IClickHouseParameterWriter<T> _writer;\n        private readonly T _value;\n        private readonly IClickHouseParameterValueWriter? _valueWriter;\n\n        public override int Length => _valueWriter?.Length ?? 0;\n\n        public ClickHouseParameterWriter(IClickHouseParameterWriter<T> writer, T value, IClickHouseParameterValueWriter? valueWriter)\n        {\n            _writer = writer;\n            _value = value;\n            _valueWriter = valueWriter;\n        }\n\n        public override StringBuilder Interpolate(StringBuilder queryBuilder)\n        {\n            return _writer.Interpolate(queryBuilder, _value);\n        }\n\n        public override StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeProvider, Func<StringBuilder, IClickHouseTypeInfo, StringBuilder> writerValue)\n        {\n            if (_valueWriter == null)\n            {\n                // Here we do not have a real value writer, so we can't pass the parameter to the query. We have to interpolate the value directly into the query.\n                return Interpolate(queryBuilder);\n            }\n\n            return _writer.Interpolate(queryBuilder, typeProvider, (qb, t, w) => w(qb, b => writerValue(b, t)));\n        }\n\n        public override int Write(Memory<byte> buffer)\n        {\n            return _valueWriter?.Write(buffer) ?? 0;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ClickHouseProtocolRevisions.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    /// <summary>\n    /// The static class that provides access to the ClickHouse's binary protocol revision numbers.\n    /// </summary>\n    public static class ClickHouseProtocolRevisions\n    {\n        /// <summary>\n        /// The number of the current revision. It is the latest revision supported by the client.\n        /// </summary>\n        public const int CurrentRevision = MinRevisionWithSshAuthentication;\n\n        /// <summary>\n        /// The number of protocol's revision that supports SSH authentication.\n        /// </summary>\n        public const int MinRevisionWithSshAuthentication = 54466;\n\n        /// <summary>\n        /// The number of protocol's revision with the timzone in data messages.\n        /// </summary>\n        public const int MinRevisionWithTimezoneUpdates = 54464;\n\n        /// <summary>\n        /// The number of protocol's revision with the number of total bytes in progress messages.\n        /// </summary>\n        public const int MinRevisionWithTotalBytesInProgress = 54463;\n\n        /// <summary>\n        /// The number of the protocol's revision that supports interserver secret (V2).\n        /// </summary>\n        public const int MinRevisionWithInterserverSecretV2 = 54462;\n\n        /// <summary>\n        /// The number of protocol's revision with the support of password complexity rules.\n        /// </summary>\n        public const int MinRevisionWithPasswordComplexityRules = 54461;\n\n        /// <summary>\n        /// The number of protocol's revision with the number of elapsed nanoseconds in progress messages.\n        /// </summary>\n        public const int MinRevisionWithServerQueryTimeInProgress = 54460;\n\n        /// <summary>\n        /// The number of protocol's revision with the support of parameters passed along with the query.\n        /// </summary>\n        public const int MinRevisionWithParameters = 54459;\n\n        /// <summary>\n        /// The number of protocol's revision with support of hello message addendum and quota keys.\n        /// </summary>\n        public const int MinRevisionWithAddendum = 54458;\n\n        /// <summary>\n        /// The number of protocol's revision with support of custom serialization.\n        /// </summary>\n        public const int MinRevisionWithCustomSerialization = 54454;\n\n        /// <summary>\n        /// The number of protocol's revision with support for parallel reading from replicas.\n        /// </summary>\n        public const int MinRevisionWithParallelReplicas = 54453;\n\n        /// <summary>\n        /// The number of protocol's revision with the initial query start time.\n        /// </summary>\n        public const int MinRevisionWithInitialQueryStartTime = 54449;\n\n        /// <summary>\n        /// The number of protocol's revision that supports distributed depth.\n        /// </summary>\n        public const int MinRevisionWithDistributedDepth = 54448;\n\n        /// <summary>\n        /// The number of protocol's revision with the support of Open Telemetry headers.\n        /// </summary>\n        public const int MinRevisionWithOpenTelemetry = 54442;\n\n        /// <summary>\n        /// The number of the protocol's revision that supports interserver secret.\n        /// </summary>\n        public const int MinRevisionWithInterserverSecret = 54441;\n\n        /// <summary>\n        /// The number of the protocol's revision with settings serialized as strings.\n        /// </summary>\n        public const int MinRevisionWithSettingsSerializedAsStrings = 54429;\n\n        /// <summary>\n        /// The minimal number of the revision supported by the client.\n        /// </summary>\n        public const int MinSupportedRevision = 54423;\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ClickHouseSyntaxHelper.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\nusing System.Text.RegularExpressions;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal static class ClickHouseSyntaxHelper\n    {\n        private static readonly Regex IdentifierRegex = new Regex(\"^[a-zA-Z_][0-9a-zA-Z_]*$\");\n\n        private const string EscapeChars = \"''\\\"\\\"\\\\\\\\0\\0a\\ab\\be\\u001bf\\fn\\nr\\rt\\tv\\v\";\n\n        private static bool TryGetUnescapedCharacter(char ch, out char unescapedCh)\n        {\n            for (int i = 0; i < EscapeChars.Length; i += 2)\n            {\n                if (ch == EscapeChars[i])\n                {\n                    unescapedCh = EscapeChars[i + 1];\n                    return true;\n                }\n            }\n\n            unescapedCh = default;\n            return false;\n        }\n\n        public static int GetIdentifierLiteralLength(string str, int startIndex)\n        {\n            return GetIdentifierLiteralLength(((ReadOnlySpan<char>)str).Slice(startIndex));\n        }\n\n        public static int GetIdentifierLiteralLength(ReadOnlySpan<char> str)\n        {\n            if (str.IsEmpty)\n                return -1;\n\n            if (str[0] == '`')\n            {\n                var length = GetQuotedTokenLength(str, '`');\n                if (length > 2)\n                    return length;\n            }\n            else if ((str[0] >= 'a' && str[0] <= 'z') || (str[0] >= 'A' && str[0] <= 'Z') || str[0] == '_')\n            {\n                int length = 1;\n                for (; length < str.Length; length++)\n                {\n                    var ch = str[length];\n                    if (char.IsWhiteSpace(ch))\n                        break;\n\n                    if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_')\n                        continue;\n\n                    return -1;\n                }\n\n                return length;\n            }\n\n            return -1;\n        }\n\n        public static int GetSingleQuoteStringLength(string str, int startIndex)\n        {\n            return GetSingleQuoteStringLength(((ReadOnlySpan<char>)str).Slice(startIndex));\n        }\n\n        public static int GetSingleQuoteStringLength(ReadOnlySpan<char> str)\n        {\n            return GetQuotedTokenLength(str, '\\'');\n        }\n\n        public static int GetQuotedTokenLength(ReadOnlySpan<char> str, char quoteSign)\n        {\n            if (str.IsEmpty || str[0] != quoteSign)\n                return -1;\n\n            int idx = 1;\n            while (idx < str.Length)\n            {\n                var slice = str.Slice(idx);\n                var nextIdx = slice.IndexOfAny(quoteSign, '\\\\');\n\n                if (nextIdx < 0)\n                    return -1;\n\n                if (slice[nextIdx] == '\\\\')\n                {\n                    idx += nextIdx + 2;\n                    continue;\n                }\n\n                return idx + nextIdx + 1;\n            }\n\n            return -1;\n        }\n\n        public static string GetIdentifier(ReadOnlySpan<char> identifierLiteral)\n        {\n            if (identifierLiteral.Length == 0)\n                throw new ArgumentException($\"The string \\\"{identifierLiteral.ToString()}\\\" is not a valid identifier.\", nameof(identifierLiteral));\n\n            var sb = new StringBuilder(identifierLiteral.Length);\n\n            if (identifierLiteral[0] == '`')\n            {\n                if (!TryParseQuotedToken(identifierLiteral, '`', out var parsedLiteral) || parsedLiteral == string.Empty)\n                    throw new ArgumentException($\"The string \\\"{identifierLiteral.ToString()}\\\" is not a valid identifier.\", nameof(identifierLiteral));\n\n                return parsedLiteral;\n            }\n\n            if ((identifierLiteral[0] >= 'a' && identifierLiteral[0] <= 'z') || (identifierLiteral[0] >= 'A' && identifierLiteral[0] <= 'Z') || identifierLiteral[0] == '_')\n            {\n                sb.Append(identifierLiteral[0]);\n                for (int i = 1; i < identifierLiteral.Length; i++)\n                {\n                    var ch = identifierLiteral[i];\n                    if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_')\n                    {\n                        sb.Append(ch);\n                        continue;\n                    }\n\n                    throw new ArgumentException($\"The string \\\"{identifierLiteral.ToString()}\\\" is not a valid identifier.\", nameof(identifierLiteral));\n                }\n\n                return sb.ToString();\n            }\n\n            throw new ArgumentException($\"The string \\\"{identifierLiteral.ToString()}\\\" is not a valid identifier.\", nameof(identifierLiteral));\n        }\n\n        public static string GetSingleQuoteString(ReadOnlySpan<char> stringToken)\n        {\n            if (!TryParseQuotedToken(stringToken, '\\'', out var result))\n                throw new ArgumentException($\"The value \\\"{stringToken.ToString()}\\\" is not a valid string token.\");\n\n            return result;\n        }\n\n        private static bool TryParseQuotedToken(ReadOnlySpan<char> token, char quoteSign, [MaybeNullWhen(false)] out string value)\n        {\n            if (token.Length < 2 || token[0] != quoteSign || token[^1] != quoteSign)\n            {\n                value = null;\n                return false;\n            }\n            \n            var sb = new StringBuilder(token.Length);\n            for (int i = 1; i < token.Length - 1; i++)\n            {\n                if (token[i] == '\\\\')\n                {\n                    if (i + 1 == token.Length - 1)\n                    {\n                        value = null;\n                        return false;\n                    }\n\n                    if (token[i + 1] == quoteSign)\n                    {\n                        sb.Append(quoteSign);\n                    }\n                    else if (TryGetUnescapedCharacter(token[i + 1], out var unescapedCh))\n                    {\n                        sb.Append(unescapedCh);\n                    }\n                    else\n                    {\n                        sb.Append(token[i]).Append(token[i + 1]);\n                    }\n\n                    i++;\n                }\n                else\n                {\n                    sb.Append(token[i]);\n                }\n            }\n\n            value = sb.ToString();\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ClientHelloMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal class ClientHelloMessage : IClientMessage\n    {\n        public ClientMessageCode MessageCode => ClientMessageCode.Hello;\n\n        public string ClientName { get;  }\n\n        public ClickHouseVersion ClientVersion { get; }\n\n        public int ProtocolRevision { get;  }\n\n        public string? Database { get;  }\n\n        public string User { get;  }\n\n        public string? Password { get; }\n\n        public string? QuotaKey { get; }\n\n        private ClientHelloMessage(Builder builder)\n        {\n            ClientName = builder.ClientName ?? throw new ArgumentException(\"The name of the client can't be null.\", nameof(ClientName));\n            ClientVersion = builder.ClientVersion ?? throw new ArgumentException(\"The version of the client can't be null.\", nameof(ClientVersion));\n            ProtocolRevision = builder.ProtocolRevision ?? throw new ArgumentException(\"The revision of the protocol is required.\", nameof(ProtocolRevision));\n            Database = builder.Database;\n            User = builder.User ?? throw new ArgumentException(\"The name of the user is required.\", nameof(User));\n            Password = builder.Password;\n            QuotaKey = builder.QuotaKey;\n        }\n\n        public void Write(ClickHouseBinaryProtocolWriter writer)\n        {\n            writer.Write7BitInt32((int) MessageCode);\n\n            writer.WriteString(ClientName);\n            writer.Write7BitInt32(ClientVersion.Major);\n            writer.Write7BitInt32(ClientVersion.Minor);\n            writer.Write7BitInt32(ProtocolRevision);\n\n            writer.WriteString(Database ?? string.Empty);\n            writer.WriteString(User);\n            writer.WriteString(Password ?? string.Empty);\n        }\n\n        public void WriteAddendum(ClickHouseBinaryProtocolWriter writer)\r\n        {\r\n            writer.WriteString(QuotaKey ?? string.Empty);\r\n        }\n\n        internal class Builder\n        {\n            /// <summary>\n            /// Required\n            /// </summary>\n            public string? ClientName { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public ClickHouseVersion? ClientVersion { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public int? ProtocolRevision { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public string? Database { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public string? User { get; set; }\n\n            /// <summary>\n            /// Optional\n            /// </summary>\n            public string? Password { get; set; }\n\n            /// <summary>\r\n            /// Optional (addendum)\r\n            /// </summary>\n            public string? QuotaKey { get; set; }\n\n            public ClientHelloMessage Build()\n            {\n                return new ClientHelloMessage(this);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ClientMessageCode.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal enum ClientMessageCode\n    {\n        Hello = 0,\n        Query = 1,\n        Data = 2,\n        Cancel = 3,\n        Ping = 4,\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ClientQueryMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023, 2025 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class ClientQueryMessage : IClientMessage\n    {\n        public ClientMessageCode MessageCode => ClientMessageCode.Query;\n        \n        public QueryKind QueryKind { get; }\n\n        public string? QueryId { get; }\n\n        public string RemoteAddress { get; }\n\n        public string Host { get; }\n\n        public string ClientName { get; }\n\n        public ClickHouseVersion ClientVersion { get; }\n\n        public int ProtocolRevision { get; }\n\n        public string Query { get; }\n\n        public bool CompressionEnabled { get; }\n\n        // https://github.com/ClickHouse/ClickHouse/blob/master/dbms/src/Core/Settings.h\n        public IReadOnlyCollection<KeyValuePair<string, string>>? Settings { get; }\n\n        public IReadOnlyCollection<KeyValuePair<string, ClickHouseParameterWriter>>? Parameters { get; }\n\n        private ClientQueryMessage(Builder builder)\n        {\n            QueryKind = builder.QueryKind ?? throw new ArgumentException(\"The kind of the query is required.\", nameof(QueryKind));\n            QueryId = string.IsNullOrEmpty(builder.QueryId) ? null : builder.QueryId;\n            RemoteAddress = builder.RemoteAddress ?? throw new ArgumentException(\"The remote address is required.\", nameof(RemoteAddress));\n            Host = builder.Host ?? throw new ArgumentException(\"The name of the host is required.\", nameof(Host));\n            ClientName = builder.ClientName ?? throw new ArgumentException(\"The name of the client is required.\", nameof(ClientName));\n            ClientVersion = builder.ClientVersion ?? throw new ArgumentException(\"The version of the client is required.\", nameof(ClientVersion));\n            ProtocolRevision = builder.ProtocolRevision ?? throw new ArgumentException(\"The revision of the protocol is required.\", nameof(ProtocolRevision));\n            Query = builder.Query ?? throw new ArgumentException(\"The query is required.\", nameof(Query));\n            CompressionEnabled = builder.CompressionEnabled ?? throw new ArgumentException(\"Unknown compression mode.\", nameof(CompressionEnabled));\n            Settings = builder.Settings == null || builder.Settings.Count == 0 ? null : builder.Settings;\n            Parameters = builder.Parameters == null || builder.Parameters.Count == 0 ? null : builder.Parameters;\n        }\n\n        public void Write(ClickHouseBinaryProtocolWriter writer)\n        {\n            writer.Write7BitInt32((int) MessageCode);\n            writer.WriteString(QueryId ?? string.Empty);\n            switch (QueryKind)\n            {\n                case QueryKind.NoQuery:\n                    break;\n\n                case QueryKind.InitialQuery:\n                    writer.Write7BitInt32((int) QueryKind);\n\n                    writer.WriteString(string.Empty); //initial user\n                    writer.WriteString(string.Empty); //initial query id\n                    writer.WriteString(RemoteAddress); //initial IP address\n\n                    if (ProtocolRevision >= ClickHouseProtocolRevisions.MinRevisionWithInitialQueryStartTime)\n                    {\n                        // Initial query start time in microseconds. An actual value of this property should be set by the server.\n                        Span<byte> zero = stackalloc byte[sizeof(ulong)];\n                        writer.WriteBytes(zero);\n                    }\n\n                    writer.Write7BitInt32(1); //TCP\n\n                    writer.WriteString(string.Empty); //OS user\n\n                    writer.WriteString(Host);\n                    writer.WriteString(ClientName);\n\n                    writer.Write7BitInt32(ClientVersion.Major);\n                    writer.Write7BitInt32(ClientVersion.Minor);\n                    writer.Write7BitInt32(ProtocolRevision);\n\n                    writer.WriteString(string.Empty); //quota key\n\n                    if (ProtocolRevision >= ClickHouseProtocolRevisions.MinRevisionWithDistributedDepth)\n                        writer.Write7BitInt32(0); //distributed depth\n\n                    writer.Write7BitInt32(ClientVersion.Build);\n\n                    if (ProtocolRevision >= ClickHouseProtocolRevisions.MinRevisionWithOpenTelemetry)\n                        writer.WriteByte(0); // TODO: add support for Open Telemetry headers\n\n                    if (ProtocolRevision >= ClickHouseProtocolRevisions.MinRevisionWithParallelReplicas)\n                    {\n                        writer.WriteByte(0); // collaborate_with_initiator\n                        writer.WriteByte(0); // count_participating_replicas\n                        writer.WriteByte(0); // number_of_current_replica\n                    }\n\n                    break;\n\n                case QueryKind.SecondaryQuery:\n                    throw new NotImplementedException();\n\n                default:\n                    throw new NotSupportedException();\n            }\n\n            if (Settings != null)\n            {\n                // All settings are serialized as strings. Before each value the flag `is_important` is serialized.\n                // https://github.com/ClickHouse/ClickHouse/blob/97d97f6b2e50ab3cf21a25a18cbf1aa327f242e5/src/Core/BaseSettings.h#L19\n\n                const int isImportantFlag = 0x1;\n                foreach (var pair in Settings)\n                {\n                    writer.WriteString(pair.Key);\n                    writer.Write7BitInt32(isImportantFlag);\n                    writer.WriteString(pair.Value);\n                }\n            }\n\n            writer.WriteString(string.Empty); // empty string is a marker of the end of the settings\n\n            if (ProtocolRevision >= ClickHouseProtocolRevisions.MinRevisionWithInterserverSecret)\n                writer.WriteString(string.Empty);\n\n            writer.Write7BitInt32(StateCodes.Complete);\n\n            writer.WriteBool(CompressionEnabled);\n\n            writer.WriteString(Query);\n\n            if (ProtocolRevision >= ClickHouseProtocolRevisions.MinRevisionWithParameters)\n            {\n                if (Parameters != null)\n                {\n                    const int isCustomFlag = 0x2;\n                    foreach (var pair in Parameters)\n                    {\n                        if (pair.Value.Length < 0)\n                            continue;\n\n                        writer.WriteString(pair.Key);\n                        writer.Write7BitInt32(isCustomFlag);\n\n                        var lenght = pair.Value.Length;\n                        writer.Write7BitInt32(lenght + 2);\n                        writer.WriteByte((byte)'\\'');\n\n                        if (lenght > 0)\n                        {\n                            var size = writer.WriteRaw(lenght, buffer => new SequenceSize(pair.Value.Write(buffer), 1));\n\n                            // The lenght must be calculated by the parameter writer correctly\n                            if (size.Bytes != lenght)\n                                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. The length of the parameter \\\"{pair.Key}\\\" in bytes is {lenght}, but the number of written bytes is {size.Bytes}.\");\n                        }\n\n                        writer.WriteByte((byte)'\\'');\n                    }\n                }\n\n                writer.WriteString(string.Empty); // empty string is a marker of the end of parameters\n            }\n            else if (Parameters != null)\n            {\n                var errMsg =\n                    \"The server doesn't support parameters in the query. \" +\n                    $\"This error is caused by one or more parameters passed in the mode \\\"{nameof(ClickHouseParameterMode.Serialize)}\\\". \" +\n                    $\"Only \\\"{nameof(ClickHouseParameterMode.Binary)}\\\" or \\\"{nameof(ClickHouseParameterMode.Interpolate)}\\\" modes are supported.\";\n\n                throw new ClickHouseException(ClickHouseErrorCodes.ProtocolRevisionNotSupported, errMsg);\n            }\n        }\n\n        public class Builder\n        {\n            /// <summary>\n            /// Required\n            /// </summary>\n            public QueryKind? QueryKind { get; set; }\n\n            /// <summary>\n            /// Optional\n            /// </summary>\n            public string? QueryId { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public string? RemoteAddress { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public string? Host { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public string? ClientName { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public ClickHouseVersion? ClientVersion { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public int? ProtocolRevision { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public string? Query { get; set; }\n\n            /// <summary>\n            /// Required\n            /// </summary>\n            public bool? CompressionEnabled { get; set; }\n\n            /// <summary>\n            /// Optional\n            /// </summary>\n            public IReadOnlyCollection<KeyValuePair<string, string>>? Settings { get; set; }\n\n            /// <summary>\n            /// Optional\n            /// </summary>\n            public IReadOnlyCollection<KeyValuePair<string, ClickHouseParameterWriter>>? Parameters { get; set; }\n\n            public ClientQueryMessage Build()\n            {\n                return new ClientQueryMessage(this);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ColumnInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Types;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal class ColumnInfo\n    {\n        public string Name { get; }\n\n        public IClickHouseColumnTypeInfo TypeInfo { get; }\n\n        public ColumnInfo(string name, IClickHouseColumnTypeInfo typeInfo)\n        {\n            Name = name;\n            TypeInfo = typeInfo;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/CompressionAlgorithm.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal enum CompressionAlgorithm\n    {\n        None = 0,\n        Lz4 = 1\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/CompressionDecoderBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Diagnostics;\nusing Octonica.ClickHouseClient.Exceptions;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal abstract class CompressionDecoderBase : IDisposable\n    {\n        private readonly int _bufferSize;\n\n        private byte[]? _compressedBuffer;\n        private byte[]? _decompressedBuffer;\n\n        private int _compressedPosition;\n        private int _compressedSize;\n\n        private int _decompressedAvailable;\n        private int _decompressedSize;\n\n        protected abstract byte AlgorithmIdentifier { get; }\n\n        public abstract CompressionAlgorithm Algorithm { get; }\n\n        public bool IsCompleted => _compressedPosition == _compressedSize;\n\n        protected CompressionDecoderBase(int bufferSize)\n        {\n            _bufferSize = bufferSize;\n        }\n\n        public int ReadHeader(ReadOnlySequence<byte> sequence)\n        {\n            if (!IsCompleted)\n                throw new ClickHouseException(ClickHouseErrorCodes.CompressionDecoderError, \"Can't start reading of a new block because reading of the current compressed block is not finished.\");\n\n            const int cityHashSize = 16, headerSize = sizeof(byte) + 2 * sizeof(int);\n            if (sequence.Length < cityHashSize + headerSize)\n                return -1;\n\n            byte algorithmIdentifier = sequence.Slice(cityHashSize).FirstSpan[0];\n            if (algorithmIdentifier != AlgorithmIdentifier)\n            {\n                throw new ClickHouseException(\n                    ClickHouseErrorCodes.CompressionDecoderError,\n                    $\"An unexpected compression algorithm identifier was received. Expected value: 0x{AlgorithmIdentifier:X}. Actual value: 0x{algorithmIdentifier:X}.\");\n            }\n\n            var slice = sequence.Slice(cityHashSize + sizeof(byte));\n            Span<byte> intBuffer = stackalloc byte[sizeof(int)];\n\n            slice.Slice(0, sizeof(int)).CopyTo(intBuffer);\n            _compressedSize = BitConverter.ToInt32(intBuffer);\n            _compressedSize -= headerSize;\n\n            slice.Slice(sizeof(int), sizeof(int)).CopyTo(intBuffer);\n            var decompressedSize = BitConverter.ToInt32(intBuffer);\n\n            _compressedPosition = 0;\n\n            if (_compressedBuffer == null || _compressedBuffer.Length < _compressedSize)\n                _compressedBuffer = new byte[GetBufferSize(_compressedSize)];\n\n            if (_decompressedBuffer == null)\n            {\n                Debug.Assert(_decompressedAvailable == 0);\n                _decompressedBuffer = new byte[GetBufferSize(decompressedSize)];\n            }\n            else if (_decompressedBuffer.Length < decompressedSize + _decompressedAvailable)\n            {\n                var newBuffer = new byte[GetBufferSize(decompressedSize + _decompressedAvailable)];\n                if (_decompressedAvailable > 0)\n                    Array.Copy(_decompressedBuffer, _decompressedSize - _decompressedAvailable, newBuffer, 0, _decompressedAvailable);\n\n                _decompressedBuffer = newBuffer;\n            }\n            else\n            {\n                for (int i = 0, j = _decompressedSize - _decompressedAvailable; j < _decompressedSize; i++, j++)\n                    _decompressedBuffer[i] = _decompressedBuffer[j];\n            }\n\n            _decompressedSize = decompressedSize + _decompressedAvailable;\n            return cityHashSize + headerSize;\n        }\n\n        public ReadOnlySequence<byte> Read()\n        {\n            if (!IsCompleted || _decompressedBuffer == null || _compressedBuffer == null)\n                return ReadOnlySequence<byte>.Empty;\n\n            return new ReadOnlySequence<byte>(_decompressedBuffer, _decompressedSize - _decompressedAvailable, _decompressedAvailable);\n        }\n\n        public void AdvanceReader(SequencePosition position)\n        {\n            if (!ReferenceEquals(position.GetObject(), _decompressedBuffer))\n                throw new ArgumentException(\"The position doesn't belong to the sequence.\", nameof(position));\n\n            var arrayIndex = position.GetInteger();\n            if (arrayIndex < 0)\n                arrayIndex = unchecked(arrayIndex - int.MinValue);\n\n            var relativePosition = arrayIndex - (_decompressedSize - _decompressedAvailable);\n            if (relativePosition < 0)\n                throw new ArgumentOutOfRangeException(nameof(position), \"The position must be a non-negative number.\");\n            if (relativePosition == 0)\n                return;\n\n            if (relativePosition > _decompressedAvailable)\n                throw new ArgumentOutOfRangeException(nameof(position), \"The position must not be greater then the length.\");\n\n            _decompressedAvailable -= relativePosition;\n        }\n\n        public int ConsumeNext(ReadOnlySequence<byte> sequence)\n        {\n            if (_compressedPosition == _compressedSize || _compressedBuffer == null)\n                return 0;\n\n            var sequencePart = sequence;\n            if (sequence.Length > _compressedSize - _compressedPosition)\n                sequencePart = sequence.Slice(0, _compressedSize - _compressedPosition);\n\n            sequencePart.CopyTo(((Span<byte>) _compressedBuffer).Slice(_compressedPosition));\n            _compressedPosition += (int) sequencePart.Length;\n\n            if (_compressedPosition == _compressedSize)\n            {\n                var sourceSpan = new Span<byte>(_compressedBuffer, 0, _compressedSize);\n                var targetSpan = new Span<byte>(_decompressedBuffer, _decompressedAvailable, _decompressedSize - _decompressedAvailable);\n                _decompressedAvailable += Decode(sourceSpan, targetSpan);\n\n                Debug.Assert(_decompressedAvailable == _decompressedSize);\n            }\n\n            return (int) sequencePart.Length;\n        }\n\n        public void Reset()\n        {\n            _compressedPosition = 0;\n            _compressedSize = 0;\n            _decompressedAvailable = 0;\n            _decompressedSize = 0;\n        }\n\n        protected abstract int Decode(ReadOnlySpan<byte> source, Span<byte> target);\n\n        public abstract void Dispose();\n\n        private int GetBufferSize(int minRequiredSize)\n        {\n            int size = _bufferSize;\n            while (size < minRequiredSize)\n                size *= 2;\n\n            return size;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/CompressionEncoderBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Utils;\n\n#if NET8_0_OR_GREATER\nusing System.Runtime.InteropServices;\n#endif\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal abstract class CompressionEncoderBase: IDisposable\n    {\n        private readonly int _bufferSize;\n        private readonly List<(byte[] buffer, int position)> _buffers = new List<(byte[] buffer, int position)>();\n        private readonly List<(int bufferIndex, int offset, int length)> _sequences = new List<(int bufferIndex, int offset, int length)>();\n\n        private int _acquiredBufferIndex = -1;\n\n        protected abstract byte AlgorithmIdentifier { get; }\n\n        public abstract CompressionAlgorithm Algorithm { get; }\n\n        protected CompressionEncoderBase(int bufferSize)\n        {\n            _bufferSize = bufferSize;\n        }\n\n        public Span<byte> GetSpan(int sizeHint)\n        {\n            var (buffer, position) = AcquireBuffer(sizeHint);\n            return new Span<byte>(buffer, position, buffer.Length - position);\n        }\n\n        public Memory<byte> GetMemory(int sizeHint)\n        {\n            var (buffer, position) = AcquireBuffer(sizeHint);\n            return new Memory<byte>(buffer, position, buffer.Length - position);\n        }\n\n        public Memory<byte> GetMemory()\n        {\n            return GetMemory(1);\n        }\n\n        private (byte[] buffer, int position) AcquireBuffer(int sizeHint)\n        {\n            if (_acquiredBufferIndex >= 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Writing is already in progress.\");\n\n            for (var i = _buffers.Count - 1; i >= 0; i--)\n            {\n                (var buffer, int position) = _buffers[i];\n                if (buffer.Length - position >= sizeHint)\n                {\n                    _acquiredBufferIndex = i;\n                    return (buffer, position);\n                }\n            }\n\n            _acquiredBufferIndex = _buffers.Count;\n            var nextBuffer = new byte[Math.Max(sizeHint, _bufferSize)];\n            _buffers.Add((nextBuffer, 0));\n            return (nextBuffer, 0);\n        }\n\n        public void Advance(int bytes)\n        {\n            if (bytes < 0)\n                throw new ArgumentOutOfRangeException(nameof(bytes));\n\n            if (_acquiredBufferIndex < 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Writing is already completer.\");\n\n            var (buffer, position) = _buffers[_acquiredBufferIndex];\n\n            if (buffer.Length - position < bytes)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Attempt to write after the end of the memory buffer.\");\n\n            if (bytes > 0)\n            {\n                _buffers[_acquiredBufferIndex] = (buffer, position + bytes);\n                (int bufferIndex, int offset, int length) lastSequence;\n                if (_sequences.Count == 0 || (lastSequence = _sequences[^1]).bufferIndex != _acquiredBufferIndex)\n                    _sequences.Add((_acquiredBufferIndex, position, bytes));\n                else\n                    _sequences[^1] = (_acquiredBufferIndex, lastSequence.offset, lastSequence.length + bytes);\n            }\n\n            _acquiredBufferIndex = -1;\n        }\n\n        public void Reset()\n        {\n            if (_acquiredBufferIndex >= 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Writing is in progress.\");\n\n            _sequences.Clear();\n\n            for (int i = 0; i < _buffers.Count; i++)\n                _buffers[i] = (_buffers[i].buffer, 0);\n        }\n\n        public void Complete(ReadWriteBuffer pipeWriter)\n        {\n            if (_acquiredBufferIndex >= 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Writing is in progress.\");\n\n            /* \n             * Compressed data consist of a sequence of compressed blocks.\n             *\n             * The structure of the block:\n             * 1. CityHash checksum (16 bytes);\n             * 2. Algorithm's identifier (1 byte);\n             * 3. The size of the block without checksum (4 bytes);\n             * 4. The size of data in the block without compression (4 bytes);\n             * 5. The block of compressed data.\n            */\n\n            var resultSequences = new List<(int bufferIndex, int offset, int length)>(_sequences.Count + 1);\n\n            const int cityHashSize = 2 * sizeof(ulong);\n            var header = new byte[cityHashSize + 1 + 2 * sizeof(int)];\n\n            var freeSequences = new Queue<(int bufferIndex, int offset, int length)>();\n            if (_buffers.Count > 0)\n            {\n                var lastBuffer = _buffers[^1];\n                if (lastBuffer.buffer.Length > lastBuffer.position)\n                {\n                    freeSequences.Enqueue((_buffers.Count - 1, lastBuffer.position, lastBuffer.buffer.Length - lastBuffer.position));\n                    _buffers[^1] = (lastBuffer.buffer, lastBuffer.buffer.Length);\n                }\n            }\n\n            int readPosition = 0, sequenceIndex = 0;\n            bool completed;\n            do\n            {\n                if (resultSequences.Count > 0)\n                {\n                    foreach (var sequence in resultSequences)\n                        freeSequences.Enqueue(sequence);\n\n                    resultSequences.Clear();\n                }\n\n                completed = true;\n                int writePosition = 0, rawSize = 0, encodedSize = 0;\n                while (sequenceIndex < _sequences.Count)\n                {\n                    var readSequence = _sequences[sequenceIndex];\n                    if (readPosition == readSequence.length)\n                    {\n                        sequenceIndex++;\n                        readPosition = 0;\n                        freeSequences.Enqueue(readSequence);\n                        continue;\n                    }\n\n                    var count = ConsumeNext(_buffers[readSequence.bufferIndex].buffer, readSequence.offset + readPosition, readSequence.length - readPosition);\n                    readPosition += count;\n                    rawSize += count;\n\n                    if (readPosition < readSequence.length)\n                    {\n                        completed = false;\n                        break;\n                    }\n                }\n\n                if (rawSize == 0)\n                    break;\n\n                (int bufferIndex, int offset, int length) currentSequence = (-1, 0, 0);\n                while (true)\n                {\n                    if (writePosition == currentSequence.length)\n                    {\n                        if (currentSequence.length > 0)\n                            resultSequences.Add(currentSequence);\n\n                        if (freeSequences.Count > 0)\n                        {\n                            currentSequence = freeSequences.Dequeue();\n                        }\n                        else\n                        {\n                            currentSequence = (_buffers.Count, 0, _bufferSize);\n                            _buffers.Add((new byte[_bufferSize], _bufferSize));\n                        }\n\n                        writePosition = 0;\n                    }\n\n                    int result = completed\n                        ? EncodeFinal(_buffers[currentSequence.bufferIndex].buffer, currentSequence.offset + writePosition, currentSequence.length - writePosition)\n                        : EncodeNext(_buffers[currentSequence.bufferIndex].buffer, currentSequence.offset + writePosition, currentSequence.length - writePosition);\n\n                    encodedSize += result;\n                    writePosition += result;\n\n                    if (writePosition < currentSequence.length)\n                        break;\n                }\n\n                if (writePosition > 0)\n                    resultSequences.Add((currentSequence.bufferIndex, currentSequence.offset, writePosition));\n\n                Span<byte> headerSpan = header;\n                headerSpan[cityHashSize] = AlgorithmIdentifier;\n                var success = BitConverter.TryWriteBytes(headerSpan.Slice(cityHashSize + 1), encodedSize + header.Length - cityHashSize);\n                Debug.Assert(success);\n                success = BitConverter.TryWriteBytes(headerSpan.Slice(cityHashSize + 1 + sizeof(int)), rawSize);\n                Debug.Assert(success);\n\n                var segments = new List<ReadOnlyMemory<byte>>(resultSequences.Count + 1) {new ReadOnlyMemory<byte>(header)};\n                segments.AddRange(resultSequences.Select(s => new ReadOnlyMemory<byte>(_buffers[s.bufferIndex].buffer, s.offset, s.length)));\n                var dataSegment = new SimpleReadOnlySequenceSegment<byte>(segments);\n\n                var dataSequence = new ReadOnlySequence<byte>(dataSegment, 0, dataSegment.LastSegment, dataSegment.LastSegment.Memory.Length);\n                var cityHash = CityHash.CityHash128(dataSequence.Slice(cityHashSize));\n\n#if NET8_0_OR_GREATER\n                var cityHashBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref cityHash, 1));\n                Debug.Assert(cityHashBytes.Length == 16);\n                cityHashBytes.CopyTo(headerSpan);\n#else\n                success = BitConverter.TryWriteBytes(headerSpan, cityHash.Low);\n                Debug.Assert(success);\n                success = BitConverter.TryWriteBytes(headerSpan.Slice(sizeof(ulong)), cityHash.High);\n                Debug.Assert(success);\n#endif\n\n                for (ReadOnlySequenceSegment<byte>? segment = dataSegment; segment != null; segment = segment.Next)\n                {\n                    var sourceMem = segment.Memory;\n                    while (true)\n                    {\n                        var targetMem = pipeWriter.GetMemory();\n                        if (sourceMem.Length > targetMem.Length)\n                        {\n                            sourceMem.Slice(0, targetMem.Length).CopyTo(targetMem);\n                            sourceMem = sourceMem.Slice(targetMem.Length);\n                            pipeWriter.ConfirmWrite(targetMem.Length);\n                        }\n                        else\n                        {\n                            sourceMem.CopyTo(targetMem);\n                            pipeWriter.ConfirmWrite(sourceMem.Length);\n                            break;\n                        }\n                    }\n                }\n\n                if (writePosition > 0)\n                {\n                    // This entire sequence should be marked as free\n                    resultSequences[^1] = currentSequence;\n                }\n\n            } while (!completed);\n        }\n\n        protected abstract int ConsumeNext(byte[] source, int offset, int length);\n\n        protected abstract int EncodeNext(byte[] target, int offset, int length);\n\n        protected abstract int EncodeFinal(byte[] target, int offset, int length);\n\n        public abstract void Dispose();\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/IClickHouseColumnReader.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    /// <summary>\n    /// The interfaces for object capable of creating an internal column for <see cref=\"ClickHouseDataReader\"/>.\n    /// </summary>\n    public interface IClickHouseColumnReader : IClickHouseColumnReaderBase\n    {\n        /// <summary>\n        /// Creates a column for <see cref=\"ClickHouseDataReader\"/> with the specified settings.\n        /// </summary>\n        /// <param name=\"settings\">The settings of the column.</param>\n        /// <returns>A column for <see cref=\"ClickHouseDataReader\"/>.</returns>\n        IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/IClickHouseColumnReaderBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Buffers;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    /// <summary>\n    /// The basic interface for objects capable of reading columnar data from a binary buffer.\n    /// </summary>\n    public interface IClickHouseColumnReaderBase\n    {\n        /// <summary>\n        /// When implemented reads as much bytes as possible from the binary buffer.\n        /// </summary>\n        /// <param name=\"sequence\">The binary buffer.</param>\n        /// <returns>The length of data that were read or <see cref=\"SequenceSize.Empty\"/> if the provided buffer is too small.</returns>\n        SequenceSize ReadNext(ReadOnlySequence<byte> sequence);\n\n        /// <summary>\n        /// When implemented reads the prefix specific to the column's type\n        /// </summary>\n        /// <param name=\"sequence\">The binary buffer.</param>\n        /// <returns>\n        /// The length of data that were read or <see cref=\"SequenceSize.Empty\"/> if the provided buffer is too small.\n        /// The prefix is counted for a single element, so the number of elements (<see cref=\"SequenceSize.Elements\"/>)\n        /// can be either 0 or 1.\n        /// </returns>\n        SequenceSize ReadPrefix(ReadOnlySequence<byte> sequence) => new SequenceSize(0, 1);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/IClickHouseColumnWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    /// <summary>\n    /// The base interface for an object capable of sequential writing column's data to a binary buffer.\n    /// </summary>\n    public interface IClickHouseColumnWriter\n    {\n        /// <summary>\n        /// The name of the column to write data to.\n        /// </summary>\n        string ColumnName { get; }\n\n        /// <summary>\n        /// The full name of the ClickHouse type of the column.\n        /// </summary>\n        string ColumnType { get; }\n\n        /// <summary>\n        /// Writes next block of data to the target span.\n        /// </summary>\n        /// <param name=\"writeTo\">The buffer to write data to.</param>\n        /// <returns>The length of written data or <see cref=\"SequenceSize.Empty\"/> if the provided buffer is too small.</returns>\n        SequenceSize WriteNext(Span<byte> writeTo);\n\n        /// <summary>\n        /// Writes prefix specific to the column's type.\n        /// </summary>\n        /// <param name=\"writeTo\">The buffer to write data to.</param>\n        /// <returns>\n        /// The length of written data or <see cref=\"SequenceSize.Empty\"/> if the provided buffer is too small.\n        /// The prefix is counted for a single element, so the number of elements (<see cref=\"SequenceSize.Elements\"/>)\n        /// can be either 0 or 1.\n        /// </returns>\n        SequenceSize WritePrefix(Span<byte> writeTo) => new SequenceSize(0, 1);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/IClickHouseColumnWriterFactory.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal interface IClickHouseColumnWriterFactory\n    {\n        IClickHouseColumnWriter Create(int offset, int length);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/IClickHouseParameterValueWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    /// <summary>\n    /// The base interface for a parameter's value writer. When implemented writes a parameter value in a binary format.\n    /// </summary>\n    /// <remarks>\n    /// Being a part of the ClickHouseClient's infrastructure, the interface <see cref=\"IClickHouseParameterValueWriter\"/> is considered unstable. It can be changed between minor versions.\n    /// </remarks>\n    public interface IClickHouseParameterValueWriter\n    {\n        /// <summary>\n        /// The length of the parameter's value in bytes.\n        /// If the length is less than zero the parameter will not be passed to the query.\n        /// </summary>\n        int Length { get; }\n\n        /// <summary>\n        /// When implemented writes the value to the <paramref name=\"buffer\"/>.\n        /// </summary>\n        /// <param name=\"buffer\">The buffer for writing the value. A caller of this method must ensure that the size of the buffer is not less than <see cref=\"Length\"/>.</param>\n        /// <returns>The number of written bytes.</returns>\n        int Write(Memory<byte> buffer);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/IClickHouseParameterWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Types;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    /// <summary>\n    /// The base interface for a value writer. When implemented writes a value as a ClickHouse parameter.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of a value.</typeparam>\n    /// <remarks>\n    /// Being a part of the ClickHouseClient's infrastructure, the interface <see cref=\"IClickHouseParameterWriter{T}\"/> is considered unstable. It can be changed between minor versions.\n    /// </remarks>\n    public interface IClickHouseParameterWriter<in T>\n    {\n        /// <summary>\n        /// Creates an instance of <see cref=\"IClickHouseParameterValueWriter\"/> which encapsulates the value.\n        /// </summary>\n        /// <param name=\"value\">The value of the parameter.</param>\n        /// <param name=\"isNested\">The flag which indicates whether the value is a part of parameter's full value, e.g. an element of an array.</param>\n        /// <param name=\"valueWriter\">>When this method returns, contains the instance of <see cref=\"IClickHouseParameterValueWriter\"/> encapsulating the value.</param>\n        /// <returns>\n        /// <see langword=\"false\"/> if the value can't be represented in a binary format. Otherwise returns <see langword=\"true\"/> and creates a <paramref name=\"valueWriter\"/>.\n        /// </returns>\n        /// <remarks><see langword=\"false\"/> returned by this method instructs a parameter's writer to interpolate the value directly to the query text.</remarks>\n        bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter);\n\n        /// <summary>\n        /// Appends the value to the query as a ClickHouse literal.\n        /// </summary>\n        /// <param name=\"queryBuilder\">The builder of the SQL query</param>\n        /// <param name=\"value\">The value for writing as a ClickHouse literal.</param>\n        /// <returns>The instance of the builder passed to the method (<paramref name=\"queryBuilder\"/>).</returns>\n        StringBuilder Interpolate(StringBuilder queryBuilder, T value);\n\n        /// <summary>\n        /// Appends the value to the query. If required, the method appends type cast.\n        /// For writing a value it invokes the callback <paramref name=\"writeValue\"/>.\n        /// </summary>\n        /// <param name=\"queryBuilder\">The builder of the SQL query</param>\n        /// <param name=\"typeInfoProvider\">The provider of type information.</param>\n        /// <param name=\"writeValue\">\n        /// The function that appends an actual value to the query. It gets three arguments: the query string builder; the type of the\n        /// parameter; the callback function for writing an external parts of the parameter. The type of the literal may differ from the type\n        /// of the value. The function must return the same instance of the builder which it gets as an argument.\n        /// </param>\n        /// <returns>The instance of the builder passed to the method (<paramref name=\"queryBuilder\"/>).</returns>\n        StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/IClickHouseTableWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal interface IClickHouseTableWriter\n    {\n        string TableName { get; }\n\n        int RowCount { get; }\n\n        IReadOnlyList<IClickHouseColumnWriter> Columns { get; }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/IClientMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal interface IClientMessage\n    {\n        ClientMessageCode MessageCode { get; }\n\n        void Write(ClickHouseBinaryProtocolWriter writer);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/IServerMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal interface IServerMessage\n    {\n        ServerMessageCode MessageCode { get; }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/Lz4CompressionDecoder.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing K4os.Compression.LZ4;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class Lz4CompressionDecoder : CompressionDecoderBase\n    {\n        protected override byte AlgorithmIdentifier => 0x82;\n\n        public override CompressionAlgorithm Algorithm => CompressionAlgorithm.Lz4;\n        \n        public Lz4CompressionDecoder(int bufferSize)\n            : base(bufferSize)\n        {\n        }\n\n        protected override int Decode(ReadOnlySpan<byte> source, Span<byte> target)\n        {\n            return LZ4Codec.Decode(source, target);\n        }\n\n        public override void Dispose()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/Lz4CompressionEncoder.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing K4os.Compression.LZ4;\nusing K4os.Compression.LZ4.Encoders;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class Lz4CompressionEncoder : CompressionEncoderBase\n    {\n        private readonly LZ4BlockEncoder _encoder;\n        private readonly byte[] _compressedBuffer;\n\n        private int _compressedSize;\n        private int _compressedAvailable = -1;\n\n        protected override byte AlgorithmIdentifier => 0x82;\n\n        public override CompressionAlgorithm Algorithm => CompressionAlgorithm.Lz4;\n\n        public Lz4CompressionEncoder(int bufferSize, int blockSize)\n            : base(bufferSize)\n        {\n            _encoder = new LZ4BlockEncoder(LZ4Level.L10_OPT, blockSize);\n            _compressedBuffer = new byte[LZ4Codec.MaximumOutputSize(blockSize)];\n        }\n\n        protected override int ConsumeNext(byte[] source, int offset, int length)\n        {\n            _compressedAvailable = -1;\n            return _encoder.Topup(source, offset, length);\n        }\n\n        protected override int EncodeNext(byte[] target, int offset, int length)\n        {\n            if (_compressedAvailable == 0)\n                return 0;\n\n            if (_compressedAvailable <0)\n            {\n                _compressedSize = _encoder.Encode(_compressedBuffer, 0, _compressedBuffer.Length, false);\n                _compressedAvailable = _compressedSize;\n            }\n\n            var maxLen = Math.Min(length, _compressedAvailable);\n            Array.Copy(_compressedBuffer, _compressedSize - _compressedAvailable, target, offset, maxLen);\n            _compressedAvailable -= maxLen;\n\n            return maxLen;\n        }\n\n        protected override int EncodeFinal(byte[] target, int offset, int length)\n        {\n            if (_compressedAvailable == 0)\n                return 0;\n\n            if (_compressedAvailable < 0)\n            {\n                _encoder.FlushAndEncode(_compressedBuffer, 0, _compressedBuffer.Length, false, out _compressedSize);\n                _compressedAvailable = _compressedSize;\n            }\n\n            var maxLen = Math.Min(length, _compressedAvailable);\n            Array.Copy(_compressedBuffer, _compressedSize - _compressedAvailable, target, offset, maxLen);\n            _compressedAvailable -= maxLen;\n\n            return maxLen;\n        }\n\n        public override void Dispose()\n        {\n            _encoder.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/QueryKind.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal enum QueryKind\n    {\n        NoQuery = 0,\n        InitialQuery = 1,\n        SecondaryQuery = 2\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/SequenceSize.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    /// <summary>\n    /// Represents the size of a sequence of elements both in the number of bytes and in the number of elements.\n    /// </summary>\n    public readonly struct SequenceSize\n    {\n        /// <summary>\n        /// The size of an empty sequence that has zero elements and zero bytes.\n        /// </summary>\n        public static readonly SequenceSize Empty = new SequenceSize(0, 0);\n\n        /// <summary>\n        /// The number of bytes in the sequence.\n        /// </summary>        \n        public int Bytes { get; }\n\n        /// <summary>\n        /// The number of elements in the sequence.\n        /// </summary>\n        /// <remarks>The sequence can contain zero elements with non-zero bytes when it has headers or other metadata.</remarks>\n        public int Elements { get; }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"SequenceSize\"/> with the specified number of bytes and elements.\n        /// </summary>\n        /// <param name=\"bytes\">The number of bytes in the sequence.</param>\n        /// <param name=\"elements\">The number of elements in the sequence.</param>\n        public SequenceSize(int bytes, int elements)\n        {\n            Bytes = bytes;\n            Elements = elements;\n        }\n\n        /// <summary>\n        /// Creates and returns a copy of <see cref=\"SequenceSize\"/> with the specified number of bytes added to it.\n        /// </summary>\n        /// <param name=\"bytes\">The number of bytes that should be added to the <see cref=\"SequenceSize\"/>.</param>\n        /// <returns>The copy of <see cref=\"SequenceSize\"/> with the specified number of bytes added to it.</returns>\n        public SequenceSize AddBytes(int bytes)\n        {\n            return new SequenceSize(Bytes + bytes, Elements);\n        }\n\n        /// <summary>\n        /// Creates and returns a copy of <see cref=\"SequenceSize\"/> with the specified number of elements added to it.\n        /// </summary>\n        /// <param name=\"elements\">The number of elements that should be added to the <see cref=\"SequenceSize\"/>.</param>\n        /// <returns>The copy of <see cref=\"SequenceSize\"/> with the specified number of bytes added to it.</returns>\n        public SequenceSize AddElements(int elements)\n        {\n            return new SequenceSize(Bytes, Elements + elements);\n        }\n\n        /// <summary>\n        /// Creates and returns a copy of <see cref=\"SequenceSize\"/> with the specified size added to it.\n        /// The number of elements and the number of bytes are summed independently.\n        /// </summary>\n        /// <param name=\"size\">The <see cref=\"SequenceSize\"/> that should be added to this <see cref=\"SequenceSize\"/>.</param>\n        /// <returns>The copy of <see cref=\"SequenceSize\"/> with the specified size added to it.</returns>\n        public SequenceSize Add(SequenceSize size)\n        {\n            return new SequenceSize(Bytes + size.Bytes, Elements + size.Elements);\n        }\n\n        /// <summary>\n        /// Creates and returns a new instance <see cref=\"SequenceSize\"/> representing the sum of two arguments.\n        /// The number of elements and the number of bytes are summed independently.\n        /// </summary>\n        /// <param name=\"x\">The first <see cref=\"SequenceSize\"/>.</param>\n        /// <param name=\"y\">The second <see cref=\"SequenceSize\"/>.</param>\n        /// <returns>The new instance <see cref=\"SequenceSize\"/> representing the sum of two arguments.</returns>\n        public static SequenceSize operator +(SequenceSize x, SequenceSize y)\n        {\n            return new SequenceSize(x.Bytes + y.Bytes, x.Elements + y.Elements);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ServerDataMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class ServerDataMessage : IServerMessage\n    {\n        public ServerMessageCode MessageCode { get; }\n\n        public string? TempTableName { get; }\n\n        private ServerDataMessage(ServerMessageCode messageCode, string? tempTableName)\n        {\n            MessageCode = messageCode;\n            TempTableName = tempTableName;\n        }\n\n        public static async ValueTask<ServerDataMessage> Read(ClickHouseBinaryProtocolReader reader, ServerMessageCode messageCode, bool async, CancellationToken cancellationToken)\n        {\n            string? tempTableName = await reader.ReadString(async, cancellationToken);\n            if (tempTableName == string.Empty)\n                tempTableName = null;\n\n            return new ServerDataMessage(messageCode, tempTableName);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ServerEndOfStreamMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class ServerEndOfStreamMessage : IServerMessage\n    {\n        public static readonly ServerEndOfStreamMessage Instance = new ServerEndOfStreamMessage();\n\n        public ServerMessageCode MessageCode => ServerMessageCode.EndOfStream;\n\n        private ServerEndOfStreamMessage()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ServerErrorMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Octonica.ClickHouseClient.Exceptions;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class ServerErrorMessage : IServerMessage\n    {\n        public ServerMessageCode MessageCode => ServerMessageCode.Error;\n\n        public ClickHouseServerException Exception { get; }\n\n        private ServerErrorMessage(ClickHouseServerException exception)\n        {\n            Exception = exception ?? throw new ArgumentNullException(nameof(exception));\n        }\n\n        public static async ValueTask<ServerErrorMessage> Read(ClickHouseBinaryProtocolReader reader, bool async, CancellationToken cancellationToken)\n        {\n            var errorCode = await reader.ReadInt32(async, cancellationToken);\n            var name = await reader.ReadString(async, cancellationToken);\n            var errorMessage = await reader.ReadString(async, cancellationToken);\n            var stackTrace = await reader.ReadString(async, cancellationToken);\n\n            bool hasNested = await reader.ReadBool(async, cancellationToken);\n            ClickHouseServerException exception;\n            if (hasNested)\n            {\n                var nested = await Read(reader, async, cancellationToken);\n                exception = new ClickHouseServerException(errorCode, name, errorMessage, stackTrace, nested.Exception);\n            }\n            else\n            {\n                exception = new ClickHouseServerException(errorCode, name, errorMessage, stackTrace);\n            }\n\n            return new ServerErrorMessage(exception);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ServerHelloMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Octonica.ClickHouseClient.Exceptions;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class ServerHelloMessage : IServerMessage\n    {\n        public ServerMessageCode MessageCode => ServerMessageCode.Hello;\n\n        public ClickHouseServerInfo ServerInfo { get; }\n\n        private ServerHelloMessage(ClickHouseServerInfo serverInfo)\n        {\n            ServerInfo = serverInfo ?? throw new ArgumentNullException(nameof(serverInfo));\n        }\n\n        public static async Task<ServerHelloMessage> Read(ClickHouseBinaryProtocolReader reader, int protocolRevision, bool async, CancellationToken cancellationToken)\n        {\n            var serverName = await reader.ReadString(async, cancellationToken);\n            var mj = await reader.Read7BitInt32(async, cancellationToken);\n            var mr = await reader.Read7BitInt32(async, cancellationToken);\n            var rv = await reader.Read7BitInt32(async, cancellationToken);\n            if (rv < ClickHouseProtocolRevisions.MinSupportedRevision)\n            {\n                throw new ClickHouseException(\n                    ClickHouseErrorCodes.ProtocolRevisionNotSupported,\n                    $\"The revision {rv} of ClickHouse server is not supported. Minimal supported revision is {ClickHouseProtocolRevisions.MinSupportedRevision}.\");\n            }\n\n            var tz = await reader.ReadString(async, cancellationToken);\n            var displayName = await reader.ReadString(async, cancellationToken);\n            var versionPatch = await reader.Read7BitInt32(async, cancellationToken);\n            var serverVersion = new ClickHouseVersion(mj, mr, versionPatch);\n\n            var negotiatedRevision = Math.Min(rv, protocolRevision);\n            List<ClickHousePasswordComplexityRule>? complexityRules = null;\n            if (negotiatedRevision >= ClickHouseProtocolRevisions.MinRevisionWithPasswordComplexityRules)\n            {\n                var rulesCount = await reader.Read7BitInt32(async, cancellationToken);\n                if (rulesCount > 0)\n                {\n                    complexityRules = new List<ClickHousePasswordComplexityRule>(rulesCount);\n                    for (int i = 0; i < rulesCount; i++)\n                    {\n                        var pattern = await reader.ReadString(async, cancellationToken);\n                        var message = await reader.ReadString(async, cancellationToken);\n                        var rule = new ClickHousePasswordComplexityRule(pattern, message);\n                        complexityRules.Add(rule);\n                    }\n                }\n            }\n\n            if (negotiatedRevision >= ClickHouseProtocolRevisions.MinRevisionWithInterserverSecretV2)\n                await reader.SkipBytes(8, async, cancellationToken); // nonce\n\n            var serverInfo = new ClickHouseServerInfo(serverName, serverVersion, serverRevision: rv, revision: negotiatedRevision, tz, displayName, complexityRules?.AsReadOnly());\n            return new ServerHelloMessage(serverInfo);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ServerMessageCode.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal enum ServerMessageCode\n    {\n        Hello = 0,\n        Data = 1,\n        Error = 2,\n        Progress = 3,\n        Pong = 4,\n        EndOfStream = 5,\n        ProfileInfo = 6,\n        Totals = 7,\n        Extremes = 8,\n        TableStatusResponse = 9,\n        Log = 10,\n        TableColumns = 11,\n        PartUuids = 12,\n        ReadTaskRequest = 13,\n        ProfileEvents = 14,\n        MergeTreeAllRangesAnnouncement = 15,\n        MergeTreeReadTaskRequest = 16,\n        TimezoneUpdate = 17\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ServerPongMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class ServerPongMessage : IServerMessage\n    {\n        public static readonly ServerPongMessage Instance = new ServerPongMessage();\n\n        public ServerMessageCode MessageCode => ServerMessageCode.Pong;\n\n        private ServerPongMessage()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ServerProfileInfoMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class ServerProfileInfoMessage : IServerMessage\n    {\n        public ServerMessageCode MessageCode => ServerMessageCode.ProfileInfo;\n\n        public ulong Rows { get; }\n\n        public ulong Blocks { get; }\n\n        public ulong Bytes { get; }\n\n        public bool LimitApplied { get; }\n\n        public ulong RowsBeforeLimit { get; }\n\n        public bool CalculatedRowsBeforeLimit { get; }\n\n        private ServerProfileInfoMessage(ulong rows, ulong blocks, ulong bytes, bool limitApplied, ulong rowsBeforeLimit, bool calculatedRowsBeforeLimit)\n        {\n            Rows = rows;\n            Blocks = blocks;\n            Bytes = bytes;\n            LimitApplied = limitApplied;\n            RowsBeforeLimit = rowsBeforeLimit;\n            CalculatedRowsBeforeLimit = calculatedRowsBeforeLimit;\n        }\n\n        public static async ValueTask<ServerProfileInfoMessage> Read(ClickHouseBinaryProtocolReader reader, bool async, CancellationToken cancellationToken)\n        {\n            ulong rows = await reader.Read7BitUInt64(async, cancellationToken);\n            ulong blocks = await reader.Read7BitUInt64(async, cancellationToken);\n            ulong bytes = await reader.Read7BitUInt64(async, cancellationToken);\n            bool limitApplied = await reader.ReadBool(async, cancellationToken);\n            ulong rowsBeforeLimit = await reader.Read7BitUInt64(async, cancellationToken);\n            bool calculatedRowsBeforeLimit = await reader.ReadBool(async, cancellationToken);\n\n            return new ServerProfileInfoMessage(rows, blocks, bytes, limitApplied, rowsBeforeLimit, calculatedRowsBeforeLimit);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ServerProgressMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020, 2023, 2026 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class ServerProgressMessage : IServerMessage\n    {\n        public ServerMessageCode MessageCode => ServerMessageCode.Progress;\n\n        public ClickHouseQueryExecutionProgress ExecutionProgress { get; }\n\n        private ServerProgressMessage(ulong rows, ulong bytes, ulong totalRows, ulong totalBytes, ulong writtenRows, ulong writtenBytes, ulong elapsedNanoseconds)\n        {\n            ExecutionProgress = new ClickHouseQueryExecutionProgress(rows, bytes, totalRows, totalBytes, writtenRows, writtenBytes, elapsedNanoseconds);\n        }\n\n        public static async ValueTask<ServerProgressMessage> Read(ClickHouseBinaryProtocolReader reader, int protocolRevision, bool async, CancellationToken cancellationToken)\n        {\n            ulong rows = await reader.Read7BitUInt64(async, cancellationToken);\n            ulong bytes = await reader.Read7BitUInt64(async, cancellationToken);\n            ulong totalRows = await reader.Read7BitUInt64(async, cancellationToken);\n\n            ulong totalBytes = 0;\n            if (protocolRevision >= ClickHouseProtocolRevisions.MinRevisionWithTotalBytesInProgress)\n                totalBytes = await reader.Read7BitUInt64(async, cancellationToken);\n\n            ulong writtenRows = await reader.Read7BitUInt64(async, cancellationToken);\n            ulong writtenBytes = await reader.Read7BitUInt64(async, cancellationToken);\n\n            ulong elapsedNanoseconds = 0;\n            if (protocolRevision >= ClickHouseProtocolRevisions.MinRevisionWithServerQueryTimeInProgress)\n                elapsedNanoseconds = await reader.Read7BitUInt64(async, cancellationToken);\n\n            return new ServerProgressMessage(rows, bytes, totalRows, totalBytes, writtenRows, writtenBytes, elapsedNanoseconds);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ServerTableColumnsMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class ServerTableColumnsMessage : IServerMessage\n    {\n        public ServerMessageCode MessageCode => ServerMessageCode.TableColumns;\n\n        public string Columns { get; }\n\n        private ServerTableColumnsMessage(string columns)\n        {\n            Columns = columns;\n        }\n\n        public static async ValueTask<ServerTableColumnsMessage> Read(ClickHouseBinaryProtocolReader reader, bool async, CancellationToken cancellationToken)\n        {\n            await reader.ReadString(async, cancellationToken);\n            var columns = await reader.ReadString(async, cancellationToken);\n\n            return new ServerTableColumnsMessage(columns);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/ServerTimeZoneUpdateMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Threading.Tasks;\nusing System.Threading;\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class ServerTimeZoneUpdateMessage : IServerMessage\n    {\n        public ServerMessageCode MessageCode => ServerMessageCode.TimezoneUpdate;\n\n        public string Timezone { get; }\n\n        public ServerTimeZoneUpdateMessage(string timezone)\n        {\n            Timezone = timezone;\n        }\n\n        public static async ValueTask<ServerTimeZoneUpdateMessage> Read(ClickHouseBinaryProtocolReader reader, bool async, CancellationToken cancellationToken)\n        {\n            var timezone = await reader.ReadString(async, cancellationToken);\n            return new ServerTimeZoneUpdateMessage(timezone);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/StateCodes.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal static class StateCodes\n    {\n        public const int Complete = 2;\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Protocol/UnknownServerMessage.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Protocol\n{\n    internal sealed class UnknownServerMessage : IServerMessage\n    {\n        public ServerMessageCode MessageCode { get; }\n\n        public UnknownServerMessage(ServerMessageCode messageCode)\n        {\n            MessageCode = messageCode;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ArrayTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class ArrayTableColumn : IClickHouseTableColumn\n    {\n        private readonly IClickHouseTableColumn _column;\n        private readonly List<(int offset, int length)> _ranges;\n        \n        public int RowCount => _ranges.Count;\n\n        public ArrayTableColumn(IClickHouseTableColumn column, List<(int offset, int length)> ranges)\n        {\n            _column = column ?? throw new ArgumentNullException(nameof(column));\n            _ranges = ranges ?? throw new ArgumentNullException(nameof(ranges));\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        public object GetValue(int index)\n        {\n            var range = _ranges[index];\n            var result = new object?[range.length];\n            if (result.Length == 0)\n                return result;\n\n            for (int i = 0; i < result.Length; i++)\n            {\n                if (_column.IsNull(range.offset + i))\n                    result[i] = null;\n                else\n                    result[i] = _column.GetValue(range.offset + i);\n            }\n\n            return result;\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            Type? elementType;\n            var type = typeof(T);\n            if (!type.IsArray || (elementType = type.GetElementType()) == null)\n                return null;\n\n            return (IClickHouseTableColumn<T>?) TypeDispatcher.Dispatch(elementType, new ArrayTableColumnTypeDispatcher(_column, _ranges));\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = default;\n            return false;\n        }\n    }\n\n    internal sealed class ArrayTableColumn<TElement> : IClickHouseTableColumn<TElement[]>, IClickHouseArrayTableColumn<TElement>\n    {\n        private readonly IClickHouseTableColumn<TElement> _column;\n        private readonly List<(int offset, int length)> _ranges;\n\n        public int RowCount => _ranges.Count;\n\n        public TElement[] DefaultValue { get; }\n\n        public ArrayTableColumn(IClickHouseTableColumn<TElement> column, List<(int offset, int length)> ranges)\n        {\n            _column = column ?? throw new ArgumentNullException(nameof(column));\n            _ranges = ranges ?? throw new ArgumentNullException(nameof(ranges));\n            DefaultValue = Array.Empty<TElement>();\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        public TElement[] GetValue(int index)\n        {\n            var range = _ranges[index];\n            if (range.length == 0)\n                return Array.Empty<TElement>();\n\n            var result = new TElement[range.length];\n            for (int i = 0; i < result.Length; i++)\n                result[i] = _column.GetValue(range.offset + i);\n\n            return result;\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            Type? elementType;\n            var type = typeof(T);\n            if (!type.IsArray || (elementType = type.GetElementType()) == null)\n                return null;\n\n            return (IClickHouseTableColumn<T>?) TypeDispatcher.Dispatch(elementType, new ArrayTableColumnTypeDispatcher(_column, _ranges));\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            var reinterpretedColumn = _column as IClickHouseTableColumn<T> ?? _column.TryReinterpret<T>();\n            if (reinterpretedColumn == null)\n                return null;\n\n            return new ReinterpretedArrayTableColumn<T>(this, new ArrayTableColumn<T>(reinterpretedColumn, _ranges));\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public int CopyTo(int index, Span<TElement> buffer, int dataOffset)\n        {\n            var range = _ranges[index];\n            if (dataOffset < 0 || dataOffset > range.length)\n                throw new ArgumentOutOfRangeException(nameof(dataOffset));\n\n            var length = Math.Min(range.length - dataOffset, buffer.Length);\n            for (int i = 0; i < length; i++)\n                buffer[dataOffset + i] = _column.GetValue(range.offset + i);\n\n            return length;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;            \n        }\n    }\n\n    internal sealed class ArrayTableColumnTypeDispatcher : ITypeDispatcher<IClickHouseTableColumn?>\n    {\n        private readonly IClickHouseTableColumn _column;\n        private readonly List<(int offset, int length)> _ranges;\n\n        public ArrayTableColumnTypeDispatcher(IClickHouseTableColumn column, List<(int offset, int length)> ranges)\n        {\n            _column = column ?? throw new ArgumentNullException(nameof(column));\n            _ranges = ranges;\n        }\n\n        public IClickHouseTableColumn? Dispatch<T>()\n        {\n            var reinterpretedColumn = _column as IClickHouseTableColumn<T> ?? _column.TryReinterpret<T>();\n            if (reinterpretedColumn == null)\n                return null;\n\n            var reinterpretedArray = new ArrayTableColumn<T>(reinterpretedColumn, _ranges);\n            return reinterpretedArray;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ArrayTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class ArrayTypeInfo : IClickHouseColumnTypeInfo\n    {\n        private readonly IClickHouseColumnTypeInfo? _elementTypeInfo;\n\n        public string ComplexTypeName { get; }\n\n        public string TypeName { get; } = \"Array\";\n\n        public int GenericArgumentsCount => _elementTypeInfo == null ? 0 : 1;\n\n        public ArrayTypeInfo()\n        {\n            ComplexTypeName = TypeName;\n        }\n\n        private ArrayTypeInfo(IClickHouseColumnTypeInfo elementTypeInfo)\n        {\n            _elementTypeInfo = elementTypeInfo ?? throw new ArgumentNullException(nameof(elementTypeInfo));\n            ComplexTypeName = $\"{TypeName}({_elementTypeInfo.ComplexTypeName})\";\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            if (_elementTypeInfo == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return new ArrayColumnReader(rowCount, _elementTypeInfo);\n        }\n\n        IClickHouseColumnReader IClickHouseColumnTypeInfo.CreateColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode != ClickHouseColumnSerializationMode.Default)\n                throw new NotSupportedException($\"Custom serialization for {ComplexTypeName} is not supported by ClickHouseClient.\");\n\n            return CreateColumnReader(rowCount);\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            if (_elementTypeInfo == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return new ArraySkippingColumnReader(rowCount, _elementTypeInfo);\n        }\n\n        IClickHouseColumnReaderBase IClickHouseColumnTypeInfo.CreateSkippingColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode != ClickHouseColumnSerializationMode.Default)\n                throw new NotSupportedException($\"Custom serialization for {ComplexTypeName} is not supported by ClickHouseClient.\");\n\n            return CreateSkippingColumnReader(rowCount);\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (_elementTypeInfo == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var rowType = typeof(T);\n            Type? elementType = null;\n            if (rowType.IsArray)\n            {\n                var rank = rowType.GetArrayRank();\n                if (rank > 1)\n                {\n                    elementType = rowType.GetElementType()!;\n                    Debug.Assert(elementType != null);\n\n                    var listAdapterInfo = MultiDimensionalArrayReadOnlyListAdapter.Dispatch(elementType, rowType.GetArrayRank());\n                    var mdaDispatcher = new MultiDimensionalArrayColumnWriterDispatcher(\n                        columnName,\n                        (IReadOnlyList<Array>) rows,\n                        columnSettings,\n                        _elementTypeInfo,\n                        listAdapterInfo.createList);\n\n                    return TypeDispatcher.Dispatch(listAdapterInfo.listElementType, mdaDispatcher);\n                }\n            }\n\n            foreach (var genericItf in rowType.GetInterfaces().Where(itf => itf.IsGenericType))\n            {\n                if (genericItf.GetGenericTypeDefinition() != typeof(IReadOnlyList<>))\n                    continue;\n\n                if (elementType == null)\n                {\n                    elementType = genericItf.GetGenericArguments()[0];\n                }\n                else\n                {\n                    var elementTypeCandidate = genericItf.GetGenericArguments()[0];\n\n                    if (elementType.IsAssignableFrom(elementTypeCandidate))\n                        elementType = elementTypeCandidate;\n                    else if (!elementTypeCandidate.IsAssignableFrom(elementType))\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.TypeNotSupported,\n                            $\"Can't detect a type of the array's element. Candidates are: \\\"{elementType}\\\" and \\\"{elementTypeCandidate}\\\".\");\n                }\n            }\n\n            ArrayColumnWriterDispatcherBase dispatcher;\n            if (elementType == null)\n            {\n                var rowGenericTypeDef = rowType.GetGenericTypeDefinition();\n                if (rowGenericTypeDef == typeof(ReadOnlyMemory<>))\n                {\n                    elementType = rowType.GetGenericArguments()[0]!;\n                    dispatcher = new ReadOnlyColumnWriterDispatcher(columnName, rows, columnSettings, _elementTypeInfo);\n                }\n                else if (rowGenericTypeDef == typeof(Memory<>))\n                {\n                    elementType = rowType.GetGenericArguments()[0]!;\n                    dispatcher = new MemoryColumnWriterDispatcher(columnName, rows, columnSettings, _elementTypeInfo);\n                }\n                else\n                {\n                    throw new ClickHouseException(\n                        ClickHouseErrorCodes.TypeNotSupported,\n                        $\"Can't detect a type of the array's element. The type \\\"{typeof(T)}\\\" doesn't implement \\\"{typeof(IReadOnlyList<>)}\\\".\");\n                }\n            }\n            else\n            {\n                dispatcher = new ArrayColumnWriterDispatcher(columnName, rows, columnSettings, _elementTypeInfo);\n            }\n\n            try\n            {\n                return TypeDispatcher.Dispatch(elementType, dispatcher);\n            }\n            catch (ClickHouseException ex) when (ex.ErrorCode == ClickHouseErrorCodes.TypeNotSupported)\n            {\n                throw new ClickHouseException(ex.ErrorCode, $\"The type \\\"{rowType}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\". See the inner exception for details.\", ex);\n            }\n        }\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            if (options.Count > 1)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"Too many arguments in the definition of \\\"{TypeName}\\\".\");\n\n            var elementTypeInfo = typeInfoProvider.GetTypeInfo(options[0]);\n            return new ArrayTypeInfo(elementTypeInfo);\n        }\n\n        public Type GetFieldType()\n        {\n            return _elementTypeInfo == null ? typeof(object[]) : _elementTypeInfo.GetFieldType().MakeArrayType();\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Array;\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            if (_elementTypeInfo == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            if (index != 0)\n                throw new IndexOutOfRangeException();\n\n            return _elementTypeInfo;\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            if (_elementTypeInfo == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            if (type == typeof(string))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            Type? elementType = null;\n            foreach (var itf in type.GetInterfaces())\n            {\n                if (!itf.IsGenericType)\n                    continue;\n\n                var typeDef = itf.GetGenericTypeDefinition();\n                if (typeDef == typeof(ICollection<>) || typeDef == typeof(IReadOnlyCollection<>))\n                {\n                    var genericArg = itf.GetGenericArguments()[0];\n                    if (elementType == null || elementType.IsAssignableFrom(genericArg))\n                    {\n                        elementType = genericArg;\n                    }\n                    else if (!genericArg.IsAssignableFrom(elementType))\n                    {\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.TypeNotSupported,\n                            $\"Can't detect a type of the array's element. Candidates are: \\\"{elementType}\\\" and \\\"{genericArg}\\\".\");\n                    }\n                }\n            }\n\n            ArrayParameterWriterDispatcher<T> dispatcher;\n            if (elementType == null)\n            {\n                int arrayRank;\n                if (!type.IsArray || (arrayRank = type.GetArrayRank()) == 1)\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n                var elementTypeInfo = _elementTypeInfo;\n                for (int i = 1; i < arrayRank; i++)\n                {\n                    if (elementTypeInfo is NullableTypeInfo nti)\n                    {\n                        elementTypeInfo = nti.UnderlyingType;\n                        --i;\n                    }\n                    else if (elementTypeInfo is ArrayTypeInfo ati)\n                    {\n                        elementTypeInfo = ati._elementTypeInfo;\n                    }\n                    else\n                    {\n                        throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The multidimensional array value can not be converted type \\\"{ComplexTypeName}\\\": dimension number mismatch.\");\n                    }\n\n                    if (elementTypeInfo == null)\n                        throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n                }\n\n                elementType = type.GetElementType();\n                Debug.Assert( elementType != null );\n                dispatcher = new ArrayParameterWriterDispatcher<T>(this, elementTypeInfo, true);\n            }\n            else\n            {\n                dispatcher = new ArrayParameterWriterDispatcher<T>(this, _elementTypeInfo, false);\n            }\n\n            var writer = TypeDispatcher.Dispatch(elementType, dispatcher);\n            return writer;\n        }\n\n        private abstract class ArrayColumnReaderBase<TElementColumnReader> : IClickHouseColumnReaderBase\n            where TElementColumnReader : class, IClickHouseColumnReaderBase\n        {\n            private readonly int _rowCount;\n\n            // A part of the inner column's header exposed outside of the beginning of the current column\n            private byte[]? _prefix;\n\n            private int _elementPosition;\n\n            protected IClickHouseColumnTypeInfo ElementType { get; }\n\n            protected List<(int offset, int length)> Ranges { get; }\n\n            protected TElementColumnReader? ElementColumnReader { get; private set; }\n\n            protected int Position { get; private set; }\n\n            public ArrayColumnReaderBase(int rowCount, IClickHouseColumnTypeInfo elementType)\n            {\n                _rowCount = rowCount;\n                ElementType = elementType;\n                Ranges = new List<(int offset, int length)>(_rowCount);\n            }\n\n            SequenceSize IClickHouseColumnReaderBase.ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                // We can't read the prefix right now, so we keep it in the buffer until we get the real reader\n                var skippingReader = ElementType.CreateSkippingColumnReader(0);\n                var updSeq = sequence;\n                if (_prefix != null)\n                {\n                    var segment = new SimpleReadOnlySequenceSegment<byte>(_prefix, updSeq);\n                    updSeq = new ReadOnlySequence<byte>(segment, 0, segment.LastSegment, segment.LastSegment.Memory.Length);\n                }\n\n                var result = skippingReader.ReadPrefix(updSeq);\n                var bufferSize = result.Bytes;\n                if (_prefix != null)\n                    result = result.AddBytes(-_prefix.Length);\n\n                if (result.Bytes < 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Byte offset calculation error. The length of the prefix is negative.\");\n\n                if (result.Bytes == 0)\n                    return result;\n\n                Array.Resize(ref _prefix, bufferSize);\n                updSeq.Slice(0, bufferSize).CopyTo(_prefix);\n                return result;\n            }\n\n            SequenceSize IClickHouseColumnReaderBase.ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                if (Position >= _rowCount)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                int bytesCount = 0;\n                var slice = sequence;\n                if (ElementColumnReader == null)\n                {\n                    var totalLength = Ranges.Aggregate((ulong)0, (acc, r) => acc + (ulong)r.length);\n\n                    Span<byte> sizeSpan = stackalloc byte[sizeof(ulong)];\n                    for (int i = Ranges.Count; i < _rowCount; i++)\n                    {\n                        if (slice.Length < sizeSpan.Length)\n                            return new SequenceSize(bytesCount, 0);\n\n                        ulong length;\n                        if (slice.FirstSpan.Length >= sizeSpan.Length)\n                        {\n                            length = BitConverter.ToUInt64(slice.FirstSpan);\n                        }\n                        else\n                        {\n                            slice.Slice(0, sizeSpan.Length).CopyTo(sizeSpan);\n                            length = BitConverter.ToUInt64(sizeSpan);\n                        }\n\n                        slice = slice.Slice(sizeSpan.Length);\n                        bytesCount += sizeSpan.Length;\n\n                        var offset = checked((int)totalLength);\n                        var rangeLength = checked((int)(length - totalLength));\n                        Ranges.Add((offset, rangeLength));\n\n                        totalLength = length;\n                    }\n\n                    ElementColumnReader = CreateElementColumnReader(checked((int)totalLength));\n\n                    if (totalLength == 0)\n                    {\n                        // Special case for an empty array\n                        var result = new SequenceSize(bytesCount, _rowCount - Position);\n                        Position = _rowCount;\n                        return result;\n                    }\n                }\n\n                if (_prefix!=null)\n                {\n                    var prefixSize = ElementColumnReader.ReadPrefix(new ReadOnlySequence<byte>(_prefix));\n                    if (prefixSize.Bytes == 0 && prefixSize.Elements == 0)\n                        throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Failed to read the column prefix.\");\n\n                    if (prefixSize.Bytes != _prefix.Length)\n                        throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. The column prefix' size is {_prefix.Length}, but the number of consumed bytes is {prefixSize.Bytes}.\");\n\n                    if (prefixSize.Elements != 1)\n                        throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. Received an unexpected number of column prefixes: {prefixSize.Elements}.\");\n\n                    _prefix = null;\n                }\n\n                var elementsSize = ElementColumnReader.ReadNext(slice);\n                _elementPosition += elementsSize.Elements;\n                var elementsCount = 0;\n                while (Position < _rowCount)\n                {\n                    var currentRange = Ranges[Position];\n                    if (currentRange.length + currentRange.offset > _elementPosition)\n                        break;\n\n                    ++elementsCount;\n                    ++Position;\n                }\n\n                return new SequenceSize(bytesCount + elementsSize.Bytes, elementsCount);\n            }\n\n            protected abstract TElementColumnReader CreateElementColumnReader(int totalLength);\n        }\n\n        private sealed class ArrayColumnReader : ArrayColumnReaderBase<IClickHouseColumnReader>, IClickHouseColumnReader\n        {\n            public ArrayColumnReader(int rowCount, IClickHouseColumnTypeInfo elementType)\n                : base(rowCount, elementType)\n            {\n            }\n\n            protected override IClickHouseColumnReader CreateElementColumnReader(int totalLength)\n            {\n                return ElementType.CreateColumnReader(totalLength);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                var elementColumnReader = ElementColumnReader ?? ElementType.CreateColumnReader(0);\n                var column = elementColumnReader.EndRead(settings);\n                var ranges = Position == Ranges.Count ? Ranges : Ranges.Take(Position).ToList();\n\n                if (!column.TryDipatch(new ArrayTableColumnDipatcher(ranges), out var result))\n                    result = new ArrayTableColumn(column, ranges);\n\n                return result;\n            }\n        }\n\n        private sealed class ArraySkippingColumnReader : ArrayColumnReaderBase<IClickHouseColumnReaderBase>\n        {\n            public ArraySkippingColumnReader(int rowCount, IClickHouseColumnTypeInfo elementType)\n                : base(rowCount, elementType)\n            {\n            }\n\n            protected override IClickHouseColumnReaderBase CreateElementColumnReader(int totalLength)\n            {\n                return ElementType.CreateSkippingColumnReader(totalLength);\n            }\n        }\n\n        private sealed class ArrayColumnWriterDispatcher : ArrayColumnWriterDispatcherBase\n        {\n            public ArrayColumnWriterDispatcher(string columnName, object rows, ClickHouseColumnSettings? columnSettings, IClickHouseColumnTypeInfo elementTypeInfo)\n                : base(columnName, rows, columnSettings, elementTypeInfo)\n            {\n            }\n\n            protected override ArrayLinearizedList<T> ToList<T>(object rows)\n            {\n                return new ArrayLinearizedList<T>(new ReadOnlyCollectionList<T>((IReadOnlyList<IReadOnlyList<T>?>) rows));\n            }\n        }\n\n        private sealed class MemoryColumnWriterDispatcher : ArrayColumnWriterDispatcherBase\n        {\n            public MemoryColumnWriterDispatcher(string columnName, object rows, ClickHouseColumnSettings? columnSettings, IClickHouseColumnTypeInfo elementTypeInfo)\n                : base(columnName, rows, columnSettings, elementTypeInfo)\n            {\n            }\n\n            protected override ArrayLinearizedList<T> ToList<T>(object rows)\n            {\n                return new ArrayLinearizedList<T>(new MemoryCollectionList<T>((IReadOnlyList<Memory<T>>) rows));\n            }\n        }\n\n        private sealed class ReadOnlyColumnWriterDispatcher : ArrayColumnWriterDispatcherBase\n        {\n            public ReadOnlyColumnWriterDispatcher(string columnName, object rows, ClickHouseColumnSettings? columnSettings, IClickHouseColumnTypeInfo elementTypeInfo)\n                : base(columnName, rows, columnSettings, elementTypeInfo)\n            {\n            }\n\n            protected override ArrayLinearizedList<T> ToList<T>(object rows)\n            {\n                return new ArrayLinearizedList<T>(new ReadOnlyMemoryCollectionList<T>((IReadOnlyList<ReadOnlyMemory<T>>) rows));\n            }\n        }\n\n        private abstract class ArrayColumnWriterDispatcherBase : ITypeDispatcher<IClickHouseColumnWriter>\n        {\n            private readonly string _columnName;            \n            private readonly object _rows;\n            private readonly ClickHouseColumnSettings? _columnSettings;\n            private readonly IClickHouseColumnTypeInfo _elementTypeInfo;\n\n            protected ArrayColumnWriterDispatcherBase(string columnName, object rows, ClickHouseColumnSettings? columnSettings, IClickHouseColumnTypeInfo elementTypeInfo)\n            {\n                _columnName = columnName;\n                _rows = rows;\n                _columnSettings = columnSettings;\n                _elementTypeInfo = elementTypeInfo;\n            }\n\n            public IClickHouseColumnWriter Dispatch<T>()\n            {\n                var linearizedList = ToList<T>(_rows);\n                var elementColumnWriter = _elementTypeInfo.CreateColumnWriter(_columnName, linearizedList, _columnSettings);\n                var columnType = $\"Array({elementColumnWriter.ColumnType})\";\n                return new ArrayColumnWriter<T>(columnType, linearizedList, elementColumnWriter);\n            }\n\n            protected abstract ArrayLinearizedList<T> ToList<T>(object rows);\n        }\n\n        private sealed class MultiDimensionalArrayColumnWriterDispatcher : ITypeDispatcher<IClickHouseColumnWriter>\n        {\n            private readonly string _columnName;\n            private readonly IReadOnlyList<Array> _rows;\n            private readonly ClickHouseColumnSettings? _columnSettings;\n            private readonly IClickHouseColumnTypeInfo _elementTypeInfo;\n            private readonly Func<Array, object> _dispatchArray;\n\n            public MultiDimensionalArrayColumnWriterDispatcher(\n                string columnName,\n                IReadOnlyList<Array> rows,\n                ClickHouseColumnSettings? columnSettings,\n                IClickHouseColumnTypeInfo elementTypeInfo,\n                Func<Array, object> dispatchArray)\n            {\n                _columnName = columnName;\n                _rows = rows;\n                _columnSettings = columnSettings;\n                _elementTypeInfo = elementTypeInfo;\n                _dispatchArray = dispatchArray;\n            }\n\n            public IClickHouseColumnWriter Dispatch<T>()\n            {\n                var mappedRows = MappedReadOnlyList<Array, IReadOnlyList<T>>.Map(_rows, arr => (IReadOnlyList<T>) _dispatchArray(arr));\n                var linearizedList = new ArrayLinearizedList<T>(new ReadOnlyCollectionList<T>(mappedRows));\n                var elementColumnWriter = _elementTypeInfo.CreateColumnWriter(_columnName, linearizedList, _columnSettings);\n                var columnType = $\"Array({elementColumnWriter.ColumnType})\";\n                return new ArrayColumnWriter<T>(columnType, linearizedList, elementColumnWriter);\n            }\n        }\n\n        private sealed class ArrayColumnWriter<T> : IClickHouseColumnWriter\n        {\n            private readonly ArrayLinearizedList<T> _rows;\n            private readonly IClickHouseColumnWriter _elementColumnWriter;\n\n            public string ColumnName { get; }\n\n            public string ColumnType { get; }\n\n            private int _headerPosition;\n            private int _position;\n            private int _elementPosition;\n\n            public ArrayColumnWriter(string columnType, ArrayLinearizedList<T> rows, IClickHouseColumnWriter elementColumnWriter)\n            {\n                _rows = rows;\n                _elementColumnWriter = elementColumnWriter;\n                ColumnName = elementColumnWriter.ColumnName;\n                ColumnType = columnType;\n            }\n\n            SequenceSize IClickHouseColumnWriter.WritePrefix(Span<byte> writeTo)\n            {\n                return _elementColumnWriter.WritePrefix(writeTo);\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                int bytesCount = 0;\n                var span = writeTo;\n                for (; _headerPosition < _rows.ListLengths.Count; _headerPosition++)\n                {\n                    if (!BitConverter.TryWriteBytes(span, (ulong) _rows.ListLengths[_headerPosition]))\n                        return new SequenceSize(bytesCount, 0);\n\n                    span = span.Slice(sizeof(ulong));\n                    bytesCount += sizeof(ulong);\n                }\n\n                if (_rows.Count == 0)\n                {\n                    // There are no actual values, only an empty array or a bunch of empty arrays\n                    Debug.Assert(_position == 0);\n                    _position = _rows.ListLengths.Count;\n                    return new SequenceSize(bytesCount, _position);\n                }\n\n                var elementSize = _elementColumnWriter.WriteNext(span);\n                _elementPosition += elementSize.Elements;\n\n                var elementsCount = 0;\n                while (_position < _rows.ListLengths.Count)\n                {\n                    if (_rows.ListLengths[_position] > _elementPosition)\n                        break;\n\n                    ++elementsCount;\n                    ++_position;\n                }\n\n                return new SequenceSize(elementSize.Bytes + bytesCount, elementsCount);\n            }\n        }\n        \n        private sealed class ArrayLinearizedList<T> : IReadOnlyList<T>\n        {\n            private readonly ICollectionList<T> _listOfLists;\n\n            public List<int> ListLengths { get; }\n\n            public int Count { get; }\n\n            public ArrayLinearizedList(ICollectionList<T> listOfLists)\n            {\n                _listOfLists = listOfLists;\n                ListLengths = new List<int>(_listOfLists.Count);\n\n                int offset = 0;\n                foreach (var listLength in _listOfLists.GetListLengths())\n                {\n                    offset += listLength;\n                    ListLengths.Add(offset);\n                }\n\n                Count = offset;\n            }\n\n            public IEnumerator<T> GetEnumerator()\n            {\n                return _listOfLists.GetItems().GetEnumerator();\n            }\n\n            IEnumerator IEnumerable.GetEnumerator()\n            {\n                return GetEnumerator();\n            }\n\n            public T this[int index]\n            {\n                get\n                {\n                    if (index < 0 || index >= Count)\n                        throw new IndexOutOfRangeException();\n\n                    var listIndex = ListLengths.BinarySearch(index);\n                    int elementIndex;\n                    if (listIndex < 0)\n                    {\n                        listIndex = ~listIndex;\n                        if (listIndex == 0)\n                        {\n                            elementIndex = index;\n                        }\n                        else\n                        {\n                            elementIndex = index - ListLengths[listIndex - 1];\n                        }\n                    }\n                    else\n                    {\n                        elementIndex = 0;\n                        listIndex++;\n                    }\n\n                    for (; listIndex < _listOfLists.Count; listIndex++)\n                    {\n                        if (elementIndex < 0)\n                            break;\n\n                        var listLength = _listOfLists.GetLength(listIndex);\n                        if (elementIndex < listLength)\n                            return _listOfLists[listIndex, elementIndex];\n\n                        elementIndex = index - ListLengths[listIndex];\n                    }\n\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error: data structure is corrupted.\");\n                }\n            }\n        }\n\n        private sealed class ArrayTableColumnDipatcher : IClickHouseTableColumnDispatcher<IClickHouseTableColumn>\n        {\n            private readonly List<(int offset, int length)> _ranges;\n\n            public ArrayTableColumnDipatcher(List<(int offset, int length)> ranges)\n            {\n                _ranges = ranges;\n            }\n\n            public IClickHouseTableColumn Dispatch<T>(IClickHouseTableColumn<T> column)\n            {\n                return new ArrayTableColumn<T>(column, _ranges);\n            }\n        }\n\n        private sealed class ArrayParameterWriterDispatcher<TArray> : ITypeDispatcher<IClickHouseParameterWriter<TArray>>\n        {\n            private readonly ArrayTypeInfo _arrayType;\n            private readonly IClickHouseColumnTypeInfo _elementType;\n            private readonly bool _isMultidimensional;\n\n            public ArrayParameterWriterDispatcher(ArrayTypeInfo arrayType, IClickHouseColumnTypeInfo elementType, bool isMultidimensional)\n            {\n                _arrayType = arrayType;\n                _elementType = elementType;\n                _isMultidimensional = isMultidimensional;\n            }\n\n            public IClickHouseParameterWriter<TArray> Dispatch<T>()\n            {\n                var elementWriter = _elementType.CreateParameterWriter<T>();\n\n                if (_isMultidimensional)\n                    return new MultidimensionalArralParameterWriter<TArray, T>(_arrayType, elementWriter);\n\n                return new ArrayParameterWriter<TArray, T>(_arrayType, elementWriter);\n            }\n        }\n\n        private sealed class MultidimensionalArralParameterWriter<TArray, TElement> : IClickHouseParameterWriter<TArray>\n        {\n            private readonly ArrayTypeInfo _arrayType;\n            private readonly IClickHouseParameterWriter<TElement> _elementWriter;\n\n            public MultidimensionalArralParameterWriter(ArrayTypeInfo arrayType, IClickHouseParameterWriter<TElement> elementWriter)\n            {\n                _arrayType = arrayType;\n                _elementWriter = elementWriter;\n            }\n\n            public bool TryCreateParameterValueWriter(TArray value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                if (value is null)\n                    throw new ArgumentNullException(nameof(value));\n\n                var enumerator = ((IEnumerable)value).GetEnumerator();\n                if (!TryCreateElementWriters((Array)(object)value, enumerator, 0, out var elementWriters))\n                {\n                    valueWriter = null;\n                    return false;\n                }\n\n                valueWriter = new ArrayLiteralValueWriter(elementWriters);\n                return true;\n            }\n\n            private bool TryCreateElementWriters(Array array, IEnumerator enumerator, int dimension, [NotNullWhen(true)] out List<IClickHouseParameterValueWriter>? writers)\n            {\n                var rankLength = array.GetLength(dimension);\n                writers = new List<IClickHouseParameterValueWriter>(rankLength);\n                if (dimension == array.Rank - 1)\n                {\n                    for (var i = 0; i < rankLength; ++i)\n                    {\n                        if (!enumerator.MoveNext())\n                            throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error: unexpected iterator out of bound.\");\n\n                        if (!_elementWriter.TryCreateParameterValueWriter((TElement)enumerator.Current!, true, out var elementWriter))\n                            return false;\n\n                        writers.Add(elementWriter);\n                    }\n                }\n                else\n                {\n                    var nextDimension = dimension + 1;\n                    for (var i = 0; i < rankLength; ++i)\n                    {\n                        if (!TryCreateElementWriters(array, enumerator, nextDimension, out var elementWriters))\n                            return false;\n\n                        writers.Add(new ArrayLiteralValueWriter(elementWriters));\n                    }\n                }\n\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, TArray value)\n            {\n                if (value is null)\n                    throw new ArgumentNullException(nameof(value));\n\n                var enumerator = ((IEnumerable)value).GetEnumerator();\n                return Interpolate(queryBuilder, (Array)(object)value, enumerator, 0);\n            }\n\n            private StringBuilder Interpolate(StringBuilder queryBuilder, Array array, IEnumerator enumerator, int dimension)\n            {\n                var rankLength = array.GetLength(dimension);\n                queryBuilder.Append('[');\n                if (dimension == array.Rank - 1)\n                {\n                    for (var i = 0; i < rankLength; ++i)\n                    {\n                        if (!enumerator.MoveNext())\n                            throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error: unexpected iterator out of bound.\");\n\n                        if (i > 0)\n                            queryBuilder.Append(',');\n\n                        _elementWriter.Interpolate(queryBuilder, (TElement)enumerator.Current!);\n                    }\n                }\n                else\n                {\n                    var nextDimension = dimension + 1;\n                    for (var i = 0; i < rankLength; ++i)\n                    {\n                        if (i > 0)\n                            queryBuilder.Append(',');\n\n                        Interpolate(queryBuilder, array, enumerator, nextDimension);\n                    }\n                }\n\n                return queryBuilder.Append(']');\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return _elementWriter.Interpolate(queryBuilder, typeInfoProvider, (qb, typeInfo, writeElement) =>\n                {\n                    int rank = 1;\n                    var elementTypeInfo = _arrayType._elementTypeInfo;\n                    while (elementTypeInfo is ArrayTypeInfo elementArray)\n                    {\n                        elementTypeInfo = elementArray._elementTypeInfo;\n                        ++rank;\n                    }\n\n                    Debug.Assert(elementTypeInfo != null);\n                    if (elementTypeInfo.ComplexTypeName == typeInfo.ComplexTypeName)\n                        return writeValue(qb, _arrayType, FunctionHelper.Apply);\n\n                    var updArrayTypeInfo = new ArrayTypeInfo(typeInfo);\n                    for (int i = rank - 1; i > 0; i--)\n                        updArrayTypeInfo = new ArrayTypeInfo(updArrayTypeInfo);\n\n                    return writeValue(qb, updArrayTypeInfo, (qb2, realWrite) =>\n                    {\n                        for (int i = 1; i <= rank; i++)\n                            qb2.AppendFormat(CultureInfo.InvariantCulture, \"arrayMap(_elt{0} -> \", i);\n\n                        writeElement(qb2, b => b.AppendFormat(CultureInfo.InvariantCulture, \"_elt{0}\", rank));\n\n                        for (int i = rank - 1; i > 0; i--)\n                            qb2.AppendFormat(CultureInfo.InvariantCulture, \", _elt{0})\", i);\n\n                        qb2.Append(\", \");\n                        realWrite(qb2);\n                        qb2.Append(')');\n                        return qb2;\n                    });\n                });\n            }\n        }\n\n        private sealed class ArrayParameterWriter<TArray, TElement> : IClickHouseParameterWriter<TArray>\n        {\n            private readonly ArrayTypeInfo _type;\n            private readonly IClickHouseParameterWriter<TElement> _elementWriter;\n\n            public ArrayParameterWriter(ArrayTypeInfo type, IClickHouseParameterWriter<TElement> elementWriter)\n            {\n                _type = type;\n                _elementWriter = elementWriter;\n            }\n\n            public bool TryCreateParameterValueWriter(TArray value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                if (value is null)\n                    throw new ArgumentNullException(nameof(value));\n\n                var elementWriters = new List<IClickHouseParameterValueWriter>();\n                foreach(var element in (IEnumerable<TElement>)value)\n                {\n                    if (!_elementWriter.TryCreateParameterValueWriter(element, true, out var elementWriter))\n                    {\n                        valueWriter = null;\n                        return false;\n                    }\n\n                    elementWriters.Add(elementWriter);\n                }\n\n                valueWriter = new ArrayLiteralValueWriter(elementWriters);\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, TArray value)\n            {\n                if (value is null)\n                    throw new ArgumentNullException(nameof(value));\n\n                var elementTypeInfo = _type._elementTypeInfo;\n                Debug.Assert(elementTypeInfo != null);\n\n                queryBuilder.Append('[');\n\n                bool isFirst = true;\n                foreach (var element in (IEnumerable<TElement>)value)\n                {\n                    if (isFirst)\n                        isFirst = false;\n                    else\n                        queryBuilder.Append(',');\n\n                    _elementWriter.Interpolate(queryBuilder, element);\n                }\n\n                return queryBuilder.Append(']');\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return _elementWriter.Interpolate(queryBuilder, typeInfoProvider, (qb, typeInfo, writeElement) =>\n                {\n                    var elementTypeInfo = _type._elementTypeInfo;\n                    Debug.Assert(elementTypeInfo != null);\n                    if (elementTypeInfo.ComplexTypeName == typeInfo.ComplexTypeName)\n                        return writeValue(qb, _type, FunctionHelper.Apply);\n\n                    var updArrayTypeInfo = new ArrayTypeInfo(typeInfo);\n                    return writeValue(qb, updArrayTypeInfo, (qb2, realWrite) =>\n                    {\n                        qb2.Append(\"arrayMap(_elt -> \");\n                        writeElement(qb2, b => b.Append(\"_elt\"));\n                        qb2.Append(\", \");\n                        realWrite(qb2);\n                        return qb2.Append(')');\n                    });\n                });\n            }\n        }\n\n        private sealed class ArrayLiteralValueWriter : IClickHouseParameterValueWriter\n        {\n            private readonly List<IClickHouseParameterValueWriter> _elementWriters;\n\n            public int Length { get; }\n\n            public ArrayLiteralValueWriter(List<IClickHouseParameterValueWriter> elementWriters)\n            {\n                Length =\n                    2 + // []\n                    elementWriters.Aggregate(0, (l, w) => w.Length + l) + // length of elements\n                    Math.Max(0, elementWriters.Count - 1); // comas\n                _elementWriters = elementWriters;\n            }\n\n            public int Write(Memory<byte> buffer)\n            {\n                int count = 0;\n                buffer.Span[count++] = (byte)'[';\n\n                bool isFirst = true;\n                foreach (var writer in _elementWriters)\n                {\n                    if (isFirst)\n                        isFirst = false;\n                    else\n                        buffer.Span[count++] = (byte)',';\n\n                    count += writer.Write(buffer.Slice(count));\n                }\n\n                buffer.Span[count++] = (byte)']';\n                Debug.Assert(count == Length);\n\n                return count;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/BigIntegerTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Numerics;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class BigIntegerTableColumn : IClickHouseTableColumn<BigInteger>\n    {\n        private readonly byte[] _rawData;\n        private readonly int _elementByteSize;\n        private readonly bool _isUnsigned;\n\n        public int RowCount { get; }\n\n        public BigInteger DefaultValue => BigInteger.Zero;\n\n        public BigIntegerTableColumn(byte[] rawData, int rowCount, int elementByteSize, bool isUnsigned)\n        {\n            _rawData = rawData;\n            RowCount = rowCount;\n            _elementByteSize = elementByteSize;\n            _isUnsigned = isUnsigned;\n        }\n\n        public BigInteger GetValue(int index)\n        {\n            if (index < 0 || index >= RowCount)\n                throw new IndexOutOfRangeException();\n\n            var slice = ((ReadOnlySpan<byte>)_rawData).Slice(index * _elementByteSize, _elementByteSize);\n            return new BigInteger(slice, _isUnsigned);\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(BigInteger?))\n                return (IClickHouseTableColumn<T>)(object)new NullableStructTableColumn<BigInteger>(null, this);\n\n            return null;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/BigIntegerTypeInfoBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Diagnostics;\nusing System.Numerics;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal abstract class BigIntegerTypeInfoBase : SimpleTypeInfo\n    {\n        int _elementByteSize;\n        bool _isUnsigned;\n\n        protected BigIntegerTypeInfoBase(string typeName, int elementByteSize, bool isUnsigned)\n            : base(typeName)\n        {\n            _elementByteSize = elementByteSize;\n            _isUnsigned = isUnsigned;\n        }\n\n        public sealed override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new BigIntegerColumnReader(rowCount, _elementByteSize, _isUnsigned);\n        }\n\n        public sealed override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(_elementByteSize, rowCount);\n        }\n\n        public sealed override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            var type = typeof(T);\n            IReadOnlyList<BigInteger>? bigIntegerRows = null;\n            if (type == typeof(BigInteger))\n            {\n                bigIntegerRows = (IReadOnlyList<BigInteger>)rows;\n            }\n            else if (type == typeof(ulong))\n            {\n                bigIntegerRows = MappedReadOnlyList<ulong, BigInteger>.Map((IReadOnlyList<ulong>)rows, v => v);\n            }\n            else if (type == typeof(uint))\n            {\n                bigIntegerRows = MappedReadOnlyList<uint, BigInteger>.Map((IReadOnlyList<uint>)rows, v => v);\n            }\n            else if (type == typeof(ushort))\n            {\n                bigIntegerRows = MappedReadOnlyList<ushort, BigInteger>.Map((IReadOnlyList<ushort>)rows, v => v);\n            }\n            else if (type == typeof(byte))\n            {\n                bigIntegerRows = MappedReadOnlyList<byte, BigInteger>.Map((IReadOnlyList<byte>)rows, v => v);\n            }\n            else if (!_isUnsigned)\n            {\n                if (type == typeof(long))\n                    bigIntegerRows = MappedReadOnlyList<long, BigInteger>.Map((IReadOnlyList<long>)rows, v => v);\n                else if (type == typeof(int))\n                    bigIntegerRows = MappedReadOnlyList<int, BigInteger>.Map((IReadOnlyList<int>)rows, v => v);\n                else if (type == typeof(short))\n                    bigIntegerRows = MappedReadOnlyList<short, BigInteger>.Map((IReadOnlyList<short>)rows, v => v);\n                else if (type == typeof(sbyte))\n                    bigIntegerRows = MappedReadOnlyList<sbyte, BigInteger>.Map((IReadOnlyList<sbyte>)rows, v => v);\n            }\n\n            if (bigIntegerRows == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new BigIntegerColumnWriter(columnName, ComplexTypeName, _elementByteSize, bigIntegerRows, _isUnsigned);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object? writer = null;\n            if (type == typeof(BigInteger))\n            {\n                if (_isUnsigned)\n                {\n                    writer = new StringParameterWriter<BigInteger>(\n                        this,\n                        bigIntegerValue =>\n                        {\n                            if (bigIntegerValue.Sign < 0)\n                                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow negative BigInteger values.\");\n\n                            return bigIntegerValue.ToString(CultureInfo.InvariantCulture).AsMemory();\n                        });\n                }\n                else\n                {\n                    writer = StringParameterWriter.Create<BigInteger>(this);\n                }\n            }\n            else if (type == typeof(ulong))\n            {\n                writer = StringParameterWriter.Create<ulong>(this);\n            }\n            else if (type == typeof(uint))\n            {\n                writer = StringParameterWriter.Create<uint>(this);\n            }\n            else if (type == typeof(ushort))\n            {\n                writer = StringParameterWriter.Create<ushort>(this);\n            }\n            else if (type == typeof(byte))\n            {\n                writer = StringParameterWriter.Create<byte>(this);\n            }\n            else if (!_isUnsigned)\n            {\n                if (type == typeof(long))\n                    writer = StringParameterWriter.Create<long>(this);\n                if (type == typeof(int))\n                    writer = StringParameterWriter.Create<int>(this);\n                if (type == typeof(short))\n                    writer = StringParameterWriter.Create<short>(this);\n                else if (type == typeof(sbyte))\n                    writer = StringParameterWriter.Create<sbyte>(this);\n            }\n\n            if (writer == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public sealed override Type GetFieldType()\n        {\n            return typeof(BigInteger);\n        }\n\n        private sealed class BigIntegerColumnReader : IClickHouseColumnReader\n        {\n            private readonly byte[] _buffer;\n            private readonly int _elementByteSize;\n            private readonly bool _isUnsigned;\n\n            private int _position;\n\n            public BigIntegerColumnReader(int rowCount, int elementByteSize, bool isUnsigned)\n            {\n                if (rowCount == 0)\n                    _buffer = Array.Empty<byte>();\n                else\n                    _buffer = new byte[rowCount * elementByteSize];\n\n                _elementByteSize = elementByteSize;\n                _isUnsigned = isUnsigned;\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                var rowCount = _buffer.Length / _elementByteSize;\n                if (_position >= rowCount)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                var elementCount = (int)Math.Min(rowCount - _position, sequence.Length / _elementByteSize);\n                var byteCount = elementCount * _elementByteSize;\n                sequence.Slice(0, byteCount).CopyTo(((Span<byte>)_buffer).Slice(_position * _elementByteSize, byteCount));\n\n                _position += elementCount;\n                return new SequenceSize(byteCount, elementCount);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                return new BigIntegerTableColumn(_buffer, _position, _elementByteSize, _isUnsigned);\n            }\n        }\n\n        private sealed class BigIntegerColumnWriter : StructureWriterBase<BigInteger>\n        {\n            private readonly bool _isUnsigned;\n\n            public BigIntegerColumnWriter(string columnName, string columnType, int elementSize, IReadOnlyList<BigInteger> rows, bool isUnsigned) \n                : base(columnName, columnType, elementSize, rows)\n            {\n                _isUnsigned = isUnsigned;\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in BigInteger value)\n            {\n                if (_isUnsigned && value.Sign < 0)\n                    throw new OverflowException($\"A negative value can't be written to the column \\\"{ColumnName}\\\" of type \\\"{ColumnType}\\\".\");\n\n                var byteCount = value.GetByteCount(_isUnsigned);\n                if (byteCount > ElementSize)\n                    throw new OverflowException($\"A value can't be written to the column \\\"{ColumnName}\\\" of type \\\"{ColumnType}\\\" because it's outside of supported range of values.\");\n\n                var success = value.TryWriteBytes(writeTo.Slice(0, byteCount), out var bytesWritten, _isUnsigned);\n                Debug.Assert(success);\n                Debug.Assert(byteCount == bytesWritten);\n\n                if (byteCount < ElementSize)\n                    writeTo.Slice(byteCount, ElementSize - byteCount).Fill(_isUnsigned || value.Sign >= 0 ? (byte)0 : (byte)0xff);                \n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/BoolTableColumn.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2022, 2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing System;\r\nusing System.Diagnostics.CodeAnalysis;\r\n\r\nnamespace Octonica.ClickHouseClient.Types\r\n{\r\n    internal sealed class BoolTableColumn : IClickHouseTableColumn<bool>\r\n    {\r\n        private readonly ReadOnlyMemory<byte> _buffer;\r\n\r\n        public int RowCount => _buffer.Length;\r\n\r\n        public bool DefaultValue => false;\r\n\r\n        public BoolTableColumn(ReadOnlyMemory<byte> buffer)\r\n        {\r\n            _buffer = buffer;\r\n        }\r\n\r\n        object IClickHouseTableColumn.GetValue(int index)\r\n        {\r\n            return GetValue(index);\r\n        }\r\n\r\n        public bool GetValue(int index)\r\n        {\r\n            return _buffer.Span[index] != 0;\r\n        }\r\n\r\n        public bool IsNull(int index)\r\n        {\r\n            return false;\r\n        }\r\n\r\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\r\n        {\r\n            // Inherit type cast logic from UInt8\r\n            var uint8Column = new UInt8TableColumn(_buffer);\r\n            var reinterpreted = uint8Column as IClickHouseTableColumn<T> ?? uint8Column.TryReinterpret<T>();\r\n            if (reinterpreted == null)\r\n                return null;\r\n\r\n            return new ReinterpretedTableColumn<T>(this, reinterpreted);\r\n        }\r\n\r\n        public bool TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\r\n        {\r\n            dispatchedValue = dispatcher.Dispatch(this);\r\n            return true;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/BoolTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2022-2023 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing Octonica.ClickHouseClient.Exceptions;\r\nusing Octonica.ClickHouseClient.Protocol;\r\nusing Octonica.ClickHouseClient.Utils;\r\nusing System;\r\nusing System.Collections.Generic;\r\n\r\nnamespace Octonica.ClickHouseClient.Types\r\n{\r\n    internal sealed class BoolTypeInfo : SimpleTypeInfo\r\n    {\r\n        public BoolTypeInfo()\r\n            : base(\"Bool\")\r\n        {\r\n        }\r\n\r\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\r\n        {\r\n            return new BoolReader(rowCount);\r\n        }\r\n\r\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\r\n        {\r\n            return new SimpleSkippingColumnReader(sizeof(byte), rowCount);\r\n        }\r\n\r\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\r\n        {\r\n            IReadOnlyList<bool> typedList;\r\n\r\n            if (typeof(T) == typeof(bool))\r\n            {\r\n                typedList = (IReadOnlyList<bool>)rows;\r\n            }\r\n            else if (typeof(T) == typeof(byte))\r\n            {\r\n                // Some kind of a compatibility mode. Write bytes as bools. Any non-zero value is treated as true.\r\n                typedList = MappedReadOnlyList<byte, bool>.Map((IReadOnlyList<byte>)rows, b => b != 0);\r\n            }\r\n            else\r\n            {\r\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\r\n            }\r\n\r\n            return new BoolWriter(columnName, ComplexTypeName, typedList);\r\n        }\r\n\r\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\r\n        {\r\n            var type = typeof(T);\r\n            if (type == typeof(DBNull))\r\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\r\n\r\n            object writer;\r\n            if (type == typeof(bool))\r\n                writer = new SimpleParameterWriter<bool, byte>(this, appendTypeCast: true, boolValue => boolValue ? (byte)1 : (byte)0);\r\n            else if (type == typeof(byte))\r\n                writer = new SimpleParameterWriter<byte>(this, appendTypeCast: true);\r\n            else\r\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\r\n\r\n            return (IClickHouseParameterWriter<T>)writer;\r\n        }\r\n\r\n        public override ClickHouseDbType GetDbType()\r\n        {\r\n            return ClickHouseDbType.Boolean;\r\n        }\r\n\r\n        public override Type GetFieldType()\r\n        {\r\n            return typeof(bool);\r\n        }\r\n\r\n        private sealed class BoolReader : StructureReaderBase<byte, bool>\r\n        {\r\n            protected override bool BitwiseCopyAllowed => true;\r\n\r\n            public BoolReader(int rowCount)\r\n                : base(sizeof(byte), rowCount)\r\n            {\r\n            }\r\n\r\n            protected override byte ReadElement(ReadOnlySpan<byte> source)\r\n            {\r\n                return source[0];\r\n            }\r\n\r\n            protected override IClickHouseTableColumn<bool> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<byte> buffer)\r\n            {\r\n                return new BoolTableColumn(buffer);\r\n            }\r\n        }\r\n\r\n        private sealed class BoolWriter : StructureWriterBase<bool, byte>\r\n        {\r\n            public BoolWriter(string columnName, string columnType, IReadOnlyList<bool> rows)\r\n                : base(columnName, columnType, sizeof(byte), rows)\r\n            {\r\n            }\r\n\r\n            protected override byte Convert(bool value)\r\n            {\r\n                return value ? (byte)1 : (byte)0;\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ClickHouseColumnReinterpreter.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2024, 2026 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing Octonica.ClickHouseClient.Exceptions;\r\nusing Octonica.ClickHouseClient.Utils;\r\nusing System;\r\nusing System.Diagnostics;\r\nusing System.Diagnostics.CodeAnalysis;\r\n\r\nnamespace Octonica.ClickHouseClient.Types\r\n{\r\n    internal sealed class ClickHouseColumnReinterpreter : ITypeDispatcher<IClickHouseColumnReinterpreter>\r\n    {\r\n        private static readonly ClickHouseColumnReinterpreter Instance = new ClickHouseColumnReinterpreter();\r\n\r\n        private ClickHouseColumnReinterpreter()\r\n        {\r\n        }\r\n\r\n        IClickHouseColumnReinterpreter ITypeDispatcher<IClickHouseColumnReinterpreter>.Dispatch<T>()\r\n        {\r\n            return ClickHouseColumnReinterpreter<T>.Instance;\r\n        }\r\n\r\n        public static IClickHouseColumnReinterpreter Create(Type type)\r\n        {\r\n            return TypeDispatcher.Create(type).Dispatch(Instance);\r\n        }\r\n\r\n        public static ClickhouseColumnReinterpreterDispatcher CreateDispatcher(IConverterDispatcher dispatcher)\r\n        {\r\n            return new ClickhouseColumnReinterpreterDispatcher(dispatcher);\r\n        }\r\n\r\n        public static IClickHouseColumnReinterpreter Create<T, TRes>(Func<T, TRes> convert)\r\n        {\r\n            var srcType = typeof(T);\r\n            var resType = typeof(TRes);\r\n            Type implTypeDef;\r\n            if (srcType.IsValueType)\r\n            {\r\n                srcType = Nullable.GetUnderlyingType(srcType) ?? srcType;\r\n                if (resType.IsValueType)\r\n                {\r\n                    resType = Nullable.GetUnderlyingType(resType) ?? resType;\r\n                    implTypeDef = typeof(ClickHouseColumnStructToStructReinterpreter<,>);\r\n                }\r\n                else\r\n                {\r\n                    implTypeDef = typeof(ClickHouseColumnStructToObjReinterpreter<,>);\r\n                }\r\n            }\r\n            else\r\n            {\r\n                if (resType.IsValueType)\r\n                {\r\n                    resType = Nullable.GetUnderlyingType(resType) ?? resType;\r\n                    implTypeDef = typeof(ClickHouseColumnObjToStructReinterpreter<,>);\r\n                }\r\n                else\r\n                {\r\n                    implTypeDef = typeof(ClickHouseColumnObjToObjReinterpreter<,>);\r\n                }\r\n            }\r\n\r\n            var implType = implTypeDef.MakeGenericType(srcType, resType);\r\n            var impl = Activator.CreateInstance(implType, convert);\r\n            Debug.Assert(impl != null);\r\n\r\n            return (IClickHouseColumnReinterpreter)impl;\r\n        }\r\n\r\n        public static TValue GuardNull<TValue>(TValue? value)\r\n            where TValue : struct\r\n        {\r\n            return value\r\n                ?? throw new ClickHouseException(\r\n                    ClickHouseErrorCodes.CallbackError,\r\n                    \"A callback function provided by the caller returned a null reference.\" + Environment.NewLine +\r\n                    \"When configuring a data reader with a custom column reader, make sure that the callback function will not return null when its argument is not null.\");\r\n        }\r\n\r\n        [return: NotNull]\r\n        public static TValue GuardNull<TValue>(TValue? value)\r\n            where TValue : class\r\n        {\r\n            return value\r\n                ?? throw new ClickHouseException(\r\n                    ClickHouseErrorCodes.CallbackError,\r\n                    \"A callback function provided by the caller returned a null reference.\" + Environment.NewLine +\r\n                    \"When configuring a data reader with a custom column reader, make sure that the callback function will not return null when its argument is not null.\");\r\n        }\r\n\r\n        public static IClickHouseTableColumn<TRes> Reinterpret<T, TRes>(IClickHouseTableColumn? root, IClickHouseTableColumn<T> column, Func<T, TRes> convert)\r\n        {\r\n            if (column is IClickHouseReinterpretedTableColumn<T> rc)\r\n                return rc.Chain(convert);\r\n\r\n            return new ReinterpretedTableColumn<T, TRes>(root, column, convert);\r\n        }\r\n    }\r\n\r\n    internal sealed class ClickhouseColumnReinterpreterDispatcher : IClickHouseTableColumnDispatcher<IClickHouseColumnReinterpreter?>, IConverterDispatcher<IClickHouseColumnReinterpreter?>\r\n    {\r\n        private readonly IConverterDispatcher _converterDispatcher;\r\n\r\n        public ClickhouseColumnReinterpreterDispatcher(IConverterDispatcher converterDispatcher)\r\n        {\r\n            _converterDispatcher = converterDispatcher;\r\n        }\r\n\r\n        public IClickHouseColumnReinterpreter? Dispatch<T>(IClickHouseTableColumn<T> column)\r\n        {\r\n            return Dispatch<T>();\r\n        }\r\n\r\n        public IClickHouseColumnReinterpreter? Dispatch<TFrom>()\r\n        {\r\n            return _converterDispatcher.Dispatch<TFrom, IClickHouseColumnReinterpreter?>(this);\r\n        }\r\n\r\n        public IClickHouseColumnReinterpreter Dispatch<TFrom, TTo>(Func<TFrom, TTo> convert)\r\n        {\r\n            if (typeof(TFrom) == typeof(object))\r\n                return new ClickHouseObjectColumnReinterpreter<TTo>((Func<object, TTo>)(object)convert);\r\n\r\n            return ClickHouseColumnReinterpreter.Create(convert);\r\n        }\r\n\r\n        public IClickHouseColumnReinterpreter? DispatchNoConvert()\r\n        {\r\n            return null;\r\n        }\r\n    }\r\n\r\n    internal sealed class ClickHouseColumnReinterpreter<T> : IClickHouseColumnReinterpreter\r\n    {\r\n        public static ClickHouseColumnReinterpreter<T> Instance = new ClickHouseColumnReinterpreter<T>();\r\n\r\n        public Type BuiltInConvertToType => typeof(T);\r\n\r\n        public Type? ExternalConvertToType => null;\r\n\r\n        private ClickHouseColumnReinterpreter()\r\n        {\r\n        }\r\n\r\n        public IClickHouseTableColumn? TryReinterpret(IClickHouseTableColumn column)\r\n        {\r\n            return (column as IClickHouseTableColumn<T>) ?? column.TryReinterpret<T>();\r\n        }\r\n    }\r\n\r\n    internal sealed class ClickHouseObjectColumnReinterpreter<T> : IClickHouseColumnReinterpreter\r\n    {\r\n        private readonly Func<object, T> _convert;\r\n\r\n        public Type? BuiltInConvertToType => null;\r\n\r\n        public Type ExternalConvertToType => typeof(T);\r\n\r\n        public ClickHouseObjectColumnReinterpreter(Func<object, T> convert)\r\n        {\r\n            _convert = Guard(convert);\r\n        }\r\n\r\n        public IClickHouseTableColumn? TryReinterpret(IClickHouseTableColumn column)\r\n        {\r\n            if (column is IClickHouseReinterpretedTableColumn<object> rc)\r\n                return rc.Chain(_convert);\r\n\r\n            return new ReinterpretedObjectTableColumn<T>(column, _convert);\r\n        }\r\n\r\n        private static Func<object, T> Guard(Func<object, T> convert)\r\n        {\r\n            return v => convert(v ?? DBNull.Value) ?? (T)(object)DBNull.Value;\r\n        }\r\n    }\r\n\r\n    internal abstract class ClickHouseColumnReinterpreter<T, TRes> : IClickHouseColumnReinterpreter\r\n    {\r\n        public Type BuiltInConvertToType => typeof(T);\r\n\r\n        public Type ExternalConvertToType => typeof(TRes);\r\n\r\n        public abstract IClickHouseTableColumn? TryReinterpret(IClickHouseTableColumn column);\r\n    }\r\n\r\n    internal sealed class ClickHouseColumnObjToObjReinterpreter<T, TRes> : ClickHouseColumnReinterpreter<T, TRes>\r\n        where T : class\r\n        where TRes : class\r\n    {\r\n        private readonly Func<T, TRes> _convert;\r\n\r\n        public ClickHouseColumnObjToObjReinterpreter(Func<T, TRes> convert)\r\n        {\r\n            _convert = convert;\r\n        }\r\n\r\n        public override IClickHouseTableColumn? TryReinterpret(IClickHouseTableColumn column)\r\n        {\r\n            if (column is NullableObjTableColumn<T> nc)\r\n                return nc.ReinterpretAsObj(Guard(_convert));\r\n\r\n            if (column is IClickHouseTableColumn<T> c)\r\n                return ClickHouseColumnReinterpreter.Reinterpret(null, c, GuardNullable(_convert));\r\n\r\n            var reinterpretedColumn = column.TryReinterpret<T>();\r\n            if (reinterpretedColumn == null)\r\n                return null;\r\n\r\n            if (reinterpretedColumn is NullableObjTableColumn<T> nrc)\r\n                nrc.ReinterpretAsObj(Guard(_convert));\r\n\r\n            return ClickHouseColumnReinterpreter.Reinterpret(column, reinterpretedColumn, GuardNullable(_convert));\r\n        }\r\n\r\n        private static Func<T, TRes> Guard(Func<T, TRes> convert)\r\n        {\r\n            return v => ClickHouseColumnReinterpreter.GuardNull(convert(v));\r\n        }\r\n\r\n        private static Func<T, TRes> GuardNullable(Func<T, TRes> convert)\r\n        {\r\n            return v => v == null ? null! : ClickHouseColumnReinterpreter.GuardNull(convert(v));\r\n        }\r\n    }\r\n\r\n    internal sealed class ClickHouseColumnObjToStructReinterpreter<T, TRes> : ClickHouseColumnReinterpreter<T, TRes>\r\n        where T : class\r\n        where TRes : struct\r\n    {\r\n        private readonly Func<T, TRes?>? _convert0;\r\n        private readonly Func<T, TRes>? _convert1;\r\n\r\n        public ClickHouseColumnObjToStructReinterpreter(Func<T, TRes> convert)\r\n        {\r\n            _convert1 = convert;\r\n        }\r\n\r\n        public ClickHouseColumnObjToStructReinterpreter(Func<T, TRes?> convert)\r\n        {\r\n            _convert0 = convert;\r\n        }\r\n\r\n        public override IClickHouseTableColumn? TryReinterpret(IClickHouseTableColumn column)\r\n        {\r\n            if (column is NullableObjTableColumn<T> nullableColumn)\r\n                return nullableColumn.ReinterpretAsStruct(GetConvert());\r\n\r\n            IClickHouseTableColumn? rootColumn = null;\r\n            var reinterpretedColumn = column as IClickHouseTableColumn<T>;\r\n            if (reinterpretedColumn == null)\r\n                rootColumn = column;\r\n\r\n            reinterpretedColumn = column.TryReinterpret<T>();\r\n            if (reinterpretedColumn == null)\r\n                return null;\r\n\r\n            if (reinterpretedColumn is NullableObjTableColumn<T> nullableReinterpretedColumn)\r\n                return nullableReinterpretedColumn.ReinterpretAsStruct(GetConvert());\r\n\r\n            if (_convert0 != null)\r\n            {\r\n                // The caller explixitly requested the conversion to a nullable struct\r\n                return ClickHouseColumnReinterpreter.Reinterpret(rootColumn, reinterpretedColumn, GuardNullable(_convert0));\r\n            }\r\n\r\n            // It's safe to assume that the column is non-nullable\r\n            return ClickHouseColumnReinterpreter.Reinterpret(rootColumn, reinterpretedColumn, GetConvert());\r\n        }\r\n\r\n        private Func<T, TRes> GetConvert()\r\n        {\r\n            if (_convert1 != null)\r\n                return _convert1;\r\n            if (_convert0 != null)\r\n                return Guard(_convert0);\r\n\r\n            throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Ivalid object state. If you see this message, please, report a bug.\");\r\n        }\r\n\r\n        private static Func<T, TRes> Guard(Func<T, TRes?> convert)\r\n        {\r\n            return v => ClickHouseColumnReinterpreter.GuardNull(convert(v));\r\n        }\r\n\r\n        private static Func<T, TRes?> GuardNullable(Func<T, TRes?> convert)\r\n        {\r\n            return v => v == null ? (TRes?)null : ClickHouseColumnReinterpreter.GuardNull(convert(v));\r\n        }\r\n    }\r\n\r\n    internal sealed class ClickHouseColumnStructToObjReinterpreter<T, TRes> : ClickHouseColumnReinterpreter<T, TRes>\r\n        where T : struct\r\n        where TRes : class\r\n    {\r\n        private readonly Func<T?, TRes>? _convert0;\r\n        private readonly Func<T, TRes>? _convert1;\r\n\r\n        public ClickHouseColumnStructToObjReinterpreter(Func<T, TRes> convert)\r\n        {\r\n            _convert1 = convert;\r\n        }\r\n\r\n        public ClickHouseColumnStructToObjReinterpreter(Func<T?, TRes> convert)\r\n        {\r\n            _convert0 = convert;\r\n        }\r\n\r\n        public override IClickHouseTableColumn? TryReinterpret(IClickHouseTableColumn column)\r\n        {\r\n            if (column is IClickHouseTableColumn<T> structColumn)\r\n                return ClickHouseColumnReinterpreter.Reinterpret(null, structColumn, GetConvert());\r\n            if (column is IClickHouseTableColumn<T?> nullableStructColumn)\r\n                return ClickHouseColumnReinterpreter.Reinterpret(null, nullableStructColumn, GetConvertNullable());\r\n\r\n            IClickHouseTableColumn<T?>? reinterpretedNullableColumn;\r\n            var reinterpretedStructColumn = column.TryReinterpret<T>();\r\n            if (reinterpretedStructColumn is NullableStructTableColumnNotNullableAdapter<T> adapter)\r\n            {\r\n                reinterpretedNullableColumn = adapter.Unguard();\r\n            }\r\n            else if (reinterpretedStructColumn != null)\r\n            {\r\n                return ClickHouseColumnReinterpreter.Reinterpret(column, reinterpretedStructColumn, GetConvert());\r\n            }\r\n            else\r\n            {\r\n                reinterpretedNullableColumn = column.TryReinterpret<T?>();\r\n            }\r\n\r\n            if (reinterpretedNullableColumn != null)\r\n            {\r\n                return ClickHouseColumnReinterpreter.Reinterpret(column, reinterpretedNullableColumn, GetConvertNullable());\r\n            }\r\n\r\n            return null;\r\n        }\r\n\r\n        private Func<T, TRes> GetConvert()\r\n        {\r\n            if (_convert1 != null)\r\n                return Guard(_convert1);\r\n            if (_convert0 != null)\r\n                return Guard(_convert0);\r\n\r\n            throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Ivalid object state. If you see this message, please, report a bug.\");\r\n        }\r\n\r\n        private Func<T?, TRes> GetConvertNullable()\r\n        {\r\n            if (_convert1 != null)\r\n                return GuardNullable(_convert1);\r\n            if (_convert0 != null)\r\n                return GuardNullable(_convert0);\r\n\r\n            throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Ivalid object state. If you see this message, please, report a bug.\");\r\n        }\r\n\r\n        private static Func<T, TRes> Guard(Func<T, TRes> convert)\r\n        {\r\n            return v => ClickHouseColumnReinterpreter.GuardNull(convert(v));\r\n        }\r\n\r\n        private static Func<T, TRes> Guard(Func<T?, TRes> convert)\r\n        {\r\n            return v => ClickHouseColumnReinterpreter.GuardNull(convert(v));\r\n        }\r\n\r\n        private static Func<T?, TRes> GuardNullable(Func<T, TRes> convert)\r\n        {\r\n            return v => v == null ? null! : ClickHouseColumnReinterpreter.GuardNull(convert(v.Value));\r\n        }\r\n\r\n        private static Func<T?, TRes> GuardNullable(Func<T?, TRes> convert)\r\n        {\r\n            return v => v == null ? null! : ClickHouseColumnReinterpreter.GuardNull(convert(v));\r\n        }\r\n    }\r\n\r\n    internal sealed class ClickHouseColumnStructToStructReinterpreter<T, TRes> : ClickHouseColumnReinterpreter<T, TRes>\r\n        where T : struct\r\n        where TRes : struct\r\n    {\r\n        private readonly Func<T?, TRes?>? _convert00;\r\n        private readonly Func<T?, TRes>? _convert01;\r\n        private readonly Func<T, TRes?>? _convert10;\r\n        private readonly Func<T, TRes>? _convert11;\r\n\r\n        public ClickHouseColumnStructToStructReinterpreter(Func<T?, TRes?> convert)\r\n        {\r\n            _convert00 = convert;\r\n        }\r\n\r\n        public ClickHouseColumnStructToStructReinterpreter(Func<T?, TRes> convert)\r\n        {\r\n            _convert01 = convert;\r\n        }\r\n\r\n        public ClickHouseColumnStructToStructReinterpreter(Func<T, TRes?> convert)\r\n        {\r\n            _convert10 = convert;\r\n        }\r\n\r\n        public ClickHouseColumnStructToStructReinterpreter(Func<T, TRes> convert)\r\n        {\r\n            _convert11 = convert;\r\n        }\r\n\r\n        public override IClickHouseTableColumn? TryReinterpret(IClickHouseTableColumn column)\r\n        {\r\n            if (column is IClickHouseTableColumn<T> structColumn)\r\n                return ClickHouseColumnReinterpreter.Reinterpret(null, structColumn, GetConvert());\r\n            if (column is IClickHouseTableColumn<T?> nullableStructColumn)\r\n                return ClickHouseColumnReinterpreter.Reinterpret(null, nullableStructColumn, GetConvertNullable());\r\n\r\n            IClickHouseTableColumn<T?>? reinterpretedNullableColumn;\r\n            var reinterpretedStructColumn = column.TryReinterpret<T>();\r\n            if (reinterpretedStructColumn is NullableStructTableColumnNotNullableAdapter<T> adapter)\r\n            {\r\n                reinterpretedNullableColumn = adapter.Unguard();\r\n            }\r\n            else if (reinterpretedStructColumn != null)\r\n            {\r\n                return ClickHouseColumnReinterpreter.Reinterpret(column, reinterpretedStructColumn, GetConvert());\r\n            }\r\n            else\r\n            {\r\n                reinterpretedNullableColumn = column.TryReinterpret<T?>();\r\n            }\r\n\r\n            if (reinterpretedNullableColumn != null)\r\n            {\r\n                return ClickHouseColumnReinterpreter.Reinterpret(column, reinterpretedNullableColumn, GetConvertNullable());\r\n            }\r\n\r\n            return null;\r\n        }\r\n\r\n        private Func<T, TRes> GetConvert()\r\n        {\r\n            if (_convert11 != null)\r\n                return _convert11;\r\n            if (_convert00 != null)\r\n                return Guard(_convert00);\r\n            if (_convert01 != null)\r\n                return Guard(_convert01);\r\n            if (_convert10 != null)\r\n                return Guard(_convert10);\r\n\r\n            throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Ivalid object state. If you see this message, please, report a bug.\");\r\n        }\r\n\r\n        private Func<T?, TRes?> GetConvertNullable()\r\n        {\r\n            if (_convert11 != null)\r\n                return GuardNullable(_convert11);\r\n            if (_convert00 != null)\r\n                return GuardNullable(_convert00);\r\n            if (_convert01 != null)\r\n                return GuardNullable(_convert01);\r\n            if (_convert10 != null)\r\n                return GuardNullable(_convert10);\r\n\r\n            throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Ivalid object state. If you see this message, please, report a bug.\");\r\n        }\r\n\r\n        private static Func<T, TRes> Guard(Func<T?, TRes?> convert)\r\n        {\r\n            return v => ClickHouseColumnReinterpreter.GuardNull(convert(v));\r\n        }\r\n\r\n        private static Func<T, TRes> Guard(Func<T, TRes?> convert)\r\n        {\r\n            return v => ClickHouseColumnReinterpreter.GuardNull(convert(v));\r\n        }\r\n\r\n        private static Func<T, TRes> Guard(Func<T?, TRes> convert)\r\n        {\r\n            return v => convert(v);\r\n        }\r\n\r\n        public static Func<T?, TRes?> GuardNullable(Func<T?, TRes?> convert)\r\n        {\r\n            return v => v == null ? (TRes?)null : ClickHouseColumnReinterpreter.GuardNull(convert(v));\r\n        }\r\n\r\n        public static Func<T?, TRes?> GuardNullable(Func<T?, TRes> convert)\r\n        {\r\n            return v => v == null ? (TRes?)null : convert(v);\r\n        }\r\n\r\n        public static Func<T?, TRes?> GuardNullable(Func<T, TRes?> convert)\r\n        {\r\n            return v => v == null ? (TRes?)null : ClickHouseColumnReinterpreter.GuardNull(convert(v.Value));\r\n        }\r\n\r\n        public static Func<T?, TRes?> GuardNullable(Func<T, TRes> convert)\r\n        {\r\n            return v => v == null ? (TRes?)null : convert(v.Value);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ClickHouseColumnSerializationMode.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nnamespace Octonica.ClickHouseClient.Types\r\n{\r\n    /// <summary>\r\n    /// Provides supported serialization modes for <see cref=\"IClickHouseColumnTypeInfo.CreateColumnReader(int, ClickHouseColumnSerializationMode)\"/>\r\n    /// and <see cref=\"IClickHouseColumnTypeInfo.CreateSkippingColumnReader(int, ClickHouseColumnSerializationMode)\"/>.\r\n    /// </summary>\r\n    public enum ClickHouseColumnSerializationMode\r\n    {\r\n        /// <summary>\r\n        /// Default serialization. In this mode the reader should expect only column values.\r\n        /// </summary>\r\n        Default = 0,\r\n\r\n        /// <summary>\r\n        /// Sparse serialization. In this mode the reader should expect the list of 'granules' followed by column values.\r\n        /// </summary>\r\n        Sparse = 1,\r\n\r\n        /// <summary>\r\n        /// Custom serialization. In this mode the reader should expect serialization settings followed by actual column values.\r\n        /// </summary>\r\n        Custom = 0xAAAA\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ClickHouseEnumConverter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    /// <summary>\n    /// The class represents a converter that can convert values between a ClickHouse's enum and <typeparamref name=\"TEnum\"/>.\n    /// The values of enums are matched by their codes (integers).\n    /// </summary>\n    /// <typeparam name=\"TEnum\">The type of the enum.</typeparam>\n    public sealed class ClickHouseEnumConverter<TEnum> : IClickHouseEnumConverter<TEnum>\n        where TEnum : Enum\n    {\n        private readonly Dictionary<int, TEnum> _values;\n\n        /// <summary>\n        /// Initializes a new instance of the converter. <typeparamref name=\"TEnum\"/>'s values are acquired via reflection.\n        /// </summary>\n        public ClickHouseEnumConverter()\n        {\n            var enumValues = Enum.GetValues(typeof(TEnum));\n            _values = new Dictionary<int, TEnum>(enumValues.Length);\n            foreach (var enumValue in enumValues)\n            {\n                var intValue = Convert.ToInt32(enumValue);\n                _values[intValue] = (TEnum) enumValue!;\n            }\n        }\n\n        T IClickHouseEnumConverter.Dispatch<T>(IClickHouseEnumConverterDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        /// <summary>\n        /// Searches for a enum's value corresponding to a numeric value of the ClickHouse enum.\n        /// </summary>\n        /// <param name=\"value\">The numeric value of the ClickHouse enum.</param>\n        /// <param name=\"stringValue\">The string value of the ClickHouse enum. This converter ignores the value of this parameter.</param>\n        /// <param name=\"enumValue\">When this method returns, contains the value of enum or the default value of <typeparamref name=\"TEnum\"/> when returns <see langword=\"false\"/>.</param>\n        /// <returns><see langword=\"true\"/> if there are <typeparamref name=\"TEnum\"/>'s value corresponding to the specified ClickHouse enum; otherwise <see langword=\"false\"/>.</returns>\n        public bool TryMap(int value, string stringValue, out TEnum enumValue)\n        {\n            return _values.TryGetValue(value, out enumValue!);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ClickHouseTableColumnHelper.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal static class ClickHouseTableColumnHelper\n    {\n        public static Type? TryGetValueType(IClickHouseTableColumn column)\n        {\n            if (column.TryDipatch(ClickHouseTableColumnValueTypeDispatcher.Instance, out var type))\n                return type;\n\n            return null;\n        }\n\n        private sealed class ClickHouseTableColumnValueTypeDispatcher : IClickHouseTableColumnDispatcher<Type>\n        {\n            public static readonly ClickHouseTableColumnValueTypeDispatcher Instance = new ClickHouseTableColumnValueTypeDispatcher();\n\n            private ClickHouseTableColumnValueTypeDispatcher()\n            {\n            }\n\n            public Type Dispatch<T>(IClickHouseTableColumn<T> column)\n            {\n                return typeof(T);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ClickHouseTypeInfoProvider.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2022, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Linq;\nusing System.Net;\nusing System.Numerics;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    #region DefaultTypeInfoProvider (obsolete)\n\n    /// <summary>\n    /// This class is obsolete and will be deleted. Use <see cref=\"ClickHouseTypeInfoProvider\"/> instead of <see cref=\"DefaultTypeInfoProvider\"/>.\n    /// </summary>\n    [Obsolete(nameof(DefaultTypeInfoProvider) + \" was renamed to \" + nameof(ClickHouseTypeInfoProvider) + \".\")]\n    public class DefaultTypeInfoProvider : ClickHouseTypeInfoProvider\n    {\n        /// <summary>\n        /// The instance of <see cref=\"DefaultTypeInfoProvider\"/> provides access to all types supported by ClickHouseClient.\n        /// </summary>\n        /// <remarks>\n        /// The class <see cref=\"DefaultTypeInfoProvider\"/> is obsolete.\n        /// Use <see cref=\"ClickHouseTypeInfoProvider.Instance\"/> instead of <see cref=\"Instance\"/>.\n        /// </remarks>\n        [Obsolete(nameof(DefaultTypeInfoProvider) + \" was renamed to \" + nameof(ClickHouseTypeInfoProvider) + \".\")]\n        public static readonly new DefaultTypeInfoProvider Instance = new DefaultTypeInfoProvider();\n\n        private DefaultTypeInfoProvider()\n            : this(GetDefaultTypes())\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"DefaultTypeInfoProvider\"/> with a collection of supported types.\n        /// </summary>\n        /// <param name=\"types\">The collection of supported types.</param>\n        /// <remarks>\n        /// The class <see cref=\"DefaultTypeInfoProvider\"/> is obsolete.\n        /// Use the base class <see cref=\"ClickHouseTypeInfoProvider\"/> instead of <see cref=\"DefaultTypeInfoProvider\"/>.\n        /// </remarks>\n        [Obsolete(nameof(DefaultTypeInfoProvider) + \" was renamed to \" + nameof(ClickHouseTypeInfoProvider) + \".\")]\n        protected DefaultTypeInfoProvider(IEnumerable<IClickHouseColumnTypeInfo> types)\n            : base(types)\n        {\n        }\n    }\n\n    #endregion DefaultTypeInfoProvider (obsolete)\n\n    /// <summary>\n    /// The default implementation of the interface <see cref=\"IClickHouseTypeInfoProvider\"/>. This class provides access to\n    /// all types supported by ClickHouseClient.\n    /// </summary>\n    public class ClickHouseTypeInfoProvider : IClickHouseTypeInfoProvider\n    {\n        /// <summary>\n        /// The instance of <see cref=\"ClickHouseTypeInfoProvider\"/> provides access to all types supported by ClickHouseClient.\n        /// </summary>\n        public static readonly ClickHouseTypeInfoProvider Instance = new ClickHouseTypeInfoProvider();\n\n        private readonly Dictionary<string, IClickHouseColumnTypeInfo> _types;\n\n        private ClickHouseTypeInfoProvider()\n            : this(GetDefaultTypes())\n        {\n        }\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"ClickHouseTypeInfoProvider\"/> with a collection of supported types.\n        /// </summary>\n        /// <param name=\"types\">The collection of supported types.</param>\n        /// <remarks>It is possible to get types supported by default with the method <see cref=\"GetDefaultTypes()\"/>.</remarks>\n        protected ClickHouseTypeInfoProvider(IEnumerable<IClickHouseColumnTypeInfo> types)\n        {\n            if (types == null)\n                throw new ArgumentNullException(nameof(types));\n\n            _types = types.ToDictionary(t => t.TypeName);\n        }\n\n        /// <inheritdoc/>\n        public IClickHouseColumnTypeInfo GetTypeInfo(string typeName)\n        {\n            var typeNameMem = typeName.AsMemory();\n            var (baseTypeName, options) = ParseTypeName(typeNameMem);\n            \n            var result = typeNameMem.Span == baseTypeName.Span ? GetTypeInfo(typeName, options) : GetTypeInfo(baseTypeName.ToString(), options);\n\n            return result ?? throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeName}\\\" is not supported.\");\n        }\n\n        /// <inheritdoc/>\n        public IClickHouseColumnTypeInfo GetTypeInfo(ReadOnlyMemory<char> typeName)\n        {\n            var (baseTypeName, options) = ParseTypeName(typeName);\n            var result = GetTypeInfo(baseTypeName.ToString(), options);\n\n            return result ?? throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeName.ToString()}\\\" is not supported.\");\n        }\n\n        private IClickHouseColumnTypeInfo? GetTypeInfo(string baseTypeName, List<ReadOnlyMemory<char>>? options)\n        {\n            if (!_types.TryGetValue(baseTypeName, out var typeInfo))\n                return null;\n\n            if (options != null && options.Count > 0)\n                typeInfo = typeInfo.GetDetailedTypeInfo(options, this);\n\n            return typeInfo;\n        }\n\n        private static (ReadOnlyMemory<char> baseTypeName, List<ReadOnlyMemory<char>>? options) ParseTypeName(ReadOnlyMemory<char> typeName)\n        {\n            var typeNameSpan = typeName.Span;\n\n            var pOpenIdx = typeNameSpan.IndexOf('(');\n            if (pOpenIdx == 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The name of the type (\\\"{typeNameSpan.ToString()}\\\") can't start with \\\"(\\\".\");\n\n            ReadOnlyMemory<char> baseTypeName;\n            List<ReadOnlyMemory<char>>? options = null;\n            if (pOpenIdx < 0)\n            {\n                baseTypeName = typeName.Trim();\n            }\n            else\n            {\n                baseTypeName = typeName.Slice(0, pOpenIdx).Trim();\n\n                int count = 1;\n                int currentIdx = pOpenIdx;\n                int optionStartIdx = pOpenIdx + 1;\n                ReadOnlySpan<char> significantChars = \"(,)'`\";\n                do\n                {\n                    if (typeNameSpan.Length - 1 == currentIdx)\n                        break;\n\n                    var pNextIdx = typeNameSpan.Slice(currentIdx + 1).IndexOfAny(significantChars);\n                    if (pNextIdx < 0)\n                        break;\n\n                    pNextIdx += currentIdx + 1;\n                    currentIdx = pNextIdx;\n                    if (\"'`\".Contains(typeNameSpan[currentIdx]))\n                    {\n                        var len = ClickHouseSyntaxHelper.GetQuotedTokenLength(typeNameSpan.Slice(currentIdx), typeNameSpan[currentIdx]);\n                        if (len < 0)\n                            break;\n\n                        Debug.Assert(len > 0);\n                        currentIdx += len - 1;\n                    }\n                    else if (typeNameSpan[currentIdx] == '(')\n                    {\n                        ++count;\n                    }\n                    else if (typeNameSpan[currentIdx] == ')')\n                    {\n                        --count;\n                        if (count == 0)\n                            break;\n                    }\n                    else if (count == 1)\n                    {\n                        var currentOption = typeName.Slice(optionStartIdx, currentIdx - optionStartIdx).Trim();\n                        optionStartIdx = currentIdx + 1;\n\n                        if (options != null)\n                            options.Add(currentOption);\n                        else\n                            options = new List<ReadOnlyMemory<char>>(2) {currentOption};\n                    }\n\n                } while (true);\n\n                if (count != 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The number of open parentheses doesn't match to the number of close parentheses in the type name \\\"{typeNameSpan.ToString()}\\\".\");\n\n                if (currentIdx != typeNameSpan.Length - 1)\n                {\n                    var unexpectedString = typeNameSpan.Slice(currentIdx + 1);\n                    if (!unexpectedString.Trim().IsEmpty)\n                    {\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.InvalidTypeName,\n                            $\"There are unexpected characters (\\\"{unexpectedString.ToString()}\\\") in the type name \\\"{typeNameSpan.ToString()}\\\" after closing parenthesis.\");\n                    }\n                }\n\n                var lastOption = typeName.Slice(optionStartIdx, currentIdx - optionStartIdx).Trim();\n                if (options != null)\n                    options.Add(lastOption);\n                else\n                    options = new List<ReadOnlyMemory<char>>(1) {lastOption};\n            }\n\n            return (baseTypeName, options);\n        }\n\n        /// <inheritdoc/>\n        public IClickHouseColumnTypeInfo GetTypeInfo(IClickHouseColumnTypeDescriptor typeDescriptor)\n        {\n            string? tzCode;\n            string typeName;\n            IntermediateClickHouseTypeInfo typeInfo;\n            switch (typeDescriptor.ClickHouseDbType)\n            {\n                case ClickHouseDbType.AnsiString:\n                case ClickHouseDbType.AnsiStringFixedLength:\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeDescriptor.ClickHouseDbType}\\\" is not supported. String encoding can be specified with the property \\\"{nameof(ClickHouseParameter.StringEncoding)}\\\".\");\n                case ClickHouseDbType.Array:\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeDescriptor.ClickHouseDbType}\\\" is not supported. An array could be declared with properties \\\"{nameof(IClickHouseColumnDescriptor.ArrayRank)}\\\" or \\\"{nameof(ClickHouseParameter.IsArray)}\\\".\");\n                case ClickHouseDbType.Enum:\n                case ClickHouseDbType.Nothing:\n                case ClickHouseDbType.Time:\n                case ClickHouseDbType.Tuple:\n                case ClickHouseDbType.Xml:\n                case ClickHouseDbType.Map:\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeDescriptor.ClickHouseDbType}\\\" is not supported.\");\n\n                case ClickHouseDbType.Binary:\n                    if (typeDescriptor.Size <= 0)\n                    {\n                        typeInfo = new IntermediateClickHouseTypeInfo(ClickHouseDbType.Byte, \"UInt8\", false, 1);\n                        goto AFTER_TYPE_INFO_DEFINED;\n                    }\n\n                    typeName = string.Format(CultureInfo.InvariantCulture, \"FixedString({0})\", typeDescriptor.Size);\n                    break;\n                case ClickHouseDbType.Byte:\n                    typeName = \"UInt8\";\n                    break;\n                case ClickHouseDbType.Boolean:\n                    typeName = \"Bool\";\n                    break;\n                case ClickHouseDbType.Currency:\n                    typeName = \"Decimal(18, 4)\";\n                    break;\n                case ClickHouseDbType.Date:\n                    typeName = \"Date\";\n                    break;\n                case ClickHouseDbType.Date32:\n                    typeName = \"Date32\";\n                    break;\n                case ClickHouseDbType.Decimal:\n                    typeName = string.Format(CultureInfo.InvariantCulture, \"Decimal({0}, {1})\", DecimalTypeInfoBase.DefaultPrecision, DecimalTypeInfoBase.DefaultScale);\n                    break;\n                case ClickHouseDbType.Double:\n                    typeName = \"Float64\";\n                    break;\n                case ClickHouseDbType.Guid:\n                    typeName = \"UUID\";\n                    break;\n                case ClickHouseDbType.Int16:\n                    typeName = \"Int16\";\n                    break;\n                case ClickHouseDbType.Int32:\n                    typeName = \"Int32\";\n                    break;\n                case ClickHouseDbType.Int64:\n                    typeName = \"Int64\";\n                    break;\n                case ClickHouseDbType.Int128:\n                    typeName = \"Int128\";\n                    break;\n                case ClickHouseDbType.Int256:\n                    typeName = \"Int256\";\n                    break;\n                case ClickHouseDbType.Object:\n                    if (typeDescriptor.ValueType != typeof(DBNull))\n                        throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeDescriptor.ClickHouseDbType}\\\" is not supported.\");\n\n                    typeName = \"Nothing\";\n                    break;\n                case ClickHouseDbType.SByte:\n                    typeName = \"Int8\";\n                    break;\n                case ClickHouseDbType.Single:\n                    typeName = \"Float32\";\n                    break;\n                case ClickHouseDbType.String:\n                    typeName = \"String\";\n                    break;\n                case ClickHouseDbType.UInt16:\n                    typeName = \"UInt16\";\n                    break;\n                case ClickHouseDbType.UInt32:\n                    typeName = \"UInt32\";\n                    break;\n                case ClickHouseDbType.UInt64:\n                    typeName = \"UInt64\";\n                    break;\n                case ClickHouseDbType.UInt128:\n                    typeName = \"UInt128\";\n                    break;\n                case ClickHouseDbType.UInt256:\n                    typeName = \"UInt256\";\n                    break;\n                case ClickHouseDbType.VarNumeric:\n                    typeName = string.Format(\n                        CultureInfo.InvariantCulture,\n                        \"Decimal({0}, {1})\",\n                        typeDescriptor.Precision ?? DecimalTypeInfoBase.DefaultPrecision,\n                        typeDescriptor.Scale ?? DecimalTypeInfoBase.DefaultScale);\n\n                    break;\n                case ClickHouseDbType.StringFixedLength:\n                    if (typeDescriptor.Size <= 0)\n                        throw new ClickHouseException(ClickHouseErrorCodes.InvalidQueryParameterConfiguration, $\"The size of the fixed string must be a positive number.\");\n                    \n                    typeName = string.Format(CultureInfo.InvariantCulture, \"FixedString({0})\", typeDescriptor.Size);\n                    break;\n                case ClickHouseDbType.DateTime2:\n                    tzCode = GetTimeZoneCode(typeDescriptor.TimeZone);\n                    typeName = tzCode == null ? \"DateTime64(7)\" : $\"DateTime64(7, '{tzCode}')\";\n                    break;\n                case ClickHouseDbType.DateTime64:\n                    tzCode = GetTimeZoneCode(typeDescriptor.TimeZone);\n                    typeName = tzCode == null\n                        ? string.Format(CultureInfo.InvariantCulture, \"DateTime64({0})\", typeDescriptor.Precision ?? DateTime64TypeInfo.DefaultPrecision)\n                        : string.Format(CultureInfo.InvariantCulture, \"DateTime64({0}, '{1}')\", typeDescriptor.Precision ?? DateTime64TypeInfo.DefaultPrecision, tzCode);\n\n                    break;\n                case ClickHouseDbType.DateTime:\n                case ClickHouseDbType.DateTimeOffset:\n                    tzCode = GetTimeZoneCode(typeDescriptor.TimeZone);\n                    typeName = tzCode == null ? \"DateTime\" : $\"DateTime('{tzCode}')\";\n                    break;\n                case ClickHouseDbType.IpV4:\n                    typeName = \"IPv4\";\n                    break;\n                case ClickHouseDbType.IpV6:\n                    typeName = \"IPv6\";\n                    break;\n                case ClickHouseDbType.ClickHouseSpecificTypeDelimiterCode:\n                    goto default;\n                case null:\n                    typeInfo = GetTypeFromValue(typeDescriptor.ValueType, typeDescriptor.IsNullable ?? false, typeDescriptor.TimeZone);\n                    goto AFTER_TYPE_INFO_DEFINED;\n                default:\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"There is no type associated with the value \\\"{typeDescriptor.ClickHouseDbType}\\\".\");\n            }\n\n            if (typeDescriptor.IsNullable != null)\n            {\n                typeInfo = new IntermediateClickHouseTypeInfo(typeDescriptor.ClickHouseDbType.Value, typeName, typeDescriptor.IsNullable.Value, typeDescriptor.ArrayRank ?? 0);\n            }\n            else\n            {\n                // Derive nullability from the value's type. It's important to know whether the value is nullable or not because\n                // nullability is a part of ClickHouse type\n                var autoType = GetTypeFromValue(typeDescriptor.ValueType, typeDescriptor.IsNullable ?? false, typeDescriptor.TimeZone);\n                typeInfo = new IntermediateClickHouseTypeInfo(typeDescriptor.ClickHouseDbType.Value, typeName, autoType.IsNullable, typeDescriptor.ArrayRank ?? 0);\n            }\n\n        // This label is an alternative exit point for switch\n        // It's a shortcut for several cases when typeInfo is fully defined\n        AFTER_TYPE_INFO_DEFINED:\n\n            bool isNull = typeDescriptor.ValueType == typeof(DBNull);\n            if (isNull && typeDescriptor.IsNullable == false)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidQueryParameterConfiguration, $\"The value of the type \\\"{typeDescriptor.ValueType}\\\" can't be declared as non-nullable.\");\n\n            bool isNullable;\n            if (typeDescriptor.IsNullable != null)\n                isNullable = typeDescriptor.IsNullable.Value;\n            else if (isNull)\n                isNullable = true;\n            else\n                isNullable = typeInfo.IsNullable;\n\n            typeName = typeInfo.ClickHouseType;\n            if (isNullable)\n                typeName = $\"Nullable({typeName})\";\n\n            var arrayRank = typeDescriptor?.ArrayRank ?? typeInfo.ArrayRank;\n            for (int i = 0; i < arrayRank; i++)\n                typeName = $\"Array({typeName})\";\n\n            return GetTypeInfo(typeName);\n        }\n\n        internal static IntermediateClickHouseTypeInfo GetTypeFromValue(Type valueType, bool valueCanBeNull, TimeZoneInfo? timeZone)\n        {\n            if (valueType == typeof(string))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.String, \"String\", valueCanBeNull, 0);\n            if (valueType == typeof(byte))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Byte, \"UInt8\", false, 0);\n            if (valueType == typeof(bool))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Boolean, \"Bool\", false, 0);\n            if (valueType == typeof(decimal))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Decimal, string.Format(CultureInfo.InvariantCulture, \"Decimal({0}, {1})\", DecimalTypeInfoBase.DefaultPrecision, DecimalTypeInfoBase.DefaultScale), false, 0);\n            if (valueType == typeof(double))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Double, \"Float64\", false, 0);\n            if (valueType == typeof(Guid))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Guid, \"UUID\", false, 0);\n            if (valueType == typeof(short))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Int16, \"Int16\", false, 0);\n            if (valueType == typeof(int))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Int32, \"Int32\", false, 0);\n            if (valueType == typeof(long))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Int64, \"Int64\", false, 0);\n            if (valueType == typeof(sbyte))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.SByte, \"Int8\", false, 0);\n            if (valueType == typeof(BigInteger))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Int256, \"Int256\", false, 0);\n            if (valueType == typeof(float))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Single, \"Float32\", false, 0);\n            if (valueType == typeof(ushort))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.UInt16, \"UInt16\", false, 0);\n            if (valueType == typeof(uint))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.UInt32, \"UInt32\", false, 0);\n            if (valueType == typeof(ulong))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.UInt64, \"UInt64\", false, 0);\n            if (valueType == typeof(IPAddress))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.IpV6, \"IPv6\", valueCanBeNull, 0);\n            if (valueType == typeof(DBNull))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Nothing, \"Nothing\", true, 0);\n\n#if NET6_0_OR_GREATER\n            if (valueType == typeof(DateOnly))\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.Date, \"Date\", false, 0);\n#endif\n\n            if (valueType == typeof(DateTime) || valueType == typeof(DateTimeOffset))\n            {\n                var tzCode = GetTimeZoneCode(timeZone);\n                return new IntermediateClickHouseTypeInfo(ClickHouseDbType.DateTime, tzCode == null ? \"DateTime\" : $\"DateTime('{tzCode}')\", false, 0);\n            }\n\n            int arrayRank = 1;\n            Type? elementType = null;\n            if (valueType.IsArray)\n            {\n                arrayRank = valueType.GetArrayRank();\n                elementType = valueType.GetElementType();\n\n                if (elementType == typeof(char))\n                {\n                    elementType = typeof(string);\n                    --arrayRank;\n                }\n            }\n            else\n            {\n                foreach (var itf in valueType.GetInterfaces())\n                {\n                    if (!itf.IsGenericType)\n                        continue;\n\n                    if (itf.GetGenericTypeDefinition() != typeof(IReadOnlyList<>))\n                        continue;\n\n                    var listElementType = itf.GetGenericArguments()[0];\n                    if (elementType != null)\n                    {\n                        throw new ClickHouseException(\n                            ClickHouseErrorCodes.TypeNotSupported,\n                            $\"The type \\\"{valueType}\\\" implements \\\"{typeof(IReadOnlyList<>)}\\\" at least twice with generic arguments \\\"{elementType}\\\" and \\\"{listElementType}\\\".\");\n                    }\n\n                    elementType = listElementType;\n                }\n            }\n\n            if (elementType == null && valueType.IsGenericType)\n            {\n                var valueTypeDef = valueType.GetGenericTypeDefinition();\n                if (valueTypeDef == typeof(Memory<>) || valueTypeDef == typeof(ReadOnlyMemory<>))\n                {\n                    elementType = valueType.GetGenericArguments()[0];\n\n                    // Memory<char> or ReadOnlyMemory<char> should be interpreted as string\n                    if (elementType == typeof(char))\n                        return GetTypeFromValue(typeof(string), false, timeZone);\n                }\n            }\n\n            if (elementType != null)\n            {\n                try\n                {\n                    var elementInfo = GetTypeFromValue(elementType, arrayRank > 0 || valueCanBeNull, timeZone);\n                    return new IntermediateClickHouseTypeInfo(elementInfo.DbType, elementInfo.ClickHouseType, elementInfo.IsNullable, elementInfo.ArrayRank + arrayRank);\n                }\n                catch (ClickHouseException ex)\n                {\n                    if (ex.ErrorCode != ClickHouseErrorCodes.TypeNotSupported)\n                        throw;\n\n                    throw new ClickHouseException(\n                        ClickHouseErrorCodes.TypeNotSupported,\n                        $\"The type \\\"{valueType}\\\" is not supported. See the inner exception for details.\",\n                        ex);\n                }\n            }\n\n            if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Nullable<>))\n            {\n                elementType = valueType.GetGenericArguments()[0];\n                try\n                {\n                    var elementInfo = GetTypeFromValue(elementType, false, timeZone);\n                    return new IntermediateClickHouseTypeInfo(elementInfo.DbType, elementInfo.ClickHouseType, true, elementInfo.ArrayRank);\n                }\n                catch (ClickHouseException ex)\n                {\n                    if (ex.ErrorCode != ClickHouseErrorCodes.TypeNotSupported)\n                        throw;\n\n                    throw new ClickHouseException(\n                        ClickHouseErrorCodes.TypeNotSupported,\n                        $\"The type \\\"{valueType}\\\" is not supported. See the inner exception for details.\",\n                        ex);\n                }\n            }\n\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{valueType}\\\" is not supported.\");\n        }\n\n        [return: NotNullIfNotNull(\"timeZone\")]\n        private static string? GetTimeZoneCode(TimeZoneInfo? timeZone)\n        {\n            if (timeZone == null)\n                return null;\n\n            return TimeZoneHelper.GetTimeZoneId(timeZone);\n        }\n\n        /// <inheritdoc/>\n        public IClickHouseTypeInfoProvider Configure(ClickHouseServerInfo serverInfo)\n        {\n            if (serverInfo == null)\n                throw new ArgumentNullException(nameof(serverInfo));\n\n            return new ClickHouseTypeInfoProvider(_types.Values.Select(t => (t as IClickHouseConfigurableTypeInfo)?.Configure(serverInfo) ?? t));\n        }\n\n        /// <summary>\n        /// Returns all types supported by the ClickHouseClient.\n        /// </summary>\n        /// <returns>All types supported by the ClickHouseClient.</returns>\n        protected static IEnumerable<IClickHouseColumnTypeInfo> GetDefaultTypes()\n        {\n            return new IClickHouseColumnTypeInfo[]\n            {\n                new ArrayTypeInfo(),\n                new LowCardinalityTypeInfo(),\n                new TupleTypeInfo(),\n\n                new BoolTypeInfo(),\n\n                new DateTypeInfo(),\n                new Date32TypeInfo(),\n                new DateTimeTypeInfo(),\n                new DateTime64TypeInfo(),\n\n                new DecimalTypeInfo(),\n                new Decimal32TypeInfo(),\n                new Decimal64TypeInfo(),\n                new Decimal128TypeInfo(),\n\n                new Float32TypeInfo(),\n                new Float64TypeInfo(),\n\n                new Int8TypeInfo(),\n                new Int16TypeInfo(),\n                new Int32TypeInfo(),\n                new Int64TypeInfo(),\n                new Int128TypeInfo(),\n                new Int256TypeInfo(),\n\n                new UInt8TypeInfo(),\n                new UInt16TypeInfo(),\n                new UInt32TypeInfo(),\n                new UInt64TypeInfo(),\n                new UInt128TypeInfo(),\n                new UInt256TypeInfo(),\n\n                new StringTypeInfo(),\n                new FixedStringTypeInfo(),\n\n                new UuidTypeInfo(),\n\n                new NothingTypeInfo(),\n                new NullableTypeInfo(),\n\n                new IpV4TypeInfo(),\n                new IpV6TypeInfo(),\n\n                new Enum8TypeInfo(),\n                new Enum16TypeInfo(),\n\n                new MapTypeInfo(),\n                new VariantTypeInfo(),\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/CustomSerializationColumnReader.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing Octonica.ClickHouseClient.Exceptions;\r\nusing Octonica.ClickHouseClient.Protocol;\r\nusing System;\r\nusing System.Buffers;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\n\r\nnamespace Octonica.ClickHouseClient.Types\r\n{\r\n    internal sealed class CustomSerializationColumnReader : IClickHouseColumnReader, IClickHouseTableColumnDispatcher<IClickHouseTableColumn>\r\n    {\r\n        private readonly int _rowCount;\r\n        private readonly IClickHouseColumnTypeInfo _typeInfo;\r\n\r\n        private ClickHouseColumnSerializationMode _mode;\r\n        private bool _trailingDefaults;\r\n        private int _sparseRowPostion;\r\n        private List<int>? _offsets;\r\n        private int _expectedBaseRowCount = -1;\r\n        private IClickHouseColumnReader? _baseReader;\r\n\r\n        public CustomSerializationColumnReader(IClickHouseColumnTypeInfo typeInfo, int rowCount, ClickHouseColumnSerializationMode mode)\r\n        {\r\n            _typeInfo = typeInfo;\r\n            _mode = mode;\r\n            _rowCount = rowCount;\r\n        }\r\n\r\n        public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\r\n        {\r\n            if (_baseReader == null)\r\n                return _typeInfo.CreateColumnReader(0).EndRead(settings);\r\n\r\n            var columnInfo = _baseReader.EndRead(settings);\r\n            if (_mode == ClickHouseColumnSerializationMode.Sparse)\r\n            {\r\n                if (columnInfo.TryDipatch(this, out var dispatchedColumn))\r\n                    return dispatchedColumn;\r\n\r\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Sparse column reader error. A type of the column was not dispatched.\");\r\n            }\r\n\r\n            return columnInfo;\r\n        }\r\n\r\n        IClickHouseTableColumn IClickHouseTableColumnDispatcher<IClickHouseTableColumn>.Dispatch<T>(IClickHouseTableColumn<T> column)\r\n        {\r\n            Debug.Assert(_offsets != null);\r\n            return new SparseColumn<T>(column, _rowCount, _offsets, _trailingDefaults);\r\n        }\r\n\r\n        public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\r\n        {\r\n            switch (_mode)\r\n            {\r\n                case ClickHouseColumnSerializationMode.Custom:\r\n                    if (sequence.IsEmpty)\r\n                        return SequenceSize.Empty;\r\n\r\n                    // The prefix consists of a single byte encoding the serialization mode\r\n                    var mode = (ClickHouseColumnSerializationMode)sequence.FirstSpan[0];\r\n                    if (mode == ClickHouseColumnSerializationMode.Sparse || mode == ClickHouseColumnSerializationMode.Default)\r\n                    {\r\n                        _mode = mode;\r\n                        var result = ReadNext(sequence.Slice(1));\r\n                        return result.AddBytes(1);\r\n                    }\r\n\r\n                    throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Expected one of serialization modes: sparse or default. Received value: {mode}.\");\r\n\r\n                case ClickHouseColumnSerializationMode.Default:\r\n                    _baseReader ??= _typeInfo.CreateColumnReader(_rowCount);\r\n                    return _baseReader.ReadNext(sequence);\r\n\r\n                case ClickHouseColumnSerializationMode.Sparse:\r\n                    return ReadSparse(sequence);\r\n\r\n                default:\r\n                    throw new InvalidOperationException($\"Internal error. Unexpected column serialization mode: {_mode}.\");\r\n            }\r\n        }\r\n\r\n        private SequenceSize ReadSparse(ReadOnlySequence<byte> sequence)\r\n        {\r\n            if (_expectedBaseRowCount == 0)\r\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\r\n\r\n            if (_expectedBaseRowCount > 0)\r\n            {\r\n                Debug.Assert(_offsets != null);\r\n                Debug.Assert(_baseReader != null);\r\n\r\n                var result = _baseReader.ReadNext(sequence);\r\n                _expectedBaseRowCount -= result.Elements;\r\n\r\n                if (_expectedBaseRowCount < 0)\r\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\r\n\r\n                if (_expectedBaseRowCount == 0)\r\n                {\r\n                    // Calculate the number of rows with default values\r\n                    int defaultRowsCount = _rowCount - _offsets.Count;\r\n                    if (!_trailingDefaults)\r\n                    {\r\n                        var lastNonDefaultIdx = _offsets.Count == 0 ? -1 : _offsets[^1];\r\n                        defaultRowsCount -= _rowCount - (lastNonDefaultIdx + 1);\r\n                    }\r\n\r\n                    return result.AddElements(defaultRowsCount);\r\n                }\r\n\r\n                return result;\r\n            }\r\n\r\n            if (_offsets == null)\r\n            {\r\n                // The default ratio from the docs\r\n                // https://clickhouse.com/docs/en/operations/settings/merge-tree-settings#ratio_of_defaults_for_sparse_serialization\r\n                const double defaultRatioForSparseSerialization = 0.9375;\r\n                int capacity = Math.Max(16, (int)(_rowCount * (1 - defaultRatioForSparseSerialization)));\r\n                _offsets = new List<int>(capacity);\r\n            }\r\n\r\n            const ulong endOfGranuleFlag = 1ul << 62;\r\n            var seq = sequence;\r\n            int totalBytes = 0;\r\n            while (true)\r\n            {\r\n                if (!ClickHouseBinaryProtocolReader.TryRead7BitInteger(seq, out var group_size, out var bytesRead))\r\n                    return new SequenceSize(totalBytes, 0);\r\n\r\n                totalBytes += bytesRead;\r\n                seq = seq.Slice(bytesRead);\r\n\r\n                var endOfGranule = (group_size & endOfGranuleFlag) == endOfGranuleFlag;\r\n                group_size &= ~endOfGranuleFlag;\r\n                _sparseRowPostion += checked((int)group_size);\r\n\r\n                if (_sparseRowPostion >= _rowCount)\r\n                {\r\n                    _trailingDefaults = true;\r\n                    _sparseRowPostion = _rowCount;\r\n                    if (endOfGranule)\r\n                        break;\r\n\r\n                    // It may be ok, but non-final group with an overflow looks suspicious\r\n                    Debug.Fail(\"Unexpected group after the end of the column\");\r\n                    continue;\r\n                }\r\n\r\n                _offsets.Add(_sparseRowPostion++);\r\n\r\n                if (endOfGranule)\r\n                    break;\r\n            }\r\n\r\n            Debug.Assert(_rowCount >= _sparseRowPostion);\r\n            Debug.Assert(_expectedBaseRowCount == -1);\r\n\r\n            _expectedBaseRowCount = _offsets.Count;\r\n            if (!_trailingDefaults)\r\n                _expectedBaseRowCount += _rowCount - _sparseRowPostion;\r\n\r\n            _baseReader = _typeInfo.CreateColumnReader(_expectedBaseRowCount);\r\n            if (_expectedBaseRowCount == 0)\r\n            {\r\n                // The column consists only of default values\r\n                return new SequenceSize(totalBytes, _rowCount);\r\n            }\r\n\r\n            Debug.Assert(_expectedBaseRowCount > 0);\r\n            var baseResult = ReadSparse(seq);\r\n            return baseResult.AddBytes(totalBytes);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/CustomSerializationSkippingColumnReader.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing Octonica.ClickHouseClient.Exceptions;\r\nusing Octonica.ClickHouseClient.Protocol;\r\nusing System;\r\nusing System.Buffers;\r\nusing System.Diagnostics;\r\n\r\nnamespace Octonica.ClickHouseClient.Types\r\n{\r\n    internal sealed class CustomSerializationSkippingColumnReader : IClickHouseColumnReaderBase\r\n    {\r\n        private readonly int _rowCount;\r\n        private readonly IClickHouseColumnTypeInfo _typeInfo;\r\n\r\n        private ClickHouseColumnSerializationMode _mode;\r\n        private int _sparseRowPostion;\r\n        private int _expectedBaseRowCount = -1;\r\n        private int _valueRowCount;\r\n        private IClickHouseColumnReaderBase? _baseReader;\r\n\r\n        public CustomSerializationSkippingColumnReader(IClickHouseColumnTypeInfo typeInfo, int rowCount, ClickHouseColumnSerializationMode mode)\r\n        {\r\n            _typeInfo = typeInfo;\r\n            _mode = mode;\r\n            _rowCount = rowCount;\r\n        }\r\n\r\n        public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\r\n        {\r\n            switch (_mode)\r\n            {\r\n                case ClickHouseColumnSerializationMode.Custom:\r\n                    if (sequence.IsEmpty)\r\n                        return SequenceSize.Empty;\r\n\r\n                    // The prefix consists of a single byte encoding the serialization mode\r\n                    var mode = (ClickHouseColumnSerializationMode)sequence.FirstSpan[0];\r\n                    if (mode == ClickHouseColumnSerializationMode.Sparse || mode == ClickHouseColumnSerializationMode.Default)\r\n                    {\r\n                        _mode = mode;\r\n                        var result = ReadNext(sequence.Slice(1));\r\n                        return result.AddBytes(1);\r\n                    }\r\n\r\n                    throw new ClickHouseException(ClickHouseErrorCodes.ProtocolUnexpectedResponse, $\"Expected one of serialization modes: sparse or default. Received value: {mode}.\");\r\n\r\n                case ClickHouseColumnSerializationMode.Default:\r\n                    _baseReader ??= _typeInfo.CreateSkippingColumnReader(_rowCount);\r\n                    return _baseReader.ReadNext(sequence);\r\n\r\n                case ClickHouseColumnSerializationMode.Sparse:\r\n                    return SkipSparse(sequence);\r\n\r\n                default:\r\n                    throw new InvalidOperationException($\"Internal error. Unexpected column serialization mode: {_mode}.\");\r\n            }\r\n        }\r\n\r\n        private SequenceSize SkipSparse(ReadOnlySequence<byte> sequence)\r\n        {\r\n            if (_expectedBaseRowCount == 0)\r\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\r\n\r\n            if (_expectedBaseRowCount > 0)\r\n            {\r\n                Debug.Assert(_baseReader != null);\r\n\r\n                var result = _baseReader.ReadNext(sequence);\r\n                _expectedBaseRowCount -= result.Elements;\r\n\r\n                if (_expectedBaseRowCount < 0)\r\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\r\n\r\n                if (_expectedBaseRowCount == 0)\r\n                {\r\n                    // Calculate the number of rows with default values\r\n                    int defaultRowsCount = _rowCount - _valueRowCount;\r\n                    return result.AddElements(defaultRowsCount);\r\n                }\r\n\r\n                return result;\r\n            }\r\n\r\n            const ulong endOfGranuleFlag = 1ul << 62;\r\n            var seq = sequence;\r\n            int totalBytes = 0;\r\n            while (true)\r\n            {\r\n                if (!ClickHouseBinaryProtocolReader.TryRead7BitInteger(seq, out var group_size, out var bytesRead))\r\n                    return new SequenceSize(totalBytes, 0);\r\n\r\n                totalBytes += bytesRead;\r\n                seq = seq.Slice(bytesRead);\r\n\r\n                var endOfGranule = (group_size & endOfGranuleFlag) == endOfGranuleFlag;\r\n                group_size &= ~endOfGranuleFlag;\r\n                _sparseRowPostion += checked((int)group_size);\r\n\r\n                if (_sparseRowPostion >= _rowCount)\r\n                {\r\n                    _sparseRowPostion = _rowCount;\r\n                    if (endOfGranule)\r\n                        break;\r\n\r\n                    // It may be ok, but non-final group with an overflow looks suspicious\r\n                    Debug.Fail(\"Unexpected group after the end of the column\");\r\n                    continue;\r\n                }\r\n\r\n                ++_sparseRowPostion;\r\n                ++_valueRowCount;\r\n\r\n                if (endOfGranule)\r\n                {\r\n                    _valueRowCount += _rowCount - _sparseRowPostion;\r\n                    break;\r\n                }\r\n            }\r\n\r\n            Debug.Assert(_rowCount >= _sparseRowPostion);\r\n            Debug.Assert(_expectedBaseRowCount == -1);\r\n            _expectedBaseRowCount = _valueRowCount;\r\n\r\n            _baseReader = _typeInfo.CreateSkippingColumnReader(_expectedBaseRowCount);\r\n            if (_expectedBaseRowCount == 0)\r\n            {\r\n                // The column consists only of default values\r\n                return new SequenceSize(totalBytes, _rowCount);\r\n            }\r\n\r\n            Debug.Assert(_expectedBaseRowCount > 0);\r\n            var baseResult = ReadNext(seq);\r\n            return baseResult.AddBytes(totalBytes);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Date32TableColumn.Net6.0.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if NET6_0_OR_GREATER\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    partial class Date32TableColumn : IClickHouseTableColumn<DateOnly>\n    {\n        private static readonly DateOnly UnixEpoch = DateOnly.FromDateTime(DateTime.UnixEpoch);\n\n        // The default sparse value '1970-01-01' is not equal to the default column value '1901-01-01'.\n        // Here we should return the value corresponding to 0, which is 1970-01-01.\n        public DateOnly DefaultValue => UnixEpoch;\n\n        public DateOnly GetValue(int index)\n        {\n            var value = _buffer.Span[index];\n            return UnixEpoch.AddDays(value);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(DateTime))\n                return (IClickHouseTableColumn<T>)(object)new ReinterpretedTableColumn<DateOnly, DateTime>(this, dateOnly => dateOnly.ToDateTime(TimeOnly.MinValue));\n\n            if (typeof(T) == typeof(DateTime?))\n                return (IClickHouseTableColumn<T>)(object)new NullableStructTableColumn<DateTime>(null, new ReinterpretedTableColumn<DateOnly, DateTime>(this, dateOnly => dateOnly.ToDateTime(TimeOnly.MinValue)));\n\n            if (typeof(T) == typeof(DateOnly?))\n                return (IClickHouseTableColumn<T>)(object)new NullableStructTableColumn<DateOnly>(null, this);\n\n            return null;\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Date32TableColumn.NetCoreApp3.1.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if NETCOREAPP3_1_OR_GREATER && !NET6_0_OR_GREATER\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    partial class Date32TableColumn : IClickHouseTableColumn<DateTime>\n    {\n        static readonly DateTime UnixEpochUnspecified = new DateTime(DateTime.UnixEpoch.Ticks, DateTimeKind.Unspecified);\n\n        public DateTime DefaultValue => UnixEpochUnspecified;\n\n        public DateTime GetValue(int index)\n        {\n            var value = _buffer.Span[index];\n            return UnixEpochUnspecified.AddDays(value);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(DateTime?))\n                return (IClickHouseTableColumn<T>)(object)new NullableStructTableColumn<DateTime>(null, this);\n\n            return null;\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Date32TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed partial class Date32TableColumn\n    {\n        private readonly ReadOnlyMemory<int> _buffer;\n\n        public int RowCount => _buffer.Length;\n\n        public Date32TableColumn(ReadOnlyMemory<int> buffer)\n        {\n            _buffer = buffer;\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public bool TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Date32TypeInfo.Net6.0.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if NET6_0_OR_GREATER\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Runtime.CompilerServices;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    partial class Date32TypeInfo\n    {\n        private static readonly DateOnly UnixEpoch = DateOnly.FromDateTime(DateTime.UnixEpoch);\n\n        private static readonly DateOnly MinDateOnlyValue = UnixEpoch.AddDays(MinValue);\n        private static readonly DateOnly MaxDateOnlyValue = UnixEpoch.AddDays(MaxValue);\n\n        public override Type GetFieldType()\n        {\n            return typeof(DateOnly);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            IReadOnlyList<DateOnly> dateOnlyRows;\n            if (typeof(T) == typeof(DateOnly))\n                dateOnlyRows = (IReadOnlyList<DateOnly>)rows;\n            else if (typeof(T) == typeof(DateTime))\n                dateOnlyRows = ((IReadOnlyList<DateTime>)rows).Map(DateOnly.FromDateTime);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new Date32Writer(columnName, ComplexTypeName, dateOnlyRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer = default(T) switch\n            {\n                DateOnly => new DateOnlyParameterWriter(this),\n                DateTime => new DateTimeParameterWriter(this),\n                _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\")\n            };\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        private static int DateOnlyToDays(DateOnly value)\n        {\n            if (value == default)\n                return MinValue;\n\n            var days = value.DayNumber - UnixEpoch.DayNumber;\n            if (days < MinValue || days > MaxValue)\n                throw new OverflowException(\"The value must be in range [1925-01-01, 2283-11-11].\");\n\n            return days;\n        }\n\n        partial class Date32Reader : StructureReaderBase<int, DateOnly>\n        {\n            protected override IClickHouseTableColumn<DateOnly> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<int> buffer)\n            {\n                return new Date32TableColumn(buffer);\n            }\n        }\n\n        partial class Date32Writer : StructureWriterBase<DateOnly, int>\n        {\n            public Date32Writer(string columnName, string columnType, IReadOnlyList<DateOnly> rows)\n                : base(columnName, columnType, sizeof(int), rows)\n            {\n            }\n\n            protected override int Convert(DateOnly value)\n            {\n                return DateOnlyToDays(value);\n            }\n        }\n\n        private sealed class DateOnlyParameterWriter : IClickHouseParameterWriter<DateOnly>\n        {\n            private readonly Date32TypeInfo _typeInfo;\n\n            public DateOnlyParameterWriter(Date32TypeInfo typeInfo)\n            {\n                _typeInfo = typeInfo;\n            }\n\n            public bool TryCreateParameterValueWriter(DateOnly value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                var strVal = ValueToString(value);\n                if (isNested)\n                    strVal = $\"'{strVal}'\";\n\n                valueWriter = new SimpleLiteralValueWriter(strVal.AsMemory());\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, DateOnly value)\n            {\n                var strVal = ValueToString(value);\n                return queryBuilder.Append('\\'').Append(strVal).Append(\"'::\").Append(_typeInfo.ComplexTypeName);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return writeValue(queryBuilder, _typeInfo, FunctionHelper.Apply);\n            }\n\n            [MethodImpl(MethodImplOptions.AggressiveInlining)]\n            private static string ValueToString(DateOnly value)\n            {\n                if (value == default)\n                    return DefaultValueStr;\n\n                if (value < MinDateOnlyValue || value > MaxDateOnlyValue)\n                    throw new OverflowException($\"The value must be in range [{MinDateOnlyValue}, {MaxDateOnlyValue}].\");\n\n                return value.ToString(FormatStr, CultureInfo.InvariantCulture);\n            }\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Date32TypeInfo.NetCoreApp3.1.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if NETCOREAPP3_1_OR_GREATER && !NET6_0_OR_GREATER\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    partial class Date32TypeInfo\n    {\n        public override Type GetFieldType()\n        {\n            return typeof(DateTime);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (typeof(T) != typeof(DateTime))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new Date32Writer(columnName, ComplexTypeName, (IReadOnlyList<DateTime>)rows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            if (type == typeof(DateTime))\n                return (IClickHouseParameterWriter<T>)(object)new DateTimeParameterWriter(this);\n\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n        }\n\n        private static int DateTimeToDays(DateTime value)\n        {\n            if (value == default)\n                return MinValue;\n\n            var days = (value.Date - DateTime.UnixEpoch).TotalDays;\n            if (days < MinValue || days > MaxValue)\n                throw new OverflowException($\"The value must be in range [{MinDateTimeValue}, {MaxDateTimeValue}].\");\n\n            return (int)days;\n        }\n\n        partial class Date32Reader : StructureReaderBase<int, DateTime>\n        {\n            protected override IClickHouseTableColumn<DateTime> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<int> buffer)\n            {\n                return new Date32TableColumn(buffer);\n            }\n        }\n\n        partial class Date32Writer : StructureWriterBase<DateTime, int>\n        {\n            public Date32Writer(string columnName, string columnType, IReadOnlyList<DateTime> rows)\n                : base(columnName, columnType, sizeof(int), rows)\n            {\n            }\n\n            protected override int Convert(DateTime value)\n            {\n                return DateTimeToDays(value);\n            }\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Date32TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Runtime.CompilerServices;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed partial class Date32TypeInfo : SimpleTypeInfo\n    {\n        public const int MinValue = -25567;\n        public const int MaxValue = 120529;\n\n        private const string FormatStr = DateTypeInfo.FormatStr;\n        private const string DefaultValueStr = \"1900-01-01\";\n\n        private static readonly DateTime MinDateTimeValue = DateTime.UnixEpoch.AddDays(MinValue);\n        private static readonly DateTime MaxDateTimeValue = DateTime.UnixEpoch.AddDays(MaxValue + 1).Subtract(TimeSpan.FromTicks(1));\n\n        public Date32TypeInfo()\n            :base(\"Date32\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new Date32Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(uint), rowCount);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Date32;\n        }\n\n        private sealed partial class Date32Reader\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Date32Reader(int rowCount) \n                : base(sizeof(int), rowCount)\n            {\n            }\n\n            protected override int ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToInt32(source);\n            }\n        }\n\n        private sealed partial class Date32Writer\n        {\n        }\n\n        private sealed class DateTimeParameterWriter : IClickHouseParameterWriter<DateTime>\n        {\n            private readonly Date32TypeInfo _typeInfo;\n\n            public DateTimeParameterWriter(Date32TypeInfo typeInfo)\n            {\n                _typeInfo = typeInfo;\n            }\n\n            public bool TryCreateParameterValueWriter(DateTime value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                var strVal = ValueToString(value);\n                valueWriter = new SimpleLiteralValueWriter(strVal.AsMemory());\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, DateTime value)\n            {\n                var strVal = ValueToString(value);\n                return queryBuilder.Append('\\'').Append(strVal).Append(\"\\'::\").Append(_typeInfo.ComplexTypeName);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return writeValue(queryBuilder, _typeInfo, FunctionHelper.Apply);\n            }\n\n            [MethodImpl(MethodImplOptions.AggressiveInlining)]\n            private static string ValueToString(DateTime value)\n            {\n                if (value == default)\n                    return DefaultValueStr;\n\n                if (value < MinDateTimeValue || value > MaxDateTimeValue)\n                    throw new OverflowException($\"The value must be in range [{MinDateTimeValue}, {MaxDateTimeValue}].\");\n\n                return value.ToString(FormatStr, CultureInfo.InvariantCulture);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DateOnlyTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if NET6_0_OR_GREATER\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class DateOnlyTableColumn : StructureTableColumn<DateOnly>\n    {\n        public DateOnlyTableColumn(ReadOnlyMemory<DateOnly> buffer)\n            : base(buffer)\n        {\n        }\n\n        public override IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(DateTime))\n                return (IClickHouseTableColumn<T>)(object)new ReinterpretedTableColumn<DateOnly, DateTime>(this, dateOnly => dateOnly.ToDateTime(TimeOnly.MinValue));\n            if (typeof(T) == typeof(DateTime?))\n                return (IClickHouseTableColumn<T>)(object)new NullableStructTableColumn<DateTime>(null, new ReinterpretedTableColumn<DateOnly, DateTime>(this, dateOnly => dateOnly.ToDateTime(TimeOnly.MinValue)));\n\n            return base.TryReinterpret<T>();\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DateTime64TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class DateTime64TableColumn : IClickHouseTableColumn<DateTimeOffset>\n    {\n        private static readonly (long min, long max)[] ClickHouseTicksRange;\n\n        private readonly ReadOnlyMemory<long> _buffer;\n        private readonly int _ticksScale;\n        private readonly (long min, long max) _range;\n        private readonly TimeZoneInfo _timeZone;\n\n        public int RowCount { get; }\n\n        public DateTimeOffset DefaultValue => new DateTimeOffset(DateTime.UnixEpoch).ToOffset(_timeZone.GetUtcOffset(DateTime.UnixEpoch));\n\n        static DateTime64TableColumn()\n        {\n            var ranges = new (long min, long max)[DateTime64TypeInfo.DateTimeTicksScales.Length];\n            var dateTimeTicksMax = (DateTime.MaxValue - DateTime.UnixEpoch).Ticks;\n            var dateTimeTicksMin = (DateTime.MinValue - DateTime.UnixEpoch).Ticks;\n            for (int i = 0; i < ranges.Length; i++)\n            {\n                long min, max;\n                var magnitude = DateTime64TypeInfo.DateTimeTicksScales[i];\n                if (magnitude < 0)\n                {\n                    if (dateTimeTicksMax > long.MaxValue / -magnitude)\n                        max = long.MaxValue;\n                    else\n                        max = checked(dateTimeTicksMax * -magnitude);\n\n                    if (dateTimeTicksMin < long.MinValue / -magnitude)\n                        min = long.MinValue;\n                    else\n                        min = checked(dateTimeTicksMin * -magnitude);\n                }\n                else\n                {\n                    max = dateTimeTicksMax / magnitude;\n                    min = dateTimeTicksMin / magnitude;\n                }\n\n                ranges[i] = (min, max);\n            }\n\n            ClickHouseTicksRange = ranges;\n        }\n\n        public DateTime64TableColumn(ReadOnlyMemory<long> buffer, int precision, TimeZoneInfo timeZone)\n        {\n            _buffer = buffer;\n            _ticksScale = DateTime64TypeInfo.DateTimeTicksScales[precision];\n            _range = ClickHouseTicksRange[precision];\n            _timeZone = timeZone;\n            RowCount = buffer.Length;\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        public DateTimeOffset GetValue(int index)\n        {\n            var ticks = _buffer.Span[index];\n            if (ticks < _range.min)\n            {\n                throw new OverflowException(\n                    $\"The value 0x{ticks:X} is lesser than the minimal value of the type \\\"{typeof(DateTime)}\\\". \" +\n                    $\"It is only possible to read this value as \\\"{typeof(long)}\\\".\");\n            }\n\n            if (ticks > _range.max)\n            {\n                throw new OverflowException(\n                    $\"The value 0x{ticks:X} is greater than the maximal value of the type \\\"{typeof(DateTime)}\\\". \" +\n                    $\"It is only possible to read this value as \\\"{typeof(long)}\\\".\");\n            }\n\n            if (_ticksScale < 0)\n                ticks /= -_ticksScale;\n            else\n                ticks = checked(ticks * _ticksScale);\n\n            var dateTime = DateTime.UnixEpoch.AddTicks(ticks);\n            var offset = _timeZone.GetUtcOffset(dateTime);\n            return new DateTimeOffset(dateTime).ToOffset(offset);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(DateTime))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<DateTimeOffset, DateTime>(this, dto => dto.DateTime);\n\n            if (typeof(T) == typeof(DateTime?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<DateTime>(null, new ReinterpretedTableColumn<DateTimeOffset, DateTime>(this, dto => dto.DateTime));\n\n            if (typeof(T) == typeof(DateTimeOffset?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<DateTimeOffset>(null, this);\n\n            var rawColumn = new StructureTableColumn<long>(_buffer);\n            var rawReinterpreted = rawColumn as IClickHouseTableColumn<T> ?? rawColumn.TryReinterpret<T>();\n            if (rawReinterpreted != null)\n                return new ReinterpretedTableColumn<T>(this, rawReinterpreted);\n\n            return null;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DateTime64TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Runtime.CompilerServices;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class DateTime64TypeInfo : IClickHouseConfigurableTypeInfo\n    {\n        internal static readonly int[] DateTimeTicksScales;\n        private static readonly (long min, long max)[] DateTimeTicksRanges;\n\n        public const int DefaultPrecision = 3;\n\n        private readonly int? _precision;\n        private readonly string? _timeZoneCode;\n\n        /// <summary>\n        /// Indicates that the <see cref=\"_timeZoneCode\"/> was acquired from the name of the type.\n        /// </summary>\n        private readonly bool _explicitTimeZoneCode;\n\n        private TimeZoneInfo? _timeZone;\n\n        public string ComplexTypeName { get; }\n\n        public string TypeName => \"DateTime64\";\n\n        public int GenericArgumentsCount => 0;\n\n        public int TypeArgumentsCount => (_precision == null ? 0 : 1) + (_timeZoneCode == null || !_explicitTimeZoneCode ? 0 : 1);\n\n        static DateTime64TypeInfo()\n        {\n            const int maxPrecision = 9;\n            var scales = new int[maxPrecision + 1];\n\n            for (int i = 0; i < scales.Length; i++)\n                scales[i] = i == 0 ? 1 : checked(scales[i - 1] * 10);\n\n            var ranges = new (long min, long max)[scales.Length];\n\n            var minValue = new DateTime(1900, 1, 1);\n            var exclusiveMaxValue = new DateTime(2300, 1, 1);\n            var dateTimeTicksMin = (minValue - DateTime.UnixEpoch).Ticks;\n            var dateTimeTicksMax = (exclusiveMaxValue - DateTime.UnixEpoch).Ticks - 1;\n            for (int i = 0; i < scales.Length; i++)\n            {\n                long scale, rem;\n                if (scales[i] <= TimeSpan.TicksPerSecond)\n                    scale = Math.DivRem(TimeSpan.TicksPerSecond, scales[i], out rem);\n                else\n                    scale = -Math.DivRem(scales[i], TimeSpan.TicksPerSecond, out rem);\n\n                if (rem != 0)\n                    throw new InvalidOperationException($\"Internal error. Expected that the value of {typeof(TimeSpan)}.{nameof(TimeSpan.TicksPerSecond)} is a power of 10.\");\n\n                scales[i] = checked((int)scale);\n\n                long max;\n                if (scale < 0)\n                    max = Math.Min(dateTimeTicksMax, long.MaxValue / -scale);\n                else\n                    max = dateTimeTicksMax;\n\n                long min;\n                if (scale < 0)\n                    min = Math.Max(dateTimeTicksMin, long.MinValue / -scale);\n                else\n                    min = dateTimeTicksMin;\n\n                ranges[i] = (min, max);\n            }\n\n            DateTimeTicksScales = scales;\n            DateTimeTicksRanges = ranges;\n        }\n\n        public DateTime64TypeInfo()\n            : this(null, null, false)\n        {\n        }\n\n        private DateTime64TypeInfo(int? precision, string? timeZoneCode, bool explicitTimeZoneCode)\n        {\n            if (precision != null)\n            {\n                if (precision.Value < 0)\n                    throw new ArgumentOutOfRangeException(nameof(precision), \"The precision must be a non-negative number.\");\n                if (precision.Value >= DateTimeTicksScales.Length)\n                    throw new ArgumentOutOfRangeException(nameof(precision), $\"The precision can't be greater than {DateTimeTicksScales.Length - 1}.\");\n            }\n\n            _explicitTimeZoneCode = explicitTimeZoneCode;\n            _precision = precision;\n            _timeZoneCode = timeZoneCode;\n\n            if (timeZoneCode != null && _explicitTimeZoneCode)\n            {\n                if (precision == null)\n                    throw new ArgumentNullException(nameof(precision));\n\n                ComplexTypeName = string.Format(CultureInfo.InvariantCulture, \"{0}({1}, '{2}')\", TypeName, precision.Value, timeZoneCode);\n            }\n            else if (precision != null)\n            {\n                ComplexTypeName = string.Format(CultureInfo.InvariantCulture, \"{0}({1})\", TypeName, precision.Value);\n            }\n            else\n            {\n                ComplexTypeName = TypeName;\n            }\n        }\n\n        public Type GetFieldType()\n        {\n            return typeof(DateTimeOffset);\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.DateTime64;\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have generic arguments.\");\n        }\n\n        public object GetTypeArgument(int index)\n        {\n            if (_precision == null)\n            {\n                Debug.Assert(_timeZoneCode == null || !_explicitTimeZoneCode);\n                throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have arguments.\");\n            }\n\n            switch (index)\n            {\n                case 0:\n                    return _precision;\n\n                case 1:\n                    if (_timeZoneCode != null && _explicitTimeZoneCode)\n                        return _timeZoneCode;\n\n                    goto default;\n\n                default:\n                    throw new IndexOutOfRangeException();\n            }\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            var timeZone = GetTimeZone();\n            return new DateTime64Reader(rowCount, _precision ?? DefaultPrecision, timeZone);\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(ulong), rowCount);\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (typeof(T) == typeof(DateTime))\n            {\n                var timeZone = GetTimeZone();\n                return new DateTimeWriter(columnName, ComplexTypeName, _precision ?? DefaultPrecision, timeZone, (IReadOnlyList<DateTime>)rows);\n            }\n\n            if (typeof(T) == typeof(DateTimeOffset))\n            {\n                var timeZone = GetTimeZone();\n                return new DateTimeOffsetWriter(columnName, ComplexTypeName, _precision ?? DefaultPrecision, timeZone, (IReadOnlyList<DateTimeOffset>)rows);\n            }\n\n            if (typeof(T) == typeof(ulong))\n                return new UInt64TypeInfo.UInt64Writer(columnName, ComplexTypeName, (IReadOnlyList<ulong>)rows);\n\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object? converter = default(T) switch\n            {\n                ulong _ => null,\n                DateTime _ => new DateTimeWriter(\"dummy\", ComplexTypeName, _precision ?? DefaultPrecision, GetTimeZone(), Array.Empty<DateTime>()),\n                DateTimeOffset _ => new DateTimeOffsetWriter(\"dummy\", ComplexTypeName, _precision ?? DefaultPrecision, GetTimeZone(), Array.Empty<DateTimeOffset>()),\n                _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\")\n            };\n\n            return new DateTime64ParameterWriter<T>(this, (IConverter<T, long>?)converter);\n        }\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            if (options.Count > 2)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"Too many arguments in the definition of \\\"{TypeName}\\\".\");\n\n            if (!int.TryParse(options[0].Span, NumberStyles.Integer, CultureInfo.InvariantCulture, out var precision))\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The first argument (precision) of the type \\\"{TypeName}\\\" must be an integer.\");\n            else if (precision < 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The precision value for the type \\\"{TypeName}\\\" must be a non-negative number.\");\n\n            if (options.Count == 2)\n            {\n                var tzCode = options[1].Trim('\\'').ToString();\n                return new DateTime64TypeInfo(precision, tzCode, true);\n            }\n\n            return new DateTime64TypeInfo(precision, _timeZoneCode, false);\n        }\n\n        public IClickHouseColumnTypeInfo Configure(ClickHouseServerInfo serverInfo)\n        {\n            return new DateTime64TypeInfo(null, serverInfo.Timezone, false);\n        }\n\n        private TimeZoneInfo GetTimeZone()\n        {\n            if (_timeZone != null)\n                return _timeZone;\n\n            if (_timeZoneCode == null)\n                return TimeZoneInfo.Utc;\n\n            _timeZone = TimeZoneHelper.GetTimeZoneInfo(_timeZoneCode);\n            return _timeZone;\n        }\n\n        private sealed class DateTime64Reader : StructureReaderBase<long, DateTimeOffset>\n        {\n            private readonly int _precision;\n            private readonly TimeZoneInfo _timeZone;\n\n            protected override bool BitwiseCopyAllowed => true;\n\n            public DateTime64Reader(int rowCount, int precision, TimeZoneInfo timeZone)\n                : base(sizeof(ulong), rowCount)\n            {\n                _precision = precision;\n                _timeZone = timeZone;\n            }\n\n            protected override long ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToInt64(source);\n            }\n\n            protected override IClickHouseTableColumn<DateTimeOffset> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<long> buffer)\n            {\n                return new DateTime64TableColumn(buffer, _precision, _timeZone);\n            }\n        }\n\n        private sealed class DateTimeWriter : StructureWriterBase<DateTime, long>, IConverter<DateTime, long>\n        {\n            private readonly int _ticksScale;\n            private readonly TimeZoneInfo _timeZone;\n            private readonly (long min, long max) _range;\n\n            public DateTimeWriter(string columnName, string columnType, int precision, TimeZoneInfo timeZone, IReadOnlyList<DateTime> rows)\n                : base(columnName, columnType, sizeof(ulong), rows)\n            {\n                _ticksScale = DateTimeTicksScales[precision];\n                _range = DateTimeTicksRanges[precision];\n                _timeZone = timeZone;\n            }\n\n            long IConverter<DateTime, long>.Convert(DateTime value) => Convert(value);\n\n            protected override long Convert(DateTime value)\n            {\n                long ticks;\n                if (value == default)\n                {\n                    ticks = 0;\n                }\n                else\n                {\n                    var dateTimeTicks = value.Ticks - DateTime.UnixEpoch.Ticks - _timeZone.GetUtcOffset(value).Ticks;\n                    if (dateTimeTicks < _range.min || dateTimeTicks > _range.max)\n                        throw new OverflowException($\"The value must be in range [{DateTime.UnixEpoch.AddTicks(_range.min):O}, {DateTime.UnixEpoch.AddTicks(_range.max):O}].\");\n\n                    ticks = ScaleTicks(dateTimeTicks, _ticksScale);\n                }\n\n                return ticks;\n            }\n\n            [MethodImpl(MethodImplOptions.AggressiveInlining)]\n            internal static long ScaleTicks(long ticks, int scaleFactor)\n            {\n                if (scaleFactor < 0)\n                    return checked(ticks * -scaleFactor);\n\n                if (ticks >= 0)\n                    return ticks / scaleFactor;\n\n                var result = Math.DivRem(ticks, scaleFactor, out var rem);\n                if (rem != 0)\n                    --result;\n\n                return result;\n            }\n        }\n\n        private sealed class DateTimeOffsetWriter : StructureWriterBase<DateTimeOffset, long>, IConverter<DateTimeOffset, long>\n        {\n            private readonly int _ticksScale;\n            private readonly (long min, long max) _range;\n            private readonly TimeZoneInfo _timeZone;\n\n            public DateTimeOffsetWriter(string columnName, string columnType, int precision, TimeZoneInfo timeZone, IReadOnlyList<DateTimeOffset> rows)\n                : base(columnName, columnType, sizeof(ulong), rows)\n            {\n                _ticksScale = DateTimeTicksScales[precision];\n                _range = DateTimeTicksRanges[precision];\n                _timeZone = timeZone;\n            }\n\n            long IConverter<DateTimeOffset, long>.Convert(DateTimeOffset value) => Convert(value);\n\n            protected override long Convert(DateTimeOffset value)\n            {\n                long ticks;\n                if (value == default)\n                {\n                    ticks = 0;\n                }\n                else\n                {\n                    var offset = _timeZone.GetUtcOffset(value);\n                    var valueWithOffset = value.ToOffset(offset);\n                    var dateTimeTicks = (valueWithOffset - DateTimeOffset.UnixEpoch).Ticks;\n                    if (dateTimeTicks < _range.min || dateTimeTicks > _range.max)\n                        throw new OverflowException($\"The value must be in range [{DateTimeOffset.UnixEpoch.AddTicks(_range.min):O}, {DateTimeOffset.UnixEpoch.AddTicks(_range.max):O}].\");\n\n                    ticks = DateTimeWriter.ScaleTicks(dateTimeTicks, _ticksScale);\n                }\n\n                return ticks;\n            }\n        }\n\n        private sealed class DateTime64ParameterWriter<T> : IClickHouseParameterWriter<T>\n        {\n            private readonly DateTime64TypeInfo _typeInfo;\n            private readonly IConverter<T, long>? _converter;\n\n            public DateTime64ParameterWriter(DateTime64TypeInfo typeInfo, IConverter<T, long>? converter)\n            {\n                Debug.Assert(converter != null || typeof(T) == typeof(long));\n\n                _typeInfo = typeInfo;\n                _converter = converter;\n            }\n\n            public bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                var ticks = Convert(value);\n                var str = ticks.ToString(CultureInfo.InvariantCulture);\n                valueWriter = new SimpleLiteralValueWriter(str.AsMemory());\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, T value)\n            {\n                var ticks = Convert(value);\n\n                queryBuilder.Append(\"reinterpret(cast(\");\n                queryBuilder.Append(ticks.ToString(CultureInfo.InvariantCulture));\n                queryBuilder.Append(\",'Int64'),'\");\n                queryBuilder.Append(_typeInfo.ComplexTypeName.Replace(\"'\", \"''\"));\n                queryBuilder.Append(\"')\");\n\n                return queryBuilder;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                var ticksType = typeInfoProvider.GetTypeInfo(\"Int64\");\n\n                return writeValue(queryBuilder, ticksType, (qb, realWrite) =>\n                {\n                    qb.Append(\"reinterpret(\");\n                    realWrite(queryBuilder);\n                    qb.Append(\",'\");\n                    qb.Append(_typeInfo.ComplexTypeName.Replace(\"'\", \"''\"));\n                    qb.Append(\"')\");\n                    return qb;\n                });\n            }\n\n            private long Convert(T value)\n            {\n                if (_converter == null)\n                {\n                    Debug.Assert(typeof(T) == typeof(long));\n                    Debug.Assert(value != null);\n                    return (long)(object)value;\n                }\n\n                return _converter.Convert(value);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DateTimeTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class DateTimeTableColumn : IClickHouseTableColumn<DateTimeOffset>\n    {\n        private readonly ReadOnlyMemory<uint> _buffer;\n        private readonly TimeZoneInfo _timeZone;\n\n        public int RowCount { get; }\n\n        public DateTimeOffset DefaultValue => default;\n\n        public DateTimeTableColumn(ReadOnlyMemory<uint> buffer, TimeZoneInfo timeZone)\n        {\n            _buffer = buffer;\n            _timeZone = timeZone;\n            RowCount = buffer.Length;\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        public DateTimeOffset GetValue(int index)\n        {\n            var seconds = _buffer.Span[index];\n            if (seconds == 0)\n                return default;\n\n            var dateTime = DateTime.UnixEpoch.AddSeconds(seconds);\n            var offset = _timeZone.GetUtcOffset(dateTime);\n            return new DateTimeOffset(dateTime).ToOffset(offset);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(DateTime))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<DateTimeOffset, DateTime>(this, dto => dto.DateTime);\n\n            if (typeof(T) == typeof(DateTime?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<DateTime>(null, new ReinterpretedTableColumn<DateTimeOffset, DateTime>(this, dto => dto.DateTime));\n\n            if (typeof(T) == typeof(DateTimeOffset?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<DateTimeOffset>(null, this);\n\n            return null;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DateTimeTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class DateTimeTypeInfo : IClickHouseConfigurableTypeInfo\n    {\n        private readonly string? _timeZoneCode;\n\n        /// <summary>\n        /// Indicates that the <see cref=\"_timeZoneCode\"/> was acquired from the name of the type.\n        /// </summary>\n        private readonly bool _explicitTimeZoneCode;\n\n        private TimeZoneInfo? _timeZone;\n\n        public string ComplexTypeName { get; }\n\n        public string TypeName => \"DateTime\";\n\n        public int GenericArgumentsCount => 0;\n\n        public int TypeArgumentsCount => _timeZoneCode == null || !_explicitTimeZoneCode ? 0 : 1;\n\n        public DateTimeTypeInfo()\n            : this(null, false)\n        {\n        }\n\n        private DateTimeTypeInfo(string? timeZoneCode, bool explicitTimeZoneCode)\n        {\n            _timeZoneCode = timeZoneCode;\n            _explicitTimeZoneCode = explicitTimeZoneCode;\n            ComplexTypeName = timeZoneCode == null || !_explicitTimeZoneCode ? TypeName : $\"{TypeName}('{timeZoneCode}')\";\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            var timeZone = GetTimeZone();\n            return new DateTimeReader(rowCount, timeZone);\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(uint), rowCount);\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (typeof(T) == typeof(DateTime))\n            {\n                var timeZone = GetTimeZone();\n                return new DateTimeWriter(columnName, ComplexTypeName, timeZone, (IReadOnlyList<DateTime>)rows);\n            }\n\n            if (typeof(T) == typeof(DateTimeOffset))\n            {\n                var timeZone = GetTimeZone();\n                return new DateTimeOffsetWriter(columnName, ComplexTypeName, timeZone, (IReadOnlyList<DateTimeOffset>)rows);\n            }\n\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object converter = default(T) switch\n            {\n                DateTime _ => new DateTimeWriter(\"dummy\", ComplexTypeName, GetTimeZone(), Array.Empty<DateTime>()),\n                DateTimeOffset _ => new DateTimeOffsetWriter(\"dummy\", ComplexTypeName, GetTimeZone(), Array.Empty<DateTimeOffset>()),\n                _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\")\n            };\n\n            return new DateTimeParameterWriter<T>(this, (IConverter<T, uint>)converter);\n        }\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            if (options.Count > 1)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"Too many arguments in the definition of \\\"{TypeName}\\\".\");\n\n            var tzCode = options[0].Trim('\\'').ToString();\n            return new DateTimeTypeInfo(tzCode, true);\n        }\n\n        public Type GetFieldType()\n        {\n            return typeof(DateTimeOffset);\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.DateTimeOffset;\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have generic arguments.\");\n        }\n\n        public object GetTypeArgument(int index)\n        {\n            if (_timeZoneCode == null || !_explicitTimeZoneCode)\n                throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have arguments.\");\n\n            if (index != 0)\n                throw new IndexOutOfRangeException();\n\n            return _timeZoneCode;\n        }\n\n        public IClickHouseColumnTypeInfo Configure(ClickHouseServerInfo serverInfo)\n        {\n            return new DateTimeTypeInfo(serverInfo.Timezone, false);\n        }\n\n        private TimeZoneInfo GetTimeZone()\n        {\n            if (_timeZone != null)\n                return _timeZone;\n\n            if (_timeZoneCode == null)\n                return TimeZoneInfo.Utc;\n\n            _timeZone = TimeZoneHelper.GetTimeZoneInfo(_timeZoneCode);\n            return _timeZone;\n        }\n\n        private sealed class DateTimeReader : StructureReaderBase<uint, DateTimeOffset>\n        {\n            private readonly TimeZoneInfo _timeZone;            \n\n            protected override bool BitwiseCopyAllowed => true;\n\n            public DateTimeReader(int rowCount, TimeZoneInfo timeZone)\n                : base(sizeof(uint), rowCount)\n            {\n                _timeZone = timeZone;\n            }\n\n            protected override uint ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToUInt32(source);\n            }\n\n            protected override IClickHouseTableColumn<DateTimeOffset> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<uint> buffer)\n            {\n                return new DateTimeTableColumn(buffer, _timeZone);\n            }\n        }\n\n        private sealed class DateTimeWriter : StructureWriterBase<DateTime, uint>, IConverter<DateTime, uint>\n        {\n            private readonly TimeZoneInfo _timeZone;\n\n            public DateTimeWriter(string columnName, string columnType, TimeZoneInfo timeZone, IReadOnlyList<DateTime> rows)\n                : base(columnName, columnType, sizeof(uint), rows)\n            {\n                _timeZone = timeZone;\n            }\n\n            uint IConverter<DateTime, uint>.Convert(DateTime value)\n            {\n                return Convert(value);\n            }\n\n            protected override uint Convert(DateTime value)\n            {\n                uint seconds;\n                if (value == default)\n                {\n                    seconds = 0;\n                }\n                else\n                {\n                    var doubleSeconds = (value - DateTime.UnixEpoch).TotalSeconds - _timeZone.GetUtcOffset(value).TotalSeconds;\n                    if (doubleSeconds < 0 || doubleSeconds > uint.MaxValue)\n                        throw new OverflowException(\"The value must be in range [1970-01-01 00:00:00, 2105-12-31 23:59:59].\");\n\n                    seconds = (uint) doubleSeconds;\n                }\n\n                return seconds;\n            }\n        }\n\n        private sealed class DateTimeOffsetWriter : StructureWriterBase<DateTimeOffset, uint>, IConverter<DateTimeOffset, uint>\n        {\n            private readonly TimeZoneInfo _timeZone;\n\n            public DateTimeOffsetWriter(string columnName, string columnType, TimeZoneInfo timeZone, IReadOnlyList<DateTimeOffset> rows)\n                : base(columnName, columnType, sizeof(uint), rows)\n            {\n                _timeZone = timeZone;\n            }\n\n            uint IConverter<DateTimeOffset, uint>.Convert(DateTimeOffset value)\n            {\n                return Convert(value);\n            }\n\n            protected override uint Convert(DateTimeOffset value)\n            {\n                uint seconds;\n                if (value == default)\n                {\n                    seconds = 0;\n                }\n                else\n                {\n                    var offset = _timeZone.GetUtcOffset(value);\n                    var valueWithOffset = value.ToOffset(offset);\n                    var doubleSeconds = (valueWithOffset - DateTimeOffset.UnixEpoch).TotalSeconds;\n                    if (doubleSeconds < 0 || doubleSeconds > uint.MaxValue)\n                        throw new OverflowException(\"The value must be in range [1970-01-01 00:00:00, 2105-12-31 23:59:59].\");\n\n                    seconds = (uint)doubleSeconds;\n                }\n\n                return seconds;\n            }\n        }\n\n        private sealed class DateTimeParameterWriter<T> : IClickHouseParameterWriter<T>\n        {\n            private readonly DateTimeTypeInfo _typeInfo;\n            private readonly IConverter<T, uint> _converter;\n\n            public DateTimeParameterWriter(DateTimeTypeInfo typeInfo, IConverter<T, uint> converter)\n            {\n                _typeInfo = typeInfo;\n                _converter = converter;\n            }\n\n            public bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                var seconds = _converter.Convert(value);\n                var str = seconds.ToString(CultureInfo.InvariantCulture);\n                valueWriter = new SimpleLiteralValueWriter(str.AsMemory());\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, T value)\n            {\n                queryBuilder.Append(\"CAST(\");\n                var seconds = _converter.Convert(value);\n                queryBuilder.Append(seconds.ToString(CultureInfo.InvariantCulture));\n                return queryBuilder.AppendFormat(\" AS \").Append(_typeInfo.ComplexTypeName).Append(')');\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                var ticksType = typeInfoProvider.GetTypeInfo(\"UInt32\");\n\n                return writeValue(queryBuilder, ticksType, (qb, realWrite) =>\n                {\n                    qb.Append(\"CAST(\");\n                    realWrite(qb);\n                    return qb.AppendFormat(\" AS \").Append(_typeInfo.ComplexTypeName).Append(')');\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DateTypeInfo.Net6.0.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if NET6_0_OR_GREATER\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Runtime.CompilerServices;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    partial class DateTypeInfo\n    {\n        private static readonly DateOnly UnixEpoch = DateOnly.FromDateTime(DateTime.UnixEpoch);\n        private static readonly DateOnly MaxDateOnlyValue = UnixEpoch.AddDays(ushort.MaxValue);\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            IReadOnlyList<DateOnly> dateOnlyRows;\n            if (typeof(T) == typeof(DateOnly))\n                dateOnlyRows = (IReadOnlyList<DateOnly>)rows;\n            else if (typeof(T) == typeof(DateTime))\n                dateOnlyRows = ((IReadOnlyList<DateTime>)rows).Map(DateOnly.FromDateTime);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new DateWriter(columnName, ComplexTypeName, dateOnlyRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer = default(T) switch\n            {\n                DateOnly => new DateOnlyParameterWriter(this),\n                DateTime => new DateTimeParameterWriter(this),\n                _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\")\n            };\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(DateOnly);\n        }\n\n        private static ushort DateOnlyToDays(DateOnly value)\n        {\n            if (value == default)\n                return 0;\n\n            var days = value.DayNumber - UnixEpoch.DayNumber;\n            if (days < 0 || days > ushort.MaxValue)\n                throw new OverflowException(\"The value must be in range [1970-01-01, 2149-06-06].\");\n\n            return (ushort)days;\n        }\n\n        partial class DateReader : StructureReaderBase<DateOnly>\n        {\n            public DateReader(int rowCount)\n                : base(sizeof(ushort), rowCount)\n            {\n            }\n\n            protected override DateOnly ReadElement(ReadOnlySpan<byte> source)\n            {\n                var value = BitConverter.ToUInt16(source);\n                if (value == 0)\n                    return default;\n\n                return UnixEpoch.AddDays(value);\n            }\n\n            protected override IClickHouseTableColumn<DateOnly> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<DateOnly> buffer)\n            {\n                return new DateOnlyTableColumn(buffer);\n            }\n        }\n\n        partial class DateWriter : StructureWriterBase<DateOnly, ushort>\n        {\n            public DateWriter(string columnName, string columnType, IReadOnlyList<DateOnly> rows)\n                : base(columnName, columnType, sizeof(ushort), rows)\n            {\n            }\n\n            protected override ushort Convert(DateOnly value)\n            {\n                return DateOnlyToDays(value);\n            }\n        }\n\n        private sealed class DateOnlyParameterWriter : IClickHouseParameterWriter<DateOnly>\n        {\n            private readonly DateTypeInfo _typeInfo;\n\n            public DateOnlyParameterWriter(DateTypeInfo typeInfo)\n            {\n                _typeInfo = typeInfo;\n            }\n\n            public bool TryCreateParameterValueWriter(DateOnly value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                var strVal = ValueToString(value);\n                if (isNested)\n                    strVal = $\"'{strVal}'\";\n\n                valueWriter = new SimpleLiteralValueWriter(strVal.AsMemory());\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, DateOnly value)\n            {\n                var strVal = ValueToString(value);\n                return queryBuilder.Append('\\'').Append(strVal).Append(\"'::\").Append(_typeInfo.ComplexTypeName);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return writeValue(queryBuilder, _typeInfo, FunctionHelper.Apply);\n            }\n\n            [MethodImpl(MethodImplOptions.AggressiveInlining)]\n            private static string ValueToString(DateOnly value)\n            {\n                if (value == default)\n                    return DefaultValueStr;\n\n                if (value < UnixEpoch || value > MaxDateOnlyValue)\n                    throw new OverflowException($\"The value must be in range [{DateTime.UnixEpoch}, {MaxDateOnlyValue}].\");\n\n                return value.ToString(FormatStr, CultureInfo.InvariantCulture);\n            }\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DateTypeInfo.NetCoreApp3.1.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if NETCOREAPP3_1_OR_GREATER && !NET6_0_OR_GREATER\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    partial class DateTypeInfo\n    {\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (typeof(T) != typeof(DateTime))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new DateWriter(columnName, ComplexTypeName, (IReadOnlyList<DateTime>)rows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            if (type == typeof(DateTime))\n                return (IClickHouseParameterWriter<T>)(object)new DateTimeParameterWriter(this);\n\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n        }\n\n        private static ushort DateTimeToDays(DateTime value)\n        {\n            if (value == default)\n                return 0;\n\n            var days = (value - DateTime.UnixEpoch).TotalDays;\n            if (days < 0 || days > ushort.MaxValue)\n                throw new OverflowException(\"The value must be in range [1970-01-01, 2149-06-06].\");\n\n            return (ushort)days;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(DateTime);\n        }\n\n        partial class DateReader : StructureReaderBase<DateTime>\n        {\n            static readonly DateTime UnixEpochUnspecified = new DateTime(DateTime.UnixEpoch.Ticks, DateTimeKind.Unspecified);\n\n            public DateReader(int rowCount)\n                : base(sizeof(ushort), rowCount)\n            {\n            }\n\n            protected override DateTime ReadElement(ReadOnlySpan<byte> source)\n            {\n                var value = BitConverter.ToUInt16(source);\n                if (value == 0)\n                    return default;\n\n                return UnixEpochUnspecified.AddDays(value);\n            }\n        }\n\n        partial class DateWriter : StructureWriterBase<DateTime, ushort>\n        {\n            public DateWriter(string columnName, string columnType, IReadOnlyList<DateTime> rows)\n                : base(columnName, columnType, sizeof(ushort), rows)\n            {\n            }\n\n            protected override ushort Convert(DateTime value)\n            {\n                return DateTimeToDays(value);\n            }\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DateTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Runtime.CompilerServices;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed partial class DateTypeInfo : SimpleTypeInfo\n    {\n        public const string FormatStr = \"yyyy-MM-dd\";\n\n        private const string DefaultValueStr = \"1970-01-01\";\n\n        private static readonly DateTime MaxDateTimeValue = DateTime.UnixEpoch.AddDays(ushort.MaxValue);\n\n        public DateTypeInfo()\n            : base(\"Date\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new DateReader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(ushort), rowCount);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Date;\n        }\n\n        private sealed partial class DateReader\n        {\n        }\n\n        private sealed partial class DateWriter\n        {\n        }\n\n        private sealed class DateTimeParameterWriter : IClickHouseParameterWriter<DateTime>\n        {\n            private readonly DateTypeInfo _typeInfo;\n\n            public DateTimeParameterWriter(DateTypeInfo typeInfo)\n            {\n                _typeInfo = typeInfo;\n            }\n\n            public bool TryCreateParameterValueWriter(DateTime value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                var strVal = ValueToString(value);\n                if (isNested)\n                    strVal = $\"'{strVal}'\";\n\n                valueWriter = new SimpleLiteralValueWriter(strVal.AsMemory());\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, DateTime value)\n            {\n                var strVal = ValueToString(value);\n                return queryBuilder.Append('\\'').Append(strVal).Append(\"'::\").Append(_typeInfo.ComplexTypeName);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return writeValue(queryBuilder, _typeInfo, FunctionHelper.Apply);\n            }\n\n            [MethodImpl(MethodImplOptions.AggressiveInlining)]\n            private static string ValueToString(DateTime value)\n            {\n                if (value == default)\n                    return DefaultValueStr;\n\n                if (value < DateTime.UnixEpoch || value > MaxDateTimeValue)\n                    throw new OverflowException($\"The value must be in range [{DateTime.UnixEpoch}, {MaxDateTimeValue}].\");\n\n                return value.ToString(FormatStr, CultureInfo.InvariantCulture);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Decimal128TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Decimal128TypeInfo : DecimalTypeInfoBase\n    {\n        private const int Precision = 38;\n\n        public override int TypeArgumentsCount => Math.Min(1, base.TypeArgumentsCount);\n\n        public Decimal128TypeInfo()\n            : base(\"Decimal128\")\n        {\n        }\n\n        private Decimal128TypeInfo(string typeName, string complexTypeName, int scale)\n            : base(typeName, complexTypeName, Precision, scale)\n        {\n        }\n\n        protected override DecimalTypeInfoBase CloneWithOptions(string complexTypeName, int? precision, int scale)\n        {\n            if (precision != null)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The value of the precision can not be redefined for the type \\\"{TypeName}\\\".\");\n\n            return new Decimal128TypeInfo(TypeName, complexTypeName, scale);\n        }\n\n        public override object GetTypeArgument(int index)\n        {\n            if (base.TypeArgumentsCount == 0)\n                return base.GetTypeArgument(index);\n\n            if (index != 0)\n                throw new IndexOutOfRangeException();\n\n            return base.GetTypeArgument(1);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Decimal32TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Decimal32TypeInfo : DecimalTypeInfoBase\n    {\n        private const int Precision = 9;\n\n        public override int TypeArgumentsCount => Math.Min(1, base.TypeArgumentsCount);\n\n        public Decimal32TypeInfo()\n            : base(\"Decimal32\")\n        {\n        }\n\n        private Decimal32TypeInfo(string typeName, string complexTypeName, int scale)\n            : base(typeName, complexTypeName, Precision, scale)\n        {\n        }\n\n        protected override DecimalTypeInfoBase CloneWithOptions(string complexTypeName, int? precision, int scale)\n        {\n            if (precision != null)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The value of the precision can not be redefined for the type \\\"{TypeName}\\\".\");\n\n            return new Decimal32TypeInfo(TypeName, complexTypeName, scale);\n        }\n\n        public override object GetTypeArgument(int index)\n        {\n            if (base.TypeArgumentsCount == 0)\n                return base.GetTypeArgument(index);\n\n            if (index != 0)\n                throw new IndexOutOfRangeException();\n\n            return base.GetTypeArgument(1);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Decimal64TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Decimal64TypeInfo : DecimalTypeInfoBase\n    {\n        private const int Precision = 18;\n\n        public override int TypeArgumentsCount => Math.Min(1, base.TypeArgumentsCount);\n\n        public Decimal64TypeInfo()\n            : base(\"Decimal64\")\n        {\n        }\n\n        private Decimal64TypeInfo(string typeName, string complexTypeName, int scale)\n            : base(typeName, complexTypeName, Precision, scale)\n        {\n        }\n\n        protected override DecimalTypeInfoBase CloneWithOptions(string complexTypeName, int? precision, int scale)\n        {\n            if (precision != null)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The value of the precision can not be redefined for the type \\\"{TypeName}\\\".\");\n\n            return new Decimal64TypeInfo(TypeName, complexTypeName, scale);\n        }\n\n        public override object GetTypeArgument(int index)\n        {\n            if (base.TypeArgumentsCount == 0)\n                return base.GetTypeArgument(index);\n\n            if (index != 0)\n                throw new IndexOutOfRangeException();\n\n            return base.GetTypeArgument(1);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DecimalTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class DecimalTableColumn : IClickHouseTableColumn<decimal>\n    {\n        private const int MaxDecimalScale = 28;\n        private static readonly uint[] Scales = {10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000};\n\n        private readonly ReadOnlyMemory<uint> _buffer;\n        private readonly int _elementSize;\n        private readonly byte _scale;\n\n        public int RowCount => _buffer.Length / _elementSize;\n\n        public decimal DefaultValue => decimal.Zero;\n\n        public DecimalTableColumn(ReadOnlyMemory<uint> buffer, int elementSize, byte scale)\n        {\n            if (elementSize != 1 && elementSize != 2 && elementSize != 4)\n                throw new ArgumentOutOfRangeException(nameof(elementSize));\n\n            _buffer = buffer;\n            _elementSize = elementSize;\n            _scale = scale;\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        public decimal GetValue(int index)\n        {\n            var startIndex = index * _elementSize;\n            var span = _buffer.Span;\n            var lowLow = span[startIndex];\n            bool isNegative;\n            uint lowHigh, highLow;\n            if (_elementSize == 1)\n            {\n                isNegative = (lowLow & unchecked((uint) int.MinValue)) != 0;\n                if (isNegative)\n                    lowLow = unchecked(0 - lowLow);\n\n                lowHigh = highLow = 0;\n            }\n            else if (_elementSize == 2)\n            {\n                lowHigh = span[startIndex + 1];\n\n                isNegative = (lowHigh & unchecked((uint) int.MinValue)) != 0;\n                if (isNegative)\n                {\n                    if (lowLow == 0)\n                    {\n                        lowHigh = unchecked(0 - lowHigh);\n                    }\n                    else\n                    {\n                        lowLow = unchecked(0 - lowLow);\n                        lowHigh ^= uint.MaxValue;\n                    }\n                }\n\n                highLow = 0;\n            }\n            else\n            {\n                lowHigh = span[startIndex + 1];\n                highLow = span[startIndex + 2];\n                var highHigh = span[startIndex + 3];\n\n                isNegative = (highHigh & unchecked((uint) int.MinValue)) != 0;\n                if (isNegative)\n                {\n                    if (lowLow == 0)\n                    {\n                        if (lowHigh == 0)\n                        {\n                            if (highLow == 0)\n                            {\n                                highHigh = unchecked(0 - highHigh);\n                            }\n                            else\n                            {\n                                highLow = unchecked(0 - highLow);\n                                highHigh ^= uint.MaxValue;\n                            }\n                        }\n                        else\n                        {\n                            lowHigh = unchecked(0 - lowHigh);\n                            highLow ^= uint.MaxValue;\n                            highHigh ^= uint.MaxValue;\n                        }\n                    }\n                    else\n                    {\n                        lowLow = unchecked(0 - lowLow);\n                        lowHigh ^= uint.MaxValue;\n                        highLow ^= uint.MaxValue;\n                        highHigh ^= uint.MaxValue;\n                    }\n                }\n\n                if (highHigh != 0 || _scale > MaxDecimalScale)\n                {\n                    uint mll = lowLow, mlh = lowHigh, mhl = highLow, mhh = highHigh;\n                    byte scale = _scale;\n                    var deltaScale = _scale > MaxDecimalScale ? _scale - MaxDecimalScale : 0;\n                    // Attempt to rescale the value without loss of significant digits. It should work for values written by this DB driver.\n                    while (mhh != 0 || deltaScale != 0)\n                    {\n                        int scaleIndex = 0;\n                        if (deltaScale > 0)\n                        {\n                            scaleIndex = Math.Min(deltaScale, Scales.Length) - 1;\n                            deltaScale -= (byte) (scaleIndex + 1);\n                        }\n                        else\n                        {\n                            for (; scaleIndex < Scales.Length - 1; scaleIndex++)\n                            {\n                                if (mhh < Scales[scaleIndex])\n                                    break;\n                            }\n                        }\n\n                        var mul = Scales[scaleIndex];\n\n                        ulong rem = mhh % mul;\n                        mhh /= mul;\n\n                        var val = mhl | rem << 32;\n                        rem = val % mul;\n                        mhl = (uint) (val / mul);\n\n                        val = mlh | rem << 32;\n                        rem = val % mul;\n                        mlh = (uint) (val / mul);\n\n                        val = mll | rem << 32;\n                        rem = val % mul;\n                        if (rem != 0 || scaleIndex >= scale)\n                            throw new NotSupportedException($\"Value 0x{highHigh:x8}_{highLow:x8}_{lowHigh:x8}_{lowLow:x8} is too long for the type \\\"{typeof(decimal).FullName}\\\".\");\n\n                        mll = (uint) (val / mul);\n                        scale = (byte) (scale - scaleIndex - 1);\n                    }\n\n                    return new decimal(unchecked((int) mll), unchecked((int) mlh), unchecked((int) mhl), isNegative, scale);\n                }\n            }\n\n            return new decimal(unchecked((int) lowLow), unchecked((int) lowHigh), unchecked((int) highLow), isNegative, _scale);\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(decimal?))\n                return (IClickHouseTableColumn<T>)(object)new NullableStructTableColumn<decimal>(null, this);\n\n            return null;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DecimalTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class DecimalTypeInfo : DecimalTypeInfoBase\n    {\n        public DecimalTypeInfo()\n            : base(\"Decimal\")\n        {\n        }\n\n        private DecimalTypeInfo(string typeName, string complexTypeName, int precision, int scale)\n            : base(typeName, complexTypeName, precision, scale)\n        {\n        }\n\n        protected override DecimalTypeInfoBase CloneWithOptions(string complexTypeName, int? precision, int scale)\n        {\n            if (precision == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The precision is required for the type \\\"{TypeName}\\\".\");\n\n            return new DecimalTypeInfo(TypeName, complexTypeName, precision.Value, scale);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/DecimalTypeInfoBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Buffers.Binary;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal abstract class DecimalTypeInfoBase : IClickHouseColumnTypeInfo\n    {\n        public const byte DefaultPrecision = 38, DefaultScale = 9;\n\n        private readonly int? _precision;\n        private readonly int? _scale;\n\n        public string ComplexTypeName { get; }\n\n        public string TypeName { get; }\n\n        public int GenericArgumentsCount => 0;\n\n        public virtual int TypeArgumentsCount => (_precision == null ? 0 : 1) + (_scale == null ? 0 : 1);\n\n        protected DecimalTypeInfoBase(string typeName)\n        {   \n            TypeName = typeName;\n            ComplexTypeName = typeName;\n        }\n\n        protected DecimalTypeInfoBase(string typeName, string complexTypeName, int precision, int scale)\n        {\n            if (precision < 1 || precision > 38)\n                throw new ArgumentOutOfRangeException(nameof(precision), \"The precision must be in the range [1:38].\");\n            if (scale < 0)\n                throw new ArgumentOutOfRangeException(nameof(scale), \"The scale must be a non-negative number.\");\n            if (scale > precision)\n                throw new ArgumentOutOfRangeException(nameof(scale), \"The scale must not be greater than the precision.\");\n\n            TypeName = typeName;\n            ComplexTypeName = complexTypeName;\n            _precision = precision;\n            _scale = scale;\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            if (_precision == null || _scale == null)\n            {\n                if (_precision == null && _scale == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Both scale and precision are required for the type \\\"{TypeName}\\\".\");\n\n                if (_scale == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Scale is required for the type \\\"{TypeName}\\\".\");\n\n                // Currently there is no implementation which requires only the precision value\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Precision is required for the type \\\"{TypeName}\\\".\");\n            }\n\n            return new DecimalReader(_precision.Value, _scale.Value, rowCount);\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            if (_precision == null || _scale == null)\n            {\n                if (_precision == null && _scale == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Both scale and precision are required for the type \\\"{TypeName}\\\".\");\n\n                if (_scale == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Scale is required for the type \\\"{TypeName}\\\".\");\n\n                // Currently there is no implementation which requires only the precision value\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Precision is required for the type \\\"{TypeName}\\\".\");\n            }\n\n            return new SimpleSkippingColumnReader(GetElementSize(_precision.Value), rowCount);\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (_precision == null && _scale == null)\n            {\n                var specifiedType = CloneWithOptions(string.Format(CultureInfo.InvariantCulture, \"Decimal128({0})\", DefaultScale), DefaultPrecision, DefaultScale);\n                return specifiedType.CreateColumnWriter(columnName, rows, columnSettings);\n            }\n\n            if (_scale == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Scale is required for the type \\\"{TypeName}\\\".\");\n\n            if (_precision == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Precision is required for the type \\\"{TypeName}\\\".\");\n\n            var type = typeof(T);\n            IReadOnlyList<decimal> decimalRows;\n            if (type == typeof(decimal))\n                decimalRows = (IReadOnlyList<decimal>)rows;\n            else if (type == typeof(long))\n                decimalRows = MappedReadOnlyList<long, decimal>.Map((IReadOnlyList<long>)rows, v => v);\n            else if (type == typeof(ulong))\n                decimalRows = MappedReadOnlyList<ulong, decimal>.Map((IReadOnlyList<ulong>)rows, v => v);\n            else if (type == typeof(int))\n                decimalRows = MappedReadOnlyList<int, decimal>.Map((IReadOnlyList<int>)rows, v => v);\n            else if (type == typeof(uint))\n                decimalRows = MappedReadOnlyList<uint, decimal>.Map((IReadOnlyList<uint>)rows, v => v);\n            else if (type == typeof(short))\n                decimalRows = MappedReadOnlyList<short, decimal>.Map((IReadOnlyList<short>)rows, v => v);\n            else if (type == typeof(ushort))\n                decimalRows = MappedReadOnlyList<ushort, decimal>.Map((IReadOnlyList<ushort>)rows, v => v);\n            else if (type == typeof(sbyte))\n                decimalRows = MappedReadOnlyList<sbyte, decimal>.Map((IReadOnlyList<sbyte>)rows, v => v);\n            else if (type == typeof(byte))\n                decimalRows = MappedReadOnlyList<byte, decimal>.Map((IReadOnlyList<byte>)rows, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new DecimalWriter(columnName, ComplexTypeName, _precision.Value, _scale.Value, decimalRows);\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            if (_precision == null && _scale == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Both scale and precision are required for the type \\\"{TypeName}\\\".\");\n\n            if (_scale == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Scale is required for the type \\\"{TypeName}\\\".\");\n\n            if (_precision == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"Precision is required for the type \\\"{TypeName}\\\".\");\n\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            var binaryWriter = new DecimalWriter(\"Value\", ComplexTypeName, _precision.Value, _scale.Value, Array.Empty<decimal>());\n            object writer = default(T) switch\n            {\n                decimal _ => new DecimalParameterWriter<decimal>(this, binaryWriter, v => v),\n                long _ => new DecimalParameterWriter<long>(this, binaryWriter, v => v),\n                ulong _ => new DecimalParameterWriter<ulong>(this, binaryWriter, v => v),\n                int _ => new DecimalParameterWriter<int>(this, binaryWriter, v => v),\n                uint _ => new DecimalParameterWriter<uint>(this, binaryWriter, v => v),\n                short _ => new DecimalParameterWriter<short>(this, binaryWriter, v => v),\n                ushort _ => new DecimalParameterWriter<ushort>(this, binaryWriter, v => v),\n                sbyte _ => new DecimalParameterWriter<sbyte>(this, binaryWriter, v => v),\n                byte _ => new DecimalParameterWriter<byte>(this, binaryWriter, v => v),\n                _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\"),\n            };\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            int? precision = null;\n            int scale;\n            if (options.Count == 1)\n            {\n                if (!int.TryParse(options[0].Span, NumberStyles.Integer, CultureInfo.InvariantCulture, out scale) || scale < 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The scale value for the type \\\"{TypeName}\\\" must be a non-negative number.\");\n            }\n            else if (options.Count == 2)\n            {\n                if (!int.TryParse(options[0].Span, NumberStyles.Integer, CultureInfo.InvariantCulture, out var firstValue) || firstValue <= 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The first parameter in options (precision) for the type \\\"{TypeName}\\\" must be a positive number.\");\n\n                precision = firstValue;\n\n                if (!int.TryParse(options[1].Span, NumberStyles.Integer, CultureInfo.InvariantCulture, out scale) || scale < 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The second parameter in options (scale) for the type \\\"{TypeName}\\\" must be a non-negative number.\");\n            }\n            else\n            {\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"Too many options for the type \\\"{TypeName}\\\".\");\n            }\n\n            if (_precision != null && precision != null)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The value of the precision can not be redefined for the type \\\"{TypeName}\\\".\");\n\n            var complexTypeName = TypeName + \"(\" + string.Join(\", \", options) + \")\";\n            return CloneWithOptions(complexTypeName, precision, scale);\n        }\n\n        public Type GetFieldType()\n        {\n            return typeof(decimal);\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Decimal;\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have generic arguments.\");\n        }\n\n        public virtual object GetTypeArgument(int index)\n        {\n            if (_precision == null && _scale == null)\n                throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have arguments.\");\n\n            switch (index)\n            {\n                case 0:\n                    if (_precision == null)\n                    {\n                        Debug.Assert(_scale != null);\n                        return _scale;\n                    }\n                    else\n                    {\n                        return _precision;\n                    }\n\n                case 1:\n                    if (_scale == null || _precision == null)\n                        goto default;\n\n                    return _scale;\n\n                default:\n                    throw new IndexOutOfRangeException();\n            }\n        }\n\n        protected abstract DecimalTypeInfoBase CloneWithOptions(string complexTypeName, int? precision, int scale);\n\n        private static int GetElementSize(int precision)\n        {\n            if (precision <= 9)\n                return 4;\n            if (precision <= 18)\n                return 8;\n\n            Debug.Assert(precision <= 38);\n            return 16;\n        }\n\n        private sealed class DecimalReader : IClickHouseColumnReader\n        {\n            private readonly int _rowCount;\n            private readonly int _elementSize;\n            private readonly byte _scale;\n\n            private readonly uint[] _values;\n\n            private int _position;\n\n            public DecimalReader(int precision, int scale, int rowCount)\n            {\n                _rowCount = rowCount;\n                _elementSize = GetElementSize(precision);\n                _values = new uint[_elementSize / 4 * rowCount];\n                _scale = (byte) scale;\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                var elementPosition = _position * sizeof(uint) / _elementSize;\n                if (elementPosition >= _rowCount)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                var byteLength = (int) Math.Min((_rowCount - elementPosition) * _elementSize, sequence.Length - sequence.Length % _elementSize);\n                var uintLength = byteLength / sizeof(uint);\n\n                var targetSpan = MemoryMarshal.AsBytes(new Span<uint>(_values, _position, uintLength));\n                Debug.Assert(targetSpan.Length == byteLength);\n\n                sequence.Slice(0, byteLength).CopyTo(targetSpan);\n\n                _position += uintLength;\n                return new SequenceSize(byteLength, byteLength / _elementSize);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                return EndReadInternal();\n            }\n\n            private DecimalTableColumn EndReadInternal()\n            {\n                var memory = new ReadOnlyMemory<uint>(_values, 0, _position);\n                return new DecimalTableColumn(memory, _elementSize / 4, _scale);\n            }\n        }\n\n        private sealed class DecimalWriter : StructureWriterBase<decimal>\n        {\n            private const byte MaxDecimalScale = 28;\n            private static readonly uint[] Scales = { 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000 };\n\n            private readonly byte _scale;\n\n            public new int ElementSize { get => base.ElementSize; }\n\n            public DecimalWriter(string columnName, string columnType, int precision, int scale, IReadOnlyList<decimal> rows)\n                : base(columnName, columnType, GetElementSize(precision), rows)\n            {\n                _scale = (byte) scale;\n            }\n\n            public void WriteDecimal(Span<byte> writeTo, decimal value)\n            {\n                WriteElement(writeTo, value);\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in decimal value)\n            {\n                var rescaledValue = Math.Round(value, Math.Min(_scale, MaxDecimalScale), MidpointRounding.AwayFromZero);\n                \n#if NET5_0_OR_GREATER\n                Span<int> bits = stackalloc int[4];\n                decimal.GetBits(rescaledValue, bits);\n#else\n                var bits = decimal.GetBits(rescaledValue);\n#endif\n                uint lowLow = unchecked((uint) bits[0]);\n                uint lowHigh = unchecked((uint) bits[1]);\n                uint highLow = unchecked((uint) bits[2]);\n                uint highHigh = 0;\n\n                bool isNegative = (bits[3] & int.MinValue) != 0;\n                int scale = (bits[3] & ~int.MinValue) >> 16;\n\n                var deltaScale = _scale - scale;\n                if (deltaScale < 0)\n                    throw new InvalidOperationException(\"Internal error: unexpected scale difference.\");\n\n                bool overflow = false;\n                while (deltaScale > 0)\n                {\n                    var iterationScale = Math.Min(deltaScale, Scales.Length);\n                    deltaScale -= iterationScale;\n                    var multiplier = (ulong) Scales[iterationScale - 1];\n\n                    ulong lowLowMul = lowLow * multiplier;\n                    ulong lowHighMul = lowHigh * multiplier;\n                    ulong highLowMul = highLow * multiplier;\n                    ulong highHighMul = highHigh * multiplier;\n\n                    lowLow = unchecked((uint) lowLowMul);\n                    lowHigh = unchecked((uint) lowHighMul);\n                    highLow = unchecked((uint) highLowMul);\n                    highHigh = unchecked((uint) highHighMul);\n\n                    var val = lowLowMul >> 32;\n                    if (val != 0)\n                    {\n                        val += lowHigh;\n                        lowHigh = unchecked((uint) val);\n\n                        val >>= 32;\n                        if (val != 0)\n                        {\n                            val += highLow;\n                            highLow = unchecked((uint) val);\n\n                            val >>= 32;\n                            if (val != 0)\n                            {\n                                val += highHigh;\n                                highHigh = unchecked((uint) val);\n\n                                val >>= 32;\n                                if (val != 0)\n                                {\n                                    overflow = true;\n                                    break;\n                                }\n                            }\n                        }\n                    }\n\n                    val = lowHighMul >> 32;\n                    if (val != 0)\n                    {\n                        val += highLow;\n                        highLow = unchecked((uint)val);\n\n                        val >>= 32;\n                        if (val != 0)\n                        {\n                            val += highHigh;\n                            highHigh = unchecked((uint)val);\n\n                            val >>= 32;\n                            if (val != 0)\n                            {\n                                overflow = true;\n                                break;\n                            }\n                        }\n                    }\n\n                    val = highLowMul >> 32;\n                    if (val != 0)\n                    {\n                        val += highHigh;\n                        highHigh = unchecked((uint)val);\n\n                        val >>= 32;\n                        if (val != 0)\n                        {\n                            overflow = true;\n                            break;\n                        }\n                    }\n\n                    val = highHighMul >> 32;\n                    if (val != 0)\n                    {\n                        overflow = true;\n                        break;\n                    }\n                }\n\n                if (!overflow)\n                {\n                    if (isNegative)\n                    {\n                        lowLow = unchecked(0 - lowLow);\n                        uint max = lowLow == 0 ? 0 : uint.MaxValue;\n\n                        lowHigh = unchecked(max - lowHigh);\n                        if (lowHigh != 0 && max == 0)\n                            max = uint.MaxValue;\n\n                        highLow = unchecked(max - highLow);\n                        if (highLow != 0 && max == 0)\n                            max = uint.MaxValue;\n\n                        highHigh = unchecked(max - highHigh);\n\n                        if (ElementSize == 4)\n                            overflow = highHigh != uint.MaxValue || highLow != uint.MaxValue || lowHigh != uint.MaxValue || (lowLow & unchecked((uint) int.MinValue)) == 0;\n                        else if (ElementSize == 8)\n                            overflow = highHigh != uint.MaxValue || highLow != uint.MaxValue || (lowHigh & unchecked((uint) int.MinValue)) == 0;\n                        else\n                            overflow = (highHigh & unchecked((uint) int.MinValue)) == 0;\n\n                        if (overflow && rescaledValue == 0)\n                            overflow = false;\n                    }\n                    else\n                    {\n                        if (ElementSize == 4)\n                            overflow = highHigh != 0 || highLow != 0 || lowHigh != 0 || (lowLow & unchecked((uint) int.MinValue)) != 0;\n                        else if (ElementSize == 8)\n                            overflow = highHigh != 0 || highLow != 0 || (lowHigh & unchecked((uint) int.MinValue)) != 0;\n                        else\n                            overflow = (highHigh & unchecked((uint) int.MinValue)) != 0;\n                    }\n                }\n\n                if (overflow)\n                    throw new OverflowException($\"The decimal value is too big and can't be written to the column of type \\\"{ColumnType}\\\".\");\n\n                var success = BitConverter.TryWriteBytes(writeTo, lowLow);\n                Debug.Assert(success);\n                if (ElementSize == 4)\n                    return;\n\n                success = BitConverter.TryWriteBytes(writeTo.Slice(4), lowHigh);\n                Debug.Assert(success);\n                if (ElementSize == 8)\n                    return;\n\n                Debug.Assert(ElementSize == 16);\n                success = BitConverter.TryWriteBytes(writeTo.Slice(8), highLow);\n                Debug.Assert(success);\n                success = BitConverter.TryWriteBytes(writeTo.Slice(12), highHigh);\n                Debug.Assert(success);\n            }\n        }\n\n        private sealed class DecimalParameterWriter<T> : IClickHouseParameterWriter<T>\n        {\n            private readonly DecimalTypeInfoBase _type;\n            private readonly DecimalWriter _binaryWriter;\n            private readonly Func<T, decimal> _convert;\n\n            public DecimalParameterWriter(DecimalTypeInfoBase type, DecimalWriter binaryWriter, Func<T, decimal> convert)\n            {\n                _type = type;\n                _binaryWriter = binaryWriter;\n                _convert = convert;\n            }\n\n            public bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                Memory<byte> binaryValue = new byte[_binaryWriter.ElementSize];\n                GetBytes(value, binaryValue.Span);\n\n                int lastNonZeroIdx = 0;\n                for (int i = 0; i < binaryValue.Length; i++)\n                {\n                    if (binaryValue.Span[i] == 0)\n                        continue;\n\n                    lastNonZeroIdx = i;\n                }\n\n                binaryValue = binaryValue.Slice(0, lastNonZeroIdx + 1);\n                valueWriter = new HexStringLiteralValueWriter(binaryValue, isNested);\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, T value)\n            {\n                Span<byte> buffer = stackalloc byte[_binaryWriter.ElementSize];\n                GetBytes(value, buffer);\n\n                // Not all decimal values can be parsed:\n                // > Real value ranges that can be stored in memory are a bit larger than specified above, which are checked only on conversion from a string.\n                // But reinterpret_cast lets obtain any Decimal value\n                queryBuilder.Append(\"reinterpret(\");\n                HexStringParameterWriter.Interpolate(queryBuilder, buffer);\n\n                queryBuilder.Append(\", '\");\n                queryBuilder.Append(_binaryWriter.ColumnType);\n                return queryBuilder.Append(\"')\");\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                var typeName = $\"FixedString({_binaryWriter.ElementSize.ToString(CultureInfo.InvariantCulture)})\";\n                var type = typeInfoProvider.GetTypeInfo(typeName);\n\n                return writeValue(queryBuilder, type, (sb, realWrite) =>\n                {\n                    sb.Append(\"reinterpret(\");\n                    realWrite(sb);\n                    sb.Append(\", '\");\n                    sb.Append(_binaryWriter.ColumnType);\n                    return queryBuilder.Append(\"')\");\n                });\n            }\n\n            private void GetBytes(T value, Span<byte> buffer)\n            {\n                var decValue = _convert(value);\n                _binaryWriter.WriteDecimal(buffer, decValue);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/EmptyParameterValueWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Protocol;\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class EmptyParameterValueWriter : IClickHouseParameterValueWriter\n    {\n        public static readonly EmptyParameterValueWriter Instance = new EmptyParameterValueWriter();\n\n        public int Length => 0;\n\n        private EmptyParameterValueWriter()\n        {\n        }\n\n        public int Write(Memory<byte> buffer)\n        {\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Enum16TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Enum16TypeInfo : EnumTypeInfoBase<short>\n    {\n        public Enum16TypeInfo()\n            : base(\"Enum16\")\n        {\n        }\n\n        private Enum16TypeInfo(string typeName, string complexTypeName, IEnumerable<KeyValuePair<string, short>> values)\n            : base(typeName, complexTypeName, values)\n        {\n        }\n\n        protected override EnumColumnReaderBase CreateColumnReader(StructureReaderBase<short> internalReader, IReadOnlyDictionary<short, string> reversedEnumMap)\n        {\n            return new EnumColumnReader(internalReader, reversedEnumMap);\n        }\n\n        protected override SimpleSkippingColumnReader CreateInternalSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(short), rowCount);\n        }\n\n        protected override IClickHouseColumnTypeInfo CreateDetailedTypeInfo(string complexTypeName, IEnumerable<KeyValuePair<string, short>> values)\n        {\n            return new Enum16TypeInfo(TypeName, complexTypeName, values);\n        }\n\n        protected override StructureReaderBase<short> CreateInternalColumnReader(int rowCount)\n        {\n            return new Int16TypeInfo.Int16Reader(rowCount);\n        }\n\n        protected override IClickHouseColumnWriter CreateInternalColumnWriter<T>(string columnName, IReadOnlyList<T> rows)\n        {\n            var type = typeof(T);\n            IReadOnlyList<short> shortRows;\n            if (type == typeof(short))\n                shortRows = (IReadOnlyList<short>)rows;\n            else if (type == typeof(byte))\n                shortRows = MappedReadOnlyList<byte, short>.Map((IReadOnlyList<byte>)rows, v => v);\n            else if (type == typeof(sbyte))\n                shortRows = MappedReadOnlyList<sbyte, short>.Map((IReadOnlyList<sbyte>)rows, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{TypeName}\\\".\");\n\n            return new Int16TypeInfo.Int16Writer(columnName, ComplexTypeName, shortRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            // TODO: ClickHouseDbType.Enum is not supported in DefaultTypeInfoProvider.GetTypeInfo\n            if (_enumMap == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, \"The list of items is not specified.\");\n\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer;\n            if (type == typeof(string))\n            {\n                writer = new EnumParameterWriter(this);\n            }\n            else\n            {\n                writer = default(T) switch\n                {\n                    short _ => new SimpleParameterWriter<short>(this),\n                    byte _ => new SimpleParameterWriter<byte>(this),\n                    sbyte _ => new SimpleParameterWriter<sbyte>(this),\n                    _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\"),\n                };\n            }\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        protected override bool TryParse(ReadOnlySpan<char> text, out short value)\n        {\n            return short.TryParse(text, out value);\n        }\n\n        private sealed class EnumColumnReader : EnumColumnReaderBase\n        {\n            public EnumColumnReader(StructureReaderBase<short> internalReader, IReadOnlyDictionary<short, string> reversedEnumMap)\n                : base(internalReader, reversedEnumMap)\n            {\n            }\n\n            protected override EnumTableColumnDispatcherBase CreateColumnDispatcher(IClickHouseTableColumn<short> column, IReadOnlyDictionary<short, string> reversedEnumMap)\n            {\n                return new EnumTableColumnDispatcher(column, reversedEnumMap);\n            }\n        }\n\n        private sealed class EnumTableColumnDispatcher : EnumTableColumnDispatcherBase\n        {\n            public EnumTableColumnDispatcher(IClickHouseTableColumn<short> column, IReadOnlyDictionary<short, string> reversedEnumMap)\n                : base(column, reversedEnumMap)\n            {\n            }\n\n            protected override bool TryMap<TEnum>(IClickHouseEnumConverter<TEnum> enumConverter, short value, string stringValue, out TEnum enumValue)\n            {\n                return enumConverter.TryMap(value, stringValue, out enumValue);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Enum8TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Enum8TypeInfo : EnumTypeInfoBase<sbyte>\n    {\n        public Enum8TypeInfo()\n            : base(\"Enum8\")\n        {\n        }\n\n        private Enum8TypeInfo(string typeName, string complexTypeName, IEnumerable<KeyValuePair<string, sbyte>> values)\n            : base(typeName, complexTypeName, values)\n        {\n        }\n\n        protected override EnumColumnReaderBase CreateColumnReader(StructureReaderBase<sbyte> internalReader, IReadOnlyDictionary<sbyte, string> reversedEnumMap)\n        {\n            return new EnumColumnReader(internalReader, reversedEnumMap);\n        }\n\n        protected override IClickHouseColumnTypeInfo CreateDetailedTypeInfo(string complexTypeName, IEnumerable<KeyValuePair<string, sbyte>> values)\n        {\n            return new Enum8TypeInfo(TypeName, complexTypeName, values);\n        }\n\n        protected override StructureReaderBase<sbyte> CreateInternalColumnReader(int rowCount)\n        {\n            return new Int8TypeInfo.Int8Reader(rowCount);\n        }\n\n        protected override SimpleSkippingColumnReader CreateInternalSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(byte), rowCount);\n        }\n\n        protected override IClickHouseColumnWriter CreateInternalColumnWriter<T>(string columnName, IReadOnlyList<T> rows)\n        {\n            if (typeof(T) != typeof(sbyte))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{TypeName}\\\".\");\n\n            return new Int8TypeInfo.Int8Writer(columnName, ComplexTypeName, (IReadOnlyList<sbyte>)rows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            // TODO: ClickHouseDbType.Enum is not supported in DefaultTypeInfoProvider.GetTypeInfo\n            if (_enumMap == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, \"The list of items is not specified.\");\n\n            var type = typeof(T);\n            if (typeof(T) == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer;\n            if (type == typeof(string))\n                writer = new EnumParameterWriter(this);\n            else if (type == typeof(sbyte))\n                writer = new SimpleParameterWriter<sbyte>(this);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        protected override bool TryParse(ReadOnlySpan<char> text, out sbyte value)\n        {\n            return sbyte.TryParse(text, out value);\n        }\n\n        private sealed class EnumColumnReader : EnumColumnReaderBase\n        {\n            public EnumColumnReader(StructureReaderBase<sbyte> internalReader, IReadOnlyDictionary<sbyte, string> reversedEnumMap)\n                : base(internalReader, reversedEnumMap)\n            {\n            }\n\n            protected override EnumTableColumnDispatcherBase CreateColumnDispatcher(IClickHouseTableColumn<sbyte> column, IReadOnlyDictionary<sbyte, string> reversedEnumMap)\n            {\n                return new EnumTableColumnDispatcher(column, reversedEnumMap);\n            }\n        }\n\n        private sealed class EnumTableColumnDispatcher : EnumTableColumnDispatcherBase\n        {\n            public EnumTableColumnDispatcher(IClickHouseTableColumn<sbyte> column, IReadOnlyDictionary<sbyte, string> reversedEnumMap)\n                : base(column, reversedEnumMap)\n            {\n            }\n\n            protected override bool TryMap<TEnum>(IClickHouseEnumConverter<TEnum> enumConverter, sbyte value, string stringValue, out TEnum enumValue)\n            {\n                return enumConverter.TryMap(value, stringValue, out enumValue);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/EnumTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class EnumTableColumn<TKey> : IClickHouseTableColumn<string>\n        where TKey : struct\n    {\n        private readonly IClickHouseTableColumn<TKey> _internalColumn;\n        private readonly IReadOnlyDictionary<TKey, string> _valueMap;\n\n        public int RowCount => _internalColumn.RowCount;\n\n        public string DefaultValue { get; }\n\n        public EnumTableColumn(IClickHouseTableColumn<TKey> internalColumn, IReadOnlyDictionary<TKey, string> valueMap)\n        {\n            _internalColumn = internalColumn;\n            _valueMap = valueMap;\n\n            if (!_valueMap.TryGetValue(_internalColumn.DefaultValue, out var defaultStr))\n                defaultStr = string.Empty;\n\n            DefaultValue = defaultStr;\n        }\n\n        public bool IsNull(int index)\n        {\n            Debug.Assert(!_internalColumn.IsNull(index));\n            return false;\n        }\n\n        public string GetValue(int index)\n        {\n            var value = _internalColumn.GetValue(index);\n            if (!_valueMap.TryGetValue(value, out var strValue))\n                throw new InvalidCastException($\"There is no string representation for the value {value} in the enum.\");\n\n            return strValue;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            var internalReinterpreted = _internalColumn as IClickHouseTableColumn<T> ?? _internalColumn.TryReinterpret<T>();\n            if (internalReinterpreted != null)\n                return new ReinterpretedTableColumn<T>(this, internalReinterpreted);\n\n            return null;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n    }\n\n    internal sealed class EnumTableColumn<TKey, TEnum> : IClickHouseTableColumn<TEnum>\n        where TKey : struct\n        where TEnum : Enum\n    {\n        private readonly IClickHouseTableColumn<TKey> _internalColumn;\n        private readonly IReadOnlyDictionary<TKey, TEnum> _enumMap;\n        private readonly IReadOnlyDictionary<TKey, string> _stringMap;\n\n        public int RowCount => _internalColumn.RowCount;\n\n        public TEnum DefaultValue { get; }\n\n        public EnumTableColumn(IClickHouseTableColumn<TKey> internalColumn, IReadOnlyDictionary<TKey, TEnum> enumMap, IReadOnlyDictionary<TKey, string> stringMap)\n        {\n            _internalColumn = internalColumn;\n            _enumMap = enumMap;\n            _stringMap = stringMap;\n\n            if (!_enumMap.TryGetValue(_internalColumn.DefaultValue, out var defaultEnum))\n                defaultEnum = default;\n\n            Debug.Assert(defaultEnum != null);\n            DefaultValue = defaultEnum;\n        }\n\n        public bool IsNull(int index)\n        {\n            Debug.Assert(!_internalColumn.IsNull(index));\n            return false;\n        }\n\n        public TEnum GetValue(int index)\n        {\n            var value = _internalColumn.GetValue(index);\n            if (!_enumMap.TryGetValue(value, out var strValue))\n            {\n                if (_stringMap.TryGetValue(value, out var nativeValue))\n                    throw new InvalidCastException($\"The value '{nativeValue}'={value} of the enum can't be converted to the type '{typeof(Enum).FullName}'.\");\n\n                throw new InvalidCastException($\"The value {value} doesn't belong to the enum.\");\n            }\n\n            return strValue;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            IClickHouseTableColumn<T>? reinterpretedColumn;\n            if (typeof(T) == typeof(string))\n                reinterpretedColumn = (IClickHouseTableColumn<T>) (object) new EnumTableColumn<TKey>(_internalColumn, _stringMap);\n            else\n                reinterpretedColumn = _internalColumn.TryReinterpret<T>();\n\n            if (reinterpretedColumn == null)\n                return null;\n\n            return new ReinterpretedTableColumn<T>(this, reinterpretedColumn);\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/EnumTypeInfoBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal abstract class EnumTypeInfoBase<TValue> : IClickHouseColumnTypeInfo\n        where TValue : struct, IFormattable\n    {\n        protected readonly Dictionary<string, TValue>? _enumMap;\n        private readonly Dictionary<TValue, string>? _reversedEnumMap;\n        private readonly List<string>? _mapOrder;\n\n        public string ComplexTypeName { get; }\n\n        public string TypeName { get; }\n\n        public int GenericArgumentsCount => 0;\n\n        public int TypeArgumentsCount => _mapOrder?.Count ?? 0;\n\n        protected EnumTypeInfoBase(string typeName)\n        {\n            TypeName = typeName;\n            ComplexTypeName = typeName;\n        }\n\n        protected EnumTypeInfoBase(string typeName, string complexTypeName, IEnumerable<KeyValuePair<string, TValue>> values)\n        {\n            TypeName = typeName;\n            ComplexTypeName = complexTypeName;\n\n            _enumMap = new Dictionary<string, TValue>(StringComparer.Ordinal);\n            _reversedEnumMap = new Dictionary<TValue, string>();\n            _mapOrder = new List<string>();\n\n            foreach (var pair in values)\n            {\n                _enumMap.Add(pair.Key, pair.Value);\n                _reversedEnumMap.Add(pair.Value, pair.Key);\n                _mapOrder.Add(pair.Key);\n            }\n        }\n\n        public Type GetFieldType()\n        {\n            return typeof(string);\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Enum;\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have generic arguments.\");\n        }\n\n        public object GetTypeArgument(int index)\n        {\n            if (_mapOrder == null || _enumMap == null)\n                throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have arguments.\");\n\n            var key = _mapOrder[index];\n            var value = _enumMap[key];\n\n            return new KeyValuePair<string, TValue>(key, value);\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            if (_enumMap == null || _reversedEnumMap == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, \"The list of items is not specified.\");\n\n            var internalReader = CreateInternalColumnReader(rowCount);\n            return CreateColumnReader(internalReader, _reversedEnumMap);\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            if (_enumMap == null || _reversedEnumMap == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, \"The list of items is not specified.\");\n\n            return CreateInternalSkippingColumnReader(rowCount);\n        }\n\n        protected abstract EnumColumnReaderBase CreateColumnReader(StructureReaderBase<TValue> internalReader, IReadOnlyDictionary<TValue, string> reversedEnumMap);        \n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (_enumMap == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, \"The list of items is not specified.\");\n\n            if (typeof(T) == typeof(string))\n            {\n                var list = MappedReadOnlyList<string, TValue>.Map(\n                    (IReadOnlyList<string>)rows,\n                    key => key == null ? default : _enumMap.TryGetValue(key, out var value) ? value : throw new InvalidCastException($\"The value \\\"{key}\\\" can't be converted to {ComplexTypeName}.\"));\n                return CreateInternalColumnWriter(columnName, list);\n            }\n\n            return CreateInternalColumnWriter(columnName, rows);\n        }\n\n        public abstract IClickHouseParameterWriter<T> CreateParameterWriter<T>();\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            var parsedOptions = new List<KeyValuePair<string, TValue>>(options.Count);\n            var complexNameBuilder = new StringBuilder(TypeName).Append('(');\n            bool isFirst = true;\n            foreach (var option in options)\n            {\n                if (isFirst)\n                    isFirst = false;\n                else\n                    complexNameBuilder.Append(\", \");\n\n                var keyStrLen = ClickHouseSyntaxHelper.GetSingleQuoteStringLength(option.Span);\n                if (keyStrLen < 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The fragment \\\"{option}\\\" is not recognized as an item of the enum.\");\n\n                var key = ClickHouseSyntaxHelper.GetSingleQuoteString(option.Slice(0, keyStrLen).Span);\n                var valuePart = option.Slice(keyStrLen);\n                var eqSignIdx = valuePart.Span.IndexOf('=');\n                if (eqSignIdx < 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The fragment \\\"{option}\\\" is not recognized as an item of the enum.\");\n\n                valuePart = valuePart.Slice(eqSignIdx + 1).Trim();                      \n                if (!TryParse(valuePart.Span, out var value))\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The value {valuePart} is not a valid value of {TypeName}.\");\n\n                complexNameBuilder.Append(option);\n                parsedOptions.Add(new KeyValuePair<string, TValue>(key, value));\n            }\n\n            var complexName = complexNameBuilder.Append(')').ToString();\n            return CreateDetailedTypeInfo(complexName, parsedOptions);\n        }\n\n        protected abstract IClickHouseColumnTypeInfo CreateDetailedTypeInfo(string complexTypeName, IEnumerable<KeyValuePair<string, TValue>> values);\n\n        protected abstract StructureReaderBase<TValue> CreateInternalColumnReader(int rowCount);\n\n        protected abstract SimpleSkippingColumnReader CreateInternalSkippingColumnReader(int rowCount);\n\n        protected abstract IClickHouseColumnWriter CreateInternalColumnWriter<T>(string columnName, IReadOnlyList<T> rows);\n\n        protected abstract bool TryParse(ReadOnlySpan<char> text, out TValue value);\n\n        protected abstract class EnumColumnReaderBase : IClickHouseColumnReader\n        {\n            private readonly StructureReaderBase<TValue> _internalReader;\n            private readonly IReadOnlyDictionary<TValue, string> _reversedEnumMap;\n            \n            public EnumColumnReaderBase(StructureReaderBase<TValue> internalReader, IReadOnlyDictionary<TValue, string> reversedEnumMap)\n            {\n                _internalReader = internalReader;\n                _reversedEnumMap = reversedEnumMap;\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                return _internalReader.ReadNext(sequence);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                var column = _internalReader.EndRead(null);\n                var enumConverter = settings?.EnumConverter;\n                if (enumConverter != null)\n                {\n                    var dispatcher = CreateColumnDispatcher(column, _reversedEnumMap);\n                    return enumConverter.Dispatch(dispatcher);\n                }\n\n                return new EnumTableColumn<TValue>(column, _reversedEnumMap);\n            }\n\n            protected abstract EnumTableColumnDispatcherBase CreateColumnDispatcher(IClickHouseTableColumn<TValue> column, IReadOnlyDictionary<TValue, string> reversedEnumMap);\n        }\n\n        protected abstract class EnumTableColumnDispatcherBase : IClickHouseEnumConverterDispatcher<IClickHouseTableColumn>\n        {\n            private readonly IClickHouseTableColumn<TValue> _column;\n            private readonly IReadOnlyDictionary<TValue, string> _reversedEnumMap;\n\n            public EnumTableColumnDispatcherBase(IClickHouseTableColumn<TValue> column, IReadOnlyDictionary<TValue, string> reversedEnumMap)\n            {\n                _column = column;\n                _reversedEnumMap = reversedEnumMap;\n            }\n\n            public IClickHouseTableColumn Dispatch<TEnum>(IClickHouseEnumConverter<TEnum> enumConverter)\n                where TEnum : Enum\n            {\n                var map = new Dictionary<TValue, TEnum>(_reversedEnumMap.Count);\n                foreach (var pair in _reversedEnumMap)\n                {\n                    if (TryMap(enumConverter, pair.Key, pair.Value, out var enumValue))\n                        map.Add(pair.Key, enumValue);\n                }\n\n                return new EnumTableColumn<TValue, TEnum>(_column, map, _reversedEnumMap);\n            }\n\n            protected abstract bool TryMap<TEnum>(IClickHouseEnumConverter<TEnum> enumConverter, TValue value, string stringValue, out TEnum enumValue)\n                where TEnum : Enum;\n        }\n\n        protected sealed class EnumParameterWriter : IClickHouseParameterWriter<string>\n        {\n            private readonly EnumTypeInfoBase<TValue> _type;\n            private readonly SimpleParameterWriter<TValue> _writer;\n\n            public EnumParameterWriter(EnumTypeInfoBase<TValue> type)\n            {\n                _type = type;\n                _writer = new SimpleParameterWriter<TValue>(_type);\n            }\n\n            public bool TryCreateParameterValueWriter(string value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                var enumValue = Convert(value);\n                return _writer.TryCreateParameterValueWriter(enumValue, isNested, out valueWriter);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, string value)\n            {\n                var enumValue = Convert(value);\n                return _writer.Interpolate(queryBuilder, enumValue);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return _writer.Interpolate(queryBuilder, typeInfoProvider, writeValue);\n            }\n\n            private TValue Convert(string value)\n            {\n                var enumMap = _type._enumMap;\n                Debug.Assert(enumMap != null);\n\n                if (enumMap.TryGetValue(value, out var enumValue))\n                    return enumValue;\n\n                throw new InvalidCastException($\"The value \\\"{value}\\\" can't be converted to the ClickHouse type \\\"{_type.ComplexTypeName}\\\".\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/FixedStringDecodedCharArrayTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class FixedStringDecodedCharArrayTableColumn : FixedStringTableColumnBase<char[]>\n    {\n        public override char[] DefaultValue => Array.Empty<char>();\n\n        public FixedStringDecodedCharArrayTableColumn(Memory<byte> buffer, int rowSize, Encoding encoding)\n            : base(buffer, rowSize, encoding)\n        {\n        }\n\n        protected override char[] GetValue(Encoding encoding, ReadOnlySpan<byte> span)\n        {\n            var charCount = encoding.GetCharCount(span);\n            var result = new char[charCount];\n\n            encoding.GetChars(span, result);\n\n            int pos;\n            for (pos = result.Length - 1; pos >= 0; pos--)\n            {\n                if (result[pos] != 0)\n                    break;\n            }\n\n            if (pos == 0)\n                return Array.Empty<char>();\n\n            if (pos + 1 < result.Length)\n                Array.Resize(ref result, pos + 1);\n\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/FixedStringDecodedTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class FixedStringDecodedTableColumn : FixedStringTableColumnBase<string>\n    {\n        public override string DefaultValue => string.Empty;\n\n        public FixedStringDecodedTableColumn(Memory<byte> buffer, int rowSize, Encoding encoding)\n            : base(buffer, rowSize, encoding)\n        {\n        }\n\n        protected override string GetValue(Encoding encoding, ReadOnlySpan<byte> span)\n        {\n            var charCount = encoding.GetCharCount(span);\n            var charSpan = new Span<char>(new char[charCount]);\n\n            encoding.GetChars(span, charSpan);\n\n            int pos;\n            for (pos = charSpan.Length - 1; pos >= 0; pos--)\n            {\n                if (charSpan[pos] != 0)\n                    break;\n            }\n\n            charSpan = charSpan.Slice(0, pos + 1);\n            if (charSpan.IsEmpty)\n                return string.Empty;\n\n            return new string(charSpan);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/FixedStringTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class FixedStringTableColumn : FixedStringTableColumnBase<byte[]>\n    {\n        public override byte[] DefaultValue => Array.Empty<byte>();\n\n        public FixedStringTableColumn(Memory<byte> buffer, int rowSize, Encoding encoding)\n            : base(buffer, rowSize, encoding)\n        {\n        }\n\n        [return: NotNull]\n        protected override byte[] GetValue(Encoding encoding, ReadOnlySpan<byte> span)\n        {\n            var result = new byte[span.Length];\n            span.CopyTo(result);\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/FixedStringTableColumnBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal abstract class FixedStringTableColumnBase<TOut> : IClickHouseTableColumn<TOut>, IClickHouseArrayTableColumn<byte>\n    {\n        private readonly Memory<byte> _buffer;\n        private readonly int _rowSize;\n        private readonly Encoding _encoding;\n\n        public int RowCount { get; }\n\n        public abstract TOut DefaultValue { get; }\n\n        protected FixedStringTableColumnBase(Memory<byte> buffer, int rowSize, Encoding encoding)\n        {\n            _buffer = buffer;\n            _rowSize = rowSize;\n            _encoding = encoding;\n            RowCount = _buffer.Length / _rowSize;\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        [return: NotNull]\n        public TOut GetValue(int index)\n        {\n            return GetValue(_encoding, _buffer.Span.Slice(index * _rowSize, _rowSize));\n        }\n\n        [return: NotNull]\n        protected abstract TOut GetValue(Encoding encoding, ReadOnlySpan<byte> span);\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(string))\n                return (IClickHouseTableColumn<T>)(object)new FixedStringDecodedTableColumn(_buffer, _rowSize, _encoding);\n            if (typeof(T) == typeof(byte[]))\n                return (IClickHouseTableColumn<T>)(object)new FixedStringTableColumn(_buffer, _rowSize, _encoding);\n            if (typeof(T) == typeof(char[]))\n                return (IClickHouseTableColumn<T>)(object)new FixedStringDecodedCharArrayTableColumn(_buffer, _rowSize, _encoding);\n\n            return null;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        public int CopyTo(int index, Span<byte> buffer, int dataOffset)\n        {\n            if (dataOffset < 0 || dataOffset > _rowSize)\n                throw new ArgumentOutOfRangeException(nameof(dataOffset));\n\n            var length = Math.Min(_rowSize - dataOffset, buffer.Length);\n            _buffer.Span.Slice(index * _rowSize + dataOffset, length).CopyTo(buffer);\n            return length;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/FixedStringTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class FixedStringTypeInfo : IClickHouseColumnTypeInfo\n    {\n        private readonly int? _length;\n\n        public string ComplexTypeName { get; }\n\n        public string TypeName => \"FixedString\";\n\n        public int GenericArgumentsCount => 0;\n\n        public int TypeArgumentsCount => _length == 0 ? 0 : 1;\n\n        public FixedStringTypeInfo()\n        {\n            ComplexTypeName = TypeName;\n        }\n\n        private FixedStringTypeInfo(int length)\n        {\n            _length = length;\n            ComplexTypeName = string.Format(CultureInfo.InvariantCulture, \"{0}({1})\", TypeName, length);\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            if (_length == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, \"The length of the fixed string is not specified.\");\n\n            return new FixedStringReader(rowCount, _length.Value);\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            if (_length == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, \"The length of the fixed string is not specified.\");\n\n            return new SimpleSkippingColumnReader(_length.Value, rowCount);\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (_length == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, \"The length of the fixed string is not specified.\");\n\n            var type = typeof(T);\n            if (type == typeof(byte[]))\n                return new FixedStringBytesColumnWriter(columnName, ComplexTypeName, (IReadOnlyList<byte[]?>)rows, _length.Value);\n            if (type == typeof(string))\n                return new FixedStringStringColumnWriter(columnName, ComplexTypeName, (IReadOnlyList<string?>)rows, _length.Value, columnSettings?.StringEncoding);\n            if (type == typeof(ReadOnlyMemory<byte>))\n                return new FixedStringBytesColumnWriter(columnName, ComplexTypeName, (IReadOnlyList<ReadOnlyMemory<byte>>)rows, _length.Value);\n            if (type == typeof(Memory<byte>))\n                return new FixedStringBytesColumnWriter(columnName, ComplexTypeName, (IReadOnlyList<Memory<byte>>)rows, _length.Value);\n            if (type == typeof(ReadOnlyMemory<char>))\n                return new FixedStringStringColumnWriter(columnName, ComplexTypeName, (IReadOnlyList<ReadOnlyMemory<char>>)rows, _length.Value, columnSettings?.StringEncoding);\n            if (type == typeof(Memory<char>))\n                return new FixedStringStringColumnWriter(columnName, ComplexTypeName, (IReadOnlyList<Memory<char>>)rows, _length.Value, columnSettings?.StringEncoding);\n\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            if (_length == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, \"The length of the fixed string is not specified.\");\n\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values\");\n\n            object writer;\n            if (type == typeof(string))\n                writer = new FixedStringParameterWriter<string>(this, s => s.AsMemory());\n            else if (type == typeof(ReadOnlyMemory<char>))\n                writer = new FixedStringParameterWriter(this);\n            else if (type == typeof(Memory<char>))\n                writer = new FixedStringParameterWriter<Memory<char>>(this, mem => mem);\n            else if (type == typeof(byte[]))\n                writer = new FixedStringHexParameterWriter<byte[]>(this, a => a.AsMemory());\n            else if (type == typeof(ReadOnlyMemory<byte>))\n                writer = new FixedStringHexParameterWriter(this);\n            else if (type == typeof(Memory<byte>))\n                writer = new FixedStringHexParameterWriter<Memory<byte>>(this, mem => mem);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            if (_length != null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, \"The type is already fully specified.\");\n\n            if (options.Count > 1)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"Too many arguments in the definition of \\\"{TypeName}\\\".\");\n\n            if (!int.TryParse(options[0].Span, NumberStyles.Integer, CultureInfo.InvariantCulture, out var length) || length <= 0)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The length of \\\"{TypeName}({options[0].ToString()})\\\" must be a positive number.\");\n\n            return new FixedStringTypeInfo(length);\n        }\n\n        public Type GetFieldType()\n        {\n            return typeof(byte[]);\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.StringFixedLength;\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have generic arguments.\");\n        }\n\n        public object GetTypeArgument(int index)\n        {\n            if (_length == null)\n                throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have arguments.\");\n\n            if (index == 0)\n                return _length;\n\n            throw new IndexOutOfRangeException();\n        }\n\n        private sealed class FixedStringReader : IClickHouseColumnReader\n        {\n            private readonly int _rowCount;\n            private readonly int _rowSize;\n\n            private readonly Memory<byte> _buffer;\n\n            private int _position;\n\n            public FixedStringReader(int rowCount, int rowSize)\n            {\n                _rowCount = rowCount;\n                _rowSize = rowSize;\n                if (rowCount > 0)\n                    _buffer = new Memory<byte>(new byte[rowCount * rowSize]);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                if (_position / _rowSize >= _rowCount)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                if (sequence.Length < _rowSize)\n                    return new SequenceSize(0, 0);\n\n                var elementsCount = Math.Min((int) sequence.Length / _rowSize, _rowCount - _position / _rowSize);\n                if (elementsCount == 0)\n                    return new SequenceSize(0, 0);\n\n                var bytesCount = _rowSize * elementsCount;\n                sequence.Slice(0, bytesCount).CopyTo(_buffer.Span.Slice(_position));\n                _position += bytesCount;\n                return new SequenceSize(bytesCount, elementsCount);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                return new FixedStringTableColumn(_buffer, _rowSize, settings?.StringEncoding ?? Encoding.UTF8);\n            }\n        }\n\n        private sealed class FixedStringBytesColumnWriter : FixedStringColumnWriterBase\n        {\n            private readonly IReadOnlyList<ReadOnlyMemory<byte>> _rows;\n\n            protected override int RowCount => _rows.Count;\n\n            public FixedStringBytesColumnWriter(string columnName, string columnType, IReadOnlyList<byte[]?> rows, int length)\n                : this(columnName, columnType, MappedReadOnlyList<byte[]?, ReadOnlyMemory<byte>>.Map(rows, b => b.AsMemory()), length)\n            {\n            }\n\n            public FixedStringBytesColumnWriter(string columnName, string columnType, IReadOnlyList<Memory<byte>> rows, int length)\n                : this(columnName, columnType, MappedReadOnlyList<Memory<byte>, ReadOnlyMemory<byte>>.Map(rows, m => m), length)\n            {\n            }\n\n            public FixedStringBytesColumnWriter(string columnName, string columnType, IReadOnlyList<ReadOnlyMemory<byte>> rows, int length)\n                : base(columnName, columnType, length)\n            {\n                _rows = rows;\n            }\n\n            protected override int GetBytes(int position, Span<byte> buffer)\n            {\n                var bytes = _rows[position];\n                if (bytes.Length > buffer.Length)\n                    throw new InvalidCastException($\"The length of the array ({bytes.Length}) is greater than the maximum length ({buffer.Length}).\");\n\n                bytes.Span.CopyTo(buffer);\n                return bytes.Length;\n            }\n        }\n\n        private sealed class FixedStringStringColumnWriter : FixedStringColumnWriterBase\n        {\n            private readonly IReadOnlyList<ReadOnlyMemory<char>> _rows;\n            private readonly Encoding _stringEncoding;\n\n            protected override int RowCount => _rows.Count;\n\n            public FixedStringStringColumnWriter(string columnName, string columnType, IReadOnlyList<Memory<char>> rows, int length, Encoding? stringEncoding)\n                : this(columnName, columnType, MappedReadOnlyList<Memory<char>, ReadOnlyMemory<char>>.Map(rows, m => m), length, stringEncoding)\n            {\n            }\n\n            public FixedStringStringColumnWriter(string columnName, string columnType, IReadOnlyList<string?> rows, int length, Encoding? stringEncoding)\n                : this(columnName, columnType, MappedReadOnlyList<string?, ReadOnlyMemory<char>>.Map(rows, str => str.AsMemory()), length, stringEncoding)\n            {\n            }\n\n            public FixedStringStringColumnWriter(string columnName, string columnType, IReadOnlyList<ReadOnlyMemory<char>> rows, int length, Encoding? stringEncoding)\n                : base(columnName, columnType, length)\n            {\n                _rows = rows;\n                _stringEncoding = stringEncoding ?? Encoding.UTF8;\n            }\n\n            protected override int GetBytes(int position, Span<byte> buffer)\n            {\n                var str = _rows[position].Span;\n                var bytesCount = _stringEncoding.GetByteCount(str);\n                if (bytesCount == 0)\n                    return 0;\n\n                if (bytesCount <= buffer.Length)\n                {\n                    _stringEncoding.GetBytes(str, buffer);\n                    return bytesCount;\n                }\n\n                throw new InvalidCastException($\"The length of the string ({bytesCount}) is greater than the maximum length ({buffer.Length}).\");\n            }\n        }\n\n        private abstract class FixedStringColumnWriterBase : IClickHouseColumnWriter\n        {\n            private readonly int _length;\n\n            public string ColumnName { get; }\n\n            public string ColumnType { get; }\n\n            protected abstract int RowCount { get; }\n\n            private int _position;\n\n            public FixedStringColumnWriterBase(string columnName, string columnType, int length)\n            {\n                _length = length;\n                ColumnName = columnName ?? throw new ArgumentNullException(nameof(columnName));\n                ColumnType = columnType;\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                var size = Math.Min(RowCount - _position, writeTo.Length / _length);\n\n                ReadOnlySpan<byte> zeroSpan = new byte[_length];\n                var span = writeTo;\n                for (int i = 0; i < size; i++, span = span.Slice(_length))\n                {\n                    var bytesCount = GetBytes(_position++, span.Slice(0, _length));\n                    zeroSpan.Slice(bytesCount).CopyTo(span.Slice(bytesCount));\n                }\n\n                return new SequenceSize(size * _length, size);\n            }\n\n            protected abstract int GetBytes(int position, Span<byte> buffer);\n        }\n\n        private class FixedStringParameterWriter : IClickHouseParameterWriter<ReadOnlyMemory<char>>\n        {\n            private readonly FixedStringTypeInfo _type;\n\n            public FixedStringParameterWriter(FixedStringTypeInfo type)\n            {\n                _type = type;\n            }\n\n            public bool TryCreateParameterValueWriter(ReadOnlyMemory<char> value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                var writer = new StringLiteralValueWriter(value, isNested);\n\n                Debug.Assert(_type._length != null);\n                if (writer.Length - 2 > _type._length.Value)\n                    ValidateLength(value); // Validate the length of the unescaped string without quota signs\n\n                valueWriter = writer;\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, ReadOnlyMemory<char> value)\n            {\n                ValidateLength(value);\n                return StringParameterWriter.Interpolate(queryBuilder, value.Span);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return writeValue(queryBuilder, _type, FunctionHelper.Apply);\n            }\n\n            private void ValidateLength(ReadOnlyMemory<char> value)\n            {\n                Debug.Assert(_type._length != null);\n                var length = _type._length.Value;\n                var encoding = Encoding.UTF8;\n                var bytesCount = encoding.GetByteCount(value.Span);\n                if (bytesCount > length)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidQueryParameterConfiguration, $\"The length of the string ({bytesCount}) is greater than the maximum length ({length}).\");\n            }\n        }\n\n        private sealed class FixedStringParameterWriter<T> : FixedStringParameterWriter, IClickHouseParameterWriter<T>\n        {\n            private readonly Func<T, ReadOnlyMemory<char>> _convert;\n\n            public FixedStringParameterWriter(FixedStringTypeInfo type, Func<T, ReadOnlyMemory<char>> convert)\n                : base(type)\n            {\n                _convert = convert;\n            }\n\n            public bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                return TryCreateParameterValueWriter(_convert(value), isNested, out valueWriter);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, T value)\n            {\n                return Interpolate(queryBuilder, _convert(value));\n            }\n        }\n\n        private class FixedStringHexParameterWriter : IClickHouseParameterWriter<ReadOnlyMemory<byte>>\n        {\n            private readonly FixedStringTypeInfo _type;\n\n            public FixedStringHexParameterWriter(FixedStringTypeInfo type)\n            {\n                _type = type;\n            }\n\n            public bool TryCreateParameterValueWriter(ReadOnlyMemory<byte> value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                ValidateLength(value);\n                valueWriter = new HexStringLiteralValueWriter(value, isNested);\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, ReadOnlyMemory<byte> value)\n            {\n                ValidateLength(value);\n                return HexStringParameterWriter.Interpolate(queryBuilder, value.Span);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return writeValue(queryBuilder, _type, FunctionHelper.Apply);\n            }\n\n            private void ValidateLength(ReadOnlyMemory<byte> value)\n            {\n                Debug.Assert(_type._length != null);\n                var length = _type._length.Value;\n                if (value.Length > length)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InvalidQueryParameterConfiguration, $\"The length of the array ({value.Length}) is greater than the maximum length ({length}).\");\n            }\n        }\n\n        private sealed class FixedStringHexParameterWriter<T> : FixedStringHexParameterWriter, IClickHouseParameterWriter<T>\n        {\n            private readonly Func<T, ReadOnlyMemory<byte>> _convert;\n\n            public FixedStringHexParameterWriter(FixedStringTypeInfo type, Func<T, ReadOnlyMemory<byte>> convert)\n            : base(type)\n            {\n                _convert = convert;\n            }\n\n            public bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                return TryCreateParameterValueWriter(_convert(value), isNested, out valueWriter);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, T value)\n            {\n                return Interpolate(queryBuilder, _convert(value));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Float32TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Float32TableColumn : StructureTableColumn<float>\n    {\n        public Float32TableColumn(ReadOnlyMemory<float> buffer)\n            : base(buffer)\n        {\n        }\n\n        public override IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(double))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<float, double>(this, v => v);\n            if (typeof(T) == typeof(double?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<double>(null, new ReinterpretedTableColumn<float, double>(this, v => v));\n\n            return base.TryReinterpret<T>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Float32TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Float32TypeInfo : SimpleTypeInfo\n    {\n        public Float32TypeInfo()\n            : base(\"Float32\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new Float32Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(float), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (typeof(T) != typeof(float))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new Float32Writer(columnName, ComplexTypeName, (IReadOnlyList<float>)rows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer = default(T) switch\n            {\n                float _ => HexStringParameterWriter.Create<float>(this),\n                _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\"),\n            };\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(float);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Single;\n        }\n\n        private sealed class Float32Reader : StructureReaderBase<float>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Float32Reader(int rowCount)\n                : base(sizeof(float), rowCount)\n            {\n            }\n\n            protected override float ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToSingle(source);\n            }\n\n            protected override IClickHouseTableColumn<float> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<float> buffer)\n            {\n                return new Float32TableColumn(buffer);\n            }\n        }\n\n        private sealed class Float32Writer : StructureWriterBase<float>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Float32Writer(string columnName, string columnType, IReadOnlyList<float> rows)\n                : base(columnName, columnType, sizeof(float), rows)\n            {\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in float value)\n            {\n                var success = BitConverter.TryWriteBytes(writeTo, value);\n                Debug.Assert(success);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Float64TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Float64TypeInfo : SimpleTypeInfo\n    {\n        public Float64TypeInfo()\n            : base(\"Float64\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new Float64Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(double), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            IReadOnlyList<double> doubleRows;\n            if (typeof(T) == typeof(double))\n                doubleRows = (IReadOnlyList<double>)rows;\n            else if (typeof(T) == typeof(float))\n                doubleRows = MappedReadOnlyList<float, double>.Map((IReadOnlyList<float>)rows, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new Float64Writer(columnName, ComplexTypeName, doubleRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values\");\n\n            object writer = default(T) switch\n            {\n                double _ => HexStringParameterWriter.Create<double>(this),\n                float _ => HexStringParameterWriter.Create<float, double>(this, v => v),\n                _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\"),\n            };\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(double);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Double;\n        }\n\n        private sealed class Float64Reader : StructureReaderBase<double>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Float64Reader(int rowCount)\n                : base(sizeof(double), rowCount)\n            {\n            }\n\n            protected override double ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToDouble(source);\n            }\n        }\n\n        private sealed class Float64Writer : StructureWriterBase<double>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Float64Writer(string columnName, string columnType, IReadOnlyList<double> rows)\n                : base(columnName, columnType, sizeof(double), rows)\n            {\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in double value)\n            {\n                var success = BitConverter.TryWriteBytes(writeTo, value);\n                Debug.Assert(success);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/HexStringLiteralValueWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Protocol;\nusing System;\nusing System.Diagnostics;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class HexStringLiteralValueWriter : IClickHouseParameterValueWriter\n    {\n        public const string HexDigits = \"0123456789ABCDEF\";\n\n        private readonly ReadOnlyMemory<byte> _value;\n        private readonly bool _includeQuotes;\n\n        public int Length { get; }\n\n        public HexStringLiteralValueWriter(ReadOnlyMemory<byte> value, bool includeQuotes)\n        {\n            _value = value;\n            _includeQuotes = includeQuotes;\n\n            if (_includeQuotes)\n                Length = 4 * value.Length + 4;\n            else\n                Length= 4 * _value.Length;\n        }\n\n        public int Write(Memory<byte> buffer)\n        {\n            Debug.Assert(buffer.Length >= Length);\n\n            int count = 0;\n            if (_includeQuotes)\n            {\n                buffer.Span[count++] = (byte)'\\\\';\n                buffer.Span[count++] = (byte)'\\'';\n            }\n\n            foreach (var byteValue in _value.Span)\n            {\n                switch (byteValue)\n                {\n                    case (byte)'\\t':\n                        buffer.Span[count++] = (byte)'\\\\';\n                        buffer.Span[count++] = (byte)'\\\\';\n                        buffer.Span[count++] = (byte)'\\\\';\n                        buffer.Span[count++] = (byte)'\\t';\n                        break;\n\n                    case (byte)'\\n':\n                        buffer.Span[count++] = (byte)'\\\\';\n                        buffer.Span[count++] = (byte)'\\\\';\n                        buffer.Span[count++] = (byte)'\\\\';\n                        buffer.Span[count++] = (byte)'\\n';\n                        break;\n\n                    case (byte)'\\\\':\n                        buffer.Span[count++] = (byte)'\\\\';\n                        buffer.Span[count++] = (byte)'\\\\';\n                        buffer.Span[count++] = (byte)'\\\\';\n                        buffer.Span[count++] = (byte)'\\\\';\n                        break;\n\n                    default:\n                        buffer.Span[count++] = (byte)'\\\\';\n                        buffer.Span[count++] = (byte)'x';\n                        buffer.Span[count++] = (byte)HexDigits[byteValue >> 4];\n                        buffer.Span[count++] = (byte)HexDigits[byteValue & 0xF];\n                        break;\n                }\n            }\n\n            if (_includeQuotes)\n            {\n                buffer.Span[count++] = (byte)'\\\\';\n                buffer.Span[count++] = (byte)'\\'';\n            }\n\n            Debug.Assert(count == Length);\n            return count;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/HexStringLiteralWriterCastMode.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal enum HexStringLiteralWriterCastMode\n    {\n        None = 0,\n        Reinterpret = 1,\n        Cast = 2\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/HexStringParameterWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Runtime.InteropServices;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class HexStringParameterWriter : IClickHouseParameterWriter<ReadOnlyMemory<byte>>\n    {\n        private const string HexDigits = HexStringLiteralValueWriter.HexDigits;\n\n        private readonly IClickHouseColumnTypeInfo _typeInfo;\n\n        public HexStringParameterWriter(IClickHouseColumnTypeInfo typeInfo)\n        {\n            _typeInfo = typeInfo;\n        }\n\n        public bool TryCreateParameterValueWriter(ReadOnlyMemory<byte> value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n        {\n            valueWriter = new HexStringLiteralValueWriter(value, isNested);\n            return true;\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, ReadOnlyMemory<byte> value)\n        {\n            return Interpolate(queryBuilder, value.Span);\n        }\n\n        public static StringBuilder Interpolate(StringBuilder queryBuilder, ReadOnlySpan<byte> value)\n        {\n            queryBuilder.Append('\\'');\n            foreach (var byteValue in value)\n            {\n                queryBuilder.Append(\"\\\\x\");\n                queryBuilder.Append(HexDigits[byteValue >> 4]);\n                queryBuilder.Append(HexDigits[byteValue & 0xF]);\n            }\n            return queryBuilder.Append('\\'');\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n        {\n            return writeValue(queryBuilder, _typeInfo, FunctionHelper.Apply);\n        }\n\n        public static HexStringParameterWriter<T> Create<T>(IClickHouseColumnTypeInfo typeInfo)\n            where T : struct\n        {\n            var dummy = default(T);\n            var dummyBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref dummy, 1));\n            var binaryTypeName = $\"FixedString({dummyBytes.Length.ToString(CultureInfo.InvariantCulture)})\";\n            return new HexStringParameterWriter<T>(typeInfo, HexStringLiteralWriterCastMode.Reinterpret, binaryTypeName, Convert);\n        }\n\n        public static HexStringParameterWriter<TIn> Create<TIn, TOut>(IClickHouseColumnTypeInfo typeInfo, Func<TIn, TOut> convert)\n            where TOut : struct\n        {\n            var dummy = default(TOut);\n            var dummyBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref dummy, 1));\n            var binaryTypeName = $\"FixedString({dummyBytes.Length.ToString(CultureInfo.InvariantCulture)})\";\n            return new HexStringParameterWriter<TIn>(typeInfo, HexStringLiteralWriterCastMode.Reinterpret, binaryTypeName, v => Convert(convert(v)));\n        }\n\n        private static ReadOnlyMemory<byte> Convert<T>(T value)\n            where T : struct\n        {\n            var src = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref value, 1));\n            var dst = new byte[src.Length];\n            src.CopyTo(dst);\n            return dst;\n        }\n    }\n\n    internal sealed class HexStringParameterWriter<T> : IClickHouseParameterWriter<T>\n    {\n        private readonly IClickHouseColumnTypeInfo _typeInfo;\n        private readonly HexStringLiteralWriterCastMode _castMode;\n        private readonly string? _valueType;\n        private readonly Func<T, ReadOnlyMemory<byte>> _convert;\n\n        public HexStringParameterWriter(IClickHouseColumnTypeInfo typeInfo, Func<T, ReadOnlyMemory<byte>> convert)\n            : this(typeInfo, HexStringLiteralWriterCastMode.None, null, convert)\n        {\n        }\n\n        public HexStringParameterWriter(IClickHouseColumnTypeInfo typeInfo, HexStringLiteralWriterCastMode castMode, string? valueType, Func<T, ReadOnlyMemory<byte>> convert)\n        {\n            _typeInfo = typeInfo;\n            _castMode = castMode;\n            _valueType = valueType;\n            _convert = convert;\n        }\n\n        public bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n        {\n            var bytes = _convert(value);\n            valueWriter = new HexStringLiteralValueWriter(bytes, isNested);\n            return true;\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, T value)\n        {\n            var bytes = _convert(value);\n\n            switch (_castMode)\n            {\n                case HexStringLiteralWriterCastMode.Cast:\n                    queryBuilder.Append(\"CAST(\");\n                    break;\n\n                case HexStringLiteralWriterCastMode.Reinterpret:\n                    queryBuilder.Append(\"reinterpret(\");\n                    break;\n\n                case HexStringLiteralWriterCastMode.None:\n                    break;\n\n                default:\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. Unknown value cast mode: \\\"{_castMode}\\\".\");\n            }\n\n            if (_valueType != null)\n                queryBuilder.Append(\"CAST(\");\n\n            HexStringParameterWriter.Interpolate(queryBuilder, bytes.Span);\n\n            if (_valueType != null)\n                queryBuilder.Append(\" AS \").Append(_valueType).Append(')');\n\n            switch (_castMode)\n            {\n                case HexStringLiteralWriterCastMode.Cast:\n                    queryBuilder.Append(\" AS \").Append(_typeInfo.ComplexTypeName).Append(')');\n                    break;\n\n                case HexStringLiteralWriterCastMode.Reinterpret:\n                    queryBuilder.Append(\",'\").Append(_typeInfo.ComplexTypeName.Replace(\"'\", \"''\")).Append(\"')\");\n                    break;\n            }\n\n            return queryBuilder;\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n        {\n            if (_valueType == null)\n                return writeValue(queryBuilder, _typeInfo, FunctionHelper.Apply);\n\n            var valueType = typeInfoProvider.GetTypeInfo(_valueType);\n\n            var castMode = _castMode;\n            if (_typeInfo.ComplexTypeName == valueType.ComplexTypeName)\n                castMode = HexStringLiteralWriterCastMode.None;\n\n            switch (castMode)\n            {\n                case HexStringLiteralWriterCastMode.Cast:\n                    return writeValue(queryBuilder, valueType, (qb, realWrite) =>\n                    {\n                        qb.Append('(');\n                        realWrite(qb);\n                        return qb.Append(\"::\").Append(_typeInfo.ComplexTypeName).Append(')');\n                    });\n\n                case HexStringLiteralWriterCastMode.Reinterpret:\n                    return writeValue(queryBuilder, valueType, (qb, realWrite) =>\n                    {\n                        qb.Append(\"reinterpret(\");\n                        realWrite(qb);\n                        return qb.Append(\",'\").Append(_typeInfo.ComplexTypeName.Replace(\"'\", \"''\")).Append(\"')\");\n                    });\n\n                default:\n                    Debug.Assert(castMode == HexStringLiteralWriterCastMode.None);\n                    return writeValue(queryBuilder, valueType, FunctionHelper.Apply);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IClickHouseColumnReinterpreter.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing System;\r\n\r\nnamespace Octonica.ClickHouseClient.Types\r\n{\r\n    internal interface IClickHouseColumnReinterpreter\r\n    {\r\n        Type? BuiltInConvertToType { get; }\r\n\r\n        Type? ExternalConvertToType { get; }\r\n\r\n        IClickHouseTableColumn? TryReinterpret(IClickHouseTableColumn column);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IClickHouseColumnTypeDescriptor.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021-2022 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    /// <summary>\n    /// Represents a set of properties describing a type of a column.\n    /// </summary>\n    public interface IClickHouseColumnTypeDescriptor\n    {\n        /// <summary>\n        /// Gets the type of the column.\n        /// </summary>\n        ClickHouseDbType? ClickHouseDbType { get; }\n\n        /// <summary>\n        /// Gets the type of the column's value (i.e. the type of column's cells).\n        /// </summary>\n        Type ValueType { get; }\n\n        /// <summary>\n        /// Gets the value indicating whether the column can contain NULLs.\n        /// </summary>\n        bool? IsNullable { get; }\n\n        /// <summary>\n        /// Gets the size. This value is applied to the ClickHouse type FixedString.\n        /// </summary>\n        int Size { get; }\n\n        /// <summary>\n        /// Gets the precision. This value is applied to ClickHouse types Decimal and DateTime64.\n        /// </summary>\n        byte? Precision { get; }\n\n        /// <summary>\n        /// Gets the scale. This value is applied to the ClickHouse type Decimal.\n        /// </summary>\n        byte? Scale { get; }\n\n        /// <summary>\n        /// Gets the time zone. This value is applied to ClickHouse types DateTime and DateTime64.\n        /// </summary>\n        TimeZoneInfo? TimeZone { get; }\n\n        /// <summary>\n        /// Gets the rank (a number of dimensions) of an array.\n        /// </summary>\n        int? ArrayRank { get; }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IClickHouseColumnTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    /// <summary>\n    /// Represents basic information about the ClickHouse type. Provides access to factory methods for creating column readers and writers.\n    /// </summary>\n    /// <remarks>\n    /// Being a part of the ClickHouseClient's infrastructure, the interface <see cref=\"IClickHouseColumnTypeInfo\"/> is considered unstable. It can be changed between minor versions.\n    /// </remarks>\n    public interface IClickHouseColumnTypeInfo : IClickHouseTypeInfo\n    {\n        /// <summary>\n        /// Creates and returns a new instance of <see cref=\"IClickHouseColumnReader\"/> configured to read the specified number of rows.\n        /// </summary>\n        /// <param name=\"rowCount\">The number of rows that the reader should read.</param>\n        /// <returns>The <see cref=\"IClickHouseColumnReader\"/> that should read the specified number of rows.</returns>\n        IClickHouseColumnReader CreateColumnReader(int rowCount);\n\n        /// <summary>\n        /// Creates and returns a new instance of <see cref=\"IClickHouseColumnReader\"/> configured to read the specified number of rows.\n        /// </summary>\n        /// <param name=\"rowCount\">The number of rows that the reader should read.</param>\n        /// <param name=\"serializationMode\">\n        /// One of supported serialization modes (see <see cref=\"ClickHouseColumnSerializationMode\"/> for details).\n        /// When the mode is <see cref=\"ClickHouseColumnSerializationMode.Default\"/> an implementation\n        /// of this method must call <see cref=\"CreateColumnReader(int)\"/>.\n        /// </param>\n        /// <returns>The <see cref=\"IClickHouseColumnReader\"/> that should read the specified number of rows.</returns>\n        IClickHouseColumnReader CreateColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            switch (serializationMode)\n            {\n                case ClickHouseColumnSerializationMode.Default:\n                    return CreateColumnReader(rowCount);\n\n                case ClickHouseColumnSerializationMode.Sparse:\n                case ClickHouseColumnSerializationMode.Custom:\n                    return new CustomSerializationColumnReader(this, rowCount, serializationMode);\n\n                default:\n                    throw new ArgumentException($\"Unknown serialization mode: {serializationMode}.\", nameof(serializationMode));\n            }\n        }\n\n        /// <summary>\n        /// Creates and returns a new instance of <see cref=\"IClickHouseColumnReaderBase\"/> configured to skip the specified number of rows.\n        /// </summary>\n        /// <param name=\"rowCount\">The number of rows that the reader should skip.</param>\n        /// <returns>The <see cref=\"IClickHouseColumnReaderBase\"/> that should skip the specified number of rows.</returns>\n        IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount);\n\n        /// <summary>\n        /// Creates and returns a new instance of <see cref=\"IClickHouseColumnReaderBase\"/> configured to skip the specified number of rows.\n        /// </summary>\n        /// <param name=\"rowCount\">The number of rows that the reader should skip.</param>\n        /// <param name=\"serializationMode\">\n        /// One of supported serialization modes (see <see cref=\"ClickHouseColumnSerializationMode\"/> for details).\n        /// When the mode is <see cref=\"ClickHouseColumnSerializationMode.Default\"/> an implementation\n        /// of this method must call <see cref=\"CreateSkippingColumnReader(int)\"/>.\n        /// </param>\n        /// <returns>The <see cref=\"IClickHouseColumnReaderBase\"/> that should skip the specified number of rows.</returns>\n        IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            switch (serializationMode)\n            {\n                case ClickHouseColumnSerializationMode.Default:\n                    return CreateSkippingColumnReader(rowCount);\n\n                case ClickHouseColumnSerializationMode.Sparse:\n                case ClickHouseColumnSerializationMode.Custom:\n                    return new CustomSerializationSkippingColumnReader(this, rowCount, serializationMode);\n\n                default:\n                    throw new ArgumentException($\"Unknown serialization mode: {serializationMode}.\", nameof(serializationMode));\n            }\n        }\n\n        /// <summary>\n        /// Creates and returns a new instance of <see cref=\"IClickHouseColumnWriter\"/> that can write specified rows to a binary stream.\n        /// </summary>\n        /// <typeparam name=\"T\">The type of the list of rows.</typeparam>\n        /// <param name=\"columnName\">The name of the column.</param>\n        /// <param name=\"rows\">The list of rows.</param>\n        /// <param name=\"columnSettings\">Optional argument. Additional settings for the column writer.</param>\n        /// <returns>The <see cref=\"IClickHouseColumnWriter\"/> that can write specified rows to a binary stream</returns>\n        IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings);\n\n        /// <summary>\n        /// Returns an instance of <see cref=\"IClickHouseColumnTypeInfo\"/> based on this type but with the specified list of type arguments.\n        /// </summary>\n        /// <param name=\"options\">The list of strings. Each string in the list describes an argument of the type.</param>\n        /// <param name=\"typeInfoProvider\">The type provider that can be used to get other types.</param>\n        /// <returns>The <see cref=\"IClickHouseColumnTypeInfo\"/> with the same <see cref=\"IClickHouseTypeInfo.TypeName\"/> as this type and with the specified list of type arguments</returns>\n        IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider);\n\n        /// <summary>\n        /// Creates an instance of <see cref=\"IClickHouseParameterWriter{T}\"/> capable of writing a value of the type <typeparamref name=\"T\"/>\n        /// as a ClickHouse parameter.\n        /// </summary>\n        /// <returns>The <see cref=\"IClickHouseParameterWriter{T}\"/> that can writer the value of the type <typeparamref name=\"T\"/> as a parameter.</returns>\n        IClickHouseParameterWriter<T> CreateParameterWriter<T>();\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IClickHouseConfigurableTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    /// <summary>\n    /// Represents basic information about the ClickHouse type that depends on the server's settings.\n    /// Provides access to factory methods for creating column readers and writers.\n    /// </summary>\n    public interface IClickHouseConfigurableTypeInfo : IClickHouseColumnTypeInfo\n    {\n        /// <summary>\n        /// Returns the <see cref=\"IClickHouseColumnTypeInfo\"/> that represents the type with the specified settings.\n        /// </summary>\n        /// <param name=\"serverInfo\">Information about the server.</param>\n        /// <returns>The <see cref=\"IClickHouseColumnTypeInfo\"/> that represents the type with the specified settings.</returns>\n        IClickHouseColumnTypeInfo Configure(ClickHouseServerInfo serverInfo);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IClickHouseEnumConverter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    /// <summary>\n    /// The basic interface for enum converters. Objects that implement this interface are responsible for the enum's type dispatching.\n    /// </summary>\n    public interface IClickHouseEnumConverter\n    {\n        /// <summary>\n        /// When implemented in a derrived class must call <see cref=\"IClickHouseEnumConverterDispatcher{T}.Dispatch{TEnum}(IClickHouseEnumConverter{TEnum})\"/>\n        /// with an appropriate generic argument.\n        /// </summary>\n        /// <typeparam name=\"T\">The type of the returned value.</typeparam>\n        /// <param name=\"dispatcher\">The dispatcher that requires the type of enum to be passed as the generic parameter.</param>\n        /// <returns>The value returned by the dispatcher.</returns>\n        public T Dispatch<T>(IClickHouseEnumConverterDispatcher<T> dispatcher);\n    }\n\n    /// <summary>\n    /// The basic interface for enum converters that can convert from and to .NET enums.\n    /// Objects that implement this interface are responsible for the enum's type dispatching.\n    /// </summary>\n    /// <typeparam name=\"TEnum\">The type of the enum.</typeparam>\n    public interface IClickHouseEnumConverter<TEnum> : IClickHouseEnumConverter\n        where TEnum : Enum\n    {\n        /// <summary>\n        /// When implemented in a derived class returns a enum's value corresponding to a numeric value of the ClickHouse enum, a string value of the ClickHouse enum, or both.\n        /// </summary>\n        /// <param name=\"value\">The numeric value of the ClickHouse enum.</param>\n        /// <param name=\"stringValue\">The string value of the ClickHouse enum.</param>\n        /// <param name=\"enumValue\">When this method returns, contains the value of enum or the default value of <typeparamref name=\"TEnum\"/> when returns <see langword=\"false\"/>.</param>\n        /// <returns><see langword=\"true\"/> if there are <typeparamref name=\"TEnum\"/>'s value corresponding to the specified ClickHouse enum; otherwise <see langword=\"false\"/>.</returns>\n        bool TryMap(int value, string stringValue, [NotNullWhen(true)] out TEnum enumValue);\n    }\n\n    /// <summary>\n    /// The interface for a enum dispatchers. \n    /// </summary>\n    /// <typeparam name=\"T\">The type of an object returned by this despatcher.</typeparam>\n    public interface IClickHouseEnumConverterDispatcher<out T>\n    {\n        /// <summary>\n        /// When implemented in a derrived class executes an arbitrary operation with respect to the enum converter\n        /// and returns the result of this operation.\n        /// </summary>\n        /// <typeparam name=\"TEnum\">The type of the enum.</typeparam>\n        /// <param name=\"enumConverter\">The enum converter for the enum of the specified type.</param>\n        /// <returns>The result of an executed operations.</returns>\n        /// <remarks>This method can be viewed as a closure of the type argument.</remarks>\n        public T Dispatch<TEnum>(IClickHouseEnumConverter<TEnum> enumConverter)\n            where TEnum : Enum;\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IClickHouseReinterpretedTableColumn.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing System;\r\n\r\nnamespace Octonica.ClickHouseClient.Types\r\n{\r\n    internal interface IClickHouseReinterpretedTableColumn<out T>: IClickHouseTableColumn<T>\r\n    {\r\n        IClickHouseReinterpretedTableColumn<TResult> Chain<TResult>(Func<T, TResult> reinterpret);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IClickHouseTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    /// <summary>\n    /// Represents basic information about the ClickHouse type.\n    /// </summary>\n    public interface IClickHouseTypeInfo\n    {\n        /// <summary>\n        /// Gets the full name of the type. The full name contains of the name of the type and the list of arguments.\n        /// </summary>\n        string ComplexTypeName { get; }\n\n        /// <summary>\n        /// Gets the name of the type without a list of arguments.\n        /// </summary>\n        string TypeName { get; }\n\n        /// <summary>\n        /// Gets the number of generic arguments in the list of arguments. Only arguments that are types themselves are counted as generics.\n        /// </summary>        \n        int GenericArgumentsCount { get; }\n\n        /// <summary>\n        /// Gets the number of arguments in the list of arguments.\n        /// </summary>\n        int TypeArgumentsCount => GenericArgumentsCount;\n\n        /// <summary>\n        /// Gets the CLR type to which this ClickHouse type is mapped.\n        /// </summary>\n        /// <returns>The <see cref=\"Type\"/> to which this ClickHouse type is mapped.</returns>\n        /// <remarks>If the ClickHouse type is mapped to a nullable structure this method returns <see langword=\"typeof\"/>(<see cref=\"Nullable{T}\"/>).</remarks>\n        Type GetFieldType();\n\n        /// <summary>\n        /// Gets the type code specific to ClickHouse.\n        /// </summary>\n        ClickHouseDbType GetDbType();\n\n        /// <summary>\n        /// Gets the generic arguments at the specified position. Generic arguments are counted separately,\n        /// which means that the index can't be greater than <see cref=\"GenericArgumentsCount\"/>.\n        /// </summary>\n        /// <param name=\"index\">The zero-based index of the generic argument. It can't be greater than <see cref=\"GenericArgumentsCount\"/>.</param>\n        /// <returns>The <see cref=\"IClickHouseTypeInfo\"/> which represents the generic argument.</returns>\n        IClickHouseTypeInfo GetGenericArgument(int index);\n\n        /// <summary>\n        /// Gets the argument of the type. For generic arguments this method returns an <see cref=\"IClickHouseTypeInfo\"/>.\n        /// </summary>\n        /// <param name=\"index\">The zero-based index of the argument. It can't be greater than <see cref=\"TypeArgumentsCount\"/>.</param>\n        /// <returns>The argument of the type.</returns>\n        object GetTypeArgument(int index)\n        {\n            if (TypeArgumentsCount == 0)\n                throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have arguments.\");\n\n            return GetGenericArgument(index);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IClickHouseTypeInfoProvider.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2022 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    /// <summary>\n    /// The base interface for an object that provides information about supported types.\n    /// </summary>\n    public interface IClickHouseTypeInfoProvider\n    {\n        /// <summary>\n        /// Gets the type by its name.\n        /// </summary>\n        /// <param name=\"typeName\">The name of the type.</param>\n        /// <returns>The <see cref=\"IClickHouseColumnTypeInfo\"/> that provides information about the type.</returns>\n        IClickHouseColumnTypeInfo GetTypeInfo(string typeName);\n\n        /// <inheritdoc cref=\"GetTypeInfo(string)\"/>\n        IClickHouseColumnTypeInfo GetTypeInfo(ReadOnlyMemory<char> typeName);\n\n        /// <summary>\n        /// Gets the type based on the <see cref=\"IClickHouseColumnDescriptor\"/>.\n        /// </summary>\n        /// <param name=\"typeDescriptor\">The descriptor of a column's type.</param>\n        /// <returns>The <see cref=\"IClickHouseColumnTypeDescriptor\"/> that provides information about the type.</returns>\n        IClickHouseColumnTypeInfo GetTypeInfo(IClickHouseColumnTypeDescriptor typeDescriptor);\n\n        /// <summary>\n        /// Returns the <see cref=\"IClickHouseColumnTypeInfo\"/> that provides access to types configured with the specified settings.\n        /// </summary>\n        /// <param name=\"serverInfo\">Information about the server.</param>\n        /// <returns>The <see cref=\"IClickHouseColumnTypeInfo\"/> that provides access to types configured with the specified settings.</returns>\n        IClickHouseTypeInfoProvider Configure(ClickHouseServerInfo serverInfo);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Int128TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Int128TypeInfo : BigIntegerTypeInfoBase\n    {\n        public Int128TypeInfo()\n            : base(\"Int128\", 128 / 8, false)\n        {\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Int128;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Int16TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Int16TableColumn : StructureTableColumn<short>\n    {\n        public Int16TableColumn(ReadOnlyMemory<short> buffer)\n            : base(buffer)\n        {\n        }\n\n        public override IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(int))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<short, int>(this, v => v);\n            if (typeof(T) == typeof(long))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<short, long>(this, v => v);\n\n            if (typeof(T) == typeof(int?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<int>(null, new ReinterpretedTableColumn<short, int>(this, v => v));\n            if (typeof(T) == typeof(long?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<long>(null, new ReinterpretedTableColumn<short, long>(this, v => v));\n\n            return base.TryReinterpret<T>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Int16TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Int16TypeInfo : SimpleTypeInfo\n    {\n        public Int16TypeInfo()\n            : base(\"Int16\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new Int16Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(short), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            var type = typeof(T);\n            IReadOnlyList<short> shortRows;\n            if (type == typeof(short))\n                shortRows = (IReadOnlyList<short>)rows;\n            else if (type == typeof(sbyte))\n                shortRows = MappedReadOnlyList<sbyte, short>.Map((IReadOnlyList<sbyte>)rows, v => v);\n            else if (type == typeof(byte))\n                shortRows = MappedReadOnlyList<byte, short>.Map((IReadOnlyList<byte>)rows, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n            \n            return new Int16Writer(columnName, ComplexTypeName, shortRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer = default(T) switch\n            {\n                short _ => new SimpleParameterWriter<short>(this, appendTypeCast: true),\n                sbyte _ => new SimpleParameterWriter<sbyte>(this, appendTypeCast: true),\n                byte _ => new SimpleParameterWriter<byte>(this, appendTypeCast: true),\n                _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\")\n            };\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(short);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Int16;\n        }\n\n        internal sealed class Int16Reader : StructureReaderBase<short>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Int16Reader(int rowCount)\n                : base(sizeof(short), rowCount)\n            {\n            }\n\n            protected override short ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToInt16(source);\n            }\n\n            protected override IClickHouseTableColumn<short> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<short> buffer)\n            {\n                return new Int16TableColumn(buffer);\n            }\n        }\n\n        internal sealed class Int16Writer : StructureWriterBase<short>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Int16Writer(string columnName, string columnType, IReadOnlyList<short> rows)\n                : base(columnName, columnType, sizeof(short), rows)\n            {\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in short value)\n            {\n                var success = BitConverter.TryWriteBytes(writeTo, value);\n                Debug.Assert(success);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Int256TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Int256TypeInfo : BigIntegerTypeInfoBase\n    {\n        public Int256TypeInfo()\n            : base(\"Int256\", 256 / 8, false)\n        {\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Int256;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Int32TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Int32TableColumn : StructureTableColumn<int>\n    {\n        public Int32TableColumn(ReadOnlyMemory<int> buffer)\n            : base(buffer)\n        {\n        }\n\n        public override IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(long))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<int, long>(this, v => v);\n            if (typeof(T) == typeof(long?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<long>(null, new ReinterpretedTableColumn<int, long>(this, v => v));\n\n            return base.TryReinterpret<T>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Int32TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Int32TypeInfo : SimpleTypeInfo\n    {\n        public Int32TypeInfo()\n            : base(\"Int32\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new Int32Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(int), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            var type = typeof(T);\n            IReadOnlyList<int> intRows;\n            if (type == typeof(int))\n                intRows = (IReadOnlyList<int>)rows;\n            else if (type == typeof(short))\n                intRows = MappedReadOnlyList<short, int>.Map((IReadOnlyList<short>)rows, v => v);\n            else if (type == typeof(ushort))\n                intRows = MappedReadOnlyList<ushort, int>.Map((IReadOnlyList<ushort>)rows, v => v);\n            else if (type == typeof(sbyte))\n                intRows = MappedReadOnlyList<sbyte, int>.Map((IReadOnlyList<sbyte>)rows, v => v);\n            else if (type == typeof(byte))\n                intRows = MappedReadOnlyList<byte, int>.Map((IReadOnlyList<byte>)rows, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n            \n            return new Int32Writer(columnName, ComplexTypeName, intRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer = default(T) switch\n            {\n                int _ => new SimpleParameterWriter<int>(this, appendTypeCast: true),\n                short _ => new SimpleParameterWriter<short>(this, appendTypeCast: true),\n                ushort _ => new SimpleParameterWriter<ushort>(this, appendTypeCast: true),\n                sbyte _ => new SimpleParameterWriter<sbyte>(this, appendTypeCast: true),\n                byte _ => new SimpleParameterWriter<byte>(this, appendTypeCast: true),\n                _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\")\n            };\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(int);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Int32;\n        }\n\n        private sealed class Int32Reader : StructureReaderBase<int>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Int32Reader(int rowCount)\n                : base(sizeof(int), rowCount)\n            {\n            }\n\n            protected override int ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToInt32(source);\n            }\n\n            protected override IClickHouseTableColumn<int> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<int> buffer)\n            {\n                return new Int32TableColumn(buffer);\n            }\n        }\n\n        private sealed class Int32Writer : StructureWriterBase<int>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Int32Writer(string columnName, string columnType, IReadOnlyList<int> rows)\n                : base(columnName, columnType, sizeof(int), rows)\n            {\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in int value)\n            {\n                var success = BitConverter.TryWriteBytes(writeTo, value);\n                Debug.Assert(success);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Int64TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Globalization;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Int64TypeInfo : SimpleTypeInfo\n    {\n        public Int64TypeInfo()\n            : base(\"Int64\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new Int64Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(long), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            var type = typeof(T);\n            IReadOnlyList<long> longRows;\n            if (type == typeof(long))\n                longRows = (IReadOnlyList<long>)rows;\n            else if (type == typeof(int))\n                longRows = MappedReadOnlyList<int, long>.Map((IReadOnlyList<int>)rows, v => v);\n            else if (type == typeof(uint))\n                longRows = MappedReadOnlyList<uint, long>.Map((IReadOnlyList<uint>)rows, v => v);\n            else if (type == typeof(short))\n                longRows = MappedReadOnlyList<short, long>.Map((IReadOnlyList<short>)rows, v => v);\n            else if (type == typeof(ushort))\n                longRows = MappedReadOnlyList<ushort, long>.Map((IReadOnlyList<ushort>)rows, v => v);\n            else if (type == typeof(sbyte))\n                longRows = MappedReadOnlyList<sbyte, long>.Map((IReadOnlyList<sbyte>)rows, v => v);\n            else if (type == typeof(byte))\n                longRows = MappedReadOnlyList<byte, long>.Map((IReadOnlyList<byte>)rows, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n            \n            return new Int64Writer(columnName, ComplexTypeName, longRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer = default(T) switch\n            {\n                long _ => new SimpleParameterWriter<long>(this, appendTypeCast: true),\n                int _ => new SimpleParameterWriter<int>(this, appendTypeCast: true),\n                short _ => new SimpleParameterWriter<short>(this, appendTypeCast: true),\n                ushort _ => new SimpleParameterWriter<ushort>(this, appendTypeCast: true),\n                sbyte _ => new SimpleParameterWriter<sbyte>(this, appendTypeCast: true),\n                byte _ => new SimpleParameterWriter<byte>(this, appendTypeCast: true),\n                _ => throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\"),\n            };\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(long);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Int64;\n        }\n\n        private sealed class Int64Reader : StructureReaderBase<long>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Int64Reader(int rowCount)\n                : base(sizeof(long), rowCount)\n            {\n            }\n\n            protected override long ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToInt64(source);\n            }\n        }\n\n        private sealed class Int64Writer : StructureWriterBase<long>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Int64Writer(string columnName, string columnType, IReadOnlyList<long> rows)\n                : base(columnName, columnType, sizeof(long), rows)\n            {\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in long value)\n            {\n                var success = BitConverter.TryWriteBytes(writeTo, value);\n                Debug.Assert(success);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Int8TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Int8TableColumn : StructureTableColumn<sbyte>\n    {\n        public Int8TableColumn(ReadOnlyMemory<sbyte> buffer)\n            : base(buffer)\n        {\n        }\n\n        public override IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(short))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<sbyte, short>(this, v => v);\n            if (typeof(T) == typeof(int))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<sbyte, int>(this, v => v);\n            if (typeof(T) == typeof(long))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<sbyte, long>(this, v => v);\n\n            if (typeof(T) == typeof(short?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<short>(null, new ReinterpretedTableColumn<sbyte, short>(this, v => v));\n            if (typeof(T) == typeof(int?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<int>(null, new ReinterpretedTableColumn<sbyte, int>(this, v => v));\n            if (typeof(T) == typeof(long?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<long>(null, new ReinterpretedTableColumn<sbyte, long>(this, v => v));\n\n            return base.TryReinterpret<T>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/Int8TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class Int8TypeInfo : SimpleTypeInfo\n    {\n        public Int8TypeInfo()\n            : base(\"Int8\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new Int8Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(sbyte), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (typeof(T) != typeof(sbyte))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new Int8Writer(columnName, ComplexTypeName, (IReadOnlyList<sbyte>)rows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            if (type == typeof(sbyte))\n                return (IClickHouseParameterWriter<T>)(object)new SimpleParameterWriter<sbyte>(this, appendTypeCast: true);\n\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(sbyte);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.SByte;\n        }\n\n        internal sealed class Int8Reader : StructureReaderBase<sbyte>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public Int8Reader(int rowCount)\n                : base(sizeof(sbyte), rowCount)\n            {\n            }\n\n            protected override sbyte ReadElement(ReadOnlySpan<byte> source)\n            {\n                return unchecked((sbyte) source[0]);\n            }\n\n            protected override IClickHouseTableColumn<sbyte> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<sbyte> buffer)\n            {\n                return new Int8TableColumn(buffer);\n            }\n        }\n\n        internal sealed class Int8Writer : IClickHouseColumnWriter\n        {\n            private readonly IReadOnlyList<sbyte> _rows;\n\n            private int _position;\n\n            public string ColumnName { get; }\n\n            public string ColumnType { get; }\n\n            public Int8Writer(string columnName, string columnType, IReadOnlyList<sbyte> rows)\n            {\n                _rows = rows;\n                ColumnName = columnName;\n                ColumnType = columnType;\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                var size = Math.Min(writeTo.Length, _rows.Count - _position);\n\n                for (int i = 0; i < size; i++, _position++)\n                    writeTo[i] = unchecked((byte) _rows[_position]);\n\n                return new SequenceSize(size, size);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IntermediateClickHouseTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal readonly struct IntermediateClickHouseTypeInfo\n    {\n        public ClickHouseDbType DbType { get; }\n        public string ClickHouseType { get; }\n        public bool IsNullable { get; }\n        public int ArrayRank { get; }\n\n        public IntermediateClickHouseTypeInfo(ClickHouseDbType dbType, string clickHouseType, bool isNullable, int arrayRank)\n        {\n            DbType = dbType;\n            ClickHouseType = clickHouseType;\n            IsNullable = isNullable;\n            ArrayRank = arrayRank;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IpColumnReaderBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Net;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal abstract class IpColumnReaderBase : IClickHouseColumnReader\n    {\n        private readonly int _rowCount;\n        private readonly Memory<byte> _buffer;\n\n        private int _position;\n\n        public int ElementSize { get; }\n\n        protected IpColumnReaderBase(int rowCount, int elementSize)\n        {\n            _rowCount = rowCount;\n            ElementSize = elementSize;\n            _buffer = new Memory<byte>(new byte[_rowCount * elementSize]);\n        }\n\n        public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n        {\n            if (_position >= _rowCount)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n            var byteSize = Math.Min(ElementSize * (_rowCount - _position), (int) (sequence.Length - sequence.Length % ElementSize));\n            var elementCount = byteSize / ElementSize;\n            if (elementCount == 0)\n                return new SequenceSize(0, 0);\n\n            sequence.Slice(0, byteSize).CopyTo(_buffer.Slice(_position * ElementSize).Span);\n\n            _position += elementCount;\n            return new SequenceSize(byteSize, elementCount);\n        }\n\n        public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n        {\n            return EndRead(_buffer.Slice(0, _position * ElementSize));\n        }\n\n        protected abstract IClickHouseTableColumn<IPAddress> EndRead(ReadOnlyMemory<byte> buffer);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IpV4TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Net;\nusing System.Runtime.InteropServices;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class IpV4TableColumn: IClickHouseTableColumn<IPAddress>\n    {\n        private readonly ReadOnlyMemory<byte> _buffer;\n\n        public int RowCount => _buffer.Length / sizeof(uint);\n\n        public IPAddress DefaultValue => IPAddress.Any;\n\n        public IpV4TableColumn(ReadOnlyMemory<byte> buffer)\n        {\n            _buffer = buffer;\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        public IPAddress GetValue(int index)\n        {\n            Span<int> address = stackalloc int[1];\n            var addressSpan = MemoryMarshal.AsBytes(address);\n            _buffer.Slice(index * sizeof(uint), sizeof(uint)).Span.CopyTo(addressSpan);\n            \n            address[0] = IPAddress.NetworkToHostOrder(address[0]);\n            return new IPAddress(addressSpan);\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            IClickHouseTableColumn? result = null;\n            if (typeof(T) == typeof(IPAddress))\n            {\n                result = this;\n            }\n            else if (typeof(T) == typeof(string))\n            {\n                result = new ReinterpretedTableColumn<IPAddress, string>(this, v => v.ToString());\n            }\n            if (typeof(T) == typeof(uint))\n            {\n                result = new ReinterpretedTableColumn<uint>(this, new RawIpV4TableColumn<uint>(_buffer, m => BitConverter.ToUInt32(m.Span)));\n            }\n            else if (typeof(T) == typeof(uint?))\n            {\n                result = new ReinterpretedTableColumn<uint?>(this, new NullableStructTableColumn<uint>(null, new RawIpV4TableColumn<uint>(_buffer, m => BitConverter.ToUInt32(m.Span))));\n            }\n            else if (typeof(T) == typeof(int))\n            {\n                result = new ReinterpretedTableColumn<int>(this, new RawIpV4TableColumn<int>(_buffer, m => BitConverter.ToInt32(m.Span)));\n            }\n            else if (typeof(T) == typeof(int?))\n            {\n                result = new ReinterpretedTableColumn<int?>(this, new NullableStructTableColumn<int>(null, new RawIpV4TableColumn<int>(_buffer, m => BitConverter.ToInt32(m.Span))));\n            }\n\n            return (IClickHouseTableColumn<T>?) result;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        private sealed class RawIpV4TableColumn<TStruct> : IClickHouseTableColumn<TStruct>\n            where TStruct : struct\n        {\n            private readonly ReadOnlyMemory<byte> _buffer;\n            private readonly Func<ReadOnlyMemory<byte>, TStruct> _getValue;\n\n            public int RowCount => _buffer.Length / sizeof(uint);\n\n            public TStruct DefaultValue => default;\n\n            public RawIpV4TableColumn(ReadOnlyMemory<byte> buffer, Func<ReadOnlyMemory<byte>, TStruct> getValue)\n            {\n                _buffer = buffer;\n                _getValue = getValue;\n            }\n\n            public bool IsNull(int index)\n            {\n                return false;\n            }\n\n            public TStruct GetValue(int index)\n            {\n                return _getValue(_buffer.Slice(index * sizeof(uint), sizeof(uint)));\n            }\n\n            object IClickHouseTableColumn.GetValue(int index)\n            {\n                return GetValue(index);\n            }\n\n            public IClickHouseTableColumn<T>? TryReinterpret<T>()\n            {\n                return this as IClickHouseTableColumn<T>;\n            }\n\n            bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n            {\n                dispatchedValue = dispatcher.Dispatch(this);\n                return true;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IpV4TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Net;\nusing System.Net.Sockets;\nusing System.Runtime.InteropServices;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class IpV4TypeInfo : SimpleTypeInfo\n    {\n        public IpV4TypeInfo()\n            : base(\"IPv4\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new IpV4Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(uint), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            var type = typeof(T);\n            IReadOnlyList<uint> preparedRows;\n\n            if (typeof(IPAddress).IsAssignableFrom(type))\n                preparedRows = MappedReadOnlyList<IPAddress, uint>.Map((IReadOnlyList<IPAddress>)rows, IpAddressToUInt32);\n            else if (type == typeof(string))\n                preparedRows = MappedReadOnlyList<string, uint>.Map((IReadOnlyList<string>)rows, IpAddressStringToUInt32);\n            else if (type == typeof(uint))\n                preparedRows = (IReadOnlyList<uint>)rows;\n            else if (type == typeof(int))\n                preparedRows = MappedReadOnlyList<int, uint>.Map((IReadOnlyList<int>)rows, v => unchecked((uint)v));\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new IpV4Writer(columnName, TypeName, preparedRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            const string valueType = \"UInt32\";\n            object writer;\n            if (type == typeof(IPAddress))\n                writer = new SimpleParameterWriter<IPAddress, uint>(valueType, this, null, true, IpAddressToUInt32);\n            else if (type == typeof(string))\n                writer = new SimpleParameterWriter<string, uint>(valueType, this, null, true, IpAddressStringToUInt32);\n            else if (type == typeof(uint))\n                writer = new SimpleParameterWriter<uint>(valueType, this, appendTypeCast: true);\n            else if (type == typeof(int))\n                writer = new SimpleParameterWriter<int, uint>(valueType, this, null, true, v => unchecked((uint)v));\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(IPAddress);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.IpV4;\n        }\n\n        private static uint IpAddressStringToUInt32(string? address)\n        {\n            if (address == null)\n                return 0;\n\n            if (!IPAddress.TryParse(address, out var ipAddress))\n                throw new InvalidCastException($\"The string \\\"{address}\\\" is not a valid IPv4 address.\");\n\n            return IpAddressToUInt32(ipAddress);\n        }\n\n        private static uint IpAddressToUInt32(IPAddress? address)\n        {\n            if (address == null)\n                return 0;\n\n            if (address.AddressFamily == AddressFamily.InterNetworkV6 && address.IsIPv4MappedToIPv6)\n                address = address.MapToIPv4();\n\n            if (address.AddressFamily != AddressFamily.InterNetwork)\n                throw new InvalidCastException($\"The network address \\\"{address}\\\" is not a IPv4 address.\");\n\n            Span<uint> result = stackalloc uint[1];\n            if (!address.TryWriteBytes(MemoryMarshal.AsBytes(result), out var written) || written != sizeof(uint))\n                throw new InvalidCastException($\"The network address \\\"{address}\\\" is not a IPv4 address.\");\n\n            return unchecked((uint) IPAddress.HostToNetworkOrder((int) result[0]));\n        }\n\n        private sealed class IpV4Reader : IpColumnReaderBase\n        {\n            public IpV4Reader(int rowCount)\n                : base(rowCount, sizeof(uint))\n            {\n            }\n\n            protected override IClickHouseTableColumn<IPAddress> EndRead(ReadOnlyMemory<byte> buffer)\n            {\n                return new IpV4TableColumn(buffer);\n            }\n        }\n\n        private sealed class IpV4Writer : StructureWriterBase<uint>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public IpV4Writer(string columnName, string columnType, IReadOnlyList<uint> rows)\n                : base(columnName, columnType, sizeof(uint), rows)\n            {\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in uint value)\n            {\n                var success = BitConverter.TryWriteBytes(writeTo, value);\n                Debug.Assert(success);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IpV6TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Net;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class IpV6TableColumn : IClickHouseTableColumn<IPAddress>\n    {\n        private readonly ReadOnlyMemory<byte> _buffer;\n\n        public int RowCount => _buffer.Length / 16;\n\n        public IPAddress DefaultValue => IPAddress.IPv6Any;\n\n        public IpV6TableColumn(ReadOnlyMemory<byte> buffer)\n        {\n            _buffer = buffer;\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        public IPAddress GetValue(int index)\n        {\n            return new IPAddress(_buffer.Span.Slice(index * 16, 16));\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(string))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<IPAddress, string>(this, v => v.ToString());\n\n            return this as IClickHouseTableColumn<T>;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/IpV6TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Net;\nusing System.Net.Sockets;\nusing System.Runtime.CompilerServices;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class IpV6TypeInfo: SimpleTypeInfo\n    {\n        private const int AddressSize = 16;\n\n        public IpV6TypeInfo()\n            : base(\"IPv6\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new IpV6Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(AddressSize, rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            var type = typeof(T);\n            IReadOnlyList<IPAddress?> preparedRows;\n            if(typeof(IPAddress).IsAssignableFrom(type))\n                preparedRows = (IReadOnlyList<IPAddress?>)rows;\n            else if(type == typeof(string))\n                preparedRows = MappedReadOnlyList<string?, IPAddress?>.Map((IReadOnlyList<string?>)rows, ParseIpAddress);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new IpV6Writer(columnName, TypeName, preparedRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (typeof(T) == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            var binaryTypeName = $\"FixedString({AddressSize.ToString(CultureInfo.InvariantCulture)})\";\n            object writer;\n            if (type == typeof(IPAddress))\n                writer = new HexStringParameterWriter<IPAddress>(this, HexStringLiteralWriterCastMode.Cast, binaryTypeName, theValue => GetBytes(theValue));\n            else if (type == typeof(string))\n                writer = new HexStringParameterWriter<string>(this, HexStringLiteralWriterCastMode.Cast, binaryTypeName, theValue => GetBytes(ParseIpAddress(theValue)));\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(IPAddress);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.IpV6;\n        }\n\n        [return: NotNullIfNotNull(\"address\")]\n        private static IPAddress? ParseIpAddress(string? address)\n        {\n            if (address == null)\n                return null;\n\n            if (!IPAddress.TryParse(address, out var ipAddress))\n                throw new InvalidCastException($\"The string \\\"{address}\\\" is not a valid IPv4 address.\");\n\n            return ipAddress;\n        }\n\n        private static byte[] GetBytes(IPAddress ipAddress)\n        {\n            var buffer = new byte[AddressSize];\n            WriteBytes(buffer, ipAddress);\n            return buffer;\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        private static void WriteBytes(Span<byte> writeTo, IPAddress ipAddress)\n        {\n            if (ipAddress.AddressFamily == AddressFamily.InterNetwork)\n                ipAddress = ipAddress.MapToIPv6();\n\n            if (ipAddress.AddressFamily != AddressFamily.InterNetworkV6)\n                throw new InvalidCastException($\"The network address \\\"{ipAddress}\\\" is not a IPv6 address.\");\n\n            if (!ipAddress.TryWriteBytes(writeTo, out var bytesWritten) || bytesWritten != AddressSize)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error: IPv6 address writing error.\");\n        }\n\n        private sealed class IpV6Reader : IpColumnReaderBase\n        {\n            public IpV6Reader(int rowCount)\n                : base(rowCount, AddressSize)\n            {\n            }\n\n            protected override IClickHouseTableColumn<IPAddress> EndRead(ReadOnlyMemory<byte> buffer)\n            {\n                return new IpV6TableColumn(buffer);\n            }\n        }\n\n        private sealed class IpV6Writer : IClickHouseColumnWriter\n        {\n            private readonly IReadOnlyList<IPAddress?> _rows;\n\n            private int _position;\n\n            public string ColumnName { get; }\n\n            public string ColumnType { get; }\n\n            public IpV6Writer(string columnName, string columnType, IReadOnlyList<IPAddress?> rows)\n            {\n                _rows = rows;\n                ColumnName = columnName;\n                ColumnType = columnType;\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                var elementsCount = Math.Min(_rows.Count - _position, writeTo.Length / AddressSize);\n\n                for (int i = 0; i < elementsCount; i++, _position++)\n                {\n                    var ipAddress = _rows[_position];\n                    if (ipAddress == null)\n                    {\n                        writeTo.Slice(i * AddressSize, AddressSize).Fill(0);\n                        continue;\n                    }\n\n                    WriteBytes(writeTo.Slice(i * AddressSize), ipAddress);\n                }\n\n                return new SequenceSize(elementsCount * AddressSize, elementsCount);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/KeyValuePairTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Collections.Generic;\nusing System.Diagnostics;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class KeyValuePairTableColumn<TKey, TValue> : TupleTableColumnBase, IClickHouseTableColumn<KeyValuePair<TKey, TValue>>\n    {\n        private readonly IClickHouseTableColumn<TKey> _keyColumn;\n        private readonly IClickHouseTableColumn<TValue> _valueColumn;\n\n        public KeyValuePair<TKey, TValue> DefaultValue => new KeyValuePair<TKey, TValue>(_keyColumn.DefaultValue, _valueColumn.DefaultValue);\n\n        public KeyValuePairTableColumn(int rowCount, IClickHouseTableColumn<TKey> keyColumn, IClickHouseTableColumn<TValue> valueColumn)\n            : base(rowCount)\n        {\n            _keyColumn = keyColumn;\n            _valueColumn = valueColumn;\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public new KeyValuePair<TKey, TValue> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new KeyValuePair<TKey, TValue>(_keyColumn.GetValue(index), _valueColumn.GetValue(index));            \n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _keyColumn;\n            yield return _valueColumn;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 2);\n\n                var keyColumn = TryReinterpret<TKey>(columns[0]);\n                if (keyColumn == null)\n                    return null;\n\n                var valueColumn = TryReinterpret<TValue>(columns[1]);\n                if (valueColumn == null)\n                    return null;\n\n                return new KeyValuePairTableColumn<TKey, TValue>(rowCount, keyColumn, valueColumn);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/LowCardinalityTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Runtime.InteropServices;\nusing Octonica.ClickHouseClient.Exceptions;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class LowCardinalityTableColumn : IClickHouseTableColumn\n    {\n        private readonly ReadOnlyMemory<byte> _keys;\n        private readonly int _keySize;\n        private readonly IClickHouseTableColumn _values;\n        private readonly bool _isNullable;\n\n        public int RowCount { get; }\n\n        public LowCardinalityTableColumn(ReadOnlyMemory<byte> keys, int keySize, IClickHouseTableColumn values, bool isNullable)\n        {\n            _keys = keys;\n            _keySize = keySize;\n            _values = values;\n            _isNullable = isNullable;\n            RowCount = _keys.Length / _keySize;\n        }\n\n        public bool IsNull(int index)\n        {\n            if (!_isNullable)\n                return false;\n\n            var valueIndex = GetValueIndex(index);\n            return valueIndex == 0;\n        }\n\n        public object GetValue(int index)\n        {\n            var valueIndex = GetValueIndex(index);\n            if (valueIndex == 0 && _isNullable)\n                return DBNull.Value;\n\n            return _values.GetValue(valueIndex);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            var reinterpretedValues = _values as IClickHouseTableColumn<T> ?? _values.TryReinterpret<T>();\n            if (reinterpretedValues == null)\n                return null;\n\n            return new LowCardinalityTableColumn<T>(_keys, _keySize, reinterpretedValues, _isNullable);\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            var reinterpretedValues = _values as IClickHouseArrayTableColumn<T> ?? _values.TryReinterpretAsArray<T>();\n            if (reinterpretedValues == null)\n                return null;\n\n            return new LowCardinalityArrayTableColumn<T>(this, _keys, _keySize, reinterpretedValues, _isNullable);\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = default;\n            return false;\n        }\n\n        private int GetValueIndex(int index)\n        {\n            if (index < 0 || index > RowCount)\n                throw new ArgumentOutOfRangeException(nameof(index));\n\n            int valueIndex = 0;\n            var valueIndexBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref valueIndex, 1)).Slice(0, _keySize);\n            _keys.Slice(index * _keySize, _keySize).Span.CopyTo(valueIndexBytes);\n\n            return valueIndex;\n        }\n    }\n\n    internal sealed class LowCardinalityTableColumn<TValue> : IClickHouseTableColumn<TValue>\n    {\n        private readonly ReadOnlyMemory<byte> _keys;\n        private readonly int _keySize;\n        private readonly IClickHouseTableColumn<TValue> _values;\n        private readonly bool _isNullable;\n\n        public int RowCount { get; }\n\n        public TValue DefaultValue => _values.DefaultValue;\n\n        public LowCardinalityTableColumn(ReadOnlyMemory<byte> keys, int keySize, IClickHouseTableColumn<TValue> values, bool isNullable)\n        {\n            _keys = keys;\n            _keySize = keySize;\n            _values = values;\n            _isNullable = isNullable;\n            RowCount = _keys.Length / _keySize;\n        }\n\n        public bool IsNull(int index)\n        {\n            if (!_isNullable)\n                return false;\n\n            var valueIndex = GetValueIndex(index);\n            return valueIndex == 0;\n        }\n\n        public TValue GetValue(int index)\n        {\n            var valueIndex = GetValueIndex(index);\n            if (valueIndex == 0 && _isNullable)\n            {\n                var defaultValue = default(TValue);\n                if (!(defaultValue is null))\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, $\"Can't convert NULL to \\\"{typeof(TValue)}\\\".\");\n\n                return defaultValue!;\n            }\n\n            return _values.GetValue(valueIndex);\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            var valueIndex = GetValueIndex(index);\n            if (valueIndex == 0 && _isNullable)\n                return DBNull.Value;\n\n            return ((IClickHouseTableColumn) _values).GetValue(valueIndex);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            var reinterpretedValues = _values as IClickHouseTableColumn<T> ?? _values.TryReinterpret<T>();\n            if (reinterpretedValues == null)\n                return null;\n\n            return new LowCardinalityTableColumn<T>(_keys, _keySize, reinterpretedValues, _isNullable);\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            var reinterpretedValues = _values as IClickHouseArrayTableColumn<T> ?? _values.TryReinterpretAsArray<T>();\n            if (reinterpretedValues == null)\n                return null;\n\n            return new LowCardinalityArrayTableColumn<T>(this, _keys, _keySize, reinterpretedValues, _isNullable);\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        private int GetValueIndex(int index)\n        {\n            if (index < 0 || index > RowCount)\n                throw new ArgumentOutOfRangeException(nameof(index));\n\n            int valueIndex = 0;\n            var valueIndexBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref valueIndex, 1)).Slice(0, _keySize);\n            _keys.Slice(index * _keySize, _keySize).Span.CopyTo(valueIndexBytes);\n\n            return valueIndex;\n        }\n    }\n\n    internal sealed class LowCardinalityArrayTableColumn<TElement> : IClickHouseArrayTableColumn<TElement>\n    {\n        private readonly IClickHouseTableColumn _reinterpretationRoot;\n        private readonly ReadOnlyMemory<byte> _keys;\n        private readonly int _keySize;\n        private readonly IClickHouseArrayTableColumn<TElement> _values;\n        private readonly bool _isNullable;\n\n        public int RowCount { get; }\n\n        public LowCardinalityArrayTableColumn(IClickHouseTableColumn reinterpretationRoot, ReadOnlyMemory<byte> keys, int keySize, IClickHouseArrayTableColumn<TElement> values, bool isNullable)\n        {\n            _reinterpretationRoot = reinterpretationRoot;\n            _keys = keys;\n            _keySize = keySize;\n            _values = values;\n            _isNullable = isNullable;\n            RowCount = _keys.Length / _keySize;\n        }\n\n        public int CopyTo(int index, Span<TElement> buffer, int dataOffset)\n        {\n            var valueIndex = GetValueIndex(index);\n            if (valueIndex == 0 && _isNullable)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Can't copy NULL value to the buffer.\");\n\n            return _values.CopyTo(valueIndex, buffer, dataOffset);\n        }\n\n        public object GetValue(int index)\n        {\n            var valueIndex = GetValueIndex(index);\n            if (valueIndex == 0 && _isNullable)\n                return DBNull.Value;\n\n            return _values.GetValue(valueIndex);\n        }\n\n        public bool IsNull(int index)\n        {\n            if (!_isNullable)\n                return false;\n\n            var valueIndex = GetValueIndex(index);\n            return valueIndex == 0;\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return _reinterpretationRoot as IClickHouseTableColumn<T> ?? _reinterpretationRoot.TryReinterpret<T>();\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            return _reinterpretationRoot as IClickHouseArrayTableColumn<T> ?? _reinterpretationRoot.TryReinterpretAsArray<T>();\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = default;\n            return false;\n        }\n\n        private int GetValueIndex(int index)\n        {\n            if (index < 0 || index > RowCount)\n                throw new ArgumentOutOfRangeException(nameof(index));\n\n            int valueIndex = 0;\n            var valueIndexBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref valueIndex, 1)).Slice(0, _keySize);\n            _keys.Slice(index * _keySize, _keySize).Span.CopyTo(valueIndexBytes);\n\n            return valueIndex;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/LowCardinalityTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class LowCardinalityTypeInfo : IClickHouseColumnTypeInfo\n    {\n        private readonly IClickHouseColumnTypeInfo? _baseType;\n\n        public string ComplexTypeName { get; }\n\n        public string TypeName => \"LowCardinality\";\n\n        public int GenericArgumentsCount => _baseType == null ? 0 : 1;\n\n        public LowCardinalityTypeInfo()\n        {\n            _baseType = null;\n            ComplexTypeName = TypeName;\n        }\n\n        private LowCardinalityTypeInfo(IClickHouseColumnTypeInfo baseType)\n        {\n            _baseType = baseType ?? throw new ArgumentNullException(nameof(baseType));\n            ComplexTypeName = $\"{TypeName}({_baseType.ComplexTypeName})\";\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            var typeInfo = GetBaseTypeInfo();\n            return new LowCardinalityColumnReader(rowCount, typeInfo.baseType, typeInfo.isNullable);\n        }\n\n        IClickHouseColumnReader IClickHouseColumnTypeInfo.CreateColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode == ClickHouseColumnSerializationMode.Default)\n                return CreateColumnReader(rowCount);\n\n            throw new NotSupportedException($\"Custom serialization for {TypeName} type is not supported by ClickHouseClient.\");\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            var typeInfo = GetBaseTypeInfo();\n            return new LowCardinalitySkippingColumnReader(rowCount, typeInfo.baseType);\n        }\n\n        IClickHouseColumnReaderBase IClickHouseColumnTypeInfo.CreateSkippingColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode == ClickHouseColumnSerializationMode.Default)\n                return CreateSkippingColumnReader(rowCount);\n\n            throw new NotSupportedException($\"Custom serialization for {TypeName} type is not supported by ClickHouseClient.\");\n        }\n\n        private (IClickHouseColumnTypeInfo baseType, bool isNullable) GetBaseTypeInfo()\n        {\n            if (_baseType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            if (_baseType is NullableTypeInfo nullableBaseType)\n            {\n                if (nullableBaseType.UnderlyingType == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{_baseType.ComplexTypeName}\\\" is not fully specified.\");\n\n                // LowCardinality column stores NULL as the key 0\n                return (nullableBaseType.UnderlyingType, true);\n            }\n\n            return (_baseType, false);\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (typeof(T) == typeof(string))\n            {\n                // In most cases values of type T can be inserted into a column of type LowCardinality(T).\n                // However, a column of type Map(LowCardinality(String), TValue) doesn't accept keys of type String.\n                // https://github.com/Octonica/ClickHouseClient/issues/86\n                return CreateColumnWriter(columnName, (IReadOnlyList<string>)rows, StringComparer.Ordinal, string.Empty, columnSettings);\n            }\n\n            if (_baseType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            // Writing values as is. Let the database do de-duplication\n            return _baseType.CreateColumnWriter(columnName, rows, columnSettings);\n        }\n\n        private LowCardinalityColumnWriter CreateColumnWriter<T>(\n            string columnName,\n            IReadOnlyList<T> rows,\n            IEqualityComparer<T> equalityComparer,\n            T defaultValue,\n            ClickHouseColumnSettings? columnSettings)\n            where T : notnull\n        {\n            var (baseType, isNullable) = GetBaseTypeInfo();\n\n            var map = new Dictionary<T, int>(equalityComparer);\n            var indices = new List<int>(rows.Count);\n            var uniqueValues = new List<T>();\n            if (isNullable)\n                uniqueValues.Add(defaultValue);\n\n            // Reserve the first non-nullable position in the dictionary for the defualt value\n            map.Add(defaultValue, uniqueValues.Count);\n            uniqueValues.Add(defaultValue);\n\n            foreach (var row in rows)\n            {\n                if (isNullable && row is null)\n                {\n                    indices.Add(0);\n                    continue;\n                }\n\n                if (!map.TryGetValue(row, out var index))\n                {\n                    index = uniqueValues.Count;\n                    map.Add(row, index);\n                    uniqueValues.Add(row);\n                }\n\n                indices.Add(index);\n            }\n\n            if (indices.Count == 0)\n                uniqueValues.Clear();\n\n            KeySizeCode keySizeCode;\n            if (uniqueValues.Count > ushort.MaxValue)\n                keySizeCode = KeySizeCode.UInt32;\n            else if (uniqueValues.Count > byte.MaxValue)\n                keySizeCode = KeySizeCode.UInt16;\n            else\n                keySizeCode = KeySizeCode.UInt8;\n\n            Debug.Assert(_baseType != null);\n            var baseWriter = baseType.CreateColumnWriter(columnName, uniqueValues, columnSettings);\n            return new LowCardinalityColumnWriter(baseWriter, uniqueValues.Count, ComplexTypeName, indices, keySizeCode);\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            if (_baseType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return _baseType.CreateParameterWriter<T>();\n        }\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            if (_baseType != null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, \"The type is already fully specified.\");\n\n            if (options.Count > 1)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"Too many arguments in the definition of \\\"{TypeName}\\\".\");\n\n            var baseType = typeInfoProvider.GetTypeInfo(options[0]);\n            return new LowCardinalityTypeInfo(baseType);\n        }\n\n        public Type GetFieldType()\n        {\n            if (_baseType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return _baseType.GetFieldType();\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            if (_baseType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return _baseType.GetDbType();\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            if (_baseType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            if (index != 0)\n                throw new IndexOutOfRangeException();\n\n            return _baseType;\n        }\n\n        private sealed class LowCardinalityColumnReader : IClickHouseColumnReader\n        {\n            private readonly int _rowCount;\n            private readonly IClickHouseColumnTypeInfo _baseType;\n            private readonly bool _isNullable;\n\n            private IClickHouseColumnReader? _baseColumnReader;\n            private int _baseRowCount;\n\n            private int _keySize;\n            private byte[]? _buffer;\n            private int _position;\n\n            public LowCardinalityColumnReader(int rowCount, IClickHouseColumnTypeInfo baseType, bool isNullable)\n            {\n                _rowCount = rowCount;\n                _baseType = baseType;\n                _isNullable = isNullable;\n            }\n\n            SequenceSize IClickHouseColumnReaderBase.ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                return ReadPrefix(sequence);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                if (_position >= _rowCount)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                var result = new SequenceSize(0, 0);\n                var slice = sequence;\n                if (_baseColumnReader == null)\n                {\n                    var header = TryReadHeader(slice);\n                    if (header == null)\n                        return result;\n\n                    _baseRowCount = header.Value.keyCount;\n                    _keySize = header.Value.keySize;\n                    _baseColumnReader = _baseType.CreateColumnReader(_baseRowCount);\n\n                    slice = slice.Slice(header.Value.bytesRead);\n                    result = result.AddBytes(header.Value.bytesRead);\n                }\n\n                if (_baseRowCount > 0)\n                {\n                    var baseResult = _baseColumnReader.ReadNext(slice);\n                    _baseRowCount -= baseResult.Elements;\n\n                    slice = slice.Slice(baseResult.Bytes);\n                    result = result.AddBytes(baseResult.Bytes);\n                }\n\n                if (_baseRowCount > 0)\n                    return result;\n\n                if (_buffer == null)\n                {\n                    if (slice.Length < sizeof(ulong))\n                        return result;\n\n                    ulong length = 0;\n                    slice.Slice(0, sizeof(ulong)).CopyTo(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref length, 1)));\n\n                    if ((int) length != _rowCount)\n                        throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, $\"Internal error. Row count check failed: {_rowCount} rows expected, {length} rows detected.\");\n\n                    _buffer = new byte[_rowCount * _keySize];\n\n                    slice = slice.Slice(sizeof(ulong));\n                    result = result.AddBytes(sizeof(ulong));\n                }\n\n                var elementCount = Math.Min(_rowCount - _position, (int) (slice.Length / _keySize));\n                var byteCount = elementCount * _keySize;\n                slice.Slice(0, byteCount).CopyTo(new Span<byte>(_buffer, _position * _keySize, byteCount));\n\n                _position += elementCount;\n                result += new SequenceSize(byteCount, elementCount);\n\n                return result;\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                ReadOnlyMemory<byte> keys;\n                int keySize;\n                if (_buffer == null)\n                {\n                    keys = ReadOnlyMemory<byte>.Empty;\n                    keySize = 1;\n                }\n                else\n                {\n                    keys = new ReadOnlyMemory<byte>(_buffer, 0, _position * _keySize);\n                    keySize = _keySize;\n                }\n\n                var valuesColumn = (_baseColumnReader ?? _baseType.CreateColumnReader(0)).EndRead(settings);\n                if (!valuesColumn.TryDipatch(new LowCardinalityTableColumnDispatcher(keys, keySize, _isNullable), out var result))\n                    result = new LowCardinalityTableColumn(keys, keySize, valuesColumn, _isNullable);\n\n                return result;\n            }\n\n            public static SequenceSize ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                if (sequence.Length < sizeof(ulong))\n                    return SequenceSize.Empty;\n\n                ulong version = 0;\n                sequence.Slice(0, sizeof(ulong)).CopyTo(MemoryMarshal.Cast<ulong, byte>(MemoryMarshal.CreateSpan(ref version, 1)));\n\n                // https://github.com/ClickHouse/ClickHouse/blob/master/src/DataTypes/DataTypeLowCardinality.cpp\n                // Dictionary is written as number N and N keys after them.\n                // Dictionary can be shared for continuous range of granules, so some marks may point to the same position.\n                // Shared dictionary is stored in state and is read once.\n                //\n                // SharedDictionariesWithAdditionalKeys = 1,\n\n                if (version != 1)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, $\"Unexpected dictionary version received: {version}.\");\n\n                return new SequenceSize(sizeof(ulong), 1);\n            }\n\n            public static (int keySize, int keyCount, int bytesRead)? TryReadHeader(ReadOnlySequence<byte> sequence)\n            {\n                Span<ulong> headerValues = stackalloc ulong[2];\n                var headerBytes = MemoryMarshal.AsBytes(headerValues);\n                if (sequence.Length < headerBytes.Length)\n                    return null;\n\n                sequence.Slice(0, headerBytes.Length).CopyTo(headerBytes);\n\n                var keySizeCode = (KeySizeCode)unchecked((byte)headerValues[0]);\n                int keySize;\n                switch (keySizeCode)\n                {\n                    case KeySizeCode.UInt8:\n                        keySize = 1;\n                        break;\n                    case KeySizeCode.UInt16:\n                        keySize = 2;\n                        break;\n                    case KeySizeCode.UInt32:\n                        keySize = 4;\n                        break;\n                    case KeySizeCode.UInt64:\n                        throw new NotSupportedException(\"64-bit keys are not supported.\");\n                    default:\n                        throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, $\"Internal error. Unexpected size of a key: {keySizeCode}.\");\n                }\n\n                // There are several control flags, but the client always receives 0x2|0x4\n                //\n                // https://github.com/ClickHouse/ClickHouse/blob/master/src/DataTypes/DataTypeLowCardinality.cpp\n                // 0x1 Need to read dictionary if it wasn't.\n                // 0x2 Need to read additional keys. Additional keys are stored before indexes as value N and N keys after them.\n                // 0x4 Need to update dictionary. It means that previous granule has different dictionary.\n\n                var flags = headerValues[0] >> 8;\n                if (flags != (0x2 | 0x4))\n                    throw new NotSupportedException(\"Received combination of flags is not supported.\");\n\n                var keyCount = checked((int)headerValues[1]);\n                return (keySize, keyCount, headerBytes.Length);\n            }\n        }\n\n        private sealed class LowCardinalitySkippingColumnReader : IClickHouseColumnReaderBase\n        {\n            private readonly int _rowCount;\n            private readonly IClickHouseColumnTypeInfo _baseType;\n\n            private IClickHouseColumnReaderBase? _baseReader;\n            private int _baseRowCount;\n            private int _keySize;\n\n            private int _basePosition;\n            private bool _headerSkipped;\n            private int _position;\n\n            public LowCardinalitySkippingColumnReader(int rowCount, IClickHouseColumnTypeInfo baseType)\n            {\n                _rowCount = rowCount;\n                _baseType = baseType;\n            }\n\n            SequenceSize IClickHouseColumnReaderBase.ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                return LowCardinalityColumnReader.ReadPrefix(sequence);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                var slice = sequence;\n                var result = new SequenceSize(0, 0);                \n                if (_baseReader == null)\n                {\n                    var header = LowCardinalityColumnReader.TryReadHeader(slice);\n                    if (header == null)\n                        return result;\n\n                    result = result.AddBytes(header.Value.bytesRead);\n                    slice = slice.Slice(header.Value.bytesRead);\n\n                    _baseRowCount = header.Value.keyCount;\n                    _keySize = header.Value.keySize;\n\n                    _baseReader = _baseType.CreateSkippingColumnReader(_baseRowCount);\n                }\n\n                if (_basePosition < _baseRowCount)\n                {\n                    var baseResult = _baseReader.ReadNext(slice);\n\n                    _basePosition += baseResult.Elements;\n                    result = result.AddBytes(baseResult.Bytes);\n\n                    if (_basePosition < _baseRowCount)\n                        return result;\n                }\n\n                if (!_headerSkipped)\n                {\n                    if (sequence.Length - result.Bytes < sizeof(ulong))\n                        return result;\n\n                    ulong length = 0;\n                    sequence.Slice(result.Bytes, sizeof(ulong)).CopyTo(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref length, 1)));\n\n                    if ((int)length != _rowCount)\n                        throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, $\"Internal error. Row count check failed: {_rowCount} rows expected, {length} rows detected.\");\n\n                    result = result.AddBytes(sizeof(ulong));\n                    _headerSkipped = true;\n                }\n\n                var maxElementsCount = _rowCount - _position;\n                if (maxElementsCount <= 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                var elementCount = (int)Math.Min((sequence.Length - result.Bytes) / _keySize, maxElementsCount);\n                _position += elementCount;\n                result += new SequenceSize(elementCount * _keySize, elementCount);\n\n                return result;\n            }\n        }\n\n        private sealed class LowCardinalityColumnWriter : IClickHouseColumnWriter\n        {\n            private readonly IClickHouseColumnWriter _baseWriter;\n            private readonly IReadOnlyList<int> _indices;\n            private readonly KeySizeCode _keySizeCode;\n\n            private bool _headerWritten;\n            private int _baseRowCount;\n            private int _position = -1;\n\n            public string ColumnName => _baseWriter.ColumnName;\n\n            public string ColumnType { get; }\n\n            public LowCardinalityColumnWriter(IClickHouseColumnWriter baseWriter, int baseRowCount, string columnType, IReadOnlyList<int> indices, KeySizeCode keySizeCode)\n            {\n                ColumnType = columnType;\n                _indices = indices;\n                _keySizeCode = keySizeCode;\n                _baseWriter = baseWriter;\n                _baseRowCount = baseRowCount;\n            }\n\n            SequenceSize IClickHouseColumnWriter.WritePrefix(Span<byte> writeTo)\n            {\n                if (writeTo.Length < sizeof(ulong))\n                    return SequenceSize.Empty;\n\n                var writeToVersion = MemoryMarshal.Cast<byte, ulong>(writeTo.Slice(0, sizeof(ulong)));\n                writeToVersion[0] = 1; // Version\n\n                return new SequenceSize(sizeof(ulong), 1);\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                var targetSpan = writeTo;\n                var result = new SequenceSize(0, 0);\n                if (!_headerWritten)\n                {\n                    Span<ulong> headerValues = stackalloc ulong[2];\n                    headerValues[0] = ((0x2 | 0x4) << 8) | (ulong)_keySizeCode; // The size of and element and flags\n                    headerValues[1] = (ulong)_baseRowCount;\n\n                    var headerBytes = MemoryMarshal.AsBytes(headerValues);\n                    if (headerBytes.Length > targetSpan.Length)\n                        return result;\n\n                    headerBytes.CopyTo(targetSpan);\n                    targetSpan = targetSpan.Slice(headerBytes.Length);\n                    result = result.AddBytes(headerBytes.Length);\n                    _headerWritten = true;\n                }\n\n                if (_baseRowCount > 0)\n                {\n                    var baseResult = _baseWriter.WriteNext(targetSpan);\n\n                    _baseRowCount -= baseResult.Elements;\n                    result = result.AddBytes(baseResult.Bytes);\n                    if (_baseRowCount > 0)\n                        return result;\n\n                    if (_baseRowCount != 0)\n                        throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. Row count check failed: written {-_baseRowCount} more rows then expected.\");\n\n                    targetSpan = targetSpan.Slice(baseResult.Bytes);\n                }\n\n                if (_position < 0)\n                {\n                    ulong length = (ulong)_indices.Count;\n                    var lengthSpan = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref length, 1));\n                    if (lengthSpan.Length > targetSpan.Length)\n                        return result;\n\n                    lengthSpan.CopyTo(targetSpan);\n                    result = result.AddBytes(lengthSpan.Length);\n                    targetSpan = targetSpan.Slice(lengthSpan.Length);\n\n                    _position = 0;\n                }\n\n                int size = _indices.Count - _position;\n                int byteSize;\n                switch (_keySizeCode)\n                {\n                    case KeySizeCode.UInt8:\n                        size = Math.Min(targetSpan.Length, size);\n                        byteSize = size;\n                        for (int i = 0; i < size; _position++, i++)\n                            targetSpan[i] = (byte)_indices[_position];\n\n                        break;\n\n                    case KeySizeCode.UInt16:\n                        size = Math.Min(targetSpan.Length / 2, size);\n                        byteSize = size * 2;\n                        var ushortSpan = MemoryMarshal.Cast<byte, ushort>(targetSpan.Slice(0, byteSize));\n\n                        for (int i = 0; i < size; _position++, i++)\n                            ushortSpan[i] = (ushort)_indices[_position];\n\n                        break;\n\n                    case KeySizeCode.UInt32:\n                        size = Math.Min(targetSpan.Length / 4, size);\n                        byteSize = size * 4;\n                        var intSpan = MemoryMarshal.Cast<byte, int>(targetSpan.Slice(0, byteSize));\n\n                        for (int i = 0; i < size; _position++, i++)\n                            intSpan[i] = _indices[_position];\n\n                        break;\n\n                    default:\n                        throw new InvalidOperationException($\"Internal error. Unexpected key size code: {_keySizeCode}.\");\n                }\n\n                result += new SequenceSize(byteSize, size);\n                return result;\n            }\n        }\n\n        private sealed class LowCardinalityTableColumnDispatcher : IClickHouseTableColumnDispatcher<IClickHouseTableColumn>\n        {\n            private readonly ReadOnlyMemory<byte> _keys;\n            private readonly int _keySize;\n            private readonly bool _isNullable;\n\n            public LowCardinalityTableColumnDispatcher(ReadOnlyMemory<byte> keys, int keySize, bool isNullable)\n            {\n                _keys = keys;\n                _keySize = keySize;\n                _isNullable = isNullable;\n            }\n\n            public IClickHouseTableColumn Dispatch<T>(IClickHouseTableColumn<T> column)\n            {\n                return new LowCardinalityTableColumn<T>(_keys, _keySize, column, _isNullable);\n            }\n        }\n\n        private enum KeySizeCode\n        {\n            UInt8 = 0,\n            UInt16 = 1,\n            UInt32 = 2,\n            UInt64 = 3\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/MapTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class MapTypeInfo : IClickHouseColumnTypeInfo\n    {\n        // Map(key, value) is the alias for Array(Tuple(key, value))\n        private readonly KeyValuePair<IClickHouseColumnTypeInfo, IClickHouseColumnTypeInfo>? _typeArgs;\n        private readonly IClickHouseColumnTypeInfo? _underlyingType;\n\n        public string ComplexTypeName { get; }\n\n        public string TypeName => \"Map\";\n\n        public int GenericArgumentsCount => _typeArgs == null ? 0 : 2;\n\n        public MapTypeInfo()\n        {\n            ComplexTypeName = TypeName;\n        }\n\n        private MapTypeInfo(IClickHouseColumnTypeInfo keyType, IClickHouseColumnTypeInfo valueType, IClickHouseColumnTypeInfo underlyingType)\n        {\n            _typeArgs = new KeyValuePair<IClickHouseColumnTypeInfo, IClickHouseColumnTypeInfo>(keyType, valueType);\n            ComplexTypeName = $\"{TypeName}({keyType.ComplexTypeName}, {valueType.ComplexTypeName})\";\n            _underlyingType = underlyingType;\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            if (_underlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var fieldType = GetFieldType();\n            return new MapReader(_underlyingType.CreateColumnReader(rowCount), fieldType);\n        }\n\n        IClickHouseColumnReader IClickHouseColumnTypeInfo.CreateColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (_underlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var fieldType = GetFieldType();\n            return new MapReader(_underlyingType.CreateColumnReader(rowCount, serializationMode), fieldType);\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            if (_underlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return _underlyingType.CreateSkippingColumnReader(rowCount);\n        }\n\n        IClickHouseColumnReaderBase IClickHouseColumnTypeInfo.CreateSkippingColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (_underlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return _underlyingType.CreateSkippingColumnReader(rowCount, serializationMode);\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (_underlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var elementType = typeof(T);\n            Type? dictionaryItf = null;\n            foreach (var itf in elementType.GetInterfaces())\n            {\n                if (itf.IsGenericType && itf.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>))\n                {\n                    if (dictionaryItf != null)\n                    {\n                        dictionaryItf = null;\n                        break;\n                    }\n\n                    dictionaryItf = itf;\n                }\n            }\n\n            IClickHouseColumnWriter underlyingWriter;\n            if (dictionaryItf != null)\n            {\n                var dispatcherType = typeof(DictionaryDispatcher<,>).MakeGenericType(dictionaryItf.GetGenericArguments());\n                var dispatcher = (IDictionaryDispatcher)Activator.CreateInstance(dispatcherType)!;\n                underlyingWriter = dispatcher.Dispatch(_underlyingType, columnName, rows, columnSettings);\n            }\n            else\n            {\n                underlyingWriter = _underlyingType.CreateColumnWriter(columnName, rows, columnSettings);\n            }\n\n            return new MapColumnWriter(underlyingWriter);\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            // TODO: ClickHouseDbType.Map is not supported in DefaultTypeInfoProvider.GetTypeInfo\n\n            if (_underlyingType == null || _typeArgs == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            Type? dictionaryItf = null;\n            foreach (var itf in type.GetInterfaces())\n            {\n                if (itf.IsGenericType && itf.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>))\n                {\n                    if (dictionaryItf != null)\n                    {\n                        dictionaryItf = null;\n                        break;\n                    }\n\n                    dictionaryItf = itf;\n                }\n            }\n\n            if (dictionaryItf == null)\n                return _underlyingType.CreateParameterWriter<T>();\n\n            var dispatcherTypeArgs = new[] { type }.Concat(dictionaryItf.GetGenericArguments()).ToArray();\n            var dispatcherType = typeof(ParameterDictionaryDispatcher<,,>).MakeGenericType(dispatcherTypeArgs);\n            var dispatcher = (IDictionaryParameterWirterDispatcher?)Activator.CreateInstance(dispatcherType);\n            Debug.Assert(dispatcher != null);\n\n            return dispatcher.Dispatch<T>(this);\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Map;\n        }\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            if (_typeArgs != null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, \"The type is already fully specified.\");\n\n            if (options.Count < 2)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"The type \\\"{TypeName}\\\" requires two type arguments: key and value.\");\n\n            if (options.Count > 2)\n                throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, $\"Too many options for the type \\\"{TypeName}\\\".\");\n\n            var keyType = typeInfoProvider.GetTypeInfo(options[0]);\n            var valueType = typeInfoProvider.GetTypeInfo(options[1]);\n\n            var underlyingTypeName = $\"Array(Tuple({keyType.ComplexTypeName}, {valueType.ComplexTypeName}))\";\n            var undelyingType = typeInfoProvider.GetTypeInfo(underlyingTypeName);\n\n            return new MapTypeInfo(keyType, valueType, undelyingType);\n        }\n\n        public Type GetFieldType()\n        {\n            if (_typeArgs == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var keyType = _typeArgs.Value.Key.GetFieldType();\n            var valueType = _typeArgs.Value.Value.GetFieldType();\n\n            var fieldType = typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType).MakeArrayType();\n            return fieldType;\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            if (_typeArgs == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            switch (index)\n            {\n                case 0:\n                    return _typeArgs.Value.Key;\n                case 1:\n                    return _typeArgs.Value.Value;\n                default:\n                    throw new IndexOutOfRangeException();\n            }\n        }\n\n        private sealed class MapReader : IClickHouseColumnReader\n        {\n            private readonly IClickHouseColumnReader _underlyingReader;\n            private readonly Type _fieldType;\n\n            public MapReader(IClickHouseColumnReader underlyingReader, Type fieldType)\n            {\n                _underlyingReader = underlyingReader;\n                _fieldType = fieldType;\n            }            \n\n            SequenceSize IClickHouseColumnReaderBase.ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                return _underlyingReader.ReadPrefix(sequence);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                return _underlyingReader.ReadNext(sequence);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                var column = _underlyingReader.EndRead(settings);\n                var dispatcher = new MapColumnDispatcher(column);\n                return TypeDispatcher.Dispatch(_fieldType, dispatcher);                \n            }\n        }\n\n        private sealed class MapColumnDispatcher : ITypeDispatcher<IClickHouseTableColumn>\n        {\n            private readonly IClickHouseTableColumn _column;\n\n            public MapColumnDispatcher(IClickHouseTableColumn column)\n            {\n                _column = column;\n            }\n\n            public IClickHouseTableColumn Dispatch<T>()\n            {\n                var column = _column.TryReinterpret<T>();\n                if (column == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. Column was not converted to the type \\\"{typeof(T)}\\\" as expected.\");\n\n                return column;\n            }\n        }\n\n        private sealed class MapColumnWriter : IClickHouseColumnWriter\n        {\n            private readonly IClickHouseColumnWriter _underlyingWriter;\n\n            public string ColumnName => _underlyingWriter.ColumnName;\n\n            public string ColumnType { get; }\n\n            public MapColumnWriter(IClickHouseColumnWriter underlyingWriter)\n            {\n                const string typeNameStart = \"Array(Tuple\", typeNameEnd = \")\";\n                var typeName = underlyingWriter.ColumnType;\n                if (!typeName.StartsWith(typeNameStart) || !typeName.EndsWith(typeNameEnd))\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. The name of the type \\\"{typeName}\\\" doesn't match to the expected pattern \\\"{Regex.Escape(typeNameStart)}.*{Regex.Escape(typeNameEnd)}\\\".\");\n\n                ColumnType = \"Map\" + typeName[typeNameStart.Length..^typeNameEnd.Length];\n                _underlyingWriter = underlyingWriter;\n            }\n\n            SequenceSize IClickHouseColumnWriter.WritePrefix(Span<byte> writeTo)\n            {\n                return _underlyingWriter.WritePrefix(writeTo);\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                return _underlyingWriter.WriteNext(writeTo);\n            }\n        }\n\n        private interface IDictionaryDispatcher\n        {\n            IClickHouseColumnWriter Dispatch(IClickHouseColumnTypeInfo underlyingType, string columnName, object rows, ClickHouseColumnSettings? columnSettings);\n        }\n\n        private sealed class DictionaryDispatcher<TKey, TValue> : IDictionaryDispatcher\n            where TKey : notnull\n        {\n            public IClickHouseColumnWriter Dispatch(IClickHouseColumnTypeInfo underlyingType, string columnName, object rows, ClickHouseColumnSettings? columnSettings)\n            {\n                var dictionaryList = (IReadOnlyList<IReadOnlyDictionary<TKey, TValue>>)rows;\n                var listOfLists = new KeyValuePair<TKey, TValue>[dictionaryList.Count][];\n                for (int i = 0; i < listOfLists.Length; i++)\n                    listOfLists[i] = ((IEnumerable<KeyValuePair<TKey, TValue>>)dictionaryList[i]).ToArray();\n                    \n                return underlyingType.CreateColumnWriter(columnName, listOfLists, columnSettings);\n            }\n        }\n\n        private interface IDictionaryParameterWirterDispatcher\n        {\n            IClickHouseParameterWriter<T> Dispatch<T>(MapTypeInfo typeInfo);\n        }\n\n        private sealed class ParameterDictionaryDispatcher<TDictionary, TKey, TValue> : IDictionaryParameterWirterDispatcher\n            where TKey : notnull\n            where TDictionary: IReadOnlyDictionary<TKey, TValue>\n        {\n            public IClickHouseParameterWriter<T> Dispatch<T>(MapTypeInfo typeInfo)\n            {\n                Debug.Assert(typeof(T) == typeof(TDictionary));\n                Debug.Assert(typeInfo._typeArgs != null);\n                var (keyType, valueType) = typeInfo._typeArgs.Value;\n\n                var keyWriter = keyType.CreateParameterWriter<TKey>();\n                var valueWriter = valueType.CreateParameterWriter<TValue>();\n                var writer = new MapParameterWriter<TDictionary, TKey, TValue>(typeInfo, keyWriter, valueWriter);\n\n                return (IClickHouseParameterWriter<T>)(object)writer;\n            }\n        }\n\n        private sealed class MapParameterWriter<TDictionary, TKey, TValue> : IClickHouseParameterWriter<TDictionary>\n            where TKey : notnull\n            where TDictionary : IReadOnlyDictionary<TKey, TValue>\n        {\n            private readonly MapTypeInfo _mapType;\n            private readonly IClickHouseParameterWriter<TKey> _keyWriter;\n            private readonly IClickHouseParameterWriter<TValue> _valueWriter;\n\n            public MapParameterWriter(MapTypeInfo mapType, IClickHouseParameterWriter<TKey> keyWriter, IClickHouseParameterWriter<TValue> valueWriter)\n            {\n                _mapType = mapType;\n                _keyWriter = keyWriter;\n                _valueWriter = valueWriter;\n            }\n\n            public bool TryCreateParameterValueWriter(TDictionary value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                valueWriter = null;\n                return false;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, TDictionary dictionary)\n            {\n                queryBuilder.Append(\"{tuple([\");\n                var valuesBuilder = new StringBuilder();\n\n                var isFirst = true;\n                foreach (var (key, value) in dictionary)\n                {\n                    if (isFirst)\n                        isFirst = false;\n                    else\n                        queryBuilder.Append(',');\n\n                    _keyWriter.Interpolate(queryBuilder, key);\n                    _valueWriter.Interpolate(valuesBuilder, value);\n                }\n\n                queryBuilder.Append(\"],[\");\n                queryBuilder.Append(valuesBuilder);\n                return queryBuilder.Append(\"])}\");\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                throw new NotImplementedException();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/NothingTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class NothingTableColumn : IClickHouseTableColumn\n    {\n        public int RowCount { get; }\n\n        public NothingTableColumn(int rowCount)\n        {\n            RowCount = rowCount;\n        }\n\n        public bool IsNull(int index)\n        {\n            if (index < 0 || index >= RowCount)\n                throw new ArgumentOutOfRangeException(nameof(index));\n\n            return true;\n        }\n\n        public object GetValue(int index)\n        {\n            if (index < 0 || index >= RowCount)\n                throw new ArgumentOutOfRangeException(nameof(index));\n\n            return DBNull.Value;\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return null;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = default;\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/NothingTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class NothingTypeInfo : IClickHouseColumnTypeInfo\n    {\n        public string ComplexTypeName => TypeName;\n\n        public string TypeName => \"Nothing\";\n\n        public int GenericArgumentsCount => 0;\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new NothingColumnReader(rowCount);\n        }\n\n        IClickHouseColumnReader IClickHouseColumnTypeInfo.CreateColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode == ClickHouseColumnSerializationMode.Default)\n                return CreateColumnReader(rowCount);\n\n            throw new NotSupportedException($\"Custom serialization for {TypeName} type is not supported by ClickHouseClient.\");\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new NothingColumnReader(rowCount);\n        }\n\n        IClickHouseColumnReaderBase IClickHouseColumnTypeInfo.CreateSkippingColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode == ClickHouseColumnSerializationMode.Default)\n                return CreateSkippingColumnReader(rowCount);\n\n            throw new NotSupportedException($\"Custom serialization for {TypeName} type is not supported by ClickHouseClient.\");\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            return new NothingColumnWriter(columnName, ComplexTypeName, rows.Count);\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                return (IClickHouseParameterWriter<T>)(object)NothingParameterWriter.Instance;\n\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n        }\n\n        IClickHouseColumnTypeInfo IClickHouseColumnTypeInfo.GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{TypeName}\\\" does not support arguments.\");\n        }\n\n        public Type GetFieldType()\n        {\n            return typeof(DBNull);\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Nothing;\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have generic arguments.\");\n        }\n\n        private sealed class NothingColumnReader : IClickHouseColumnReader\n        {\n            private readonly int _rowCount;\n\n            private int _position;\n\n            public NothingColumnReader(int rowCount)\n            {\n                _rowCount = rowCount;\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                if (_position >= _rowCount)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                var size = (int)Math.Min(sequence.Length, _rowCount - _position);\n                _position += size;\n                return new SequenceSize(size, size);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                return new NothingTableColumn(_position);\n            }\n        }\n\n        private sealed class NothingColumnWriter : IClickHouseColumnWriter\n        {\n            private readonly int _count;\n\n            public string ColumnName { get; }\n\n            public string ColumnType { get; }\n\n            private int _position;\n\n            public NothingColumnWriter(string columnName, string columnType, int count)\n            {\n                ColumnName = columnName;\n                ColumnType = columnType;\n                _count = count;\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                var size = Math.Min(_count - _position, writeTo.Length);\n                for (int i = 0; i < size; i++)\n                    writeTo[i] = 48; // 48 == NOTHING\n\n                _position += size;\n                return new SequenceSize(size, size);\n            }\n        }\n\n        internal sealed class NothingParameterWriter : IClickHouseParameterWriter<DBNull>\n        {\n            public static readonly NothingParameterWriter Instance = new NothingParameterWriter();\n\n            private NothingParameterWriter()\n            {\n            }\n\n            public bool TryCreateParameterValueWriter(DBNull value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                valueWriter = EmptyParameterValueWriter.Instance;\n                return true;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, DBNull value)\n            {\n                return queryBuilder.Append(\"null\");\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                var nothingType = typeInfoProvider.GetTypeInfo(\"Nothing\");\n                return writeValue(queryBuilder, nothingType, (qb, _) => qb.Append(\"null\"));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/NullableTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing Octonica.ClickHouseClient.Exceptions;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class NullableTableColumn : IClickHouseTableColumn\n    {\n        private readonly BitArray? _nullFlags;\n        private readonly IClickHouseTableColumn _baseColumn;\n\n        public int RowCount => _baseColumn.RowCount;\n\n        private NullableTableColumn(BitArray? nullFlags, IClickHouseTableColumn baseColumn)\n        {\n            _nullFlags = nullFlags;\n            _baseColumn = baseColumn;\n        }\n\n        public bool IsNull(int index)\n        {\n            if (_nullFlags != null && _nullFlags[index])\n                return true;\n\n            return _baseColumn.IsNull(index);\n        }\n\n        public object GetValue(int index)\n        {\n            if (_nullFlags != null && _nullFlags[index])\n                return DBNull.Value;\n\n            return _baseColumn.GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return TryMakeNullableColumn<T>(_nullFlags, _baseColumn);\n        }\n\n        public static IClickHouseTableColumn MakeNullableColumn(BitArray? nullFlags, IClickHouseTableColumn baseColumn)\n        {\n            if (!baseColumn.TryDipatch(new NullableTableColumnDispatcher(nullFlags), out var result) || result == null)\n                result = new NullableTableColumn(nullFlags, baseColumn);\n\n            return result;\n        }\n\n        public static IClickHouseTableColumn<T>? TryMakeNullableColumn<T>(BitArray? nullFlags, IClickHouseTableColumn notNullableColumn)\n        {\n            return (IClickHouseTableColumn<T>?) TryMakeNullableColumn(typeof(T), nullFlags, notNullableColumn);\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            return TryMakeNullableArrayColumn<T>(this, _nullFlags, _baseColumn);\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = default;\n            return false;\n        }\n\n        public static IClickHouseArrayTableColumn<T>? TryMakeNullableArrayColumn<T>(IClickHouseTableColumn reinterpretationRoot, BitArray? nullFlags, IClickHouseTableColumn notNullableColumn)\n        {\n            var notNullableArrayColumn = notNullableColumn as IClickHouseArrayTableColumn<T> ?? notNullableColumn.TryReinterpretAsArray<T>();\n            if (notNullableArrayColumn == null)\n                return null;\n\n            return new NullableArrayTableColumn<T>(reinterpretationRoot, nullFlags, notNullableArrayColumn);\n        }\n\n        private static IClickHouseTableColumn? TryMakeNullableColumn(Type underlyingType, BitArray? nullFlags, IClickHouseTableColumn notNullableColumn)\n        {\n            Type dispatcherType;\n            bool reinterpretAsNotNullable = false;\n            if (underlyingType.IsValueType)\n            {\n                var columnType = Nullable.GetUnderlyingType(underlyingType);\n                if (columnType == null)\n                {\n                    reinterpretAsNotNullable = true;\n                    columnType = underlyingType;\n                }\n\n                dispatcherType = typeof(NullableStructTableColumnDispatcher<>).MakeGenericType(columnType);\n            }\n            else if (underlyingType.IsClass)\n            {\n                dispatcherType = typeof(NullableObjTableColumnDispatcher<>).MakeGenericType(underlyingType);\n            }\n            else\n            {\n                return null;\n            }\n\n            var dispatcher = (INullableColumnDispatcher) Activator.CreateInstance(dispatcherType)!;\n\n            return dispatcher.Dispatch(nullFlags, notNullableColumn, reinterpretAsNotNullable);\n        }\n\n        private interface INullableColumnDispatcher\n        {\n            IClickHouseTableColumn? Dispatch(BitArray? nullFlags, IClickHouseTableColumn notNullableColumn, bool reinterpretAsNotNullable);\n        }\n\n        private sealed class NullableStructTableColumnDispatcher<TStruct> : INullableColumnDispatcher\n            where TStruct : struct\n        {\n            public IClickHouseTableColumn? Dispatch(BitArray? nullFlags, IClickHouseTableColumn notNullableColumn, bool reinterpretAsNotNullable)\n            {\n                var reinterpretedColumn = notNullableColumn as IClickHouseTableColumn<TStruct> ?? notNullableColumn.TryReinterpret<TStruct>();\n                if (reinterpretedColumn == null)\n                    return null;\n\n                var result = new NullableStructTableColumn<TStruct>(nullFlags, reinterpretedColumn);\n                if (!reinterpretAsNotNullable)\n                    return result;\n\n                return result.AsNotNullable();\n            }\n        }\n\n        private sealed class NullableObjTableColumnDispatcher<TObj> : INullableColumnDispatcher\n            where TObj : class\n        {\n            public IClickHouseTableColumn? Dispatch(BitArray? nullFlags, IClickHouseTableColumn notNullableColumn, bool reinterpretAsNotNullable)\n            {\n                Debug.Assert(!reinterpretAsNotNullable);\n\n                var reinterpretedColumn = notNullableColumn as IClickHouseTableColumn<TObj> ?? notNullableColumn.TryReinterpret<TObj>();\n                if (reinterpretedColumn == null)\n                    return null;\n\n                if (nullFlags == null)\n                    return reinterpretedColumn;\n                \n                return new NullableObjTableColumn<TObj>(nullFlags, reinterpretedColumn);\n            }\n        }\n\n        private sealed class NullableTableColumnDispatcher : IClickHouseTableColumnDispatcher<IClickHouseTableColumn?>\n        {\n            private readonly BitArray? _nullFlags;\n\n            public NullableTableColumnDispatcher(BitArray? nullFlags)\n            {\n                _nullFlags = nullFlags;\n            }\n\n            public IClickHouseTableColumn? Dispatch<T>(IClickHouseTableColumn<T> column)\n            {\n                Type type = typeof(T);\n                Type dispatcherType;\n                if (type.IsValueType)\n                {\n                    var columnType = Nullable.GetUnderlyingType(type) ?? type;\n                    dispatcherType = typeof(NullableStructTableColumnDispatcher<>).MakeGenericType(columnType);\n                }\n                else if (type.IsClass)\n                {\n                    dispatcherType = typeof(NullableObjTableColumnDispatcher<>).MakeGenericType(type);\n                }\n                else\n                {\n                    return null;\n                }\n\n                var dispatcher = (INullableColumnDispatcher)Activator.CreateInstance(dispatcherType)!;\n                return dispatcher.Dispatch(_nullFlags, column, false);\n            }\n        }\n    }\n\n    internal sealed class NullableStructTableColumn<TStruct> : IClickHouseTableColumn<TStruct?>\n        where TStruct : struct\n    {\n        private readonly BitArray? _nullFlags;\n        private readonly IClickHouseTableColumn<TStruct> _baseColumn;\n\n        public int RowCount => _baseColumn.RowCount;\n\n        public TStruct? DefaultValue => null;\n\n        public NullableStructTableColumn(BitArray? nullFlags, IClickHouseTableColumn<TStruct> baseColumn)\n        {\n            _nullFlags = nullFlags;\n            _baseColumn = baseColumn;\n        }\n\n        public bool IsNull(int index)\n        {\n            if (_nullFlags != null && _nullFlags[index])\n                return true;\n\n            return _baseColumn.IsNull(index);\n        }\n\n        public TStruct? GetValue(int index)\n        {\n            if (_nullFlags != null && _nullFlags[index])\n                return null;\n\n            return _baseColumn.GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return NullableTableColumn.TryMakeNullableColumn<T>(_nullFlags, _baseColumn);\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            return NullableTableColumn.TryMakeNullableArrayColumn<T>(this, _nullFlags, _baseColumn);\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return (object?) GetValue(index) ?? DBNull.Value;\n        }\n\n        public IClickHouseTableColumn<TStruct> AsNotNullable()\n        {\n            if (_nullFlags == null)\n                return _baseColumn;\n\n            return new NullableStructTableColumnNotNullableAdapter<TStruct>(_nullFlags, _baseColumn);\n        }\n    }\n\n    internal sealed class NullableStructTableColumnNotNullableAdapter<TStruct> : IClickHouseTableColumn<TStruct>\n        where TStruct : struct\n    {\n        private readonly BitArray _nullFlags;\n        private readonly IClickHouseTableColumn<TStruct> _baseColumn;\n\n        public int RowCount => _baseColumn.RowCount;\n\n        public TStruct DefaultValue => _baseColumn.DefaultValue;\n\n        public NullableStructTableColumnNotNullableAdapter(BitArray nullFlags, IClickHouseTableColumn<TStruct> baseColumn)\n        {\n            _nullFlags = nullFlags;\n            _baseColumn = baseColumn;\n        }\n\n        public bool IsNull(int index)\n        {\n            return _nullFlags[index] || _baseColumn.IsNull(index);\n        }\n\n        public TStruct GetValue(int index)\n        {\n            if (_nullFlags[index])\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, $\"Can't convert NULL to \\\"{typeof(TStruct)}\\\".\");\n\n            return _baseColumn.GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return NullableTableColumn.TryMakeNullableColumn<T>(_nullFlags, _baseColumn);\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            return NullableTableColumn.TryMakeNullableArrayColumn<T>(this, _nullFlags, _baseColumn);\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            if (_nullFlags != null && _nullFlags[index])\n                return DBNull.Value;\n\n            return _baseColumn.GetValue(index);\n        }\n\n        public NullableStructTableColumn<TStruct> Unguard()\n        {\n            return new NullableStructTableColumn<TStruct>(_nullFlags, _baseColumn);\n        }\n    }\n\n    internal sealed class NullableObjTableColumn<TObj> : IClickHouseTableColumn<TObj?>\n        where TObj : class\n    {\n        private readonly BitArray _nullFlags;\n        private readonly IClickHouseTableColumn<TObj> _baseColumn;\n\n        public int RowCount => _baseColumn.RowCount;\n\n        public TObj? DefaultValue => null;\n\n        public NullableObjTableColumn(BitArray nullFlags, IClickHouseTableColumn<TObj> baseColumn)\n        {\n            _nullFlags = nullFlags;\n            _baseColumn = baseColumn;\n        }\n\n        public bool IsNull(int index)\n        {\n            return _nullFlags[index] || _baseColumn.IsNull(index);\n        }\n\n        public TObj? GetValue(int index)\n        {\n            if (_nullFlags[index])\n                return null;\n\n            return _baseColumn.GetValue(index);\n        }\n\n        public NullableObjTableColumn<TRes> ReinterpretAsObj<TRes>(Func<TObj, TRes> convert)\n            where TRes : class\n        {\n            IClickHouseTableColumn<TRes> updColumn;\n            if (_baseColumn is IClickHouseReinterpretedTableColumn<TObj> baseReinterpreted)\n                updColumn = baseReinterpreted.Chain(convert);\n            else\n                updColumn = new ReinterpretedTableColumn<TObj, TRes>(_baseColumn, convert);\n\n            return new NullableObjTableColumn<TRes>(_nullFlags, updColumn);\n        }\n\n        public NullableStructTableColumn<TRes> ReinterpretAsStruct<TRes>(Func<TObj, TRes> convert)\n            where TRes : struct\n        {\n            IClickHouseTableColumn<TRes> updColumn;\n            if (_baseColumn is IClickHouseReinterpretedTableColumn<TObj> baseReinterpreted)\n                updColumn = baseReinterpreted.Chain(convert);\n            else\n                updColumn = new ReinterpretedTableColumn<TObj, TRes>(_baseColumn, convert);\n\n            return new NullableStructTableColumn<TRes>(_nullFlags, updColumn);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return NullableTableColumn.TryMakeNullableColumn<T>(_nullFlags, _baseColumn);\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            return NullableTableColumn.TryMakeNullableArrayColumn<T>(this, _nullFlags, _baseColumn);\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return (object?) GetValue(index) ?? DBNull.Value;\n        }\n    }\n\n    internal sealed class NullableArrayTableColumn<TElement> : IClickHouseArrayTableColumn<TElement>\n    {\n        private readonly IClickHouseTableColumn _reinterpretationRoot;\n        private readonly BitArray? _nullFlags;\n        private readonly IClickHouseArrayTableColumn<TElement> _arrayColumn;\n\n        public int RowCount => throw new NotImplementedException();\n\n        public NullableArrayTableColumn(IClickHouseTableColumn reinterpretationRoot, BitArray? nullFlags, IClickHouseArrayTableColumn<TElement> arrayColumn)\n        {\n            _reinterpretationRoot = reinterpretationRoot;\n            _nullFlags = nullFlags;\n            _arrayColumn = arrayColumn;\n        }\n\n        public object GetValue(int index)\n        {\n            if (_nullFlags != null && _nullFlags[index])\n                return DBNull.Value;\n\n            return _arrayColumn.GetValue(index);\n        }\n\n        public bool IsNull(int index)\n        {\n            return (_nullFlags != null && _nullFlags[index]) || _arrayColumn.IsNull(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return _reinterpretationRoot as IClickHouseTableColumn<T> ?? _reinterpretationRoot.TryReinterpret<T>();\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            return _reinterpretationRoot as IClickHouseArrayTableColumn<T> ?? _reinterpretationRoot.TryReinterpretAsArray<T>();\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = default;\n            return false;\n        }\n\n        public int CopyTo(int index, Span<TElement> buffer, int dataOffset)\n        {\n            if (_nullFlags != null && _nullFlags[index])\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Can't copy NULL value to the buffer.\");\n\n            return _arrayColumn.CopyTo(index, buffer, dataOffset);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/NullableTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class NullableTypeInfo : IClickHouseColumnTypeInfo\n    {\n        public string ComplexTypeName { get; }\n\n        public string TypeName => \"Nullable\";\n\n        public int GenericArgumentsCount => UnderlyingType == null ? 0 : 1;\n\n        public IClickHouseColumnTypeInfo? UnderlyingType { get; }\n\n        public NullableTypeInfo()\n        {\n            ComplexTypeName = TypeName;\n        }\n\n        public NullableTypeInfo(IClickHouseColumnTypeInfo underlyingType)\n        {\n            if (underlyingType is NullableTypeInfo)\n                throw new ArgumentException(\"The underlying type can't be nullable.\", nameof(underlyingType));\n\n            UnderlyingType = underlyingType ?? throw new ArgumentNullException(nameof(underlyingType));\n            ComplexTypeName = $\"{TypeName}({UnderlyingType.ComplexTypeName})\";\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            if (UnderlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return new NullableColumnReader(rowCount, UnderlyingType);\n        }\n\n        IClickHouseColumnReader IClickHouseColumnTypeInfo.CreateColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode == ClickHouseColumnSerializationMode.Default)\n                return CreateColumnReader(rowCount);\n\n            throw new NotSupportedException($\"Custom serialization for {TypeName} type is not supported by ClickHouseClient.\");\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            if (UnderlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return new NullableSkippingColumnReader(rowCount, UnderlyingType);\n        }\n\n        IClickHouseColumnReaderBase IClickHouseColumnTypeInfo.CreateSkippingColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode == ClickHouseColumnSerializationMode.Default)\n                return CreateSkippingColumnReader(rowCount);\n\n            throw new NotSupportedException($\"Custom serialization for {TypeName} type is not supported by ClickHouseClient.\");\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (UnderlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return new NullableColumnWriter<T>(columnName, rows, columnSettings, UnderlyingType);\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            if (UnderlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                return (IClickHouseParameterWriter<T>)(object)new NullableParameterWriter<DBNull>(this, NothingTypeInfo.NothingParameterWriter.Instance);\n\n            if (type.IsValueType && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))\n            {\n                var valueType = type.GetGenericArguments()[0];\n                var dispatcherType = typeof(NullableStructParameterWriterDispatcher<>).MakeGenericType(valueType);\n                var dispatcher = (INullableParameterWriterDispatcher<T>?)Activator.CreateInstance(dispatcherType);\n                Debug.Assert(dispatcher != null);\n\n                return dispatcher.Dispatch(this);\n            }\n\n            // The type is either a non-value or a non-nullable stucture. In both cases it can be interpreted as non-nullable.\n            var underlyingWriter = UnderlyingType.CreateParameterWriter<T>();\n            return new NullableParameterWriter<T>(this, underlyingWriter);\n        }\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            if (UnderlyingType != null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, \"The type is already fully specified.\");\n\n            if (options.Count > 1)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"Too many arguments in the definition of \\\"{TypeName}\\\".\");\n\n            var underlyingType = typeInfoProvider.GetTypeInfo(options[0]);\n            return new NullableTypeInfo(underlyingType);\n        }\n\n        public Type GetFieldType()\n        {\n            if (UnderlyingType == null)\n                return typeof(DBNull);\n\n            var underlyingFieldType = UnderlyingType.GetFieldType();\n            if (underlyingFieldType.IsValueType)\n                return typeof(Nullable<>).MakeGenericType(underlyingFieldType);\n\n            return underlyingFieldType;\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            if (UnderlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return UnderlyingType.GetDbType();\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            if (UnderlyingType == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            if (index != 0)\n                throw new IndexOutOfRangeException();\n\n            return UnderlyingType;\n        }\n\n        private sealed class NullableColumnReader : IClickHouseColumnReader\n        {\n            private readonly int _rowCount;\n            private readonly IClickHouseColumnTypeInfo _underlyingType;\n\n            private BitArray? _nullFlags;\n            private IClickHouseColumnReader? _baseColumnReader;\n\n            private int _nullFlagPosition;\n\n            public NullableColumnReader(int rowCount, IClickHouseColumnTypeInfo underlyingType)\n            {\n                _rowCount = rowCount;\n                _underlyingType = underlyingType;\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                int bytesCount = 0;\n                if (_baseColumnReader == null)\n                {\n                    foreach (var mem in sequence)\n                    {\n                        if (_nullFlagPosition == _rowCount)\n                            break;\n\n                        foreach (var byteVal in mem.Span)\n                        {\n                            if (byteVal != 0)\n                            {\n                                if (_nullFlags == null)\n                                    _nullFlags = new BitArray(_rowCount, false);\n\n                                _nullFlags[_nullFlagPosition] = true;\n                            }\n\n                            bytesCount++;\n                            if (_rowCount == ++_nullFlagPosition)\n                                break;\n                        }\n                    }\n\n                    if (_nullFlagPosition < _rowCount)\n                        return new SequenceSize(bytesCount, 0);\n\n                    _baseColumnReader = _underlyingType.CreateColumnReader(_rowCount);\n                }\n\n                var baseResult = _baseColumnReader.ReadNext(sequence.Slice(bytesCount));\n\n                return new SequenceSize(bytesCount + baseResult.Bytes, baseResult.Elements);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                var baseReader = _baseColumnReader ?? _underlyingType.CreateColumnReader(0);\n                return NullableTableColumn.MakeNullableColumn(_nullFlags, baseReader.EndRead(settings));\n            }\n        }\n\n        private sealed class NullableSkippingColumnReader : IClickHouseColumnReaderBase\n        {\n            private readonly int _rowCount;\n            private readonly IClickHouseColumnTypeInfo _underlyingType;\n\n            private IClickHouseColumnReaderBase? _baseReader;\n            private int _position;\n\n            public NullableSkippingColumnReader(int rowCount, IClickHouseColumnTypeInfo underlyingType)\n            {\n                _rowCount = rowCount;\n                _underlyingType = underlyingType;\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                if (_baseReader != null)\n                {\n                    var result = _baseReader.ReadNext(sequence);\n                    return result;\n                }                \n\n                var prefixBytesCount = _rowCount - _position;\n                if (prefixBytesCount < 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                if (sequence.Length <= prefixBytesCount)\n                {\n                    _position += (int)sequence.Length;\n                    return new SequenceSize((int)sequence.Length, 0);\n                }\n                else\n                {\n                    _position += prefixBytesCount;\n                }\n\n                _baseReader = _underlyingType.CreateSkippingColumnReader(_rowCount);\n                var baseSize = _baseReader.ReadNext(sequence.Slice(prefixBytesCount));\n\n                return new SequenceSize(baseSize.Bytes + prefixBytesCount, baseSize.Elements);\n            }\n        }\n\n        private sealed class NullableColumnWriter<T> : IClickHouseColumnWriter\n        {\n            private readonly IReadOnlyList<T> _rows;\n            private readonly IClickHouseColumnWriter _internalColumnWriter;\n\n            public string ColumnName { get; }\n\n            public string ColumnType { get; }\n\n            private int _position;\n\n            public NullableColumnWriter(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings, IClickHouseColumnTypeInfo underlyingTypeInfo)\n            {\n                if (underlyingTypeInfo == null)\n                    throw new ArgumentNullException(nameof(underlyingTypeInfo));\n                \n                _rows = rows ?? throw new ArgumentNullException(nameof(rows));\n                ColumnName = columnName ?? throw new ArgumentNullException(nameof(columnName));\n\n                if (typeof(T).IsValueType && typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>))\n                {\n                    var valueType = typeof(T).GetGenericArguments()[0];\n                    var dispatcherType = typeof(ValueOrDefaultListDispatcher<>).MakeGenericType(valueType);\n                    var columnDispatcher = (IValueOrDefaultListDispatcherBase) Activator.CreateInstance(dispatcherType)!;\n                    _internalColumnWriter = columnDispatcher.Dispatch(columnName, rows, columnSettings, underlyingTypeInfo);\n                }\n                else\n                {\n                    _internalColumnWriter = underlyingTypeInfo.CreateColumnWriter(columnName, rows, columnSettings);\n                }\n\n                ColumnType = $\"Nullable({_internalColumnWriter.ColumnType})\";\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                if (_position == _rows.Count)\n                    return _internalColumnWriter.WriteNext(writeTo);\n\n                var len = Math.Min(_rows.Count - _position, writeTo.Length);\n                for (int i = 0; i < len; i++)\n                {\n                    writeTo[i] = _rows[_position + i] == null ? (byte) 1 : (byte) 0;\n                }\n\n                _position += len;\n\n                if (_position < _rows.Count)\n                    return new SequenceSize(len, 0);\n\n                var size = _internalColumnWriter.WriteNext(writeTo.Slice(len));\n                return new SequenceSize(size.Bytes + len, size.Elements);\n            }\n        }\n\n        private interface IValueOrDefaultListDispatcherBase\n        {\n            IClickHouseColumnWriter Dispatch(string columnName, object rows, ClickHouseColumnSettings? columnSettings, IClickHouseColumnTypeInfo underlyingTypeInfo);\n        }\n\n        private sealed class ValueOrDefaultListDispatcher<TValue> : IValueOrDefaultListDispatcherBase\n            where TValue : struct\n        {\n            public IClickHouseColumnWriter Dispatch(string columnName, object rows, ClickHouseColumnSettings? columnSettings, IClickHouseColumnTypeInfo underlyingTypeInfo)\n            {\n                var genericList = (IReadOnlyList<TValue?>) rows;\n                var listWrapper = MappedReadOnlyList<TValue?, TValue>.Map(genericList, item => item ?? default);\n                return underlyingTypeInfo.CreateColumnWriter(columnName, listWrapper, columnSettings);\n            }\n        }\n\n        private interface INullableParameterWriterDispatcher<T>\n        {\n            IClickHouseParameterWriter<T> Dispatch(NullableTypeInfo typeInfo);\n        }\n\n        private sealed class NullableStructParameterWriterDispatcher<T> : INullableParameterWriterDispatcher<T?>\n            where T : struct\n        {\n            public IClickHouseParameterWriter<T?> Dispatch(NullableTypeInfo typeInfo)\n            {\n                Debug.Assert(typeInfo.UnderlyingType != null);\n                var underlyingWriter = typeInfo.UnderlyingType.CreateParameterWriter<T>();\n                return new NullableStructParameterWriter<T>(typeInfo, underlyingWriter);\n            }\n        }\n\n        private sealed class NullableStructParameterWriter<T> : IClickHouseParameterWriter<T?>\n            where T : struct\n        {\n            private readonly NullableTypeInfo _typeIfno;\n            private readonly IClickHouseParameterWriter<T> _underlyingWritrer;\n\n            public NullableStructParameterWriter(NullableTypeInfo typeIfno, IClickHouseParameterWriter<T> underlyingWritrer)\n            {\n                _typeIfno = typeIfno;\n                _underlyingWritrer = underlyingWritrer;\n            }\n\n            public bool TryCreateParameterValueWriter(T? value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                if (value == null)\n                {\n                    valueWriter = new SimpleLiteralValueWriter(\"null\".AsMemory());\n                    return true;\n                }\n\n                return _underlyingWritrer.TryCreateParameterValueWriter(value.Value, isNested, out valueWriter);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, T? value)\n            {\n                if (value != null)\n                {\n                    queryBuilder.Append(\"CAST((\");\n                    _underlyingWritrer.Interpolate(queryBuilder, value.Value);\n                    queryBuilder.Append(\") AS \").Append(_typeIfno.ComplexTypeName).Append(')');\n                }\n                else\n                {\n                    queryBuilder.Append(\"null::\").Append(_typeIfno.ComplexTypeName);\n                }\n\n                return queryBuilder;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return _underlyingWritrer.Interpolate(queryBuilder, typeInfoProvider, (qb, typeInfo, writeElement) =>\n                {\n                    Debug.Assert(_typeIfno.UnderlyingType != null);\n                    if (typeInfo.ComplexTypeName == _typeIfno.UnderlyingType.ComplexTypeName)\n                    {\n                        // The value of the type Nullable(Nothing) can't be passed as a parameter\n                        if (typeInfo.ComplexTypeName != \"Nothing\")\n                            return writeValue(qb, _typeIfno, FunctionHelper.Apply);\n                    }\n\n                    var nullableTypeInfo = typeInfo;\n                    if (nullableTypeInfo.TypeName != \"Nullable\")\n                        nullableTypeInfo = new NullableTypeInfo(nullableTypeInfo);\n\n                    return writeValue(qb, nullableTypeInfo, (qb2, realWrite) =>\n                    {\n                        qb2.Append(\"CAST((\");\n                        writeElement(qb2, realWrite);\n                        qb2.Append(\") AS \").Append(_typeIfno.ComplexTypeName).Append(')');\n                        return qb2;\n                    });\n                });\n            }\n        }\n\n        private sealed class NullableParameterWriter<T> : IClickHouseParameterWriter<T>\n        {\n            private readonly NullableTypeInfo _typeIfno;\n            private readonly IClickHouseParameterWriter<T> _underlyingWritrer;\n\n            public NullableParameterWriter(NullableTypeInfo typeIfno, IClickHouseParameterWriter<T> underlyingWritrer)\n            {\n                _typeIfno = typeIfno;\n                _underlyingWritrer = underlyingWritrer;\n            }\n\n            public bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                if (value == null)\n                {\n                    valueWriter = new SimpleLiteralValueWriter(\"null\".AsMemory());\n                    return true;\n                }\n\n                return _underlyingWritrer.TryCreateParameterValueWriter(value, isNested, out valueWriter);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, T value)\n            {\n                if (value != null)\n                {\n                    queryBuilder.Append(\"CAST((\");\n                    _underlyingWritrer.Interpolate(queryBuilder, value);\n                    queryBuilder.Append(\") AS \").Append(_typeIfno.ComplexTypeName).Append(')');\n                }\n                else\n                {\n                    queryBuilder.Append(\"null::\").Append(_typeIfno.ComplexTypeName);\n                }\n\n                return queryBuilder;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return _underlyingWritrer.Interpolate(queryBuilder, typeInfoProvider, (qb, typeInfo, writeElement) =>\n                {\n                    Debug.Assert(_typeIfno.UnderlyingType != null);\n                    if (typeInfo.ComplexTypeName == _typeIfno.UnderlyingType.ComplexTypeName)\n                    {\n                        // The value of the type Nullable(Nothing) can't be passed as a parameter\n                        if (typeInfo.ComplexTypeName != \"Nothing\")\n                            return writeValue(qb, _typeIfno, FunctionHelper.Apply);\n                    }\n\n                    var nullableTypeInfo = typeInfo;\n                    if (nullableTypeInfo.TypeName != \"Nullable\")\n                        nullableTypeInfo = new NullableTypeInfo(nullableTypeInfo);\n\n                    return writeValue(qb, nullableTypeInfo, (qb2, realWrite)=>\n                    {\n                        qb2.Append(\"CAST((\");\n                        writeElement(qb2, realWrite);\n                        qb2.Append(\") AS \").Append(_typeIfno.ComplexTypeName).Append(')');\n                        return qb2;\n                    });\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ObjectColumnAdapter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class ObjectColumnAdapter : IClickHouseReinterpretedTableColumn<object>\n    {\n        private readonly IClickHouseTableColumn _tableColumn;\n\n        public int RowCount => _tableColumn.RowCount;\n\n        public object DefaultValue => throw new NotSupportedException(\"The default value is not supported for the column of type Object.\");\n\n        public ObjectColumnAdapter(IClickHouseTableColumn tableColumn)\n        {\n            _tableColumn = tableColumn;\n        }\n\n        public bool IsNull(int index)\n        {\n            return _tableColumn.IsNull(index);\n        }\n\n        public object GetValue(int index)\n        {\n            return _tableColumn.GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return _tableColumn.TryReinterpret<T>();\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public IClickHouseReinterpretedTableColumn<TResult> Chain<TResult>(Func<object, TResult> reinterpret)\n        {\n            return new ReinterpretedObjectTableColumn<TResult>(_tableColumn, reinterpret);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ReinterpretedArrayTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class ReinterpretedArrayTableColumn<TElement> : IClickHouseArrayTableColumn<TElement>\n    {\n        private readonly IClickHouseTableColumn _reinterpretationRoot;\n        private readonly IClickHouseArrayTableColumn<TElement> _arrayColumn;\n\n        public int RowCount => _arrayColumn.RowCount;\n\n        public ReinterpretedArrayTableColumn(IClickHouseTableColumn reinterpretationRoot, IClickHouseArrayTableColumn<TElement> arrayColumn)\n        {\n            _reinterpretationRoot = reinterpretationRoot;\n            _arrayColumn = arrayColumn;\n        }\n\n        public int CopyTo(int index, Span<TElement> buffer, int dataOffset)\n        {\n            return _arrayColumn.CopyTo(index, buffer, dataOffset);\n        }\n\n        public object GetValue(int index)\n        {\n            return _arrayColumn.GetValue(index);\n        }\n\n        public bool IsNull(int index)\n        {\n            return _arrayColumn.IsNull(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return _reinterpretationRoot as IClickHouseTableColumn<T> ?? _reinterpretationRoot.TryReinterpret<T>();\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            return _reinterpretationRoot as IClickHouseArrayTableColumn<T> ?? _reinterpretationRoot.TryReinterpretAsArray<T>();\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = default;\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ReinterpretedObjectTableColumn.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing Octonica.ClickHouseClient.Utils;\r\nusing System;\r\nusing System.Diagnostics.CodeAnalysis;\r\n\r\nnamespace Octonica.ClickHouseClient.Types\r\n{\r\n    internal sealed class ReinterpretedObjectTableColumn<TRes> : IClickHouseReinterpretedTableColumn<TRes>\r\n    {\r\n        private readonly IClickHouseTableColumn _column;\r\n        private readonly Func<object, TRes> _reinterpret;\r\n\r\n        public int RowCount => _column.RowCount;\r\n\r\n        TRes IClickHouseTableColumn<TRes>.DefaultValue => throw new NotSupportedException(\"The default value is not supported for the column of type Object.\");\r\n\r\n        public ReinterpretedObjectTableColumn(IClickHouseTableColumn column, Func<object, TRes> reinterpret)\r\n        {\r\n            _column = column ?? throw new ArgumentNullException(nameof(column));\r\n            _reinterpret = reinterpret ?? throw new ArgumentNullException(nameof(reinterpret));\r\n        }\r\n\r\n        public bool IsNull(int index)\r\n        {\r\n            return _column.IsNull(index);\r\n        }\r\n\r\n        public TRes GetValue(int index)\r\n        {\r\n            var value = _column.GetValue(index);\r\n            var converted = _reinterpret(value);\r\n            return converted;\r\n        }\r\n\r\n        object IClickHouseTableColumn.GetValue(int index)\r\n        {\r\n            var value = _column.GetValue(index);\r\n            var converted = _reinterpret(value);\r\n            if (converted is null)\r\n                return DBNull.Value;\r\n\r\n            return converted;\r\n        }\r\n\r\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\r\n        {\r\n            return (_column as IClickHouseTableColumn<T>) ?? _column.TryReinterpret<T>();\r\n        }\r\n\r\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\r\n        {\r\n            return (_column as IClickHouseArrayTableColumn<T>) ?? _column.TryReinterpretAsArray<T>();\r\n        }\r\n\r\n        public bool TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\r\n        {\r\n            dispatchedValue = dispatcher.Dispatch(this);\r\n            return true;\r\n        }\r\n\r\n        public IClickHouseReinterpretedTableColumn<TResult> Chain<TResult>(Func<TRes, TResult> convert)\r\n        {\r\n            return new ReinterpretedObjectTableColumn<TResult>(_column, FunctionHelper.Combine(_reinterpret, convert));\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/ReinterpretedTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class ReinterpretedTableColumn<TFrom, TTo> : IClickHouseReinterpretedTableColumn<TTo>\n    {\n        private readonly IClickHouseTableColumn? _reinterpretationRoot;\n        private readonly IClickHouseTableColumn<TFrom> _sourceColumn;\n        private readonly Func<TFrom, TTo> _reinterpret;\n\n        public int RowCount => _sourceColumn.RowCount;\n\n        public TTo DefaultValue { get; }\n\n        public ReinterpretedTableColumn(IClickHouseTableColumn<TFrom> sourceColumn, Func<TFrom, TTo> reinterpret)\n            : this(null, sourceColumn, reinterpret)\n        {\n        }\n\n        public ReinterpretedTableColumn(IClickHouseTableColumn? reinterpretationRoot, IClickHouseTableColumn<TFrom> sourceColumn, Func<TFrom, TTo> reinterpret)\n        {\n            _reinterpretationRoot = reinterpretationRoot;\n            _sourceColumn = sourceColumn ?? throw new ArgumentNullException(nameof(sourceColumn));\n            _reinterpret = reinterpret ?? throw new ArgumentNullException(nameof(reinterpret));\n            DefaultValue = _reinterpret(_sourceColumn.DefaultValue);\n        }\n\n        public bool IsNull(int index)\n        {\n            return _sourceColumn.IsNull(index);\n        }\n\n        public TTo GetValue(int index)\n        {\n            var value = _sourceColumn.GetValue(index);\n            return _reinterpret(value);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            var reinterpretationRoot = _reinterpretationRoot ?? _sourceColumn;\n            return (reinterpretationRoot as IClickHouseTableColumn<T>) ?? reinterpretationRoot.TryReinterpret<T>();\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            var reinterpretationRoot = _reinterpretationRoot ?? _sourceColumn;\n            return (reinterpretationRoot as IClickHouseArrayTableColumn<T>) ?? reinterpretationRoot.TryReinterpretAsArray<T>();\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            if (_sourceColumn.IsNull(index))\n                return DBNull.Value;\n\n            var sourceValue = _sourceColumn.GetValue(index);\n            var reinterpreted = _reinterpret(sourceValue);\n            if (reinterpreted is null)\n                return DBNull.Value;\n\n            return reinterpreted;\n        }\n\n        public IClickHouseReinterpretedTableColumn<TResult> Chain<TResult>(Func<TTo, TResult> reinterpret)\n        {\n            return new ReinterpretedTableColumn<TFrom, TResult>(_reinterpretationRoot, _sourceColumn, FunctionHelper.Combine(_reinterpret, reinterpret));\n        }\n    }\n\n    internal sealed class ReinterpretedTableColumn<TValue> : IClickHouseReinterpretedTableColumn<TValue>\n    {\n        private readonly IClickHouseTableColumn _reinterpretationRoot;\n        private readonly IClickHouseTableColumn<TValue> _column;\n\n        public int RowCount => _column.RowCount;\n\n        public TValue DefaultValue => _column.DefaultValue;\n\n        public ReinterpretedTableColumn(IClickHouseTableColumn reinterpretationRoot, IClickHouseTableColumn<TValue> column)\n        {\n            _reinterpretationRoot = reinterpretationRoot ?? throw new ArgumentNullException(nameof(reinterpretationRoot));\n            _column = column ?? throw new ArgumentNullException(nameof(column));\n        }\n\n        public bool IsNull(int index)\n        {\n            return _column.IsNull(index);\n        }\n\n        public TValue GetValue(int index)\n        {\n            return _column.GetValue(index);\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return ((IClickHouseTableColumn) _column).GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return (_reinterpretationRoot as IClickHouseTableColumn<T>) ?? _reinterpretationRoot.TryReinterpret<T>();\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            return (_reinterpretationRoot as IClickHouseArrayTableColumn<T>) ?? _reinterpretationRoot.TryReinterpretAsArray<T>();\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        public IClickHouseReinterpretedTableColumn<TResult> Chain<TResult>(Func<TValue, TResult> reinterpret)\n        {\n            return new ReinterpretedTableColumn<TValue, TResult>(_reinterpretationRoot, _column, reinterpret);\n        }\n    }\n\n    internal sealed class ReinterpretedTableColumn : IClickHouseTableColumn\n    {\n        private readonly IClickHouseTableColumn _column;\n        private readonly Func<object, object> _convertValue;\n\n        public int RowCount => _column.RowCount;\n\n        public ReinterpretedTableColumn(IClickHouseTableColumn column, Func<object, object> convertValue)\n        {\n            _column = column ?? throw new ArgumentNullException(nameof(column));\n            _convertValue = convertValue ?? throw new ArgumentNullException(nameof(convertValue));\n        }\n\n        public bool IsNull(int index)\n        {\n            return _column.IsNull(index);\n        }\n\n        public object GetValue(int index)\n        {\n            var value = _column.GetValue(index);\n            return _convertValue(value);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return (_column as IClickHouseTableColumn<T>) ?? _column.TryReinterpret<T>();\n        }\n\n        IClickHouseArrayTableColumn<T>? IClickHouseTableColumn.TryReinterpretAsArray<T>()\n        {\n            return (_column as IClickHouseArrayTableColumn<T>) ?? _column.TryReinterpretAsArray<T>();\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = default;\n            return false;\n        }\n\n        public static IClickHouseTableColumn GetReinterpetedTableColumn(IClickHouseTableColumn column, Type targetType, Func<object, object> fallbackConvertValue)\n        {\n            return GetReinterpetedTableColumn(column, TypeDispatcher.Create(targetType), fallbackConvertValue);\n        }\n\n        internal static IClickHouseTableColumn GetReinterpetedTableColumn(IClickHouseTableColumn column, ITypeDispatcher typeDispatcher, Func<object, object> fallbackConvertValue)\n        {\n            return typeDispatcher.Dispatch(new ReinterpretedTableColumnDispatcher(column, fallbackConvertValue));\n        }\n\n        private sealed class ReinterpretedTableColumnDispatcher : ITypeDispatcher<IClickHouseTableColumn>\n        {\n            private readonly IClickHouseTableColumn _column;\n            private readonly Func<object, object> _fallbackConvertValue;\n\n            public ReinterpretedTableColumnDispatcher(IClickHouseTableColumn column, Func<object, object> fallbackConvertValue)\n            {\n                _column = column;\n                _fallbackConvertValue = fallbackConvertValue;\n            }\n\n            public IClickHouseTableColumn Dispatch<T>()\n            {\n                var reinterpretedColumn = (_column as IClickHouseTableColumn<T>) ?? _column.TryReinterpret<T>();\n                if (reinterpretedColumn != null)\n                    return reinterpretedColumn;\n\n                return new ReinterpretedTableColumn(_column, _fallbackConvertValue);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/SimpleLiteralValueWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Protocol;\nusing System;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class SimpleLiteralValueWriter : IClickHouseParameterValueWriter\n    {\n        private readonly ReadOnlyMemory<char> _value;\n\n        public int Length => Encoding.UTF8.GetByteCount(_value.Span);\n\n        public SimpleLiteralValueWriter(ReadOnlyMemory<char> value)\n        {\n            _value = value;\n        }\n\n        public int Write(Memory<byte> buffer)\n        {\n            return Encoding.UTF8.GetBytes(_value.Span, buffer.Span);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/SimpleParameterWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class SimpleParameterWriter<T> : IClickHouseParameterWriter<T>\n        where T : IFormattable\n    {\n        private readonly string? _valueType;\n        private readonly IClickHouseColumnTypeInfo _type;\n        private readonly string? _format;\n        private readonly bool _appendTypeCast;\n\n        public SimpleParameterWriter(IClickHouseColumnTypeInfo type, string? format = null, bool appendTypeCast = false)\n            : this(null, type, format, appendTypeCast)\n        {\n        }\n\n        public SimpleParameterWriter(string? valueType, IClickHouseColumnTypeInfo type, string? format = null, bool appendTypeCast = false)\n        {\n            _valueType = valueType;\n            _type = type;\n            _format = format;\n            _appendTypeCast = appendTypeCast;\n        }\n\n        public bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n        {\n            var strVal = value.ToString(_format, CultureInfo.InvariantCulture);\n            valueWriter = new SimpleLiteralValueWriter(strVal.AsMemory());\n            return true;\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, T value)\n        {\n            var strVal = value.ToString(_format, CultureInfo.InvariantCulture);\n            queryBuilder.Append(strVal);\n            if (_appendTypeCast)\n                queryBuilder.Append(\"::\").Append(_type.ComplexTypeName);\n\n            return queryBuilder;\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n        {\n            if (_valueType == null)\n                return writeValue(queryBuilder, _type, FunctionHelper.Apply);\n\n            var valueTypeInfo = typeInfoProvider.GetTypeInfo(_valueType);\n            if (_valueType != _type.ComplexTypeName)\n                return writeValue(queryBuilder, valueTypeInfo, (qb, realWrite) => realWrite(qb).Append(\"::\").Append(_type.ComplexTypeName));\n\n            return writeValue(queryBuilder, valueTypeInfo, FunctionHelper.Apply);\n        }\n    }\n\n    internal sealed class SimpleParameterWriter<TIn, TOut> : IClickHouseParameterWriter<TIn>\n        where TOut : IFormattable\n    {\n        private readonly string? _valueType;\n        private readonly IClickHouseColumnTypeInfo _type;\n        private readonly Func<TIn, TOut> _convert;\n        private readonly string? _format;\n        private readonly bool _appendTypeCast;\n\n        public SimpleParameterWriter(IClickHouseColumnTypeInfo type, Func<TIn, TOut> convert)\n            : this(null, type, null, false, convert)\n        {\n        }\n\n        public SimpleParameterWriter(IClickHouseColumnTypeInfo type, bool appendTypeCast, Func<TIn, TOut> convert)\n            : this(null, type, null, appendTypeCast, convert)\n        {\n        }\n\n        public SimpleParameterWriter(IClickHouseColumnTypeInfo type, string? format, Func<TIn, TOut> convert)\n            : this(null, type, format, false, convert)\n        {\n        }\n\n        public SimpleParameterWriter(string? valueType, IClickHouseColumnTypeInfo type, string? format, bool appendTypeCast, Func<TIn, TOut> convert)\n        {\n            _valueType = valueType;\n            _type = type;\n            _format = format;\n            _appendTypeCast = appendTypeCast;\n            _convert = convert;\n        }\n\n        public bool TryCreateParameterValueWriter(TIn value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n        {\n            var strVal = _convert(value).ToString(_format, CultureInfo.InvariantCulture);\n            valueWriter = new SimpleLiteralValueWriter(strVal.AsMemory());\n            return true;\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, TIn value)\n        {\n            var strVal = _convert(value).ToString(_format, CultureInfo.InvariantCulture);\n\n            queryBuilder.Append(strVal);\n            if (_valueType != null)\n                queryBuilder.Append(\"::\").Append(_valueType);\n\n            if (_appendTypeCast)\n            {\n                if (_valueType == null || _valueType != _type.ComplexTypeName)\n                    queryBuilder.Append(\"::\").Append(_type.ComplexTypeName);\n            }\n\n            return queryBuilder;\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n        {\n            if (_valueType == null)\n                return writeValue(queryBuilder, _type, FunctionHelper.Apply);\n\n            var valueTypeInfo = typeInfoProvider.GetTypeInfo(_valueType);\n            if (_valueType != _type.ComplexTypeName)\n                return writeValue(queryBuilder, valueTypeInfo, (qb, realWrite) => realWrite(qb).Append(\"::\").Append(_type.ComplexTypeName));\n\n            return writeValue(queryBuilder, valueTypeInfo, FunctionHelper.Apply);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/SimpleSkippingColumnReader.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing System;\nusing System.Buffers;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class SimpleSkippingColumnReader : IClickHouseColumnReaderBase\n    {\n        private readonly int _elementSize;\n        private readonly int _rowCount;\n\n        private int _position;\n\n        public SimpleSkippingColumnReader(int elementSize, int rowCount)\n        {\n            _elementSize = elementSize;\n            _rowCount = rowCount;\n        }\n\n        public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n        {\n            if (_position >= _rowCount)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n            var elementCount = (int)Math.Min(_rowCount - _position, sequence.Length / _elementSize);\n            var byteCount = elementCount * _elementSize;\n\n            _position += elementCount;\n            return new SequenceSize(byteCount, elementCount);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/SimpleTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    /// <summary>\n    /// Represents the base class for types that has no arguments.\n    /// </summary>\n    public abstract class SimpleTypeInfo : IClickHouseColumnTypeInfo\n    {\n        /// <inheritdoc/>\n        public string ComplexTypeName => TypeName;\n\n        /// <inheritdoc/>\n        public string TypeName { get; }\n\n        /// <summary>\n        /// Gets the number of generic arguments in the list of arguments.\n        /// </summary>\n        /// <returns>Always returns 0.</returns>\n        public int GenericArgumentsCount => 0;\n\n        /// <summary>\n        /// Initializes a new instance of <see cref=\"SimpleTypeInfo\"/> with the specified name.\n        /// </summary>\n        /// <param name=\"typeName\">The name of the type</param>\n        protected SimpleTypeInfo(string typeName)\n        {\n            TypeName = typeName ?? throw new ArgumentNullException(nameof(typeName));\n        }\n\n        /// <inheritdoc/>\n        public abstract IClickHouseColumnReader CreateColumnReader(int rowCount);\n\n        /// <inheritdoc/>\n        public abstract IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount);\n\n        /// <inheritdoc/>\n        public abstract IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings);\n\n        /// <inheritdoc/>\n        public abstract IClickHouseParameterWriter<T> CreateParameterWriter<T>();\n\n        /// <inheritdoc/>\n        public abstract Type GetFieldType();\n\n        /// <inheritdoc/>\n        public abstract ClickHouseDbType GetDbType();\n\n        /// <summary>\n        /// Gets the generic arguments at the specified position.\n        /// </summary>\n        /// <param name=\"index\">The zero-based index of the generic argument.</param>\n        /// <exception cref=\"NotSupportedException\">Always throws <see cref=\"NotSupportedException\"/>.</exception>\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            throw new NotSupportedException($\"The type \\\"{TypeName}\\\" doesn't have generic arguments.\");\n        }\n\n        IClickHouseColumnTypeInfo IClickHouseColumnTypeInfo.GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{TypeName}\\\" does not support arguments.\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/SparseColumn.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics.CodeAnalysis;\r\n\r\nnamespace Octonica.ClickHouseClient.Types\r\n{\r\n    internal sealed class SparseColumn<T> : IClickHouseTableColumn<T>\r\n    {\r\n        private readonly IClickHouseTableColumn<T> _valuesColumn;\r\n        private readonly List<int> _offsets;\r\n        private readonly bool _trailingDefaults;\r\n\r\n        private int _lastHit = 0;\r\n\r\n        public int RowCount { get; }\r\n\r\n        public T DefaultValue { get; }\r\n\r\n        public SparseColumn(IClickHouseTableColumn<T> valuesColumn, int rowCount, List<int> offsets, bool trailingDefaults)\r\n        {\r\n            _valuesColumn = valuesColumn;\r\n            RowCount = rowCount;\r\n            _offsets = offsets;\r\n            _trailingDefaults = trailingDefaults;\r\n            DefaultValue = _valuesColumn.DefaultValue;\r\n        }\r\n\r\n        private SparseColumn(IClickHouseTableColumn<T> valuesColumn, int rowCount, List<int> offsets, bool trailingDefaults, int lastHit)\r\n        {\r\n            _valuesColumn = valuesColumn;\r\n            RowCount = rowCount;\r\n            _offsets = offsets;\r\n            _trailingDefaults = trailingDefaults;\r\n            _lastHit = lastHit;\r\n            DefaultValue = _valuesColumn.DefaultValue;\r\n        }\r\n\r\n        public T GetValue(int index)\r\n        {\r\n            var valueIndex = GetValueIndex(index);\r\n            if (valueIndex < 0)\r\n                return DefaultValue;\r\n\r\n            return _valuesColumn.GetValue(valueIndex);\r\n        }\r\n\r\n        public bool IsNull(int index)\r\n        {\r\n            var valueIndex = GetValueIndex(index);\r\n            if (valueIndex < 0)\r\n                return DefaultValue is null;\r\n\r\n            return _valuesColumn.IsNull(valueIndex);\r\n        }\r\n\r\n        public bool TryDipatch<TOut>(IClickHouseTableColumnDispatcher<TOut> dispatcher, [MaybeNullWhen(false)] out TOut dispatchedValue)\r\n        {\r\n            dispatchedValue = dispatcher.Dispatch(this);\r\n            return true;\r\n        }\r\n\r\n        public IClickHouseTableColumn<TAs>? TryReinterpret<TAs>()\r\n        {\r\n            var valuesReinterpreted = _valuesColumn.TryReinterpret<TAs>();\r\n            if (valuesReinterpreted == null)\r\n                return null;\r\n\r\n            return new SparseColumn<TAs>(valuesReinterpreted, RowCount, _offsets, _trailingDefaults, _lastHit);\r\n        }\r\n\r\n        object IClickHouseTableColumn.GetValue(int index)\r\n        {\r\n            return (object?)GetValue(index) ?? DBNull.Value;\r\n        }\r\n\r\n        private int GetValueIndex(int index)\r\n        {\r\n            if (index < 0 || index >= RowCount)\r\n                throw new ArgumentOutOfRangeException(nameof(index));\r\n\r\n            if (_offsets.Count == 0)\r\n                return -1;\r\n\r\n            var lastHit = _lastHit;\r\n            var lastHitIdx = _offsets[lastHit];\r\n            if (index > lastHitIdx)\r\n            {\r\n                var next = lastHit + 1;\r\n                if (next == _offsets.Count)\r\n                {\r\n                    if (_trailingDefaults)\r\n                        return -1;\r\n\r\n                    return _offsets.Count + (index - lastHitIdx);\r\n                }\r\n\r\n                var nextIdx = _offsets[next];\r\n                if (index < nextIdx)\r\n                    return -1; // The most expected case\r\n\r\n                if (index == nextIdx)\r\n                {\r\n                    _lastHit = next;\r\n                    return next;\r\n                }\r\n            }\r\n            else if (index == lastHitIdx)\r\n            {\r\n                return lastHit;\r\n            }\r\n            else if (lastHit == 0)\r\n            {\r\n                // index < lastHitIdx\r\n                return -1;\r\n            }\r\n\r\n            lastHit = _offsets.BinarySearch(index);\r\n            if (lastHit >= 0)\r\n            {\r\n                _lastHit = lastHit;\r\n                return lastHit;\r\n            }\r\n\r\n            lastHit = ~lastHit;\r\n            _lastHit = Math.Max(lastHit - 1, 0);\r\n            if (lastHit < _offsets.Count || _trailingDefaults)\r\n                return -1;\r\n\r\n            return _offsets.Count + (index - _offsets[^1]);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/StringByteArrayTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class StringByteArrayTableColumn : StringTableColumnBase<byte[]>\n    {\n        public override byte[] DefaultValue => Array.Empty<byte>();\n\n        public StringByteArrayTableColumn(Encoding encoding, List<(int segmentIndex, int offset, int length)> layouts, List<Memory<byte>> segments)\n            : base(encoding, layouts, segments)\n        {\n        }\n\n        protected override byte[] GetValue(Encoding encoding, ReadOnlySpan<byte> span)\n        {\n            if (span.IsEmpty)\n                return Array.Empty<byte>();\n\n            var result = new byte[span.Length];\n            span.CopyTo(result);\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/StringCharArrayTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class StringCharArrayTableColumn : StringTableColumnBase<char[]>\n    {\n        public override char[] DefaultValue => Array.Empty<char>();\n\n        public StringCharArrayTableColumn(Encoding encoding, List<(int segmentIndex, int offset, int length)> layouts, List<Memory<byte>> segments) \n            : base(encoding, layouts, segments)\n        {\n        }\n\n        protected override char[] GetValue(Encoding encoding, ReadOnlySpan<byte> span)\n        {\n            if (span.IsEmpty)\n                return Array.Empty<char>();\n\n            var charCount = encoding.GetCharCount(span);\n            var result = new char[charCount];\n\n            var length = encoding.GetChars(span, result);\n            Debug.Assert(length == charCount);\n            return result;            \n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/StringLiteralValueWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Protocol;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class StringLiteralValueWriter : IClickHouseParameterValueWriter\n    {\n        private readonly ReadOnlyMemory<char> _value;\n        private readonly bool _includeQuotes;\n        private readonly List<int>? _escapeIndices;\n\n        public int Length { get; }\n\n        public StringLiteralValueWriter(ReadOnlyMemory<char> value, bool includeQuotes)\n        {\n            var encoding = Encoding.UTF8;\n            int length = includeQuotes ? 4 : 0, i = 0;\n            while (i < value.Length)\n            {\n                var idx = value.Span.Slice(i).IndexOfAny(\"\\\\'\\r\\n\\t\");\n                if (idx >= 0)\n                {\n                    _escapeIndices ??= new List<int>(4);\n\n                    length += 4;\n                    _escapeIndices.Add(i + idx);\n                    var slice = value.Slice(i, idx).Span;\n                    length += encoding.GetByteCount(slice);\n                    i += idx + 1;\n                }\n                else\n                {\n                    var slice = value.Slice(i).Span;\n                    length += encoding.GetByteCount(slice);\n                    i = value.Length;\n                }\n            }\n\n            _value = value;\n            _includeQuotes = includeQuotes;\n            Length = length;\n        }\n\n        public int Write(Memory<byte> buffer)\n        {\n            Debug.Assert(buffer.Length >= Length);\n\n            var encoding = Encoding.UTF8;\n            int i = 0, bytesWritten = 0;\n            if (_includeQuotes)\n            {\n                buffer.Span[bytesWritten++] = (byte)'\\\\';\n                buffer.Span[bytesWritten++] = (byte)'\\'';\n            }\n\n            if (_escapeIndices != null)\n            {\n                foreach (var escapeIdx in _escapeIndices)\n                {\n                    var slice = _value.Slice(i, escapeIdx - i);\n                    bytesWritten += encoding.GetBytes(slice.Span, buffer.Slice(bytesWritten).Span);\n                    var escapeBuffer = buffer.Slice(bytesWritten).Span;\n\n                    escapeBuffer[0] = (byte)'\\\\';\n                    escapeBuffer[1] = (byte)'\\\\';\n                    escapeBuffer[2] = (byte)'\\\\';\n                    escapeBuffer[3] = (byte)(_value.Span[escapeIdx] switch\n                    {\n                        '\\r' => 'r',\n                        '\\n' => 'n',\n                        '\\t' => 't',\n                        var c => c\n                    });\n\n                    bytesWritten += 4;\n                    i = escapeIdx + 1;\n                }\n            }\n\n            if (i < _value.Length)\n                bytesWritten += encoding.GetBytes(_value.Slice(i).Span, buffer.Slice(bytesWritten).Span);\n\n            if (_includeQuotes)\n            {\n                buffer.Span[bytesWritten++] = (byte)'\\\\';\n                buffer.Span[bytesWritten++] = (byte)'\\'';\n            }\n\n            Debug.Assert(bytesWritten == Length);\n            return bytesWritten;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/StringParameterWriter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Globalization;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class StringParameterWriter : IClickHouseParameterWriter<ReadOnlyMemory<char>>\n    {\n        private readonly IClickHouseColumnTypeInfo _type;\n\n        public StringParameterWriter(IClickHouseColumnTypeInfo type)\n        {\n            _type = type;\n        }\n\n        public bool TryCreateParameterValueWriter(ReadOnlyMemory<char> value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n        {\n            valueWriter = new StringLiteralValueWriter(value, isNested);\n            return true;\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, ReadOnlyMemory<char> value)\n        {\n            Interpolate(queryBuilder, value.Span);\n\n            if (_type.ComplexTypeName != \"String\")\n                queryBuilder.Append(\"::\").Append(_type.ComplexTypeName);\n\n            return queryBuilder;\n        }\n\n        public static StringBuilder Interpolate(StringBuilder queryBuilder, ReadOnlySpan<char> stringSpan)\n        {\n            queryBuilder.Append('\\'');\n            foreach (var charValue in stringSpan)\n            {\n                switch (charValue)\n                {\n                    case '\\\\':\n                        queryBuilder.Append(\"\\\\\\\\\");\n                        break;\n                    case '\\'':\n                        queryBuilder.Append(\"''\");\n                        break;\n                    default:\n                        queryBuilder.Append(charValue);\n                        break;\n                }\n            }\n\n            return queryBuilder.Append('\\'');\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n        {\n            return writeValue(queryBuilder, _type, FunctionHelper.Apply);\n        }\n\n        public static StringParameterWriter<T> Create<T>(IClickHouseColumnTypeInfo typeInfo, string? format = null)\n            where T : IFormattable\n        {\n            return new StringParameterWriter<T>(typeInfo, value => value.ToString(format, CultureInfo.InvariantCulture).AsMemory());\n        }\n    }\n\n    internal sealed class StringParameterWriter<T> : IClickHouseParameterWriter<T>\n    {\n        private readonly IClickHouseColumnTypeInfo _type;\n        private readonly Func<T, ReadOnlyMemory<char>> _toString;\n\n        public StringParameterWriter(IClickHouseColumnTypeInfo type, Func<T, ReadOnlyMemory<char>> toString)\n        {\n            _type = type;\n            _toString = toString;\n        }\n\n        public bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n        {\n            valueWriter = new StringLiteralValueWriter(_toString(value), isNested);\n            return true;\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, T value)\n        {\n            var str = _toString(value);\n            StringParameterWriter.Interpolate(queryBuilder, str.Span);\n\n            if (_type.ComplexTypeName != \"String\")\n                queryBuilder.Append(\"::\").Append(_type.ComplexTypeName);\n\n            return queryBuilder;\n        }\n\n        public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n        {\n            return writeValue(queryBuilder, _type, FunctionHelper.Apply);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/StringTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class StringTableColumn : StringTableColumnBase<string>\n    {\n        public override string DefaultValue => string.Empty;\n\n        public StringTableColumn(Encoding encoding, List<(int segmentIndex, int offset, int length)> layouts, List<Memory<byte>> segments)\n            : base(encoding, layouts, segments)\n        {\n        }\n\n        protected override string GetValue(Encoding encoding, ReadOnlySpan<byte> span)\n        {\n            if (span.IsEmpty)\n                return string.Empty;\n\n            return encoding.GetString(span);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/StringTableColumnBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal abstract class StringTableColumnBase<TOut> : IClickHouseTableColumn<TOut>, IClickHouseArrayTableColumn<byte>\n    {\n        private readonly Encoding _encoding;\n        private readonly List<(int segmentIndex, int offset, int length)> _layouts;\n        private readonly List<Memory<byte>> _segments;\n\n        public int RowCount => _layouts.Count;\n\n        public abstract TOut DefaultValue { get; }\n\n        protected StringTableColumnBase(Encoding encoding, List<(int segmentIndex, int offset, int length)> layouts, List<Memory<byte>> segments)\n        {\n            _encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));\n            _layouts = layouts ?? throw new ArgumentNullException(nameof(layouts));\n            _segments = segments ?? throw new ArgumentNullException(nameof(segments));\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        [return: NotNull]\n        public TOut GetValue(int index)\n        {\n            var(segmentIndex, offset, length) = _layouts[index];\n            if (length == 0)\n                return GetValue(_encoding, Span<byte>.Empty);\n\n            var span = _segments[segmentIndex].Slice(offset, length).Span;\n            return GetValue(_encoding, span);\n        }\n\n        [return: NotNull]\n        protected abstract TOut GetValue(Encoding encoding, ReadOnlySpan<byte> span);\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(byte[]))\n                return (IClickHouseTableColumn<T>)(object)new StringByteArrayTableColumn(_encoding, _layouts, _segments);\n            if (typeof(T) == typeof(string))\n                return (IClickHouseTableColumn<T>)(object)new StringTableColumn(_encoding, _layouts, _segments);\n            if (typeof(T) == typeof(char[]))\n                return (IClickHouseTableColumn<T>)(object)new StringCharArrayTableColumn(_encoding, _layouts, _segments);\n\n            return null;\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n\n        public int CopyTo(int index, Span<byte> buffer, int dataOffset)\n        {\n            var (segmentIndex, offset, length) = _layouts[index];\n            if (dataOffset < 0 || dataOffset > length)\n                throw new ArgumentOutOfRangeException(nameof(dataOffset));\n\n            if (length == 0)\n                return 0;\n\n            var maxLength = Math.Min(length - dataOffset, buffer.Length);\n            var slice = _segments[segmentIndex].Slice(offset + dataOffset, maxLength);\n            slice.Span.CopyTo(buffer);\n            return maxLength;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/StringTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class StringTypeInfo : SimpleTypeInfo\n    {\n        public StringTypeInfo()\n            : base(\"String\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new StringColumnReader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new StringSkippingColumnReader(rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (typeof(T) == typeof(string))\n                return new StringColumnWriter(columnName, ComplexTypeName, (IReadOnlyList<string>) rows, columnSettings?.StringEncoding ?? Encoding.UTF8);\n\n            if (typeof(T) == typeof(char[]))\n            {\n                var mappedList = MappedReadOnlyList<char[]?, ReadOnlyMemory<char>>.Map((IReadOnlyList<char[]?>)rows, m => m.AsMemory());\n                return new StringSpanColumnWriter(columnName, ComplexTypeName, mappedList, columnSettings?.StringEncoding ?? Encoding.UTF8);\n            }\n\n            if (typeof(T) == typeof(ReadOnlyMemory<char>))\n                return new StringSpanColumnWriter(columnName, ComplexTypeName, (IReadOnlyList<ReadOnlyMemory<char>>) rows, columnSettings?.StringEncoding ?? Encoding.UTF8);\n\n            if (typeof(T) == typeof(Memory<char>))\n            {\n                var mappedList = MappedReadOnlyList<Memory<char>, ReadOnlyMemory<char>>.Map((IReadOnlyList<Memory<char>>) rows, m => m);\n                return new StringSpanColumnWriter(columnName, ComplexTypeName, mappedList, columnSettings?.StringEncoding ?? Encoding.UTF8);\n            }\n\n            if (typeof(T) == typeof(byte[]))\n                return new BinaryStringColumnWriter(columnName, ComplexTypeName, (IReadOnlyList<byte[]>) rows);\n\n            if (typeof(T) == typeof(ReadOnlyMemory<byte>))\n                return new BinaryStringSpanColumnWriter(columnName, ComplexTypeName, (IReadOnlyList<ReadOnlyMemory<byte>>) rows);\n\n            if (typeof(T) == typeof(Memory<byte>))\n            {\n                var mappedList = MappedReadOnlyList<Memory<byte>, ReadOnlyMemory<byte>>.Map((IReadOnlyList<Memory<byte>>) rows, m => m);\n                return new BinaryStringSpanColumnWriter(columnName, ComplexTypeName, mappedList);\n            }\n\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer;\n            if (type == typeof(string))\n                writer = new StringParameterWriter<string>(this, str => str.AsMemory());\n            else if (type == typeof(char[]))\n                writer = new StringParameterWriter<char[]>(this, arr => arr);\n            else if (type == typeof(ReadOnlyMemory<char>))\n                writer = new StringParameterWriter(this);\n            else if (type == typeof(Memory<char>))\n                writer = new StringParameterWriter<Memory<char>>(this, mem => mem);\n            else if (type == typeof(byte[]))\n                writer = new HexStringParameterWriter<byte[]>(this, arr => arr);\n            else if (type == typeof(ReadOnlyMemory<byte>))\n                writer = new HexStringParameterWriter(this);\n            else if (type == typeof(Memory<byte>))\n                writer = new HexStringParameterWriter<Memory<byte>>(this, mem => mem);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(string);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.String;\n        }\n\n        private class StringColumnReader : IClickHouseColumnReader\n        {\n            private readonly int _rowCount;\n            private readonly int _bufferSize;\n            \n            private readonly List<(int segmentIndex, int offset, int length)> _layouts;\n            private readonly List<Memory<byte>> _segments = new List<Memory<byte>>(1);\n\n            private int _position;\n\n            public StringColumnReader(int rowCount)\n            {\n                _rowCount = rowCount;\n                _bufferSize = 4096; //TODO: from settings\n                _layouts = new List<(int segmentIndex, int offset, int length)>(rowCount);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                if (_layouts.Count >= _rowCount)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                int expectedElementsCount = _rowCount - _layouts.Count;\n                int elementsCount = 0, bytesCount = 0;\n                while (elementsCount < expectedElementsCount)\n                {\n                    var slice = sequence.Slice(bytesCount);\n                    if (!ClickHouseBinaryProtocolReader.TryRead7BitInteger(slice, out var longSize, out var bytesRead))\n                        break;\n\n                    var size = (int) longSize;\n                    if (slice.Length - bytesRead < size)\n                        break;\n\n                    if (size == 0)\n                    {\n                        _layouts.Add((0, 0, 0));\n                    }\n                    else\n                    {\n                        var lastSegment = _segments.Count == 0 ? default : _segments[^1];\n                        lastSegment = lastSegment.Slice(_position);\n                        if (lastSegment.Length < size)\n                        {\n                            lastSegment = new Memory<byte>(new byte[Math.Max(_bufferSize, size)]);\n                            _position = 0;\n                            _segments.Add(lastSegment);\n                        }\n\n                        var stringBytes = slice.Slice(bytesRead, size);\n                        stringBytes.CopyTo(lastSegment.Span);\n\n                        _layouts.Add((_segments.Count - 1, _position, size));\n                        _position += size;\n                    }\n\n                    ++elementsCount;\n                    bytesCount += size + bytesRead;\n                }\n\n                return new SequenceSize(bytesCount, elementsCount);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                return new StringTableColumn(settings?.StringEncoding ?? Encoding.UTF8, _layouts, _segments);\n            }\n        }\n\n        private sealed class StringSkippingColumnReader : IClickHouseColumnReaderBase\n        {\n            private readonly int _rowCount;\n\n            private int _position;\n\n            public StringSkippingColumnReader(int rowCount)\n            {\n                _rowCount = rowCount;\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                var maxElementsCount = _rowCount - _position;\n                if (maxElementsCount <= 0)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                int offset = 0;\n                int count = 0;\n                while (count < maxElementsCount)\n                {\n                    var slice = sequence.Slice(offset);\n                    if (!ClickHouseBinaryProtocolReader.TryRead7BitInteger(slice, out var size, out var bytesRead))\n                        break;\n\n                    var totalLength = bytesRead + (int) size;\n                    if (slice.Length < totalLength)\n                        break;\n\n                    offset += totalLength;\n                    ++count;                    \n                }\n\n                _position += count;\n                return new SequenceSize(offset, count);\n            }\n        }\n\n        private sealed class BinaryStringColumnWriter : StringColumnWriterBase\n        {\n            private readonly IReadOnlyList<byte[]> _rows;\n\n            protected override int RowCount => _rows.Count;\n\n            public BinaryStringColumnWriter(string columnName, string columnType, IReadOnlyList<byte[]> rows)\n                : base(columnName, columnType)\n            {\n                _rows = rows;\n            }\n\n            protected override int GetByteCount(int rowIndex)\n            {\n                return _rows[rowIndex]?.Length ?? 0;\n            }\n\n            protected override void WriteBytes(int rowIndex, Span<byte> writeTo)\n            {\n                _rows[rowIndex].CopyTo(writeTo);\n            }\n        }\n\n        private sealed class BinaryStringSpanColumnWriter : StringColumnWriterBase\n        {\n            private readonly IReadOnlyList<ReadOnlyMemory<byte>> _rows;\n\n            protected override int RowCount => _rows.Count;\n\n            public BinaryStringSpanColumnWriter(string columnName, string columnType, IReadOnlyList<ReadOnlyMemory<byte>> rows)\n                : base(columnName, columnType)\n            {\n                _rows = rows;\n            }\n\n            protected override int GetByteCount(int rowIndex)\n            {\n                return _rows[rowIndex].Length;\n            }\n\n            protected override void WriteBytes(int rowIndex, Span<byte> writeTo)\n            {\n                _rows[rowIndex].Span.CopyTo(writeTo);\n            }\n        }\n\n        private sealed class StringColumnWriter : StringColumnWriterBase\n        {\n            private readonly IReadOnlyList<string> _rows;\n            private readonly Encoding _encoding;\n\n            protected override int RowCount => _rows.Count;\n\n            public StringColumnWriter(string columnName, string columnType, IReadOnlyList<string> rows, Encoding encoding)\n                : base(columnName, columnType)\n            {\n                _rows = rows;\n                _encoding = encoding;\n            }\n\n            protected override int GetByteCount(int rowIndex)\n            {\n                var str = _rows[rowIndex];\n                if (string.IsNullOrEmpty(str))\n                    return 0;\n\n                return _encoding.GetByteCount(str);\n            }\n\n            protected override void WriteBytes(int rowIndex, Span<byte> writeTo)\n            {\n                _encoding.GetBytes(_rows[rowIndex], writeTo);\n            }\n        }\n\n        private sealed class StringSpanColumnWriter : StringColumnWriterBase\n        {\n            private readonly IReadOnlyList<ReadOnlyMemory<char>> _rows;\n            private readonly Encoding _encoding;\n\n            protected override int RowCount => _rows.Count;\n\n            public StringSpanColumnWriter(string columnName, string columnType, IReadOnlyList<ReadOnlyMemory<char>> rows, Encoding encoding)\n                : base(columnName, columnType)\n            {\n                _rows = rows;\n                _encoding = encoding;\n            }\n\n            protected override int GetByteCount(int rowIndex)\n            {\n                return _encoding.GetByteCount(_rows[rowIndex].Span);\n            }\n\n            protected override void WriteBytes(int rowIndex, Span<byte> writeTo)\n            {\n                _encoding.GetBytes(_rows[rowIndex].Span, writeTo);\n            }\n        }\n\n        private abstract class StringColumnWriterBase : IClickHouseColumnWriter\n        {\n            public string ColumnName { get; }\n\n            public string ColumnType { get; }\n\n            protected abstract int RowCount { get; }\n\n            private int _position;\n\n            protected StringColumnWriterBase(string columnName, string columnType)\n            {\n                ColumnName = columnName;\n                ColumnType = columnType;\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                var rowCount = RowCount;\n                if (_position == rowCount)\n                    return new SequenceSize(0, 0);\n\n                var result = new SequenceSize(0, 0);\n                for (; _position < rowCount; _position++)\n                {\n                    var span = writeTo.Slice(result.Bytes);\n                    if (span.IsEmpty)\n                        break;\n\n                    var byteCount = GetByteCount(_position);\n                    if (byteCount == 0)\n                    {\n                        span[0] = 0;\n                        result = new SequenceSize(result.Bytes + 1, result.Elements + 1);\n                    }\n                    else\n                    {\n                        var prefixLength = ClickHouseBinaryProtocolWriter.TryWrite7BitInteger(span, checked((ulong) byteCount));\n                        if (prefixLength <= 0 || prefixLength + byteCount > span.Length)\n                            return result;\n\n                        WriteBytes(_position, span.Slice(prefixLength, byteCount));\n                        result = new SequenceSize(result.Bytes + prefixLength + byteCount, result.Elements + 1);\n                    }\n                }\n\n                return result;\n            }\n\n            protected abstract int GetByteCount(int rowIndex);\n\n            protected abstract void WriteBytes(int rowIndex, Span<byte> writeTo);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/StructureReaderBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    /// <summary>\n    /// Represents a base class capable of reading columns of value types.\n    /// </summary>\n    /// <typeparam name=\"TIn\">The type of the column that must be a value type (struct).</typeparam>\n    /// <typeparam name=\"TOut\">The type of the output column.</typeparam>\n    public abstract class StructureReaderBase<TIn, TOut> : IClickHouseColumnReader\n        where TIn : struct\n    {\n        private readonly int _rowCount;\n\n        private int _position;\n        private readonly TIn[]? _buffer;\n\n        /// <summary>\n        /// Gets the size of a single element in bytes.\n        /// </summary>\n        protected int ElementSize { get; }\n\n        /// <summary>\n        /// Gets the value indicating whether bytes from an input buffer can be copied to the column's buffer bitwise. The default is <see langword=\"false\"/>.\n        /// </summary>\n        protected virtual bool BitwiseCopyAllowed => false;\n\n        /// <summary>\n        /// Initializes <see cref=\"StructureWriterBase{TIn, TOut}\"/> with specified parameters.\n        /// </summary>\n        /// <param name=\"elementSize\">The size of a single element in bytes.</param>\n        /// <param name=\"rowCount\">The number of rows that the reader should read.</param>\n        protected StructureReaderBase(int elementSize, int rowCount)\n        {\n            ElementSize = elementSize;\n            _rowCount = rowCount;\n\n            if (rowCount > 0)\n                _buffer = new TIn[rowCount];\n        }\n\n        /// <summary>\n        /// Reads as much elements as possible from the provided binary buffer.\n        /// </summary>\n        /// <param name=\"sequence\">The binary buffer.</param>\n        /// <returns>The <see cref=\"SequenceSize\"/> that contains the number of bytes and the number of elements which were read.</returns>\n        public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n        {\n            if (_position >= _rowCount)\n                throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n            var byteSize = Math.Min(ElementSize * (_rowCount - _position), (int) (sequence.Length - sequence.Length % ElementSize));\n            var elementCount = byteSize / ElementSize;\n            if (elementCount == 0)\n                return new SequenceSize(0, 0);\n\n            int count;\n            if (BitwiseCopyAllowed)\n            {\n                var targetBytes = MemoryMarshal.AsBytes(new Span<TIn>(_buffer, _position, elementCount));\n                Debug.Assert(byteSize == targetBytes.Length);\n                sequence.Slice(0, byteSize).CopyTo(targetBytes);\n                count = elementCount;\n            }\n            else\n            {\n                count = CopyTo(sequence.Slice(0, byteSize), ((Span<TIn>) _buffer).Slice(_position, elementCount));\n                Debug.Assert(count >= 0 && count <= elementCount);\n            }\n\n            _position += count;\n            return new SequenceSize(count * ElementSize, count);\n        }\n\n        private int CopyTo(ReadOnlySequence<byte> source, Span<TIn> target)\n        {\n            Span<byte> tmpSpan = stackalloc byte[ElementSize];\n            int count = 0;\n            for (var slice = source; !slice.IsEmpty; slice = slice.Slice(ElementSize), count++)\n            {\n                if (slice.FirstSpan.Length >= ElementSize)\n                    target[count] = ReadElement(slice.FirstSpan);\n                else\n                {\n                    slice.Slice(0, ElementSize).CopyTo(tmpSpan);\n                    target[count] = ReadElement(tmpSpan);\n                }\n            }\n\n            return count;\n        }\n\n        /// <summary>\n        /// When overriden in a derived class reads a single element from the provided binary buffer.\n        /// </summary>\n        /// <param name=\"source\">The binary buffer.</param>\n        /// <returns>The decoded value.</returns>\n        protected abstract TIn ReadElement(ReadOnlySpan<byte> source);\n\n        /// <summary>\n        /// When overriden in a derived class creates a column for <see cref=\"ClickHouseDataReader\"/> with the specified settings.\n        /// </summary>\n        /// <param name=\"settings\">The settings of the column.</param>\n        /// <param name=\"buffer\">The buffer that contains the column's rows.</param>\n        /// <returns>A column for <see cref=\"ClickHouseDataReader\"/>.</returns>\n        protected abstract IClickHouseTableColumn<TOut> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<TIn> buffer);\n\n        /// <inheritdoc/>\n        public IClickHouseTableColumn<TOut> EndRead(ClickHouseColumnSettings? settings)\n        {\n            return EndRead(settings, ((ReadOnlyMemory<TIn>)_buffer).Slice(0, _position));\n        }\n\n        IClickHouseTableColumn IClickHouseColumnReader.EndRead(ClickHouseColumnSettings? settings)\n        {\n            return EndRead(settings);\n        }\n    }\n\n    /// <summary>\n    /// Represents a base class capable of reading columns of value types.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of the column that must be a value type (struct).</typeparam>\n    public abstract class StructureReaderBase<T> : StructureReaderBase<T, T>\n        where T : struct\n    {\n        /// <summary>\n        /// Initializes <see cref=\"StructureWriterBase{TIn, TOut}\"/> with specified parameters.\n        /// </summary>\n        /// <param name=\"elementSize\">The size of a single element in bytes.</param>\n        /// <param name=\"rowCount\">The number of rows that the reader should read.</param>\n        public StructureReaderBase(int elementSize, int rowCount)\n            : base(elementSize, rowCount)\n        {\n        }\n\n        /// <summary>\n        /// Creates a column for <see cref=\"ClickHouseDataReader\"/> from the provided buffer. The column settings are ignored.\n        /// </summary>\n        /// <param name=\"settings\">The settings of the column. This argument is ignored by this method.</param>\n        /// <param name=\"buffer\">The buffer that contains the column's rows.</param>\n        /// <returns>A column for <see cref=\"ClickHouseDataReader\"/>.</returns>\n        protected override IClickHouseTableColumn<T> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<T> buffer)\n        {\n            return new StructureTableColumn<T>(buffer);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/StructureTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal class StructureTableColumn<T> : IClickHouseTableColumn<T>\n        where T : struct\n    {\n        private readonly ReadOnlyMemory<T> _buffer;\n\n        public int RowCount => _buffer.Length;\n\n        public T DefaultValue => default;\n\n        public StructureTableColumn(ReadOnlyMemory<T> buffer)\n        {\n            _buffer = buffer;\n        }\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        public T GetValue(int index)\n        {\n            return _buffer.Span[index];\n        }\n\n        public virtual IClickHouseTableColumn<TAs>? TryReinterpret<TAs>()\n        {\n            if (typeof(TAs) == typeof(T?))\n                return (IClickHouseTableColumn<TAs>) (object) new NullableStructTableColumn<T>(null, this);\n\n            return null;\n        }\n\n        object IClickHouseTableColumn.GetValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<TRes>(IClickHouseTableColumnDispatcher<TRes> dispatcher, out TRes dispatchedValue)\n        {\n            dispatchedValue = dispatcher.Dispatch(this);\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/StructureWriterBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Runtime.InteropServices;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    /// <summary>\n    /// Represents a base class capable of writing columns of value types.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of the column that must be a value type (struct).</typeparam>\n    public abstract class StructureWriterBase<T> : IClickHouseColumnWriter\n        where T : struct\n    {\n        private readonly IReadOnlyList<T> _rows;\n\n        private int _position;\n\n        /// <summary>\n        /// Gets the size of a single element in bytes.\n        /// </summary>\n        protected int ElementSize { get; }\n\n        /// <inheritdoc/>\n        public string ColumnName { get; }\n\n        /// <inheritdoc/>\n        public string ColumnType { get; }\n\n        /// <summary>\n        /// Gets the value indicating whether elements from the column can be copied to an output buffer bitwise. The default is <see langword=\"false\"/>.\n        /// </summary>\n        protected virtual bool BitwiseCopyAllowed => false;\n\n        /// <summary>\n        /// Initializes <see cref=\"StructureWriterBase{T}\"/> with specified arguments.\n        /// </summary>\n        /// <param name=\"columnName\">The name of the column to write data to.</param>\n        /// <param name=\"columnType\">The full name of the ClickHouse type of the column.</param>\n        /// <param name=\"elementSize\">The size of a single element in bytes.</param>\n        /// <param name=\"rows\">The list of rows that the writer should write.</param>\n        protected StructureWriterBase(string columnName, string columnType, int elementSize, IReadOnlyList<T> rows)\n        {\n            ElementSize = elementSize;\n            _rows = rows;\n            ColumnName = columnName;\n            ColumnType = columnType;\n        }\n\n        /// <inheritdoc/>\n        public SequenceSize WriteNext(Span<byte> writeTo)\n        {\n            var elementsCount = Math.Min(_rows.Count - _position, writeTo.Length / ElementSize);\n\n            if (BitwiseCopyAllowed) \n            {\n                var targetSpan = MemoryMarshal.Cast<byte, T>(writeTo.Slice(0, elementsCount * ElementSize));\n                var length = _rows.CopyTo(targetSpan, _position);\n                Debug.Assert(length == elementsCount);\n                _position += length;\n            }\n            else\n            {\n                for (int i = 0; i < elementsCount; i++, _position++)\n                {\n                    WriteElement(writeTo.Slice(i * ElementSize), _rows[_position]);\n                }\n            }\n\n            return new SequenceSize(elementsCount * ElementSize, elementsCount);\n        }\n\n        /// <summary>\n        /// Writes a single value to the target span.\n        /// </summary>\n        /// <param name=\"writeTo\">The buffer to write a single value to. It's guaranteed that the size of the buffer is not less than the size of an element.</param>\n        /// <param name=\"value\">The value that should be written.</param>\n        protected abstract void WriteElement(Span<byte> writeTo, in T value);\n    }\n\n    /// <summary>\n    /// Represents a base class capable of converting column's values to a value type and writing them to a column.\n    /// </summary>\n    /// <typeparam name=\"TIn\">The type of the input data.</typeparam>\n    /// <typeparam name=\"TOut\">The type of the column that must be a value type (struct).</typeparam>\n    public abstract class StructureWriterBase<TIn, TOut> : IClickHouseColumnWriter\n        where TOut: struct\n    {\n        private readonly IReadOnlyList<TIn> _rows;\n\n        private int _position;\n\n        /// <summary>\n        /// Gets the size of a single element in bytes.\n        /// </summary>\n        protected int ElementSize { get; }\n\n        /// <inheritdoc/>\n        public string ColumnName { get; }\n\n        /// <inheritdoc/>\n        public string ColumnType { get; }\n\n        /// <summary>\n        /// Initializes <see cref=\"StructureWriterBase{TIn, TOut}\"/> with specified arguments.\n        /// </summary>\n        /// <param name=\"columnName\">The name of the column to write data to.</param>\n        /// <param name=\"columnType\">The full name of the ClickHouse type of the column.</param>\n        /// <param name=\"elementSize\">The size of a single element in bytes.</param>\n        /// <param name=\"rows\">The list of rows that the writer should write.</param>\n        protected StructureWriterBase(string columnName, string columnType, int elementSize, IReadOnlyList<TIn> rows)\n        {\n            ElementSize = elementSize;\n            _rows = rows;\n            ColumnName = columnName;\n            ColumnType = columnType;\n        }\n\n        /// <inheritdoc/>\n        public SequenceSize WriteNext(Span<byte> writeTo)\n        {\n            var elementsCount = Math.Min(_rows.Count - _position, writeTo.Length / ElementSize);\n\n            var targetSpan = MemoryMarshal.Cast<byte, TOut>(writeTo.Slice(0, elementsCount * ElementSize));\n            _position += _rows.Map(Convert).CopyTo(targetSpan, _position);\n\n            return new SequenceSize(elementsCount * ElementSize, elementsCount);\n        }\n\n        /// <summary>\n        /// When overriden in a derived type converts a single element of the type <typeparamref name=\"TIn\"/>\n        /// to a value of the type <typeparamref name=\"TOut\"/>.\n        /// </summary>\n        /// <param name=\"value\">The value that should be converted.</param>\n        /// <returns>The value converted to the type <typeparamref name=\"TOut\"/>.</returns>\n        protected abstract TOut Convert(TIn value);        \n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/TupleTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal abstract class TupleTableColumnBase : IClickHouseTableColumn\n    {\n        public int RowCount { get; }\n\n        protected TupleTableColumnBase(int rowCount)\n        {\n            RowCount = rowCount;\n        }\n\n        public abstract IEnumerable<IClickHouseTableColumn> GetColumns();\n\n        protected abstract object GetTupleValue(int index);\n\n        public bool IsNull(int index)\n        {\n            return false;\n        }\n\n        public object GetValue(int index)\n        {\n            return GetTupleValue(index);\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        protected void CheckIndex(int index)\n        {\n            if (index < 0 || index > RowCount)\n                throw new ArgumentOutOfRangeException(nameof(index));\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            var columns = GetColumns();\n            var columnList = columns as IReadOnlyList<IClickHouseTableColumn> ?? columns.ToList();\n\n            return (IClickHouseTableColumn<T>?) TryMakeTupleColumn(typeof(T), RowCount, columnList);\n        }\n\n        bool IClickHouseTableColumn.TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, out T dispatchedValue)\n        {\n            dispatchedValue = Dispatch(dispatcher);\n            return true;\n        }\n\n        protected abstract T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher);\n\n        public static TupleTableColumnBase MakeTupleColumn(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n        {\n            var columnElementTypes = new Type[columns.Count];\n\n            for (var i = 0; i < columns.Count; i++)\n                columnElementTypes[i] = ClickHouseTableColumnHelper.TryGetValueType(columns[i]) ?? typeof(object);\n\n            var tupleType = TupleTypeInfo.MakeTupleType(columnElementTypes);\n            var tupleColumn = TryMakeTupleColumn(tupleType, rowCount, columns);\n\n            if (tupleColumn == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. The column of the required type can't be created.\");\n\n            return tupleColumn;\n        }\n\n        private static TupleTableColumnBase? TryMakeTupleColumn(Type type, int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n        {\n            if (columns == null)\n                throw new ArgumentNullException(nameof(columns));\n            if (!type.IsGenericType)\n                return null;\n\n            var typeDef = type.GetGenericTypeDefinition();\n            Type? reinterpreterTypeDef = null;\n            switch (columns.Count)\n            {\n                case 1:\n                    if (typeDef == typeof(Tuple<>))\n                        reinterpreterTypeDef = typeof(TupleTableColumn<>.Reinterpreter);\n                    else if (typeDef == typeof(ValueTuple<>))\n                        reinterpreterTypeDef = typeof(ValueTupleTableColumn<>.Reinterpreter);\n                    break;\n                case 2:\n                    if (typeDef == typeof(Tuple<,>))\n                        reinterpreterTypeDef = typeof(TupleTableColumn<,>.Reinterpreter);\n                    else if (typeDef == typeof(ValueTuple<,>))\n                        reinterpreterTypeDef = typeof(ValueTupleTableColumn<,>.Reinterpreter);\n                    else if (typeDef == typeof(KeyValuePair<,>))\n                        reinterpreterTypeDef = typeof(KeyValuePairTableColumn<,>.Reinterpreter);\n                    break;\n                case 3:\n                    if (typeDef == typeof(Tuple<,,>))\n                        reinterpreterTypeDef = typeof(TupleTableColumn<,,>.Reinterpreter);\n                    else if (typeDef == typeof(ValueTuple<,,>))\n                        reinterpreterTypeDef = typeof(ValueTupleTableColumn<,,>.Reinterpreter);\n                    break;\n                case 4:\n                    if (typeDef == typeof(Tuple<,,,>))\n                        reinterpreterTypeDef = typeof(TupleTableColumn<,,,>.Reinterpreter);\n                    else if (typeDef == typeof(ValueTuple<,,,>))\n                        reinterpreterTypeDef = typeof(ValueTupleTableColumn<,,,>.Reinterpreter);\n                    break;\n                case 5:\n                    if (typeDef == typeof(Tuple<,,,,>))\n                        reinterpreterTypeDef = typeof(TupleTableColumn<,,,,>.Reinterpreter);\n                    else if (typeDef == typeof(ValueTuple<,,,,>))\n                        reinterpreterTypeDef = typeof(ValueTupleTableColumn<,,,,>.Reinterpreter);\n                    break;\n                case 6:\n                    if (typeDef == typeof(Tuple<,,,,,>))\n                        reinterpreterTypeDef = typeof(TupleTableColumn<,,,,,>.Reinterpreter);\n                    else if (typeDef == typeof(ValueTuple<,,,,,>))\n                        reinterpreterTypeDef = typeof(ValueTupleTableColumn<,,,,,>.Reinterpreter);\n                    break;\n                case 7:\n                    if (typeDef == typeof(Tuple<,,,,,,>))\n                        reinterpreterTypeDef = typeof(TupleTableColumn<,,,,,,>.Reinterpreter);\n                    else if (typeDef == typeof(ValueTuple<,,,,,,>))\n                        reinterpreterTypeDef = typeof(ValueTupleTableColumn<,,,,,,>.Reinterpreter);\n                    break;\n                default:\n                    if (columns.Count < 8)\n                        break;\n\n                    if (typeDef == typeof(Tuple<,,,,,,,>))\n                        reinterpreterTypeDef = typeof(TupleTableColumn<,,,,,,,,>.Reinterpreter);\n                    else if (typeDef == typeof(ValueTuple<,,,,,,,>))\n                        reinterpreterTypeDef = typeof(ValueTupleTableColumn<,,,,,,,,>.Reinterpreter);\n                    else\n                        break;\n\n                    var tuple8Args = type.GetGenericArguments();\n                    var extraTupleType = tuple8Args[^1];\n                    var extraColumn = TryMakeTupleColumn(extraTupleType, rowCount, columns.Slice(7));\n                    if (extraColumn == null)\n                        return null;\n\n                    var extraColumnType = extraColumn.GetType();\n                    if (!typeof(IClickHouseTableColumn<>).MakeGenericType(extraTupleType).IsAssignableFrom(extraColumnType))\n                        return null;\n\n                    var tuple8ColumnTypeArgs = new Type[9];\n                    tuple8Args.CopyTo(tuple8ColumnTypeArgs, 0);\n                    tuple8ColumnTypeArgs[^1] = extraColumnType;\n\n                    var tuple8ColumnInterpreter = (ReinterpreterBase) Activator.CreateInstance(reinterpreterTypeDef.MakeGenericType(tuple8ColumnTypeArgs))!;\n\n                    var tuple8Columns = new List<IClickHouseTableColumn>(8);\n                    tuple8Columns.AddRange(columns.Take(7));\n                    tuple8Columns.Add(extraColumn);\n\n                    return tuple8ColumnInterpreter.TryReinterpret(rowCount, tuple8Columns);\n            }\n\n            if (reinterpreterTypeDef == null)\n                return null;\n\n            var tupleArgs = type.GetGenericArguments();\n            var reinterpreterType = reinterpreterTypeDef.MakeGenericType(tupleArgs);\n            var reinterpreter = (ReinterpreterBase) Activator.CreateInstance(reinterpreterType)!;\n\n            return reinterpreter.TryReinterpret(rowCount, columns);\n        }\n\n        internal abstract class ReinterpreterBase\n        {\n            public abstract TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns);\n\n            protected static IClickHouseTableColumn<T>? TryReinterpret<T>(IClickHouseTableColumn column)\n            {\n                var result = column as IClickHouseTableColumn<T> ?? column.TryReinterpret<T>();\n                if (result == null && typeof(T) == typeof(object))\n                    return (IClickHouseTableColumn<T>) (object) new ObjectColumnAdapter(column);\n\n                return result;\n            }\n        }\n    }\n\n    internal sealed class TupleTableColumn<T1> : TupleTableColumnBase, IClickHouseTableColumn<Tuple<T1>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n\n        public Tuple<T1> DefaultValue => Tuple.Create(_column1.DefaultValue);\n\n        private TupleTableColumn(int rowCount, IClickHouseTableColumn<T1> column1)\n            : base(rowCount)\n        {\n            _column1 = column1;\n        }\n\n        public new Tuple<T1> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new Tuple<T1>(_column1.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 1);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                return new TupleTableColumn<T1>(rowCount, column1);\n            }\n        }\n    }\n\n    internal sealed class TupleTableColumn<T1, T2> : TupleTableColumnBase, IClickHouseTableColumn<Tuple<T1, T2>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n\n        public Tuple<T1, T2> DefaultValue => Tuple.Create(_column1.DefaultValue, _column2.DefaultValue);\n\n        private TupleTableColumn(int rowCount, IClickHouseTableColumn<T1> column1, IClickHouseTableColumn<T2> column2)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n        }\n\n        public new Tuple<T1, T2> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new Tuple<T1, T2>(_column1.GetValue(index), _column2.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 2);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                return new TupleTableColumn<T1, T2>(rowCount, column1, column2);\n            }\n        }\n    }\n\n    internal sealed class TupleTableColumn<T1, T2, T3> : TupleTableColumnBase, IClickHouseTableColumn<Tuple<T1, T2, T3>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n\n        public Tuple<T1, T2, T3> DefaultValue => Tuple.Create(_column1.DefaultValue, _column2.DefaultValue, _column3.DefaultValue);\n\n        private TupleTableColumn(int rowCount, IClickHouseTableColumn<T1> column1, IClickHouseTableColumn<T2> column2, IClickHouseTableColumn<T3> column3)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n        }\n\n        public new Tuple<T1, T2, T3> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new Tuple<T1, T2, T3>(_column1.GetValue(index), _column2.GetValue(index), _column3.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 3);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                return new TupleTableColumn<T1, T2, T3>(rowCount, column1, column2, column3);\n            }\n        }\n    }\n\n    internal sealed class TupleTableColumn<T1, T2, T3, T4> : TupleTableColumnBase, IClickHouseTableColumn<Tuple<T1, T2, T3, T4>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n        private readonly IClickHouseTableColumn<T4> _column4;\n\n        public Tuple<T1, T2, T3, T4> DefaultValue => Tuple.Create(_column1.DefaultValue, _column2.DefaultValue, _column3.DefaultValue, _column4.DefaultValue);\n\n        private TupleTableColumn(int rowCount, IClickHouseTableColumn<T1> column1, IClickHouseTableColumn<T2> column2, IClickHouseTableColumn<T3> column3, IClickHouseTableColumn<T4> column4)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n            _column4 = column4;\n        }\n\n        public new Tuple<T1, T2, T3, T4> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new Tuple<T1, T2, T3, T4>(_column1.GetValue(index), _column2.GetValue(index), _column3.GetValue(index), _column4.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n            yield return _column4;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 4);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                var column4 = TryReinterpret<T4>(columns[3]);\n                if (column4 == null)\n                    return null;\n\n                return new TupleTableColumn<T1, T2, T3, T4>(rowCount, column1, column2, column3, column4);\n            }\n        }\n    }\n\n    internal sealed class TupleTableColumn<T1, T2, T3, T4, T5> : TupleTableColumnBase, IClickHouseTableColumn<Tuple<T1, T2, T3, T4, T5>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n        private readonly IClickHouseTableColumn<T4> _column4;\n        private readonly IClickHouseTableColumn<T5> _column5;\n\n        public Tuple<T1, T2, T3, T4, T5> DefaultValue =>\n            Tuple.Create(\n                _column1.DefaultValue,\n                _column2.DefaultValue,\n                _column3.DefaultValue,\n                _column4.DefaultValue,\n                _column5.DefaultValue);\n\n        private TupleTableColumn(\n            int rowCount,\n            IClickHouseTableColumn<T1> column1,\n            IClickHouseTableColumn<T2> column2,\n            IClickHouseTableColumn<T3> column3,\n            IClickHouseTableColumn<T4> column4,\n            IClickHouseTableColumn<T5> column5)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n            _column4 = column4;\n            _column5 = column5;\n        }\n\n        public new Tuple<T1, T2, T3, T4, T5> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new Tuple<T1, T2, T3, T4, T5>(_column1.GetValue(index), _column2.GetValue(index), _column3.GetValue(index), _column4.GetValue(index), _column5.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n            yield return _column4;\n            yield return _column5;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 5);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                var column4 = TryReinterpret<T4>(columns[3]);\n                if (column4 == null)\n                    return null;\n\n                var column5 = TryReinterpret<T5>(columns[4]);\n                if (column5 == null)\n                    return null;\n\n                return new TupleTableColumn<T1, T2, T3, T4, T5>(rowCount, column1, column2, column3, column4, column5);\n            }\n        }\n    }\n\n    internal sealed class TupleTableColumn<T1, T2, T3, T4, T5, T6> : TupleTableColumnBase, IClickHouseTableColumn<Tuple<T1, T2, T3, T4, T5, T6>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n        private readonly IClickHouseTableColumn<T4> _column4;\n        private readonly IClickHouseTableColumn<T5> _column5;\n        private readonly IClickHouseTableColumn<T6> _column6;\n\n        public Tuple<T1, T2, T3, T4, T5, T6> DefaultValue => Tuple.Create(\n            _column1.DefaultValue,\n            _column2.DefaultValue,\n            _column3.DefaultValue,\n            _column4.DefaultValue,\n            _column5.DefaultValue,\n            _column6.DefaultValue);\n\n        private TupleTableColumn(\n            int rowCount,\n            IClickHouseTableColumn<T1> column1,\n            IClickHouseTableColumn<T2> column2,\n            IClickHouseTableColumn<T3> column3,\n            IClickHouseTableColumn<T4> column4,\n            IClickHouseTableColumn<T5> column5,\n            IClickHouseTableColumn<T6> column6)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n            _column4 = column4;\n            _column5 = column5;\n            _column6 = column6;\n        }\n\n        public new Tuple<T1, T2, T3, T4, T5, T6> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new Tuple<T1, T2, T3, T4, T5, T6>(\n                _column1.GetValue(index),\n                _column2.GetValue(index),\n                _column3.GetValue(index),\n                _column4.GetValue(index),\n                _column5.GetValue(index),\n                _column6.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n            yield return _column4;\n            yield return _column5;\n            yield return _column6;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 6);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                var column4 = TryReinterpret<T4>(columns[3]);\n                if (column4 == null)\n                    return null;\n\n                var column5 = TryReinterpret<T5>(columns[4]);\n                if (column5 == null)\n                    return null;\n\n                var column6 = TryReinterpret<T6>(columns[5]);\n                if (column6 == null)\n                    return null;\n\n                return new TupleTableColumn<T1, T2, T3, T4, T5, T6>(rowCount, column1, column2, column3, column4, column5, column6);\n            }\n        }\n    }\n\n    internal sealed class TupleTableColumn<T1, T2, T3, T4, T5, T6, T7> : TupleTableColumnBase, IClickHouseTableColumn<Tuple<T1, T2, T3, T4, T5, T6, T7>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n        private readonly IClickHouseTableColumn<T4> _column4;\n        private readonly IClickHouseTableColumn<T5> _column5;\n        private readonly IClickHouseTableColumn<T6> _column6;\n        private readonly IClickHouseTableColumn<T7> _column7;\n\n        public Tuple<T1, T2, T3, T4, T5, T6, T7> DefaultValue =>\n            Tuple.Create(\n                _column1.DefaultValue,\n                _column2.DefaultValue,\n                _column3.DefaultValue,\n                _column4.DefaultValue,\n                _column5.DefaultValue,\n                _column6.DefaultValue,\n                _column7.DefaultValue);\n\n        private TupleTableColumn(\n            int rowCount,\n            IClickHouseTableColumn<T1> column1,\n            IClickHouseTableColumn<T2> column2,\n            IClickHouseTableColumn<T3> column3,\n            IClickHouseTableColumn<T4> column4,\n            IClickHouseTableColumn<T5> column5,\n            IClickHouseTableColumn<T6> column6,\n            IClickHouseTableColumn<T7> column7)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n            _column4 = column4;\n            _column5 = column5;\n            _column6 = column6;\n            _column7 = column7;\n        }\n\n        public new Tuple<T1, T2, T3, T4, T5, T6, T7> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new Tuple<T1, T2, T3, T4, T5, T6, T7>(\n                _column1.GetValue(index),\n                _column2.GetValue(index),\n                _column3.GetValue(index),\n                _column4.GetValue(index),\n                _column5.GetValue(index),\n                _column6.GetValue(index),\n                _column7.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n            yield return _column4;\n            yield return _column5;\n            yield return _column6;\n            yield return _column7;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 7);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                var column4 = TryReinterpret<T4>(columns[3]);\n                if (column4 == null)\n                    return null;\n\n                var column5 = TryReinterpret<T5>(columns[4]);\n                if (column5 == null)\n                    return null;\n\n                var column6 = TryReinterpret<T6>(columns[5]);\n                if (column6 == null)\n                    return null;\n\n                var column7 = TryReinterpret<T7>(columns[6]);\n                if (column7 == null)\n                    return null;\n\n                return new TupleTableColumn<T1, T2, T3, T4, T5, T6, T7>(rowCount, column1, column2, column3, column4, column5, column6, column7);\n            }\n        }\n    }\n\n#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint.\n    internal sealed class TupleTableColumn<T1, T2, T3, T4, T5, T6, T7, TRest, TColumnRest> : TupleTableColumnBase, IClickHouseTableColumn<Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>>\n        where TColumnRest : TupleTableColumnBase, IClickHouseTableColumn<TRest>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n        private readonly IClickHouseTableColumn<T4> _column4;\n        private readonly IClickHouseTableColumn<T5> _column5;\n        private readonly IClickHouseTableColumn<T6> _column6;\n        private readonly IClickHouseTableColumn<T7> _column7;\n        private readonly TColumnRest _columnRest;\n\n        public Tuple<T1, T2, T3, T4, T5, T6, T7, TRest> DefaultValue =>\n            new Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>(\n                _column1.DefaultValue,\n                _column2.DefaultValue,\n                _column3.DefaultValue,\n                _column4.DefaultValue,\n                _column5.DefaultValue,\n                _column6.DefaultValue,\n                _column7.DefaultValue,\n                _columnRest.DefaultValue);\n\n        private TupleTableColumn(\n            int rowCount,\n            IClickHouseTableColumn<T1> column1,\n            IClickHouseTableColumn<T2> column2,\n            IClickHouseTableColumn<T3> column3,\n            IClickHouseTableColumn<T4> column4,\n            IClickHouseTableColumn<T5> column5,\n            IClickHouseTableColumn<T6> column6,\n            IClickHouseTableColumn<T7> column7,\n            TColumnRest columnRest)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n            _column4 = column4;\n            _column5 = column5;\n            _column6 = column6;\n            _column7 = column7;\n            _columnRest = columnRest;\n        }\n\n        public new Tuple<T1, T2, T3, T4, T5, T6, T7, TRest> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>(\n                _column1.GetValue(index),\n                _column2.GetValue(index),\n                _column3.GetValue(index),\n                _column4.GetValue(index),\n                _column5.GetValue(index),\n                _column6.GetValue(index),\n                _column7.GetValue(index),\n                ((IClickHouseTableColumn<TRest>) _columnRest).GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n            yield return _column4;\n            yield return _column5;\n            yield return _column6;\n            yield return _column7;\n\n            foreach (var extraColumn in _columnRest.GetColumns())\n                yield return extraColumn;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 8);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                var column4 = TryReinterpret<T4>(columns[3]);\n                if (column4 == null)\n                    return null;\n\n                var column5 = TryReinterpret<T5>(columns[4]);\n                if (column5 == null)\n                    return null;\n\n                var column6 = TryReinterpret<T6>(columns[5]);\n                if (column6 == null)\n                    return null;\n\n                var column7 = TryReinterpret<T7>(columns[6]);\n                if (column7 == null)\n                    return null;\n\n                var column8 = TryReinterpret<TRest>(columns[7]);\n                if (!(column8 is TColumnRest columnRest))\n                    return null;\n\n                return new TupleTableColumn<T1, T2, T3, T4, T5, T6, T7, TRest, TColumnRest>(rowCount, column1, column2, column3, column4, column5, column6, column7, columnRest);\n            }\n        }\n    }\n#pragma warning restore CS8714\n\n    internal sealed class ValueTupleTableColumn<T1> : TupleTableColumnBase, IClickHouseTableColumn<ValueTuple<T1>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n\n        public ValueTuple<T1> DefaultValue => ValueTuple.Create(_column1.DefaultValue);\n\n        private ValueTupleTableColumn(int rowCount, IClickHouseTableColumn<T1> column1)\n            : base(rowCount)\n        {\n            _column1 = column1;\n        }\n\n        public new ValueTuple<T1> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new ValueTuple<T1>(_column1.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 1);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                return new ValueTupleTableColumn<T1>(rowCount, column1);\n            }\n        }\n    }\n\n    internal sealed class ValueTupleTableColumn<T1, T2> : TupleTableColumnBase, IClickHouseTableColumn<ValueTuple<T1, T2>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n\n        public ValueTuple<T1, T2> DefaultValue => ValueTuple.Create(_column1.DefaultValue, _column2.DefaultValue);\n\n        private ValueTupleTableColumn(int rowCount, IClickHouseTableColumn<T1> column1, IClickHouseTableColumn<T2> column2)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n        }\n\n        public new ValueTuple<T1, T2> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new ValueTuple<T1, T2>(_column1.GetValue(index), _column2.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 2);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                return new ValueTupleTableColumn<T1, T2>(rowCount, column1, column2);\n            }\n        }\n    }\n\n    internal sealed class ValueTupleTableColumn<T1, T2, T3> : TupleTableColumnBase, IClickHouseTableColumn<ValueTuple<T1, T2, T3>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n\n        public ValueTuple<T1, T2, T3> DefaultValue => ValueTuple.Create(_column1.DefaultValue, _column2.DefaultValue, _column3.DefaultValue);\n\n        private ValueTupleTableColumn(int rowCount, IClickHouseTableColumn<T1> column1, IClickHouseTableColumn<T2> column2, IClickHouseTableColumn<T3> column3)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n        }\n\n        public new ValueTuple<T1, T2, T3> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new ValueTuple<T1, T2, T3>(_column1.GetValue(index), _column2.GetValue(index), _column3.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 3);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                return new ValueTupleTableColumn<T1, T2, T3>(rowCount, column1, column2, column3);\n            }\n        }\n    }\n\n    internal sealed class ValueTupleTableColumn<T1, T2, T3, T4> : TupleTableColumnBase, IClickHouseTableColumn<ValueTuple<T1, T2, T3, T4>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n        private readonly IClickHouseTableColumn<T4> _column4;\n\n        public ValueTuple<T1, T2, T3, T4> DefaultValue => ValueTuple.Create(_column1.DefaultValue, _column2.DefaultValue, _column3.DefaultValue, _column4.DefaultValue);\n\n        private ValueTupleTableColumn(int rowCount, IClickHouseTableColumn<T1> column1, IClickHouseTableColumn<T2> column2, IClickHouseTableColumn<T3> column3, IClickHouseTableColumn<T4> column4)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n            _column4 = column4;\n        }\n\n        public new ValueTuple<T1, T2, T3, T4> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new ValueTuple<T1, T2, T3, T4>(_column1.GetValue(index), _column2.GetValue(index), _column3.GetValue(index), _column4.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n            yield return _column4;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 4);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                var column4 = TryReinterpret<T4>(columns[3]);\n                if (column4 == null)\n                    return null;\n\n                return new ValueTupleTableColumn<T1, T2, T3, T4>(rowCount, column1, column2, column3, column4);\n            }\n        }\n    }\n\n    internal sealed class ValueTupleTableColumn<T1, T2, T3, T4, T5> : TupleTableColumnBase, IClickHouseTableColumn<ValueTuple<T1, T2, T3, T4, T5>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n        private readonly IClickHouseTableColumn<T4> _column4;\n        private readonly IClickHouseTableColumn<T5> _column5;\n\n        public ValueTuple<T1, T2, T3, T4, T5> DefaultValue =>\n            ValueTuple.Create(\n                _column1.DefaultValue,\n                _column2.DefaultValue,\n                _column3.DefaultValue,\n                _column4.DefaultValue,\n                _column5.DefaultValue);\n\n        private ValueTupleTableColumn(\n            int rowCount,\n            IClickHouseTableColumn<T1> column1,\n            IClickHouseTableColumn<T2> column2,\n            IClickHouseTableColumn<T3> column3,\n            IClickHouseTableColumn<T4> column4,\n            IClickHouseTableColumn<T5> column5)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n            _column4 = column4;\n            _column5 = column5;\n        }\n\n        public new ValueTuple<T1, T2, T3, T4, T5> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new ValueTuple<T1, T2, T3, T4, T5>(_column1.GetValue(index), _column2.GetValue(index), _column3.GetValue(index), _column4.GetValue(index), _column5.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n            yield return _column4;\n            yield return _column5;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 5);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                var column4 = TryReinterpret<T4>(columns[3]);\n                if (column4 == null)\n                    return null;\n\n                var column5 = TryReinterpret<T5>(columns[4]);\n                if (column5 == null)\n                    return null;\n\n                return new ValueTupleTableColumn<T1, T2, T3, T4, T5>(rowCount, column1, column2, column3, column4, column5);\n            }\n        }\n    }\n\n    internal sealed class ValueTupleTableColumn<T1, T2, T3, T4, T5, T6> : TupleTableColumnBase, IClickHouseTableColumn<ValueTuple<T1, T2, T3, T4, T5, T6>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n        private readonly IClickHouseTableColumn<T4> _column4;\n        private readonly IClickHouseTableColumn<T5> _column5;\n        private readonly IClickHouseTableColumn<T6> _column6;\n\n        public ValueTuple<T1, T2, T3, T4, T5, T6> DefaultValue =>\n            ValueTuple.Create(\n                _column1.DefaultValue,\n                _column2.DefaultValue,\n                _column3.DefaultValue,\n                _column4.DefaultValue,\n                _column5.DefaultValue,\n                _column6.DefaultValue);\n\n        private ValueTupleTableColumn(\n            int rowCount,\n            IClickHouseTableColumn<T1> column1,\n            IClickHouseTableColumn<T2> column2,\n            IClickHouseTableColumn<T3> column3,\n            IClickHouseTableColumn<T4> column4,\n            IClickHouseTableColumn<T5> column5,\n            IClickHouseTableColumn<T6> column6)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n            _column4 = column4;\n            _column5 = column5;\n            _column6 = column6;\n        }\n\n        public new ValueTuple<T1, T2, T3, T4, T5, T6> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new ValueTuple<T1, T2, T3, T4, T5, T6>(\n                _column1.GetValue(index),\n                _column2.GetValue(index),\n                _column3.GetValue(index),\n                _column4.GetValue(index),\n                _column5.GetValue(index),\n                _column6.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n            yield return _column4;\n            yield return _column5;\n            yield return _column6;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 6);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                var column4 = TryReinterpret<T4>(columns[3]);\n                if (column4 == null)\n                    return null;\n\n                var column5 = TryReinterpret<T5>(columns[4]);\n                if (column5 == null)\n                    return null;\n\n                var column6 = TryReinterpret<T6>(columns[5]);\n                if (column6 == null)\n                    return null;\n\n                return new ValueTupleTableColumn<T1, T2, T3, T4, T5, T6>(rowCount, column1, column2, column3, column4, column5, column6);\n            }\n        }\n    }\n\n    internal sealed class ValueTupleTableColumn<T1, T2, T3, T4, T5, T6, T7> : TupleTableColumnBase, IClickHouseTableColumn<ValueTuple<T1, T2, T3, T4, T5, T6, T7>>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n        private readonly IClickHouseTableColumn<T4> _column4;\n        private readonly IClickHouseTableColumn<T5> _column5;\n        private readonly IClickHouseTableColumn<T6> _column6;\n        private readonly IClickHouseTableColumn<T7> _column7;\n\n        public ValueTuple<T1, T2, T3, T4, T5, T6, T7> DefaultValue =>\n            ValueTuple.Create(\n                _column1.DefaultValue,\n                _column2.DefaultValue,\n                _column3.DefaultValue,\n                _column4.DefaultValue,\n                _column5.DefaultValue,\n                _column6.DefaultValue,\n                _column7.DefaultValue);\n\n        private ValueTupleTableColumn(\n            int rowCount,\n            IClickHouseTableColumn<T1> column1,\n            IClickHouseTableColumn<T2> column2,\n            IClickHouseTableColumn<T3> column3,\n            IClickHouseTableColumn<T4> column4,\n            IClickHouseTableColumn<T5> column5,\n            IClickHouseTableColumn<T6> column6,\n            IClickHouseTableColumn<T7> column7)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n            _column4 = column4;\n            _column5 = column5;\n            _column6 = column6;\n            _column7 = column7;\n        }\n\n        public new ValueTuple<T1, T2, T3, T4, T5, T6, T7> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new ValueTuple<T1, T2, T3, T4, T5, T6, T7>(\n                _column1.GetValue(index),\n                _column2.GetValue(index),\n                _column3.GetValue(index),\n                _column4.GetValue(index),\n                _column5.GetValue(index),\n                _column6.GetValue(index),\n                _column7.GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n            yield return _column4;\n            yield return _column5;\n            yield return _column6;\n            yield return _column7;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 7);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                var column4 = TryReinterpret<T4>(columns[3]);\n                if (column4 == null)\n                    return null;\n\n                var column5 = TryReinterpret<T5>(columns[4]);\n                if (column5 == null)\n                    return null;\n\n                var column6 = TryReinterpret<T6>(columns[5]);\n                if (column6 == null)\n                    return null;\n\n                var column7 = TryReinterpret<T7>(columns[6]);\n                if (column7 == null)\n                    return null;\n\n                return new ValueTupleTableColumn<T1, T2, T3, T4, T5, T6, T7>(rowCount, column1, column2, column3, column4, column5, column6, column7);\n            }\n        }\n    }\n\n    internal sealed class ValueTupleTableColumn<T1, T2, T3, T4, T5, T6, T7, TRest, TColumnRest> : TupleTableColumnBase, IClickHouseTableColumn<ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>>\n        where TRest : struct\n        where TColumnRest : TupleTableColumnBase, IClickHouseTableColumn<TRest>\n    {\n        private readonly IClickHouseTableColumn<T1> _column1;\n        private readonly IClickHouseTableColumn<T2> _column2;\n        private readonly IClickHouseTableColumn<T3> _column3;\n        private readonly IClickHouseTableColumn<T4> _column4;\n        private readonly IClickHouseTableColumn<T5> _column5;\n        private readonly IClickHouseTableColumn<T6> _column6;\n        private readonly IClickHouseTableColumn<T7> _column7;\n        private readonly TColumnRest _columnRest;\n\n        public ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest> DefaultValue =>\n            new ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>(\n                _column1.DefaultValue,\n                _column2.DefaultValue,\n                _column3.DefaultValue,\n                _column4.DefaultValue,\n                _column5.DefaultValue,\n                _column6.DefaultValue,\n                _column7.DefaultValue,\n                _columnRest.DefaultValue);\n\n        private ValueTupleTableColumn(\n            int rowCount,\n            IClickHouseTableColumn<T1> column1,\n            IClickHouseTableColumn<T2> column2,\n            IClickHouseTableColumn<T3> column3,\n            IClickHouseTableColumn<T4> column4,\n            IClickHouseTableColumn<T5> column5,\n            IClickHouseTableColumn<T6> column6,\n            IClickHouseTableColumn<T7> column7,\n            TColumnRest columnRest)\n            : base(rowCount)\n        {\n            _column1 = column1;\n            _column2 = column2;\n            _column3 = column3;\n            _column4 = column4;\n            _column5 = column5;\n            _column6 = column6;\n            _column7 = column7;\n            _columnRest = columnRest;\n        }\n\n        public new ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest> GetValue(int index)\n        {\n            CheckIndex(index);\n\n            return new ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>(\n                _column1.GetValue(index),\n                _column2.GetValue(index),\n                _column3.GetValue(index),\n                _column4.GetValue(index),\n                _column5.GetValue(index),\n                _column6.GetValue(index),\n                _column7.GetValue(index),\n                ((IClickHouseTableColumn<TRest>) _columnRest).GetValue(index));\n        }\n\n        protected override object GetTupleValue(int index)\n        {\n            return GetValue(index);\n        }\n\n        public override IEnumerable<IClickHouseTableColumn> GetColumns()\n        {\n            yield return _column1;\n            yield return _column2;\n            yield return _column3;\n            yield return _column4;\n            yield return _column5;\n            yield return _column6;\n            yield return _column7;\n\n            foreach (var extraColumn in _columnRest.GetColumns())\n                yield return extraColumn;\n        }\n\n        protected override T Dispatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher)\n        {\n            return dispatcher.Dispatch(this);\n        }\n\n        internal class Reinterpreter : ReinterpreterBase\n        {\n            public override TupleTableColumnBase? TryReinterpret(int rowCount, IReadOnlyList<IClickHouseTableColumn> columns)\n            {\n                Debug.Assert(columns.Count == 8);\n\n                var column1 = TryReinterpret<T1>(columns[0]);\n                if (column1 == null)\n                    return null;\n\n                var column2 = TryReinterpret<T2>(columns[1]);\n                if (column2 == null)\n                    return null;\n\n                var column3 = TryReinterpret<T3>(columns[2]);\n                if (column3 == null)\n                    return null;\n\n                var column4 = TryReinterpret<T4>(columns[3]);\n                if (column4 == null)\n                    return null;\n\n                var column5 = TryReinterpret<T5>(columns[4]);\n                if (column5 == null)\n                    return null;\n\n                var column6 = TryReinterpret<T6>(columns[5]);\n                if (column6 == null)\n                    return null;\n\n                var column7 = TryReinterpret<T7>(columns[6]);\n                if (column7 == null)\n                    return null;\n\n                var column8 = TryReinterpret<TRest>(columns[7]);\n                if (!(column8 is TColumnRest columnRest))\n                    return null;\n\n                return new ValueTupleTableColumn<T1, T2, T3, T4, T5, T6, T7, TRest, TColumnRest>(rowCount, column1, column2, column3, column4, column5, column6, column7, columnRest);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/TupleTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Text;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class TupleTypeInfo : IClickHouseColumnTypeInfo\n    {\n        private readonly List<IClickHouseColumnTypeInfo>? _elementTypes;\n        private readonly List<string>? _elementNames;\n\n        public string ComplexTypeName { get; }\n\n        public string TypeName => \"Tuple\";\n\n        public int GenericArgumentsCount => _elementTypes?.Count ?? 0;\n\n        public TupleTypeInfo()\n        {\n            ComplexTypeName = TypeName;\n            _elementTypes = null;\n        }\n\n        private TupleTypeInfo(string complexTypeName, List<IClickHouseColumnTypeInfo> elementTypes, List<string>? elementNames)\n        {\n            if (elementNames != null && elementTypes.Count != elementNames.Count)\n                throw new ArgumentException(\"The number of elements must be equal to the number of element's types.\", nameof(elementNames));\n\n            ComplexTypeName = complexTypeName;\n            _elementTypes = elementTypes;\n            _elementNames = elementNames;\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            if (_elementTypes == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var elementReaders = _elementTypes.Select(t => t.CreateColumnReader(rowCount)).ToList();\n            return new TupleColumnReader(rowCount, elementReaders);\n        }\n\n        IClickHouseColumnReader IClickHouseColumnTypeInfo.CreateColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode == ClickHouseColumnSerializationMode.Default)\n                return CreateColumnReader(rowCount);\n\n            if (serializationMode == ClickHouseColumnSerializationMode.Custom)\n            {\n                if (_elementTypes == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n                return new TupleCustomSerializationColumnReader(this, rowCount);\n            }\n\n            throw new NotSupportedException($\"The serialization mode {serializationMode} for {TypeName} type is not supported by ClickHouseClient.\");\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            if (_elementTypes == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var elementReaders = _elementTypes.Select(t => t.CreateSkippingColumnReader(rowCount)).ToList();\n            return new TupleSkippingColumnReader(rowCount, elementReaders);\n        }\n\n        IClickHouseColumnReaderBase IClickHouseColumnTypeInfo.CreateSkippingColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode == ClickHouseColumnSerializationMode.Default)\n                return CreateSkippingColumnReader(rowCount);\n\n            if (serializationMode == ClickHouseColumnSerializationMode.Custom)\n            {\n                if (_elementTypes == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n                return new TupleCustomSerializationSkippingColumnReader(this, rowCount);\n            }\n\n            throw new NotSupportedException($\"The serialization mode {serializationMode} for {TypeName} type is not supported by ClickHouseClient.\");\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (_elementTypes == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return TupleColumnWriter.CreateColumnWriter(columnName, ComplexTypeName, _elementTypes, rows, columnSettings);\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            // TODO: ClickHouseDbType.Tuple is not supported in DefaultTypeInfoProvider.GetTypeInfo\n            if (_elementTypes == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var tupeType = typeof(T);\n            if (tupeType == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            return TupleColumnWriter.CreateParameterWriter<T>(this, ComplexTypeName, _elementTypes, false);\n        }\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            if (_elementTypes != null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, \"The type is already fully specified.\");\n\n            var complexTypeNameBuilder = new StringBuilder(TypeName).Append('(');\n            var elementTypes = new List<IClickHouseColumnTypeInfo>(options.Count);\n            List<string>? elementNames = null;\n            foreach(var option in options)\n            {\n                if (elementTypes.Count > 0)\n                    complexTypeNameBuilder.Append(\", \");\n\n                var identifierLen = ClickHouseSyntaxHelper.GetIdentifierLiteralLength(option.Span);\n                if (identifierLen == option.Span.Length)\n                    identifierLen = -1;\n                \n                if (identifierLen < 0)\n                {\n                    if (elementNames != null)\n                        throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, \"A tuple can be either named or not. Mixing of named and unnamed arguments is not allowed.\");\n\n                    var typeInfo = typeInfoProvider.GetTypeInfo(option);\n                    elementTypes.Add(typeInfo);\n                }\n                else\n                {\n                    if (elementNames == null)\n                    {\n                        if (elementTypes.Count > 0)\n                            throw new ClickHouseException(ClickHouseErrorCodes.InvalidTypeName, \"A tuple can be either named or not. Mixing of named and unnamed arguments is not allowed.\");\n\n                        elementNames = new List<string>(options.Count);\n                    }\n\n                    var name = ClickHouseSyntaxHelper.GetIdentifier(option.Span.Slice(0, identifierLen));\n                    var typeInfo = typeInfoProvider.GetTypeInfo(option.Slice(identifierLen + 1));\n\n                    elementTypes.Add(typeInfo);\n                    elementNames.Add(name);\n\n                    complexTypeNameBuilder.Append(option.Slice(0, identifierLen)).Append(' ');\n                }\n\n                complexTypeNameBuilder.Append(elementTypes[^1].ComplexTypeName);\n            }\n\n            var complexTypeName = complexTypeNameBuilder.Append(')').ToString();\n            return new TupleTypeInfo(complexTypeName, elementTypes, elementNames);\n        }\n\n        public Type GetFieldType()\n        {\n            if (_elementTypes == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return MakeTupleType(_elementTypes.Select(elt => elt.GetFieldType()).ToArray());\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Tuple;\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            if (_elementTypes == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return _elementTypes[index];\n        }\n\n        public object GetTypeArgument(int index)\n        {\n            if (_elementTypes == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            if (_elementNames == null)\n                return _elementTypes[index];\n\n            return new KeyValuePair<string, IClickHouseTypeInfo>(_elementNames[index], _elementTypes[index]);\n        }\n\n        public static Type MakeTupleType(IReadOnlyList<Type> genericArgs)\n        {\n            Type tupleType;\n            switch (genericArgs.Count)\n            {\n                case 0:\n                    throw new ArgumentException(\"No type arguments specified.\", nameof(genericArgs));\n                case 1:\n                    tupleType = typeof(Tuple<>);\n                    break;\n                case 2:\n                    tupleType = typeof(Tuple<,>);\n                    break;\n                case 3:\n                    tupleType = typeof(Tuple<,,>);\n                    break;\n                case 4:\n                    tupleType = typeof(Tuple<,,,>);\n                    break;\n                case 5:\n                    tupleType = typeof(Tuple<,,,,>);\n                    break;\n                case 6:\n                    tupleType = typeof(Tuple<,,,,,>);\n                    break;\n                case 7:\n                    tupleType = typeof(Tuple<,,,,,,>);\n                    break;\n                default:\n                    tupleType = typeof(Tuple<,,,,,,,>);\n                    var restType = MakeTupleType(genericArgs.Slice(7));\n\n                    var tuple8ArgsArr = new Type[8];\n                    for (int i = 0; i < 7; i++)\n                        tuple8ArgsArr[i] = genericArgs[i];\n\n                    tuple8ArgsArr[7] = restType;\n                    return tupleType.MakeGenericType(tuple8ArgsArr);\n            }\n\n            var genericArgsArr = genericArgs as Type[] ?? genericArgs.ToArray();\n            return tupleType.MakeGenericType(genericArgsArr);\n        }\n\n        private static void AddParameterWriter<TTuple, TItem>(List<IClickHouseParameterWriter<TTuple>> writers, IClickHouseColumnTypeInfo elementType, Func<TTuple, TItem> getItem)\n        {\n            var elementWriter = elementType.CreateParameterWriter<TItem>();\n            var writer = new TupleItemParameterWriter<TTuple, TItem>(elementWriter, getItem);\n            writers.Add(writer);\n        }\n\n        private static IClickHouseParameterWriter<TTuple> CreateParameterWriter<TTuple>(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseParameterWriter<TTuple>> itemWriters, bool isRest)\n        {\n            return new TupleParameterWriter<TTuple>(typeInfo, itemWriters, isRest);\n        }\n\n        private sealed class TupleCustomSerializationColumnReader : IClickHouseColumnReader\n        {\n            private readonly TupleTypeInfo _typeInfo;\n            private readonly int _rowCount;\n\n            private TupleColumnReader? _realReader;\n\n            public TupleCustomSerializationColumnReader(TupleTypeInfo typeInfo, int rowCount)\n            {\n                _typeInfo = typeInfo;\n                _rowCount = rowCount;\n            }\n\n            SequenceSize IClickHouseColumnReaderBase.ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                if (_realReader != null)\n                    return ((IClickHouseColumnReaderBase)_realReader).ReadPrefix(sequence);\n\n                var elementTypes = _typeInfo._elementTypes;\n                Debug.Assert(elementTypes != null);\n\n                if (sequence.Length < elementTypes.Count + 1)\n                    return SequenceSize.Empty;\n\n                var mode = (ClickHouseColumnSerializationMode)sequence.FirstSpan[0];\n                if (mode != ClickHouseColumnSerializationMode.Default)\n                    throw new NotSupportedException($\"The serialization mode {mode} is not supported by {_typeInfo.TypeName} type. Only the default mode is supported.\");\n\n                var seq = sequence.Slice(1);\n                var typeModes = new List<(IClickHouseColumnTypeInfo type, ClickHouseColumnSerializationMode mode)>(elementTypes.Count);\n                foreach (var type in elementTypes)\n                {\n                    mode = (ClickHouseColumnSerializationMode)seq.FirstSpan[0];\n                    if (mode != ClickHouseColumnSerializationMode.Default && mode != ClickHouseColumnSerializationMode.Sparse)\n                        throw new NotSupportedException($\"Invalid serialization mode ({mode}) for an elment of tuple.\");\n\n                    typeModes.Add((type, mode));\n                    seq = seq.Slice(1);\n                }\n\n                var readers = typeModes.Select(tm => tm.type.CreateColumnReader(_rowCount, tm.mode)).ToList();\n                _realReader = new TupleColumnReader(_rowCount, readers);\n                var result = ((IClickHouseColumnReaderBase)_realReader).ReadPrefix(seq);\n                return result.AddBytes(elementTypes.Count + 1);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                if (_realReader == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Detected an attempt to read the column before reading its prefix.\");\n\n                return _realReader.ReadNext(sequence);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                if (_realReader == null)\n                    return _typeInfo.CreateColumnReader(0).EndRead(settings);\n\n                return _realReader.EndRead(settings);\n            }\n        }\n\n        private sealed class TupleColumnReader : IClickHouseColumnReader\n        {\n            private readonly int _rowCount;\n            private readonly IReadOnlyList<IClickHouseColumnReader> _readers;\n\n            private int _prefixPosition;\n            private int _readerPosition;\n            private int _position;\n\n            public TupleColumnReader(int rowCount, IReadOnlyList<IClickHouseColumnReader> readers)\n            {\n                _rowCount = rowCount;\n                _readers = readers;\n            }\n\n            SequenceSize IClickHouseColumnReaderBase.ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                return ReadPrefix(sequence, _readers, ref _prefixPosition);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                var isLastColumn = _readerPosition == _readers.Count - 1;\n                if (!isLastColumn && _position >= _rowCount)\n                    throw new ClickHouseException(ClickHouseErrorCodes.DataReaderError, \"Internal error. Attempt to read after the end of the column.\");\n\n                var currentReader = _readers[_readerPosition];\n                var result = new SequenceSize(0, 0);\n                while (!isLastColumn)\n                {\n                    if (_position < _rowCount)\n                    {\n                        var elementsCount = _rowCount - _position;\n                        var size = currentReader.ReadNext(sequence.Slice(result.Bytes));\n\n                        result = new SequenceSize(result.Bytes + size.Bytes, result.Elements);\n                        _position += size.Elements;\n                        if (size.Elements < elementsCount)\n                            return result;\n                    }\n\n                    _position = 0;\n                    currentReader = _readers[++_readerPosition];\n                    isLastColumn = _readerPosition == _readers.Count - 1;\n                }\n\n                var lastColumnSize = currentReader.ReadNext(sequence.Slice(result.Bytes));\n                _position += lastColumnSize.Elements;\n\n                return new SequenceSize(result.Bytes + lastColumnSize.Bytes, lastColumnSize.Elements);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                var columns = new List<IClickHouseTableColumn>(_readers.Count);\n                foreach(var reader in _readers)\n                {\n                    var column = reader.EndRead(settings);\n                    columns.Add(column);\n                }\n\n                return TupleTableColumnBase.MakeTupleColumn(columns[^1].RowCount, columns);\n            }\n\n            public static SequenceSize ReadPrefix(ReadOnlySequence<byte> sequence, IReadOnlyList<IClickHouseColumnReaderBase> elementReaders, ref int prefixPosition)\n            {\n                var totalBytes = 0;\n                var prefixReader = elementReaders[prefixPosition];\n                while (prefixPosition < elementReaders.Count - 1)\n                {\n                    var prefixSize = prefixReader.ReadPrefix(sequence);\n                    totalBytes += prefixSize.Bytes;\n\n                    if (prefixSize.Elements == 0)\n                        return new SequenceSize(totalBytes, 0);\n\n                    if (prefixSize.Elements != 1)\n                        throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. Received an unexpected number of column prefixes: {prefixSize.Elements}.\");\n\n                    prefixReader = elementReaders[++prefixPosition];\n                }\n\n                var lastPrefixSize = prefixReader.ReadPrefix(sequence);\n                return lastPrefixSize.AddBytes(totalBytes);\n            }\n        }\n\n        private sealed class TupleCustomSerializationSkippingColumnReader : IClickHouseColumnReaderBase\n        {\n            private readonly TupleTypeInfo _typeInfo;\n            private readonly int _rowCount;\n\n            private TupleSkippingColumnReader? _realReader;\n\n            public TupleCustomSerializationSkippingColumnReader(TupleTypeInfo typeInfo, int rowCount)\n            {\n                _typeInfo = typeInfo;\n                _rowCount = rowCount;\n            }\n\n            SequenceSize IClickHouseColumnReaderBase.ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                if (_realReader != null)\n                    return ((IClickHouseColumnReaderBase)_realReader).ReadPrefix(sequence);\n\n                var elementTypes = _typeInfo._elementTypes;\n                Debug.Assert(elementTypes != null);\n\n                if (sequence.Length < elementTypes.Count + 1)\n                    return SequenceSize.Empty;\n\n                var mode = (ClickHouseColumnSerializationMode)sequence.FirstSpan[0];\n                if (mode != ClickHouseColumnSerializationMode.Default)\n                    throw new NotSupportedException($\"The serialization mode {mode} is not supported by {_typeInfo.TypeName} type. Only the default mode is supported.\");\n\n                var seq = sequence.Slice(1);\n                var typeModes = new List<(IClickHouseColumnTypeInfo type, ClickHouseColumnSerializationMode mode)>(elementTypes.Count);\n                foreach (var type in elementTypes)\n                {\n                    mode = (ClickHouseColumnSerializationMode)seq.FirstSpan[0];\n                    if (mode != ClickHouseColumnSerializationMode.Default && mode != ClickHouseColumnSerializationMode.Sparse)\n                        throw new NotSupportedException($\"Invalid serialization mode ({mode}) for an elment of tuple.\");\n\n                    typeModes.Add((type, mode));\n                    seq = seq.Slice(1);\n                }\n\n                var readers = typeModes.Select(tm => tm.type.CreateSkippingColumnReader(_rowCount, tm.mode)).ToList();\n                _realReader = new TupleSkippingColumnReader(_rowCount, readers);\n                var result = ((IClickHouseColumnReaderBase)_realReader).ReadPrefix(seq);\n                return result.AddBytes(elementTypes.Count + 1);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                if (_realReader == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Detected an attempt to read the column before reading its prefix.\");\n\n                return _realReader.ReadNext(sequence);\n            }\n        }\n\n        private sealed class TupleSkippingColumnReader : IClickHouseColumnReaderBase\n        {\n            private readonly int _rowCount;\n            private readonly IReadOnlyList<IClickHouseColumnReaderBase> _elementReaders;\n\n            private int _prefixPosition;\n            private int _elementReaderIndex;\n\n            private int _position;\n\n            public TupleSkippingColumnReader(int rowCount, IReadOnlyList<IClickHouseColumnReaderBase> elementReaders)\n            {\n                _rowCount = rowCount;\n                _elementReaders = elementReaders;\n            }\n\n            SequenceSize IClickHouseColumnReaderBase.ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                return TupleColumnReader.ReadPrefix(sequence, _elementReaders, ref _prefixPosition);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                var result = new SequenceSize(0, 0);\n                var elementReader = _elementReaders[_elementReaderIndex];\n                while (_elementReaderIndex < _elementReaders.Count - 1)\n                {\n                    var actualCount = elementReader.ReadNext(sequence.Slice(result.Bytes));\n\n                    result = result.AddBytes(actualCount.Bytes);\n                    _position += actualCount.Elements;\n                    if (_position < _rowCount)\n                        return result;\n\n                    _position = 0;\n                    elementReader = _elementReaders[++_elementReaderIndex];\n                }\n\n                var lastColumnCount = elementReader.ReadNext(sequence.Slice(result.Bytes));\n                _position += lastColumnCount.Elements;\n\n                return lastColumnCount.AddBytes(result.Bytes);\n            }\n        }\n\n        private sealed class TupleColumnWriter : IClickHouseColumnWriter\n        {\n            private readonly int _rowCount;\n            private readonly List<IClickHouseColumnWriter> _columns;\n\n            public string ColumnName { get; }\n\n            public string ColumnType { get; }\n\n            private int _prefixPosition;\n\n            private int _currentWriterIdx;\n            private int _currentWriterPosition;\n\n            private TupleColumnWriter(string columnName, List<IClickHouseColumnWriter> columns, int rowCount)\n            {\n                _rowCount = rowCount;\n                ColumnName = columnName;\n                _columns = columns;\n\n                var typeNameBuilder = _columns.Aggregate(new StringBuilder(\"Tuple(\"), (b, c) => b.Append(c.ColumnType).Append(','));\n                typeNameBuilder[^1] = ')';\n                ColumnType = typeNameBuilder.ToString();\n            }\n\n            SequenceSize IClickHouseColumnWriter.WritePrefix(Span<byte> writeTo)\n            {\n                var totalBytes = 0;\n                for (; _prefixPosition < _columns.Count; _prefixPosition++)\n                {\n                    var slice = writeTo.Slice(totalBytes);\n                    var size = _columns[_prefixPosition].WritePrefix(slice);\n\n                    totalBytes += size.Bytes;\n                    if (size.Elements == 0)\n                        break;\n                }\n\n                if (_prefixPosition == _columns.Count)\n                    return new SequenceSize(totalBytes, 1);\n\n                return new SequenceSize(totalBytes, 0);\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                var result = new SequenceSize(0, 0);\n\n                while (_currentWriterIdx < _columns.Count - 1)\n                {\n                    var columnWriter = _columns[_currentWriterIdx];\n                    if (_currentWriterPosition < _rowCount)\n                    {\n                        var expectedElementsCount = _rowCount - _currentWriterPosition;\n                        var actualCount = columnWriter.WriteNext(writeTo.Slice(result.Bytes));\n\n                        _currentWriterPosition += actualCount.Elements;\n                        result = result.AddBytes(actualCount.Bytes);\n                        if (actualCount.Elements < expectedElementsCount)\n                            return result;\n                    }\n\n                    ++_currentWriterIdx;\n                    _currentWriterPosition = 0;\n                }\n\n                var lastColumnCount = _columns[^1].WriteNext(writeTo.Slice(result.Bytes));\n                _currentWriterPosition += lastColumnCount.Elements;\n\n                return result.Add(lastColumnCount);\n            }\n\n            public static TupleColumnWriter CreateColumnWriter<T>(string columnName, string columnType, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n            {\n                var factory = CreateWriterFactory(typeof(T), columnType, elementTypes);\n\n                try\n                {\n                    return factory.CreateColumnWriter(columnName, elementTypes, rows, columnSettings);\n                }\n                catch (Exception ex)\n                {\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{columnType}\\\".\", ex);\n                }\n            }\n\n            public static IClickHouseParameterWriter<T> CreateParameterWriter<T>(TupleTypeInfo typeInfo, string columnType, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n            {\n                var factory = CreateWriterFactory(typeof(T), columnType, elementTypes);\n\n                object writer;\n                try\n                {\n                    writer = factory.CreateParameterWriter(typeInfo, elementTypes, isRest);\n                }\n                catch (Exception ex)\n                {\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{columnType}\\\".\", ex);\n                }\n\n                return (IClickHouseParameterWriter<T>)writer;\n            }\n\n            private static ITupleWriterFactory CreateWriterFactory(Type tupleType, string columnType, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes)\n            {\n                Type? factoryType = null;\n                if (tupleType.IsGenericType)\n                {\n                    var listItemTypeDef = tupleType.GetGenericTypeDefinition();\n                    switch (elementTypes.Count)\n                    {\n                        case 1:\n                            if (listItemTypeDef == typeof(Tuple<>))\n                                factoryType = typeof(TupleColumnFactory<>);\n                            else if (listItemTypeDef == typeof(ValueTuple<>))\n                                factoryType = typeof(ValueTupleColumnFactory<>);\n                            break;\n                        case 2:\n                            if (listItemTypeDef == typeof(Tuple<,>))\n                                factoryType = typeof(TupleColumnFactory<,>);\n                            else if (listItemTypeDef == typeof(ValueTuple<,>))\n                                factoryType = typeof(ValueTupleColumnFactory<,>);\n                            else if (listItemTypeDef == typeof(KeyValuePair<,>))\n                                factoryType = typeof(KeyValuePairColumnFactory<,>);\n                            break;\n                        case 3:\n                            if (listItemTypeDef == typeof(Tuple<,,>))\n                                factoryType = typeof(TupleColumnFactory<,,>);\n                            else if (listItemTypeDef == typeof(ValueTuple<,,>))\n                                factoryType = typeof(ValueTupleColumnFactory<,,>);\n                            break;\n                        case 4:\n                            if (listItemTypeDef == typeof(Tuple<,,,>))\n                                factoryType = typeof(TupleColumnFactory<,,,>);\n                            else if (listItemTypeDef == typeof(ValueTuple<,,,>))\n                                factoryType = typeof(ValueTupleColumnFactory<,,,>);\n                            break;\n                        case 5:\n                            if (listItemTypeDef == typeof(Tuple<,,,,>))\n                                factoryType = typeof(TupleColumnFactory<,,,,>);\n                            else if (listItemTypeDef == typeof(ValueTuple<,,,,>))\n                                factoryType = typeof(ValueTupleColumnFactory<,,,,>);\n                            break;\n                        case 6:\n                            if (listItemTypeDef == typeof(Tuple<,,,,,>))\n                                factoryType = typeof(TupleColumnFactory<,,,,,>);\n                            else if (listItemTypeDef == typeof(ValueTuple<,,,,,>))\n                                factoryType = typeof(ValueTupleColumnFactory<,,,,,>);\n                            break;\n                        case 7:\n                            if (listItemTypeDef == typeof(Tuple<,,,,,,>))\n                                factoryType = typeof(TupleColumnFactory<,,,,,,>);\n                            else if (listItemTypeDef == typeof(ValueTuple<,,,,,,>))\n                                factoryType = typeof(ValueTupleColumnFactory<,,,,,,>);\n                            break;\n                        default:\n                            if (elementTypes.Count >= 8)\n                            {\n                                if (listItemTypeDef == typeof(Tuple<,,,,,,,>))\n                                    factoryType = typeof(TupleColumnFactory<,,,,,,,>);\n                                else if (listItemTypeDef == typeof(ValueTuple<,,,,,,,>))\n                                    factoryType = typeof(ValueTupleColumnFactory<,,,,,,,>);\n                            }\n\n                            break;\n                    }\n\n                    if (factoryType != null)\n                    {\n                        var args = tupleType.GetGenericArguments();\n                        factoryType = factoryType.MakeGenericType(args);\n                    }\n                }\n\n                if (factoryType == null)\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{tupleType}\\\" can't be converted to the ClickHouse type \\\"{columnType}\\\".\");\n\n                ITupleWriterFactory? factory;\n                try\n                {\n                    factory = (ITupleWriterFactory?)Activator.CreateInstance(factoryType);\n                }\n                catch (Exception ex)\n                {\n                    throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{tupleType}\\\" can't be converted to the ClickHouse type \\\"{columnType}\\\".\", ex);\n                }\n\n                Debug.Assert(factory != null);\n                return factory;\n            }\n\n            private interface ITupleWriterFactory\n            {\n                TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings);\n\n                object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest);\n            }\n\n            private sealed class TupleColumnFactory<T1> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<Tuple<T1>>) untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(1)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<Tuple<T1>>>(elementTypes.Count);\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class TupleColumnFactory<T1, T2> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<Tuple<T1, T2>>) untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(2)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<Tuple<T1, T2>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class TupleColumnFactory<T1, T2, T3> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<Tuple<T1, T2, T3>>) untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(3)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<Tuple<T1, T2, T3>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class TupleColumnFactory<T1, T2, T3, T4> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<Tuple<T1, T2, T3, T4>>) untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(4)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings),\n                        elementTypes[3].CreateColumnWriter(columnName, rows.Map(t => t.Item4), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<Tuple<T1, T2, T3, T4>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n                    AddParameterWriter(elementWriters, elementTypes[3], t => t.Item4);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class TupleColumnFactory<T1, T2, T3, T4, T5> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<Tuple<T1, T2, T3, T4, T5>>) untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(5)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings),\n                        elementTypes[3].CreateColumnWriter(columnName, rows.Map(t => t.Item4), columnSettings),\n                        elementTypes[4].CreateColumnWriter(columnName, rows.Map(t => t.Item5), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<Tuple<T1, T2, T3, T4, T5>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n                    AddParameterWriter(elementWriters, elementTypes[3], t => t.Item4);\n                    AddParameterWriter(elementWriters, elementTypes[4], t => t.Item5);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class TupleColumnFactory<T1, T2, T3, T4, T5, T6> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<Tuple<T1, T2, T3, T4, T5, T6>>) untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(6)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings),\n                        elementTypes[3].CreateColumnWriter(columnName, rows.Map(t => t.Item4), columnSettings),\n                        elementTypes[4].CreateColumnWriter(columnName, rows.Map(t => t.Item5), columnSettings),\n                        elementTypes[5].CreateColumnWriter(columnName, rows.Map(t => t.Item6), columnSettings)\n                    };\n                    \n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<Tuple<T1, T2, T3, T4, T5, T6>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n                    AddParameterWriter(elementWriters, elementTypes[3], t => t.Item4);\n                    AddParameterWriter(elementWriters, elementTypes[4], t => t.Item5);\n                    AddParameterWriter(elementWriters, elementTypes[5], t => t.Item6);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class TupleColumnFactory<T1, T2, T3, T4, T5, T6, T7> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<Tuple<T1, T2, T3, T4, T5, T6, T7>>) untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(7)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings),\n                        elementTypes[3].CreateColumnWriter(columnName, rows.Map(t => t.Item4), columnSettings),\n                        elementTypes[4].CreateColumnWriter(columnName, rows.Map(t => t.Item5), columnSettings),\n                        elementTypes[5].CreateColumnWriter(columnName, rows.Map(t => t.Item6), columnSettings),\n                        elementTypes[6].CreateColumnWriter(columnName, rows.Map(t => t.Item7), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<Tuple<T1, T2, T3, T4, T5, T6, T7>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n                    AddParameterWriter(elementWriters, elementTypes[3], t => t.Item4);\n                    AddParameterWriter(elementWriters, elementTypes[4], t => t.Item5);\n                    AddParameterWriter(elementWriters, elementTypes[5], t => t.Item6);\n                    AddParameterWriter(elementWriters, elementTypes[6], t => t.Item7);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class TupleColumnFactory<T1, T2, T3, T4, T5, T6, T7, TRest> : ITupleWriterFactory\n                where TRest : notnull\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>>) untypedRows;\n\n                    var subColumns = elementTypes.Slice(7);\n                    var subType = \"Tuple(\" + string.Join(\", \", subColumns.Select(c => c.ComplexTypeName)) + \")\";\n                    var lastColumn = TupleColumnWriter.CreateColumnWriter(columnName, subType, subColumns, rows.Map(t => t.Rest), columnSettings);\n\n                    var columns = new List<IClickHouseColumnWriter>(7 + lastColumn._columns.Count)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings),\n                        elementTypes[3].CreateColumnWriter(columnName, rows.Map(t => t.Item4), columnSettings),\n                        elementTypes[4].CreateColumnWriter(columnName, rows.Map(t => t.Item5), columnSettings),\n                        elementTypes[5].CreateColumnWriter(columnName, rows.Map(t => t.Item6), columnSettings),\n                        elementTypes[6].CreateColumnWriter(columnName, rows.Map(t => t.Item7), columnSettings)\n                    };\n                    columns.AddRange(lastColumn._columns);\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n                    AddParameterWriter(elementWriters, elementTypes[3], t => t.Item4);\n                    AddParameterWriter(elementWriters, elementTypes[4], t => t.Item5);\n                    AddParameterWriter(elementWriters, elementTypes[5], t => t.Item6);\n                    AddParameterWriter(elementWriters, elementTypes[6], t => t.Item7);\n\n                    var restElements = elementTypes.Slice(7);\n                    var restType = \"Tuple(\" + string.Join(\", \", restElements.Select(c => c.ComplexTypeName)) + \")\";\n                    var restWriter = CreateParameterWriter<TRest>(typeInfo, restType, restElements, true);\n\n                    elementWriters.Add(new TupleItemParameterWriter<Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>, TRest>(restWriter, t => t.Rest));\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class ValueTupleColumnFactory<T1> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<ValueTuple<T1>>)untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(1)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<ValueTuple<T1>>>(elementTypes.Count);\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class ValueTupleColumnFactory<T1, T2> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<ValueTuple<T1, T2>>)untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(2)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<ValueTuple<T1, T2>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class ValueTupleColumnFactory<T1, T2, T3> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<ValueTuple<T1, T2, T3>>)untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(3)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<ValueTuple<T1, T2, T3>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class ValueTupleColumnFactory<T1, T2, T3, T4> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<ValueTuple<T1, T2, T3, T4>>)untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(4)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings),\n                        elementTypes[3].CreateColumnWriter(columnName, rows.Map(t => t.Item4), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<ValueTuple<T1, T2, T3, T4>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n                    AddParameterWriter(elementWriters, elementTypes[3], t => t.Item4);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class ValueTupleColumnFactory<T1, T2, T3, T4, T5> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<ValueTuple<T1, T2, T3, T4, T5>>)untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(5)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings),\n                        elementTypes[3].CreateColumnWriter(columnName, rows.Map(t => t.Item4), columnSettings),\n                        elementTypes[4].CreateColumnWriter(columnName, rows.Map(t => t.Item5), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<ValueTuple<T1, T2, T3, T4, T5>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n                    AddParameterWriter(elementWriters, elementTypes[3], t => t.Item4);\n                    AddParameterWriter(elementWriters, elementTypes[4], t => t.Item5);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class ValueTupleColumnFactory<T1, T2, T3, T4, T5, T6> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<ValueTuple<T1, T2, T3, T4, T5, T6>>)untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(6)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings),\n                        elementTypes[3].CreateColumnWriter(columnName, rows.Map(t => t.Item4), columnSettings),\n                        elementTypes[4].CreateColumnWriter(columnName, rows.Map(t => t.Item5), columnSettings),\n                        elementTypes[5].CreateColumnWriter(columnName, rows.Map(t => t.Item6), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<ValueTuple<T1, T2, T3, T4, T5, T6>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n                    AddParameterWriter(elementWriters, elementTypes[3], t => t.Item4);\n                    AddParameterWriter(elementWriters, elementTypes[4], t => t.Item5);\n                    AddParameterWriter(elementWriters, elementTypes[5], t => t.Item6);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class ValueTupleColumnFactory<T1, T2, T3, T4, T5, T6, T7> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<ValueTuple<T1, T2, T3, T4, T5, T6, T7>>)untypedRows;\n                    var columns = new List<IClickHouseColumnWriter>(7)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings),\n                        elementTypes[3].CreateColumnWriter(columnName, rows.Map(t => t.Item4), columnSettings),\n                        elementTypes[4].CreateColumnWriter(columnName, rows.Map(t => t.Item5), columnSettings),\n                        elementTypes[5].CreateColumnWriter(columnName, rows.Map(t => t.Item6), columnSettings),\n                        elementTypes[6].CreateColumnWriter(columnName, rows.Map(t => t.Item7), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<ValueTuple<T1, T2, T3, T4, T5, T6, T7>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n                    AddParameterWriter(elementWriters, elementTypes[3], t => t.Item4);\n                    AddParameterWriter(elementWriters, elementTypes[4], t => t.Item5);\n                    AddParameterWriter(elementWriters, elementTypes[5], t => t.Item6);\n                    AddParameterWriter(elementWriters, elementTypes[6], t => t.Item7);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class ValueTupleColumnFactory<T1, T2, T3, T4, T5, T6, T7, TRest> : ITupleWriterFactory\n                where TRest : struct\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>>) untypedRows;\n\n                    var subColumns = elementTypes.Slice(7);\n                    var subType = \"Tuple(\" + string.Join(\", \", subColumns.Select(c => c.ComplexTypeName)) + \")\";\n                    var lastColumn = TupleColumnWriter.CreateColumnWriter(columnName, subType, subColumns, rows.Map(t => t.Rest), columnSettings);\n\n                    var columns = new List<IClickHouseColumnWriter>(7 + lastColumn._columns.Count)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(t => t.Item1), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(t => t.Item2), columnSettings),\n                        elementTypes[2].CreateColumnWriter(columnName, rows.Map(t => t.Item3), columnSettings),\n                        elementTypes[3].CreateColumnWriter(columnName, rows.Map(t => t.Item4), columnSettings),\n                        elementTypes[4].CreateColumnWriter(columnName, rows.Map(t => t.Item5), columnSettings),\n                        elementTypes[5].CreateColumnWriter(columnName, rows.Map(t => t.Item6), columnSettings),\n                        elementTypes[6].CreateColumnWriter(columnName, rows.Map(t => t.Item7), columnSettings)\n                    };\n                    columns.AddRange(lastColumn._columns);\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var elementWriters = new List<IClickHouseParameterWriter<ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>>>(elementTypes.Count);\n\n                    AddParameterWriter(elementWriters, elementTypes[0], t => t.Item1);\n                    AddParameterWriter(elementWriters, elementTypes[1], t => t.Item2);\n                    AddParameterWriter(elementWriters, elementTypes[2], t => t.Item3);\n                    AddParameterWriter(elementWriters, elementTypes[3], t => t.Item4);\n                    AddParameterWriter(elementWriters, elementTypes[4], t => t.Item5);\n                    AddParameterWriter(elementWriters, elementTypes[5], t => t.Item6);\n                    AddParameterWriter(elementWriters, elementTypes[6], t => t.Item7);\n\n                    var restElements = elementTypes.Slice(7);\n                    var restType = \"Tuple(\" + string.Join(\", \", restElements.Select(c => c.ComplexTypeName)) + \")\";\n                    var restWriter = CreateParameterWriter<TRest>(typeInfo, restType, restElements, true);\n\n                    elementWriters.Add(new TupleItemParameterWriter<ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>, TRest>(restWriter, t => t.Rest));\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, elementWriters, isRest);\n                }\n            }\n\n            private sealed class KeyValuePairColumnFactory<TKey, TValue> : ITupleWriterFactory\n            {\n                public TupleColumnWriter CreateColumnWriter(string columnName, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, object untypedRows, ClickHouseColumnSettings? columnSettings)\n                {\n                    var rows = (IReadOnlyList<KeyValuePair<TKey, TValue>>)untypedRows;\n\n                    var columns = new List<IClickHouseColumnWriter>(2)\n                    {\n                        elementTypes[0].CreateColumnWriter(columnName, rows.Map(p => p.Key), columnSettings),\n                        elementTypes[1].CreateColumnWriter(columnName, rows.Map(p => p.Value), columnSettings)\n                    };\n\n                    return new TupleColumnWriter(columnName, columns, rows.Count);\n                }\n\n                public object CreateParameterWriter(TupleTypeInfo typeInfo, IReadOnlyList<IClickHouseColumnTypeInfo> elementTypes, bool isRest)\n                {\n                    var writers = new List<IClickHouseParameterWriter<KeyValuePair<TKey, TValue>>>(2);\n                    AddParameterWriter(writers, elementTypes[0], pair => pair.Key);\n                    AddParameterWriter(writers, elementTypes[1], pair => pair.Value);\n\n                    return TupleTypeInfo.CreateParameterWriter(typeInfo, writers, isRest);\n                }\n            }\n        }\n\n        private sealed class TupleItemParameterWriter<TTuple, TItem> : IClickHouseParameterWriter<TTuple>\n        {\n            private readonly IClickHouseParameterWriter<TItem> _itemWriter;\n            private readonly Func<TTuple, TItem> _getItem;\n\n            public TupleItemParameterWriter(IClickHouseParameterWriter<TItem> itemWriter, Func<TTuple, TItem> getItem)\n            {\n                _itemWriter = itemWriter;\n                _getItem = getItem;\n            }\n\n            public bool TryCreateParameterValueWriter(TTuple value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                return _itemWriter.TryCreateParameterValueWriter(_getItem(value), isNested, out valueWriter);\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, TTuple value)\n            {\n                return _itemWriter.Interpolate(queryBuilder, _getItem(value));\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                return _itemWriter.Interpolate(queryBuilder, typeInfoProvider, writeValue);\n            }\n        }\n\n        private sealed class TupleParameterWriter<T> : IClickHouseParameterWriter<T>\n        {\n            private readonly TupleTypeInfo _type;\n            private readonly IReadOnlyList<IClickHouseParameterWriter<T>> _itemWriters;\n            private readonly bool _isRest;\n\n            public TupleParameterWriter(TupleTypeInfo type, IReadOnlyList<IClickHouseParameterWriter<T>> itemWriters, bool isRest)\n            {\n                _type = type;\n                _itemWriters = itemWriters;\n                _isRest = isRest;\n            }\n\n            public bool TryCreateParameterValueWriter(T value, bool isNested, [NotNullWhen(true)] out IClickHouseParameterValueWriter? valueWriter)\n            {\n                valueWriter = null;\n                return false;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, T value)\n            {\n                if(!_isRest)\n                    queryBuilder.Append(\"tuple(\");\n\n                bool isFirst = true;\n                foreach(var itemWriter in _itemWriters)\n                {\n                    if (isFirst)\n                        isFirst = false;\n                    else\n                        queryBuilder.Append(',');\n\n                    itemWriter.Interpolate(queryBuilder, value);\n                }\n\n                if (!_isRest)\n                    queryBuilder.Append(')');\n\n                return queryBuilder;\n            }\n\n            public StringBuilder Interpolate(StringBuilder queryBuilder, IClickHouseTypeInfoProvider typeInfoProvider, Func<StringBuilder, IClickHouseColumnTypeInfo, Func<StringBuilder, Func<StringBuilder, StringBuilder>, StringBuilder>, StringBuilder> writeValue)\n            {\n                Debug.Assert(!_isRest);\n                return writeValue(queryBuilder, _type, FunctionHelper.Apply);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/UInt128TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class UInt128TypeInfo : BigIntegerTypeInfoBase\n    {\n        public UInt128TypeInfo() \n            : base(\"UInt128\", 128 / 8, true)\n        {\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.UInt128;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/UInt16TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class UInt16TableColumn : StructureTableColumn<ushort>\n    {\n        public UInt16TableColumn(ReadOnlyMemory<ushort> buffer)\n            : base(buffer)\n        {\n        }\n\n        public override IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(int))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<ushort, int>(this, v => v);\n            if (typeof(T) == typeof(uint))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<ushort, uint>(this, v => v);\n            if (typeof(T) == typeof(long))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<ushort, long>(this, v => v);\n            if (typeof(T) == typeof(ulong))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<ushort, ulong>(this, v => v);\n\n            if (typeof(T) == typeof(int?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<int>(null, new ReinterpretedTableColumn<ushort, int>(this, v => v));\n            if (typeof(T) == typeof(uint?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<uint>(null, new ReinterpretedTableColumn<ushort, uint>(this, v => v));\n            if (typeof(T) == typeof(long?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<long>(null, new ReinterpretedTableColumn<ushort, long>(this, v => v));\n            if (typeof(T) == typeof(ulong?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<ulong>(null, new ReinterpretedTableColumn<ushort, ulong>(this, v => v));\n\n            return base.TryReinterpret<T>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/UInt16TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class UInt16TypeInfo : SimpleTypeInfo\n    {\n        public UInt16TypeInfo()\n            : base(\"UInt16\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new UInt16Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(ushort), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            var type = typeof(T);\n            IReadOnlyList<ushort> ushortRows;\n            if (type == typeof(ushort))\n                ushortRows = (IReadOnlyList<ushort>)rows;\n            else if (type == typeof(byte))\n                ushortRows = MappedReadOnlyList<byte, ushort>.Map((IReadOnlyList<byte>)rows, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new UInt16Writer(columnName, ComplexTypeName, ushortRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer;\n            if (type == typeof(ushort))\n                writer = new SimpleParameterWriter<ushort>(this, appendTypeCast: true);\n            else if (type == typeof(byte))\n                writer = new SimpleParameterWriter<byte, ushort>(this, appendTypeCast: true, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(ushort);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.UInt16;\n        }\n\n        private sealed class UInt16Reader : StructureReaderBase<ushort>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public UInt16Reader(int rowCount)\n                : base(sizeof(ushort), rowCount)\n            {\n            }\n\n            protected override ushort ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToUInt16(source);\n            }\n\n            protected override IClickHouseTableColumn<ushort> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<ushort> buffer)\n            {\n                return new UInt16TableColumn(buffer);\n            }\n        }\n\n        private sealed class UInt16Writer : StructureWriterBase<ushort>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public UInt16Writer(string columnName, string columnType, IReadOnlyList<ushort> rows)\n                : base(columnName, columnType, sizeof(ushort), rows)\n            {\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in ushort value)\n            {\n                var success = BitConverter.TryWriteBytes(writeTo, value);\n                Debug.Assert(success);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/UInt256TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class UInt256TypeInfo : BigIntegerTypeInfoBase\n    {\n        public UInt256TypeInfo() \n            : base(\"UInt256\", 256 / 8, true)\n        {\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.UInt256;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/UInt32TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class UInt32TableColumn : StructureTableColumn<uint>\n    {\n        public UInt32TableColumn(ReadOnlyMemory<uint> buffer)\n            : base(buffer)\n        {\n        }\n\n        public override IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(long))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<uint, long>(this, v => v);\n            if (typeof(T) == typeof(ulong))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<uint, ulong>(this, v => v);\n\n            if (typeof(T) == typeof(long?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<long>(null, new ReinterpretedTableColumn<uint, long>(this, v => v));\n            if (typeof(T) == typeof(ulong?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<ulong>(null, new ReinterpretedTableColumn<uint, ulong>(this, v => v));\n\n            return base.TryReinterpret<T>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/UInt32TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class UInt32TypeInfo : SimpleTypeInfo\n    {\n        public UInt32TypeInfo()\n            : base(\"UInt32\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new UInt32Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(uint), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            var type = typeof(T);\n            IReadOnlyList<uint> uintRows;\n\n            if (type == typeof(uint))\n                uintRows = (IReadOnlyList<uint>)rows;\n            else if (type == typeof(ushort))\n                uintRows = MappedReadOnlyList<ushort, uint>.Map((IReadOnlyList<ushort>)rows, v => v);\n            else if (type == typeof(byte))\n                uintRows = MappedReadOnlyList<byte, uint>.Map((IReadOnlyList<byte>)rows, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n            \n            return new UInt32Writer(columnName, ComplexTypeName, uintRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer = default(T) switch\n            {\n                uint _ => new SimpleParameterWriter<uint>(this, appendTypeCast: true),\n                ushort _ => new SimpleParameterWriter<ushort, uint>(this, appendTypeCast: true, v => v),\n                byte _ => new SimpleParameterWriter<byte, uint>(this, appendTypeCast: true, v => v),\n                _ => new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\")\n            };\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(uint);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.UInt32;\n        }\n\n        private sealed class UInt32Reader : StructureReaderBase<uint>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public UInt32Reader(int rowCount)\n                : base(sizeof(uint), rowCount)\n            {\n            }\n\n            protected override uint ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToUInt32(source);\n            }\n\n            protected override IClickHouseTableColumn<uint> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<uint> buffer)\n            {\n                return new UInt32TableColumn(buffer);\n            }\n        }\n\n        private sealed class UInt32Writer : StructureWriterBase<uint>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public UInt32Writer(string columnName, string columnType, IReadOnlyList<uint> rows)\n                : base(columnName, columnType, sizeof(uint), rows)\n            {\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in uint value)\n            {\n                var success = BitConverter.TryWriteBytes(writeTo, value);\n                Debug.Assert(success);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/UInt64TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class UInt64TypeInfo : SimpleTypeInfo\n    {\n        public UInt64TypeInfo()\n            : base(\"UInt64\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new UInt64Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(ulong), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            var type = typeof(T);\n            IReadOnlyList<ulong> ulongRows;\n\n            if (type == typeof(ulong))\n                ulongRows = (IReadOnlyList<ulong>)rows;\n            else if (type == typeof(uint))\n                ulongRows = MappedReadOnlyList<uint, ulong>.Map((IReadOnlyList<uint>)rows, v => v);\n            else if (type == typeof(ushort))\n                ulongRows = MappedReadOnlyList<ushort, ulong>.Map((IReadOnlyList<ushort>)rows, v => v);\n            else if (type == typeof(byte))\n                ulongRows = MappedReadOnlyList<byte, ulong>.Map((IReadOnlyList<byte>)rows, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new UInt64Writer(columnName, ComplexTypeName, ulongRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer;\n            if (type == typeof(ulong))\n                writer = new SimpleParameterWriter<ulong>(this, appendTypeCast: true);\n            else if (type == typeof(uint))\n                writer = new SimpleParameterWriter<uint, ulong>(this, appendTypeCast: true, v => v);\n            else if (type == typeof(ushort))\n                writer = new SimpleParameterWriter<ushort, ulong>(this, appendTypeCast: true, v => v);\n            else if (type == typeof(byte))\n                writer = new SimpleParameterWriter<byte, ulong>(this, appendTypeCast: true, v => v);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(ulong);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.UInt64;\n        }\n\n        private sealed class UInt64Reader : StructureReaderBase<ulong>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public UInt64Reader(int rowCount)\n                : base(sizeof(ulong), rowCount)\n            {\n            }\n\n            protected override ulong ReadElement(ReadOnlySpan<byte> source)\n            {\n                return BitConverter.ToUInt64(source);\n            }\n        }\n\n        internal sealed class UInt64Writer : StructureWriterBase<ulong>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public UInt64Writer(string columnName, string columnType, IReadOnlyList<ulong> rows)\n                : base(columnName, columnType, sizeof(ulong), rows)\n            {\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in ulong value)\n            {\n                var success = BitConverter.TryWriteBytes(writeTo, value);\n                Debug.Assert(success);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/UInt8TableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class UInt8TableColumn : StructureTableColumn<byte>\n    {\n        public UInt8TableColumn(ReadOnlyMemory<byte> buffer)\n            : base(buffer)\n        {\n        }\n\n        public override IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            if (typeof(T) == typeof(short))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<byte, short>(this, v => v);\n            if (typeof(T) == typeof(ushort))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<byte, ushort>(this, v => v);\n            if (typeof(T) == typeof(int))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<byte, int>(this, v => v);\n            if (typeof(T) == typeof(uint))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<byte, uint>(this, v => v);\n            if (typeof(T) == typeof(long))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<byte, long>(this, v => v);\n            if (typeof(T) == typeof(ulong))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<byte, ulong>(this, v => v);\n            if (typeof(T) == typeof(bool))\n                return (IClickHouseTableColumn<T>) (object) new ReinterpretedTableColumn<byte, bool>(this, v => v != 0);\n\n            if (typeof(T) == typeof(short?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<short>(null, new ReinterpretedTableColumn<byte, short>(this, v => v));\n            if (typeof(T) == typeof(ushort?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<ushort>(null, new ReinterpretedTableColumn<byte, ushort>(this, v => v));\n            if (typeof(T) == typeof(int?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<int>(null, new ReinterpretedTableColumn<byte, int>(this, v => v));\n            if (typeof(T) == typeof(uint?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<uint>(null, new ReinterpretedTableColumn<byte, uint>(this, v => v));\n            if (typeof(T) == typeof(long?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<long>(null, new ReinterpretedTableColumn<byte, long>(this, v => v));\n            if (typeof(T) == typeof(ulong?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<ulong>(null, new ReinterpretedTableColumn<byte, ulong>(this, v => v));\n            if (typeof(T) == typeof(bool?))\n                return (IClickHouseTableColumn<T>) (object) new NullableStructTableColumn<bool>(null, new ReinterpretedTableColumn<byte, bool>(this, v => v != 0));\n\n            return base.TryReinterpret<T>();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/UInt8TypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class UInt8TypeInfo : SimpleTypeInfo\n    {\n        public UInt8TypeInfo()\n            : base(\"UInt8\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new UInt8Reader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(sizeof(byte), rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            IReadOnlyList<byte> byteRows;\n            if (typeof(T) == typeof(byte))\n                byteRows = (IReadOnlyList<byte>) rows;\n            else if (typeof(T) == typeof(bool))\n                byteRows = MappedReadOnlyList<bool, byte>.Map((IReadOnlyList<bool>) rows, v => v ? (byte) 1 : (byte) 0);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new UInt8Writer(columnName, ComplexTypeName, byteRows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            object writer;\n            if (type == typeof(byte))\n                writer = new SimpleParameterWriter<byte>(this);\n            else if (type == typeof(bool))\n                writer = new SimpleParameterWriter<bool, byte>(this, b => b ? (byte)1 : (byte)0);\n            else\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return (IClickHouseParameterWriter<T>)writer;\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(byte);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Byte;\n        }\n\n        private sealed class UInt8Reader : StructureReaderBase<byte>\n        {\n            protected override bool BitwiseCopyAllowed => true;\n\n            public UInt8Reader(int rowCount)\n                : base(sizeof(byte), rowCount)\n            {\n            }\n\n            protected override byte ReadElement(ReadOnlySpan<byte> source)\n            {\n                return source[0];\n            }\n\n            protected override IClickHouseTableColumn<byte> EndRead(ClickHouseColumnSettings? settings, ReadOnlyMemory<byte> buffer)\n            {\n                return new UInt8TableColumn(buffer);\n            }\n        }\n\n        private sealed class UInt8Writer : IClickHouseColumnWriter\n        {\n            private readonly IReadOnlyList<byte> _rows;\n\n            private int _position;\n\n            public string ColumnName { get; }\n\n            public string ColumnType { get; }\n\n            public UInt8Writer(string columnName, string columnType, IReadOnlyList<byte> rows)\n            {\n                _rows = rows;\n                ColumnName = columnName;\n                ColumnType = columnType;\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                var size = Math.Min(writeTo.Length, _rows.Count - _position);\n\n                for (int i = 0; i < size; i++, _position++)\n                    writeTo[i] = _rows[_position];\n\n                return new SequenceSize(size, size);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/UuidTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal sealed class UuidTypeInfo : SimpleTypeInfo\n    {\n        private const int UuidSize = 16;\n\n        public UuidTypeInfo()\n            : base(\"UUID\")\n        {\n        }\n\n        public override IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            return new UuidReader(rowCount);\n        }\n\n        public override IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            return new SimpleSkippingColumnReader(UuidSize, rowCount);\n        }\n\n        public override IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (typeof(T) != typeof(Guid))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{typeof(T)}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n\n            return new UuidWriter(columnName, ComplexTypeName, (IReadOnlyList<Guid>)rows);\n        }\n\n        public override IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            var type = typeof(T);\n            if (type == typeof(DBNull))\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The ClickHouse type \\\"{ComplexTypeName}\\\" does not allow null values.\");\n\n            if (type == typeof(Guid))\n                return (IClickHouseParameterWriter<T>)(object)new StringParameterWriter<Guid>(this, uuidValue => uuidValue.ToString().AsMemory());\n\n            throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, $\"The type \\\"{type}\\\" can't be converted to the ClickHouse type \\\"{ComplexTypeName}\\\".\");\n        }\n\n        public override Type GetFieldType()\n        {\n            return typeof(Guid);\n        }\n\n        public override ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Guid;\n        }\n\n        private sealed class UuidReader : StructureReaderBase<Guid>\n        {\n            public UuidReader(int rowCount)\n                : base(UuidSize, rowCount)\n            {\n            }\n\n            protected override Guid ReadElement(ReadOnlySpan<byte> source)\n            {\n                ushort c = BitConverter.ToUInt16(source.Slice(0));\n                ushort b = BitConverter.ToUInt16(source.Slice(2));\n                uint a = BitConverter.ToUInt32(source.Slice(4));\n                \n                return new Guid(a, b, c, source[15], source[14], source[13], source[12], source[11], source[10], source[9], source[8]);\n            }\n        }\n\n        private sealed class UuidWriter:StructureWriterBase<Guid>\n        {\n            public UuidWriter(string columnName, string columnType, IReadOnlyList<Guid> rows)\n                : base(columnName, columnType, UuidSize, rows)\n            {\n            }\n\n            protected override void WriteElement(Span<byte> writeTo, in Guid value)\n            {\n                var success = value.TryWriteBytes(writeTo);\n                Debug.Assert(success);\n\n                var tmp = writeTo[0];\n                writeTo[0] = writeTo[6];\n                writeTo[6] = writeTo[2];\n                writeTo[2] = writeTo[4];\n                writeTo[4] = tmp;\n\n                tmp = writeTo[1];\n                writeTo[1] = writeTo[7];\n                writeTo[7] = writeTo[3];\n                writeTo[3] = writeTo[5];\n                writeTo[5] = tmp;\n\n                for (int i = 0; i < 4; i++)\n                {\n                    tmp = writeTo[8 + i];\n                    writeTo[8 + i] = writeTo[15 - i];\n                    writeTo[15 - i] = tmp;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/VariantTableColumn.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal class VariantTableColumn : IClickHouseTableColumn\n    {\n        private readonly List<IClickHouseTableColumn> _values;\n        private readonly byte[] _columnIndices;\n        private readonly int[] _valueIndices;\n\n        public int RowCount => _columnIndices.Length;\n\n        public VariantTableColumn(List<IClickHouseTableColumn> values, byte[] columnIndices, int[] valueIndices)\n        {\n            Debug.Assert(columnIndices.Length == valueIndices.Length);\n\n            _values = values;\n            _columnIndices = columnIndices;\n            _valueIndices = valueIndices;\n        }\n\n        public object GetValue(int index)\n        {\n            var columnIndex = _columnIndices[index];\n            // 0xFF stands for NULL\n            if (columnIndex == 0xFF)\n                return DBNull.Value;\n\n            return _values[columnIndex].GetValue(_valueIndices[index]);\n        }\n\n        public bool IsNull(int index)\n        {\n            return _columnIndices[index] == 0xFF;\n        }\n\n        public bool TryDipatch<T>(IClickHouseTableColumnDispatcher<T> dispatcher, [MaybeNullWhen(false)] out T dispatchedValue)\n        {\n            dispatchedValue = default;\n            return false;\n        }\n\n        public IClickHouseTableColumn<T>? TryReinterpret<T>()\n        {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Types/VariantTypeInfo.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Types\n{\n    internal class VariantTypeInfo : IClickHouseColumnTypeInfo\n    {\n        private readonly List<IClickHouseColumnTypeInfo>? _types;\n\n        public string ComplexTypeName { get; }\n\n        public string TypeName => \"Variant\";\n\n        public int GenericArgumentsCount => _types?.Count ?? 0;\n\n        public VariantTypeInfo()\n        {\n            ComplexTypeName = TypeName;\n        }\n\n        private VariantTypeInfo(string complexTypeName, List<IClickHouseColumnTypeInfo> types)\n        {\n            ComplexTypeName = complexTypeName;\n            _types = types;\n        }\n\n        public IClickHouseColumnReader CreateColumnReader(int rowCount)\n        {\n            if (_types == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return new VariantColumnReader(rowCount, this);\n        }\n\n        IClickHouseColumnReader IClickHouseColumnTypeInfo.CreateColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode != ClickHouseColumnSerializationMode.Default)\n                throw new NotSupportedException($\"Custom serialization for \\\"{ComplexTypeName}\\\" is not supported by ClickHouseClient.\");\n\n            return CreateColumnReader(rowCount);\n        }\n\n        public IClickHouseColumnReaderBase CreateSkippingColumnReader(int rowCount)\n        {\n            if (_types == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return new VariantSkippingColumnReader(rowCount, this);\n        }\n\n        IClickHouseColumnReaderBase IClickHouseColumnTypeInfo.CreateSkippingColumnReader(int rowCount, ClickHouseColumnSerializationMode serializationMode)\n        {\n            if (serializationMode != ClickHouseColumnSerializationMode.Default)\n                throw new NotSupportedException($\"Custom serialization for \\\"{ComplexTypeName}\\\" is not supported by ClickHouseClient.\");\n\n            return CreateSkippingColumnReader(rowCount);\n        }\n\n        public IClickHouseColumnWriter CreateColumnWriter<T>(string columnName, IReadOnlyList<T> rows, ClickHouseColumnSettings? columnSettings)\n        {\n            if (_types == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            var valueHandlers = new List<IValueHandler>(_types.Count);\n            var nonGenericHandlerType = typeof(ValueHandler<>);\n            foreach (var type in _types)\n            {\n                var clrType = type.GetFieldType();\n                var handler = (IValueHandler?)Activator.CreateInstance(nonGenericHandlerType.MakeGenericType(clrType));\n                Debug.Assert(handler != null);\n                valueHandlers.Add(handler);\n            }\n\n            var indices = new byte[rows.Count];\n            int count = 0;\n            foreach (var value in rows)\n            {\n                if (value == null || value is DBNull)\n                {\n                    indices[count++] = 0xFF;\n                    continue;\n                }\n\n                bool handled = false;\n                for (int i = 0; i < valueHandlers.Count; i++)\n                {\n                    if (valueHandlers[i].TryHandle(value))\n                    {\n                        indices[count++] = checked((byte)i);\n                        handled = true;\n                        break;\n                    }\n                }\n\n                if (!handled)\n                {\n                    var allowedTypeList = string.Join(\", \", _types.Select(t => $\"\\\"{t.GetFieldType().Name}\\\"\"));\n                    throw new ClickHouseException(\n                        ClickHouseErrorCodes.TypeNotSupported,\n                        $\"Column \\\"{columnName}\\\". A value of type \\\"{value.GetType().Name}\\\" can't be written to the column of type \\\"{ComplexTypeName}\\\". Allowed types are: {allowedTypeList}.\");\n                }\n            }\n\n            Debug.Assert(count == indices.Length);\n            var rowCounts = new List<int>(valueHandlers.Count);\n            var writers = new List<IClickHouseColumnWriter>(valueHandlers.Count);\n            for (int i = 0; i < valueHandlers.Count; i++)\n            {\n                IValueHandler? handler = valueHandlers[i];\n                rowCounts.Add(handler.RowCount);\n                writers.Add(handler.CreateColumnWriter(columnName, _types[i], columnSettings));\n            }\n\n            return new VariantColumnWriter(columnName, ComplexTypeName, indices, rowCounts, writers);\n        }\n\n        public IClickHouseParameterWriter<T> CreateParameterWriter<T>()\n        {\n            throw new NotSupportedException($\"Parameters of type \\\"{ComplexTypeName}\\\" are not supported.\");\n        }\n\n        public ClickHouseDbType GetDbType()\n        {\n            return ClickHouseDbType.Variant;\n        }\n\n        public IClickHouseColumnTypeInfo GetDetailedTypeInfo(List<ReadOnlyMemory<char>> options, IClickHouseTypeInfoProvider typeInfoProvider)\n        {\n            if (_types != null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotSupported, \"The type is already fully specified.\");\n\n            var types = new List<IClickHouseColumnTypeInfo>(options.Count);\n            var typeNameBuilder = new StringBuilder(TypeName).Append('(');\n            bool isFirst = true;\n            foreach(var option in options)\n            {\n                if (isFirst)\n                    isFirst = false;\n                else\n                    typeNameBuilder.Append(\", \");\n\n                var type = typeInfoProvider.GetTypeInfo(option);\n                types.Add(type);\n                typeNameBuilder.Append(type.ComplexTypeName);\n            }\n\n            var complexTypeName = typeNameBuilder.Append(')').ToString();\n            return new VariantTypeInfo(complexTypeName, types);\n        }\n\n        public Type GetFieldType()\n        {\n            return typeof(object);\n        }\n\n        public IClickHouseTypeInfo GetGenericArgument(int index)\n        {\n            if (_types == null)\n                throw new ClickHouseException(ClickHouseErrorCodes.TypeNotFullySpecified, $\"The type \\\"{ComplexTypeName}\\\" is not fully specified.\");\n\n            return _types[index];\n        }\n\n        private abstract class VariantColumnReaderBase<TReader>\n            where TReader:IClickHouseColumnReaderBase\n        {\n            private readonly List<byte[]> _prefixes;\n\n            private IClickHouseColumnReaderBase? _prefixReader;\n            private int _readerPosition;\n            private int _rowPosition;\n\n            protected readonly int RowCount;\n            protected readonly VariantTypeInfo TypeInfo;\n            protected readonly List<TReader> Readers;\n\n            public VariantColumnReaderBase(int rowCount, VariantTypeInfo typeInfo)\n            {\n                Debug.Assert(typeInfo._types != null);\n\n                Readers = new List<TReader>(typeInfo._types.Count);\n                _prefixes = new List<byte[]>(typeInfo._types.Count);\n                RowCount = rowCount;\n                TypeInfo = typeInfo;\n            }\n\n            public SequenceSize ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                var types = TypeInfo._types;\n                Debug.Assert(types != null);\n                var totalBytes = 0;\n                for (int i = _prefixes.Count; i < types.Count; i++)\n                {\n                    var slice = sequence.Slice(totalBytes);\n                    var reader = _prefixReader ?? types[i].CreateSkippingColumnReader(0);\n                    var prefixSize = reader.ReadPrefix(slice);\n                    if (prefixSize.Elements == 0)\n                    {\n                        _prefixReader = reader;\n                        return new SequenceSize(totalBytes, 0);\n                    }\n\n                    _prefixReader = null;\n                    totalBytes += prefixSize.Bytes;\n                    if (prefixSize.Bytes > 0)\n                    {\n                        var prefix = new byte[prefixSize.Bytes];\n                        slice.Slice(0, prefix.Length).CopyTo(prefix);\n                        _prefixes.Add(prefix);\n                    }\n                    else\n                    {\n                        _prefixes.Add(Array.Empty<byte>());\n                    }\n                }\n\n                return new SequenceSize(totalBytes, 1);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence, int[] rowCounts)\n            {\n                var seq = sequence;\n                int totalBytes = 0;\n                var totalElements = 0;\n                Debug.Assert(TypeInfo._types != null);\n                for (; _readerPosition < rowCounts.Length; _readerPosition++)\n                {\n                    TReader reader;\n                    if (Readers.Count == _readerPosition)\n                    {\n                        reader = CreateReader(TypeInfo._types[_readerPosition], rowCounts[_readerPosition]);\n\n                        var prefixBytes = _prefixes[_readerPosition];\n                        if (prefixBytes.Length > 0)\n                        {\n                            var prefixSize = reader.ReadPrefix(new ReadOnlySequence<byte>(prefixBytes));\n\n                            if (prefixSize.Bytes == 0 && prefixSize.Elements == 0)\n                                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, \"Internal error. Failed to read the column prefix.\");\n\n                            if (prefixSize.Bytes != prefixBytes.Length)\n                                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. The column prefix' size is {prefixBytes.Length}, but the number of consumed bytes is {prefixSize.Bytes}.\");\n\n                            if (prefixSize.Elements != 1)\n                                throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $\"Internal error. Received an unexpected number of column prefixes: {prefixSize.Elements}.\");\n                        }\n\n                        Readers.Add(reader);\n                    }\n                    else\n                    {\n                        reader = Readers[^1];\n                    }\n\n                    SequenceSize size;\n                    if (rowCounts[_readerPosition] == 0)\n                        size = SequenceSize.Empty;\n                    else\n                        size = reader.ReadNext(seq);\n\n                    totalBytes += size.Bytes;\n                    _rowPosition += size.Elements;\n\n                    if (_readerPosition == rowCounts.Length - 1)\n                    {\n                        totalElements += size.Elements;\n                        if (_rowPosition == rowCounts[_readerPosition])\n                            totalElements += RowCount - rowCounts[_readerPosition];\n                    }\n\n                    if (_rowPosition < rowCounts[_readerPosition])\n                        break;\n\n                    _rowPosition = 0;\n                    seq = seq.Slice(size.Bytes);\n                }\n\n                return new SequenceSize(totalBytes, totalElements);\n            }\n\n            protected abstract TReader CreateReader(IClickHouseColumnTypeInfo typeInfo, int rowCount);\n        }\n\n        private sealed class VariantColumnReader : VariantColumnReaderBase<IClickHouseColumnReader>, IClickHouseColumnReader\n        {\n            private readonly byte[] _typeIndices;\n            private readonly int[] _elementIndices;\n            private readonly int[] _rowCounts;\n\n            private int _indexPosition;\n\n            public VariantColumnReader(int rowCount, VariantTypeInfo typeInfo)\n                :base(rowCount, typeInfo)\n            {\n                Debug.Assert(typeInfo._types != null);\n\n                _typeIndices = rowCount == 0 ? Array.Empty<byte>() : new byte[rowCount];\n                _elementIndices = new int[rowCount];\n                _rowCounts = new int[typeInfo._types.Count];\n            }\n\n            SequenceSize IClickHouseColumnReaderBase.ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                return ReadPrefix(sequence);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                int totalBytes = 0;\n                var seq = sequence;\n                if (_indexPosition < _typeIndices.Length)\n                {\n                    var length = Math.Min(_typeIndices.Length - _indexPosition, (int)seq.Length);\n                    seq.Slice(0, length).CopyTo(((Span<byte>)_typeIndices).Slice(_indexPosition));\n\n                    totalBytes += length;\n\n                    for (int i = 0; i < length; i++, _indexPosition++)\n                    {\n                        var typeIndex = _typeIndices[_indexPosition];\n                        // 0xFF stands for NULL\n                        if (typeIndex != 0xFF)\n                            _elementIndices[_indexPosition] = _rowCounts[typeIndex]++;\n                    }\n\n                    if (_indexPosition < _typeIndices.Length)\n                        return new SequenceSize(totalBytes, 0);\n\n                    seq = seq.Slice(length);\n                }\n\n                var result = ReadNext(seq, _rowCounts);\n                result = result.AddBytes(totalBytes);\n                return result;\n            }\n\n            protected override IClickHouseColumnReader CreateReader(IClickHouseColumnTypeInfo typeInfo, int rowCount)\n            {\n                return typeInfo.CreateColumnReader(rowCount);\n            }\n\n            public IClickHouseTableColumn EndRead(ClickHouseColumnSettings? settings)\n            {\n                if (_typeIndices.Length == 0)\n                    return new StructureTableColumn<int>(ReadOnlyMemory<int>.Empty);\n\n                Debug.Assert(TypeInfo._types != null);\n                var columns = new List<IClickHouseTableColumn>(_rowCounts.Length);\n                for (int i = 0; i < TypeInfo._types.Count; i++)\n                {\n                    if (i < Readers.Count)\n                    {\n                        columns.Add(Readers[i].EndRead(settings));\n                    }\n                    else\n                    {\n                        var reader = TypeInfo._types[i].CreateColumnReader(0);\n                        columns.Add(reader.EndRead(settings));\n                    }\n                }\n\n                return new VariantTableColumn(columns, _typeIndices, _elementIndices);\n            }\n        }\n\n        private sealed class VariantSkippingColumnReader : VariantColumnReaderBase<IClickHouseColumnReaderBase>, IClickHouseColumnReaderBase\n        {\n            private readonly int[] _rowCounts;\n\n            private int _indexPosition;\n\n            public VariantSkippingColumnReader(int rowCount, VariantTypeInfo typeInfo)\n                : base(rowCount, typeInfo)\n            {\n                Debug.Assert(typeInfo._types != null);\n                _rowCounts = new int[typeInfo._types.Count];\n            }\n\n            SequenceSize IClickHouseColumnReaderBase.ReadPrefix(ReadOnlySequence<byte> sequence)\n            {\n                return ReadPrefix(sequence);\n            }\n\n            public SequenceSize ReadNext(ReadOnlySequence<byte> sequence)\n            {\n                int totalBytes = 0;\n                var seq = sequence;\n                if (_indexPosition < RowCount)\n                {\n                    var length = Math.Min(RowCount - _indexPosition, (int)seq.Length);\n                    var typeIndices = new byte[length];\n                    seq.Slice(0, length).CopyTo(typeIndices);\n\n                    totalBytes += length;\n\n                    for (int i = 0; i < length; i++, _indexPosition++)\n                    {\n                        // 0xFF stands for NULL\n                        if (typeIndices[i] != 0xFF)\n                            ++_rowCounts[typeIndices[i]];\n                    }\n\n                    if (_indexPosition < RowCount)\n                        return new SequenceSize(totalBytes, 0);\n\n                    seq = seq.Slice(length);\n                }\n\n                var result = ReadNext(seq, _rowCounts);\n                result = result.AddBytes(totalBytes);\n                return result;\n            }\n\n            protected override IClickHouseColumnReaderBase CreateReader(IClickHouseColumnTypeInfo typeInfo, int rowCount)\n            {\n                return typeInfo.CreateSkippingColumnReader(rowCount);\n            }\n        }\n\n        private interface IValueHandler\n        {\n            int RowCount { get; }\n\n            bool TryHandle(object value);\n\n            IClickHouseColumnWriter CreateColumnWriter(string columnName, IClickHouseColumnTypeInfo typeInfo, ClickHouseColumnSettings? columnSettings);\n        }\n\n        private sealed class ValueHandler<T> : IValueHandler\n        {\n            private readonly List<T> _rows = new List<T>();\n\n            public int RowCount => _rows.Count;\n\n            public IClickHouseColumnWriter CreateColumnWriter(string columnName, IClickHouseColumnTypeInfo typeInfo, ClickHouseColumnSettings? columnSettings)\n            {\n                return typeInfo.CreateColumnWriter(columnName + \".\" + typeInfo.ComplexTypeName, _rows, columnSettings);\n            }\n\n            public bool TryHandle(object value)\n            {\n                if(value is T typedValue)\n                {\n                    _rows.Add(typedValue);\n                    return true;\n                }\n\n                return false;\n            }\n        }\n\n        private sealed class VariantColumnWriter : IClickHouseColumnWriter\n        {\n            private readonly byte[] _indices;\n            private readonly List<int> _rowCounts;\n            private readonly List<IClickHouseColumnWriter> _columnWriters;\n\n            private int _prefixPosition;\n            private int _indexPosition;\n            private int _columnPosition;\n            private int _rowPosition;\n\n            public string ColumnName { get; }\n\n            public string ColumnType { get; }\n\n            public VariantColumnWriter(string columnName, string columnType, byte[] indices, List<int> rowCounts, List<IClickHouseColumnWriter> columnWriters)\n            {\n                ColumnName = columnName;\n                ColumnType = columnType;\n                _indices = indices;\n                _rowCounts = rowCounts;\n                _columnWriters = columnWriters;\n            }\n\n            SequenceSize IClickHouseColumnWriter.WritePrefix(Span<byte> writeTo)\n            {\n                var totalBytes = 0;\n                for (; _prefixPosition < _columnWriters.Count; _prefixPosition++)\n                {\n                    var prefixSize = _columnWriters[_prefixPosition].WritePrefix(writeTo.Slice(totalBytes));\n                    totalBytes += prefixSize.Bytes;\n                    if (prefixSize.Elements == 0)\n                        return new SequenceSize(totalBytes, 0);\n                }\n\n                return new SequenceSize(totalBytes, 1);\n            }\n\n            public SequenceSize WriteNext(Span<byte> writeTo)\n            {\n                var totalBytes = 0;\n                var span = writeTo;\n                if (_indexPosition < _indices.Length)\n                {\n                    var length = Math.Min(writeTo.Length, _indices.Length - _indexPosition);\n                    ((ReadOnlySpan<byte>)_indices).Slice(_indexPosition, length).CopyTo(span.Slice(0, length));\n                    totalBytes += length;\n                    _indexPosition += length;\n\n                    if (_indexPosition < _indices.Length)\n                        return new SequenceSize(totalBytes, 0);\n\n                    span = span.Slice(length);\n                }\n\n                var totalElements = 0;\n                for (; _columnPosition < _columnWriters.Count; _columnPosition++)\n                {\n                    SequenceSize size;\n                    if (_rowCounts[_columnPosition] == 0)\n                        size = SequenceSize.Empty;\n                    else\n                        size = _columnWriters[_columnPosition].WriteNext(span);\n\n                    _rowPosition += size.Elements;\n                    totalBytes += size.Bytes;\n\n                    if (_columnPosition == _columnWriters.Count - 1)\n                    {\n                        totalElements += size.Elements;\n                        if (_rowPosition == _rowCounts[_columnPosition])\n                            totalElements += _indices.Length - _rowPosition;\n                    }\n\n                    if (_rowPosition < _rowCounts[_columnPosition])\n                        break;\n\n                    _rowPosition = 0;\n                    span = span.Slice(size.Bytes);\n                }\n\n                return new SequenceSize(totalBytes, totalElements);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/CertificateHelper.Net5.0.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if NET5_0_OR_GREATER\n\nusing System.Runtime.CompilerServices;\nusing System.Security.Cryptography.X509Certificates;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    partial class CertificateHelper\n    {\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        static partial void ImportPemCertificates(string filePath, X509Certificate2Collection collection)\n        {\n            collection.ImportFromPemFile(filePath);\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/CertificateHelper.NetCoreApp3.1.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if !NET5_0_OR_GREATER\n\nusing System.IO;\nusing System.Security.Cryptography.X509Certificates;\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    partial class CertificateHelper\n    {\n        static partial void ImportPemCertificates(string filePath, X509Certificate2Collection collection)\n        {\n            /* PEM file without private keys looks like a bunch of concatenated CRT files\n             * \n             * -----BEGIN CERTIFICATE-----\n             * Base64-encoded Certificate 1\n             * -----END CERTIFICATE-----\n             * ...\n             * -----BEGIN CERTIFICATE-----\n             * Base64-encoded Certificate N\n             * -----END CERTIFICATE-----\n             */\n\n            using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);\n            using var streamReader = new StreamReader(fs);\n\n            var sb = new StringBuilder();\n            string? entity = null;\n\n            int lineNumber = 0;\n            const string delimiter = \"-----\";\n            const string beginPrefix = delimiter + \"BEGIN \";\n            const string endPrefix = delimiter + \"END \";\n            while (!streamReader.EndOfStream)\n            {\n                ++lineNumber;\n                var line = streamReader.ReadLine();\n                if (string.IsNullOrWhiteSpace(line))\n                    continue;\n\n                if (entity == null)\n                {\n                    if (!line.StartsWith(beginPrefix))\n                    {\n                        var actualValue = line.Length <= beginPrefix.Length ? line : line.Substring(0, beginPrefix.Length);\n                        throw new InvalidDataException($\"Unexpected value '{actualValue}' at the line {lineNumber}, position {0} of the certificate file. Expected value is '{beginPrefix}'.\");\n                    }\n                    else if (!line.EndsWith(delimiter))\n                    {\n                        var actualValue = line.Substring(line.Length - delimiter.Length);\n                        throw new InvalidDataException($\"Unexpected value '{actualValue}' at the line {lineNumber}, position {line.Length - delimiter.Length} of the certificate file. Expected value is '{delimiter}'.\");\n                    }\n\n                    entity = line.Substring(beginPrefix.Length, line.Length - beginPrefix.Length - delimiter.Length);\n                    if (string.IsNullOrWhiteSpace(entity))\n                    {\n                        throw new InvalidDataException($\"Missing non-empty value between '{beginPrefix}' and '{delimiter}' at the line {lineNumber}, position {beginPrefix.Length} of the certificate file.\");\n                    }\n\n                    sb.AppendLine(line);\n                }\n                else if (!line.StartsWith(endPrefix))\n                {\n                    sb.AppendLine(line);\n                }\n                else if (!line.EndsWith(delimiter))\n                {\n                    var actualValue = line.Substring(line.Length - delimiter.Length);\n                    throw new InvalidDataException($\"Unexpected value '{actualValue}' at the line {lineNumber}, position {line.Length - delimiter.Length} of the certificate file. Expected value is '{delimiter}'.\");\n                }\n                else\n                {\n                    var endEntity = line.Substring(endPrefix.Length, line.Length - endPrefix.Length - delimiter.Length);\n                    if (entity != endEntity)\n                    {\n                        throw new InvalidDataException($\"The END value '{endEntity}' at the line {lineNumber}, position {beginPrefix.Length} of the certificate file is not equal to the BEGIN value '{entity}'.\");\n                    }\n\n                    if (entity == \"CERTIFICATE\")\n                    {\n                        sb.AppendLine(line);\n                        collection.Import(Encoding.UTF8.GetBytes(sb.ToString()));\n                    }\n\n                    sb.Clear();\n                    entity = null;\n                }\n            }\n\n            if (entity != null)\n                throw new InvalidDataException($\"Unexpected end of the certificate file. Missing '{endPrefix}{entity}{delimiter}'.\");\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/CertificateHelper.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.IO;\nusing System.Security.Cryptography.X509Certificates;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal static partial class CertificateHelper\n    {\n        public static X509Certificate2Collection LoadFromFile(string filePath)\n        {\n            var collection = new X509Certificate2Collection();\n            switch (Path.GetExtension(filePath).ToLowerInvariant())\n            {\n                case \".pem\":\n                case \".crt\":\n                    ImportPemCertificates(filePath, collection);\n                    break;\n                default:\n                    collection.Import(filePath);\n                    break;\n            }\n\n            return collection;\n        }\n\n        static partial void ImportPemCertificates(string filePath, X509Certificate2Collection collection);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/CommonUtils.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.ObjectModel;\nusing Octonica.ClickHouseClient.Protocol;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal static class CommonUtils\n    {\n        internal static int GetColumnIndex(ReadOnlyCollection<ColumnInfo> columns, string name)\n        {\n            int? caseInsensitiveIdx = null;\n            for (int i = 0; i < columns.Count; i++)\n            {\n                if (string.Equals(columns[i].Name, name, StringComparison.Ordinal))\n                    return i;\n\n                if (string.Equals(columns[i].Name, name, StringComparison.OrdinalIgnoreCase))\n                {\n                    if (caseInsensitiveIdx == null)\n                        caseInsensitiveIdx = i;\n                    else\n                        caseInsensitiveIdx = -1;\n                }\n            }\n\n            if (caseInsensitiveIdx >= 0)\n                return caseInsensitiveIdx.Value;\n\n            if (caseInsensitiveIdx == null)\n                throw new IndexOutOfRangeException($\"There is no column with the name \\\"{name}\\\" in the table.\");\n\n            throw new IndexOutOfRangeException($\"There are two or more columns with the name \\\"{name}\\\" in the table. Please, provide an exact name (case-sensitive) of the column.\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/ConstantReadOnlyList.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal class ConstantReadOnlyList<T> : IReadOnlyListExt<T>\n    {\n        [AllowNull]\n        private readonly T _value;\n\n        public int Count { get; }\n\n        public ConstantReadOnlyList([AllowNull] T value, int count)\n        {\n            if (count < 0)\n                throw new ArgumentOutOfRangeException(nameof(count));\n\n            Count = count;\n            _value = value;\n        }\n\n        public IEnumerator<T> GetEnumerator()\n        {\n            return Enumerable.Repeat(_value, Count).GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        public IReadOnlyListExt<T> Slice(int start, int length)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n            if (length < 0 || start + length > Count)\n                throw new ArgumentOutOfRangeException(nameof(length));\n\n            return new ConstantReadOnlyList<T>(_value, length);\n        }\n\n        public IReadOnlyListExt<TOut> Map<TOut>(Func<T, TOut> map)\n        {\n            if (map == null)\n                throw new ArgumentNullException(nameof(map));\n\n            return new ConstantReadOnlyList<TOut>(map(_value), Count);            \n        }\n\n        public int CopyTo(Span<T> span, int offset)\n        {\n            if (offset < 0 || offset > Count)\n                throw new ArgumentOutOfRangeException(nameof(offset));\n\n            var length = Math.Min(span.Length, Count - offset);\n            span.Slice(0, length).Fill(_value);\n            return length;\n        }\n\n        public T this[int index]\n        {\n            get\n            {\n                if (index >= 0 && index < Count)\n                    return _value;\n\n                throw new IndexOutOfRangeException();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/FunctionHelper.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal static class FunctionHelper\n    {\n        public static TOut Apply<TIn, TOut>(TIn input, Func<TIn, TOut> func) => func(input);\n\n        public static Func<T1, T3> Combine<T1, T2, T3>(Func<T1, T2> func1, Func<T2, T3> func2) => v => func2(func1(v));\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/ICollectionList.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal interface ICollectionList<out T>\n    {\n        int Count { get; }\n        T this[int listIndex, int index] { get; }\n        IEnumerable<T> GetItems();\n        IEnumerable<int> GetListLengths();\n        int GetLength(int listIndex);\n    }\n}"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/IConverter.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2023 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nnamespace Octonica.ClickHouseClient.Utils\r\n{\r\n    internal interface IConverter<in TIn, out TOut>\r\n    {\r\n        TOut Convert(TIn value);\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/IConverterDispatcher.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2026 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nusing System;\r\n\r\nnamespace Octonica.ClickHouseClient.Utils\r\n{\r\n    /// <summary>\r\n    /// The interface for an object providing a type converter callback function.\r\n    /// </summary>\r\n    public interface IConverterDispatcher\r\n    {\r\n        /// <summary>\r\n        /// When implemented, the method recieves the source type and an instance of an object for configuring with a callback function.\r\n        /// </summary>\r\n        /// <typeparam name=\"TFrom\">The source type for a type converter callback function.</typeparam>\r\n        /// <typeparam name=\"T\">The type of a configurable object.</typeparam>\r\n        /// <param name=\"converterDispatcher\">The dispatcher which recieves a type converter callback function and returns a configured object.</param>\r\n        /// <returns>The object configured with a type converter callback function.</returns>\r\n        T Dispatch<TFrom, T>(IConverterDispatcher<T> converterDispatcher);\r\n    }\r\n\r\n    /// <summary>\r\n    /// The interface for an object receiving a type converter callback function.\r\n    /// </summary>\r\n    public interface IConverterDispatcher<out T>\r\n    {\r\n        /// <summary>\r\n        /// The method configures an object with a type converter callback function.\r\n        /// </summary>\r\n        /// <typeparam name=\"TFrom\">The source type of the type converter callback function.</typeparam>\r\n        /// <typeparam name=\"TTo\">The result type of the type converter callback function.</typeparam>\r\n        /// <param name=\"convert\">The type converter callback function</param>\r\n        /// <returns>The object configured with the type converter callback function.</returns>\r\n        /// <remarks>\r\n        /// If <typeparamref name=\"TFrom\"/> and <typeparamref name=\"TTo\"/> are the same type and the converter function is an identity function, call the method <see cref=\"DispatchNoConvert\"/> instead of this method.\r\n        /// </remarks>\r\n        T Dispatch<TFrom, TTo>(Func<TFrom, TTo> convert);\r\n\r\n        /// <summary>\r\n        /// The method configures an object to not perform a type conversion.\r\n        /// </summary>\r\n        /// <returns>The object configured with no type converter.</returns>\r\n        T DispatchNoConvert();\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/IReadOnlyListExt.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal interface IReadOnlyListExt<T> : IReadOnlyList<T>\n    {\n        IReadOnlyListExt<T> Slice(int start, int length);\n\n        IReadOnlyListExt<TOut> Map<TOut>(Func<T, TOut> map);\n\n        int CopyTo(Span<T> span, int start);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/ITypeDispatcher.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal interface ITypeDispatcher\n    {\n        T Dispatch<T>(ITypeDispatcher<T> dispatcher);\n    }\n\n    internal interface ITypeDispatcher<out TOut>\n    {\n        TOut Dispatch<T>();\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/IndexedCollectionBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    /// <summary>\n    /// Represents a collection of items accessible both by key and index.\n    /// </summary>\n    /// <typeparam name=\"TKey\">The type of keys.</typeparam>\n    /// <typeparam name=\"TValue\">The type of items.</typeparam>\n    public abstract class IndexedCollectionBase<TKey, TValue> :\n        IList<TValue>,\n        IReadOnlyList<TValue>,\n        IReadOnlyDictionary<TKey, TValue>,\n        ICollection\n        where TKey : notnull\n        where TValue : notnull\n    {\n        private readonly Dictionary<TKey, TValue> _items;\n        private readonly List<TKey> _keys;\n\n        /// <inheritdoc/>\n        public int Count => _items.Count;\n\n        bool ICollection<TValue>.IsReadOnly => false;\n\n        IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => this;\n\n        IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => _keys.AsReadOnly();\n\n        bool ICollection.IsSynchronized => false;\n\n        object ICollection.SyncRoot => ((ICollection)_items).SyncRoot;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"IndexedCollectionBase{TKey, TValue}\"/> class that is empty, has the\n        /// default initial capacity, and uses the specified <see cref=\"IEqualityComparer{T}\"/>.\n        /// </summary>\n        /// <param name=\"comparer\">\n        /// The <see cref=\"IEqualityComparer{T}\"/> implementation to use when comparing keys, or <see langword=\"null\"/> to use the\n        /// default <see cref=\"EqualityComparer{T}\"/> for the type of the key.\n        /// </param>\n        protected IndexedCollectionBase(IEqualityComparer<TKey>? comparer)\n        {\n            _items = new Dictionary<TKey, TValue>(comparer);\n            _keys = new List<TKey>();\n        }\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"IndexedCollectionBase{TKey, TValue}\"/> class that is empty, has the\n        /// specified initial capacity, and uses the specified <see cref=\"IEqualityComparer{T}\"/>.\n        /// </summary>\n        /// <param name=\"capacity\">The initial number of elements that the collection can contain.</param>\n        /// <param name=\"comparer\">\n        /// The <see cref=\"IEqualityComparer{T}\"/> implementation to use when comparing keys, or <see langword=\"null\"/> to use the\n        /// default <see cref=\"EqualityComparer{T}\"/> for the type of the key.\n        /// </param>\n        protected IndexedCollectionBase(int capacity, IEqualityComparer<TKey>? comparer)\n        {\n            _items = new Dictionary<TKey, TValue>(capacity, comparer);\n            _keys = new List<TKey>(capacity);\n        }\n\n        /// <summary>\n        /// Extracts and returns the key of the item.\n        /// </summary>\n        /// <param name=\"item\">The item from which the key should be extracted.</param>\n        /// <returns>The key of the item.</returns>\n        protected abstract TKey GetKey(TValue item);\n\n        /// <inheritdoc/>\n        public bool ContainsKey(TKey key)\n        {\n            return _items.ContainsKey(key);\n        }\n\n        /// <inheritdoc/>\n        public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)\n        {\n            return _items.TryGetValue(key, out value);\n        }\n\n        /// <summary>\n        /// Determines the index of the item with the specific key.\n        /// </summary>\n        /// <param name=\"key\">The key of the item in the collection.</param>\n        /// <returns>The index of the item found in the collection; otherwise -1.</returns>\n        public int IndexOf(TKey key)\n        {\n            if (key == null)\n                throw new ArgumentNullException(nameof(key));\n\n            if (!_items.ContainsKey(key))\n                return -1;\n\n            var comparer = _items.Comparer;\n            return _keys.FindIndex(item => comparer.Equals(item, key));\n        }\n\n        /// <inheritdoc/>\n        public int IndexOf(TValue item)\n        {\n            if (item == null)\n                throw new ArgumentNullException(nameof(item));\n\n            var key = GetKey(item);\n            if (!_items.TryGetValue(key, out var existingValue) || !existingValue.Equals(item))\n                return -1;\n\n            var comparer = _items.Comparer;\n            return _keys.FindIndex(item => comparer.Equals(item, key));\n        }\n\n        /// <inheritdoc/>\n        public void Insert(int index, TValue item)\n        {\n            if (item == null)\n                throw new ArgumentNullException(nameof(item));\n\n            var key = GetKey(item);\n            _items.Add(key, item);\n            try\n            {\n                _keys.Insert(index, key);\n            }\n            catch\n            {\n                _items.Remove(key);\n                throw;\n            }\n        }\n\n        /// <inheritdoc/>\n        public bool Remove(TValue item)\n        {\n            if (item == null)\n                return false;\n\n            var key = GetKey(item);\n            if (!_items.TryGetValue(key, out var existingValue) || !existingValue.Equals(item))\n                return false;\n\n            var comparer = _items.Comparer;\n            var idx = _keys.FindIndex(item => comparer.Equals(item, key));\n            Debug.Assert(idx >= 0);\n\n            RemoveAt(idx);\n            return true;\n        }\n\n        /// <summary>\n        /// Removes the item with the specific key from the collection.\n        /// </summary>\n        /// <param name=\"key\">The key of the item in the collection.</param>\n        /// <returns><see langword=\"true\"/> if the item was successfully removed from the collection. <see langword=\"false\"/> if an item was not found in the collection.</returns>\n        public bool Remove(TKey key)\n        {\n            if (key == null)\n                return false;\n\n            if (!_items.TryGetValue(key, out var existingValue))\n                return false;\n\n            var comparer = _items.Comparer;\n            var idx = _keys.FindIndex(item => comparer.Equals(item, key));\n            Debug.Assert(idx >= 0);\n\n            RemoveAt(idx);\n            return true;\n        }\n\n        /// <inheritdoc/>\n        public void RemoveAt(int index)\n        {\n            var key = _keys[index];\n            _keys.RemoveAt(index);\n            _items.Remove(key);\n        }\n\n        /// <inheritdoc/>\n        public void Add(TValue item)\n        {\n            if (item == null)\n                throw new ArgumentNullException(nameof(item));\n\n            var key = GetKey(item);\n            _items.Add(key, item);\n            _keys.Add(key);\n        }\n\n        /// <inheritdoc/>\n        public void Clear()\n        {\n            _keys.Clear();\n            _items.Clear();\n        }\n\n        /// <inheritdoc/>\n        public bool Contains(TValue item)\n        {\n            if (item == null)\n                throw new ArgumentNullException(nameof(item));\n\n            var key = GetKey(item);\n            if (!_items.TryGetValue(key, out var existingItem))\n                return false;\n\n            return existingItem.Equals(item);\n        }\n\n        /// <inheritdoc/>\n        public void CopyTo(TValue[] array, int arrayIndex)\n        {\n            int sourceIdx = 0, targetIdx = arrayIndex;\n            while (sourceIdx < _keys.Count)\n                array[targetIdx++] = _items[_keys[sourceIdx++]];\n        }\n\n        void ICollection.CopyTo(Array array, int index)\n        {\n            int sourceIdx = 0, targetIdx = index;\n            while (sourceIdx < _keys.Count)\n                array.SetValue(_items[_keys[sourceIdx++]], targetIdx++);\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()\n        {\n            foreach (var name in _keys)\n                yield return new KeyValuePair<TKey, TValue>(name, _items[name]);\n        }\n\n        /// <inheritdoc/>\n        public IEnumerator<TValue> GetEnumerator()\n        {\n            foreach (var name in _keys)\n                yield return _items[name];\n        }\n\n        /// <inheritdoc/>\n        public TValue this[TKey key] => _items[key];\n\n        /// <inheritdoc/>\n        public TValue this[int index]\n        {\n            get => _items[_keys[index]];\n            set\n            {\n                if (value == null)\n                    throw new ArgumentNullException(nameof(value));\n\n                var valueKey = GetKey(value);\n                var item = _items[_keys[index]];\n                var itemKey = GetKey(item);\n                if (_items.Comparer.Equals(itemKey, valueKey))\n                {\n                    _items[valueKey] = value;\n                    Debug.Assert(_keys.Count == _items.Count);\n                }\n                else\n                {\n                    _items.Add(valueKey, value);\n                    _items.Remove(itemKey);\n                }\n\n                _keys[index] = valueKey;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/ListExtensions.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal static class ListExtensions\n    {\n        public static IReadOnlyList<TOut> Map<TIn, TOut>(this IReadOnlyList<TIn> list, Func<TIn, TOut> map)\n        {\n            return MappedReadOnlyList<TIn, TOut>.Map(list, map);\n        }\n\n        public static IReadOnlyList<TOut> Map<TIn, TOut>(this IList<TIn> list, Func<TIn, TOut> map)\n        {\n            if (list == null)\n                throw new ArgumentNullException(nameof(list));\n\n            return list.Slice(0).Map(map);\n        }\n\n        public static IReadOnlyList<T> Slice<T>(this IReadOnlyList<T> list, int start)\n        {\n            if (list == null)\n                throw new ArgumentNullException(nameof(list));\n\n            if (start == 0)\n                return list;\n\n            return list.Slice(start, list.Count - start);            \n        }\n\n        public static IReadOnlyList<T> Slice<T>(this IReadOnlyList<T> list, int start, int length)\n        {\n            if (list == null)\n                throw new ArgumentNullException(nameof(list));\n\n            if (start == 0 && length == list.Count)\n                return list;\n\n            if (list is T[] array)\n                return new ReadOnlyMemoryList<T>(array).Slice(start, length);\n\n            return ReadOnlyListSpan<T>.Slice(list, start, length);\n        }\n\n        public static IReadOnlyList<T> Slice<T>(this IList<T> list, int start)\n        {\n            if (list == null)\n                throw new ArgumentNullException(nameof(list));\n\n            if (start == 0 && list is IReadOnlyList<T> readOnlyList)\n                return readOnlyList;\n\n            return list.Slice(start, list.Count - start);            \n        }\n\n        public static IReadOnlyList<T> Slice<T>(this IList<T> list, int start, int length)\n        {\n            if (list == null)\n                throw new ArgumentNullException(nameof(list));\n\n            if (start == 0 && list is IReadOnlyList<T> readOnlyList && length == readOnlyList.Count)\n                return readOnlyList;\n\n            if (list is T[] array)\n                return new ReadOnlyMemoryList<T>(array).Slice(start, length);\n\n            return ListSpan<T>.Slice(list, start, length);\n        }\n\n        public static int CopyTo<T>(this IReadOnlyList<T> list, Span<T> span, int start)\n        {\n            if (list == null)\n                throw new ArgumentNullException(nameof(list));\n\n            if (list is IReadOnlyListExt<T> readOnlyListExt)\n                return readOnlyListExt.CopyTo(span, start);\n\n            if (start < 0 || start > list.Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n\n            var length = Math.Min(list.Count - start, span.Length);\n            if (list is T[] array)\n            {\n                new ReadOnlySpan<T>(array, start, length).CopyTo(span);\n            }\n            else\n            {\n                var end = start + length;\n                for (int i = start, j = 0; i < end; i++, j++)\n                    span[j] = list[i];\n            }\n\n            return length;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/ListSpan.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class ListSpan<T> : IReadOnlyListExt<T>\n    {\n        private readonly IList<T> _innerList;\n        private readonly int _offset;\n\n        public int Count { get; }\n\n        private ListSpan(IList<T> innerList, int offset, int count)\n        {\n            if (innerList == null)\n                throw new ArgumentNullException(nameof(innerList));\n            if (offset < 0 || offset > innerList.Count)\n                throw new ArgumentOutOfRangeException(nameof(offset));\n            if (count < 0 || offset + count > innerList.Count)\n                throw new ArgumentOutOfRangeException(nameof(count));\n\n            _innerList = innerList;\n            _offset = offset;\n            Count = count;\n        }\n\n        public IEnumerator<T> GetEnumerator()\n        {\n            return _innerList.Skip(_offset).Take(Count).GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        public IReadOnlyListExt<T> Slice(int start, int length)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n            if (start + length > Count)\n                throw new ArgumentOutOfRangeException(nameof(length));\n\n            return new ListSpan<T>(_innerList, start + _offset, length);\n        }\n\n        public IReadOnlyListExt<TOut> Map<TOut>(Func<T, TOut> map)\n        {\n            return MappedListSpan<T, TOut>.Create(_innerList, map, _offset, Count);\n        }\n\n        public int CopyTo(Span<T> span, int start)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n            \n            var length = Math.Min(Count - start, span.Length);\n            if (_innerList is T[] array)\n            {\n                new ReadOnlySpan<T>(array, _offset + start, length).CopyTo(span);\n            }\n            else\n            {\n                var end = _offset + start + length;\n                for (int i = _offset + start, j = 0; i < end; i++, j++)\n                    span[j] = _innerList[i];\n            }\n\n            return length;            \n        }\n\n        public T this[int index]\n        {\n            get\n            {\n                if (index < 0 || index >= Count)\n                    throw new IndexOutOfRangeException();\n\n                return _innerList[index + _offset];\n            }\n        }\n\n        public static IReadOnlyListExt<T> Slice(IList<T> list, int offset, int count)\n        {\n            if (list is IReadOnlyListExt<T> readOnlyListExt)\n                return readOnlyListExt.Slice(offset, count);\n\n            return new ListSpan<T>(list, offset, count);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/MappedListSpan.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class MappedListSpan<TIn, TOut> : IReadOnlyListExt<TOut>\n    {\n        private readonly IList<TIn> _innerList;\n        private readonly Func<TIn, TOut> _map;\n        private readonly int _offset;\n\n        public int Count { get; }\n\n        private MappedListSpan(IList<TIn> innerList, Func<TIn, TOut> map, int offset, int count)\n        {\n            if (innerList == null)\n                throw new ArgumentNullException(nameof(innerList));\n            if (map == null)\n                throw new ArgumentNullException(nameof(map));\n            if (offset < 0 || offset > innerList.Count)\n                throw new ArgumentOutOfRangeException(nameof(offset));\n            if (count < 0 || offset + count > innerList.Count)\n                throw new ArgumentOutOfRangeException(nameof(count));\n\n            _innerList = innerList;\n            _map = map;\n            _offset = offset;\n            Count = count;\n        }\n\n        public IEnumerator<TOut> GetEnumerator()\n        {\n            return _innerList.Skip(_offset).Take(Count).Select(_map).GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        public IReadOnlyListExt<TOut> Slice(int start, int length)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n            if (length < 0 || start + length > Count)\n                throw new ArgumentOutOfRangeException(nameof(length));\n\n            return new MappedListSpan<TIn, TOut>(_innerList, _map, _offset + start, length);\n        }\n\n        public IReadOnlyListExt<T> Map<T>(Func<TOut, T> map)\n        {\n            if (map == null)\n                throw new ArgumentNullException(nameof(map));\n\n            return new MappedListSpan<TIn, T>(_innerList, Combine(_map, map), _offset, Count);\n\n            static Func<TIn, T> Combine(Func<TIn, TOut> f1, Func<TOut, T> f2)\n            {\n                return v => f2(f1(v));\n            }\n        }\n\n        public int CopyTo(Span<TOut> span, int start)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n\n            var length = Math.Min(Count - start, span.Length);\n            var end = _offset + start + length;\n            for (int i = _offset + start, j = 0; i < end; i++, j++)\n                span[j] = _map(_innerList[i]);\n\n            return length;\n        }\n\n        public TOut this[int index]\n        {\n            get\n            {\n                if (index < 0 || index >= Count)\n                    throw new IndexOutOfRangeException();\n\n                return _map(_innerList[index + _offset]);\n            }\n        }\n\n        public static IReadOnlyListExt<TOut> Create(IList<TIn> list, Func<TIn, TOut> map, int offset, int count)\n        {\n            if (list is IReadOnlyListExt<TIn> readOnlyListExt)\n                return readOnlyListExt.Slice(offset, count).Map(map);\n\n            return new MappedListSpan<TIn, TOut>(list, map, offset, count);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/MappedReadOnlyList.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class MappedReadOnlyList<TIn, TOut> : IReadOnlyListExt<TOut>\n    {\n        private readonly IReadOnlyList<TIn> _innerList;\n        private readonly Func<TIn, TOut> _map;\n\n        public int Count => _innerList.Count;\n\n        private MappedReadOnlyList(IReadOnlyList<TIn> innerList, Func<TIn, TOut> map)\n        {\n            _innerList = innerList ?? throw new ArgumentNullException(nameof(innerList));\n            _map = map ?? throw new ArgumentNullException(nameof(map));\n        }\n\n        public IEnumerator<TOut> GetEnumerator()\n        {\n            return _innerList.Select(_map).GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        public IReadOnlyListExt<TOut> Slice(int start, int length)\n        {\n            return MappedReadOnlyListSpan<TIn, TOut>.Create(_innerList, _map, start, length);\n        }\n\n        public IReadOnlyListExt<T> Map<T>(Func<TOut, T> map)\n        {\n            if (map == null)\n                throw new ArgumentNullException(nameof(map));\n\n            return new MappedReadOnlyList<TIn, T>(_innerList, Combine(_map, map));\n\n            static Func<TIn, T> Combine(Func<TIn, TOut> f1, Func<TOut, T> f2)\n            {\n                return v => f2(f1(v));\n            }\n        }\n\n        public int CopyTo(Span<TOut> span, int start)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n\n            var length = Math.Min(Count - start, span.Length);\n            var end = start + length;\n            for (int i = start, j = 0; i < end; i++, j++)\n                span[j] = _map(_innerList[i]);\n\n            return length;\n        }\n\n        public TOut this[int index] => _map(_innerList[index]);\n\n        public static IReadOnlyListExt<TOut> Map(IReadOnlyList<TIn> list, Func<TIn, TOut> map)\n        {\n            if (list is IReadOnlyListExt<TIn> readOnlyListExt)\n                return readOnlyListExt.Map(map);\n\n            return new MappedReadOnlyList<TIn, TOut>(list, map);\n        }\n\n        public static IReadOnlyListExt<TOut> Map(ReadOnlyMemory<TIn> memory, Func<TIn, TOut> map)\n        {\n            return new MappedReadOnlyList<TIn, TOut>(new ReadOnlyMemoryList<TIn>(memory), map);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/MappedReadOnlyListSpan.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class MappedReadOnlyListSpan<TIn, TOut> : IReadOnlyListExt<TOut>\n    {\n        private readonly IReadOnlyList<TIn> _innerList;\n        private readonly Func<TIn, TOut> _map;\n        private readonly int _offset;\n\n        public int Count { get; }\n\n        private MappedReadOnlyListSpan(IReadOnlyList<TIn> innerList, Func<TIn, TOut> map, int offset, int count)\n        {\n            if (innerList == null)\n                throw new ArgumentNullException(nameof(innerList));\n            if (map == null)\n                throw new ArgumentNullException(nameof(map));\n            if (offset < 0 || offset > innerList.Count)\n                throw new ArgumentOutOfRangeException(nameof(offset));\n            if (count < 0 || offset + count > innerList.Count)\n                throw new ArgumentOutOfRangeException(nameof(count));\n\n            _innerList = innerList;\n            _map = map;\n            _offset = offset;\n            Count = count;\n        }\n\n        public IEnumerator<TOut> GetEnumerator()\n        {\n            return _innerList.Skip(_offset).Take(Count).Select(_map).GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        public IReadOnlyListExt<TOut> Slice(int start, int length)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n            if (length < 0 || start + length > Count)\n                throw new ArgumentOutOfRangeException(nameof(length));\n\n            if (_innerList is IReadOnlyListExt<TIn> readOnlyListExt)\n                return readOnlyListExt.Slice(_offset + start, length).Map(_map);\n\n            return new MappedReadOnlyListSpan<TIn, TOut>(_innerList, _map, _offset + start, length);\n        }\n\n        public IReadOnlyListExt<T> Map<T>(Func<TOut, T> map)\n        {\n            if (map == null)\n                throw new ArgumentNullException(nameof(map));\n\n            return new MappedReadOnlyListSpan<TIn, T>(_innerList, Combine(_map, map), _offset, Count);            \n\n            static Func<TIn,T> Combine(Func<TIn,TOut> f1, Func<TOut,T> f2)\n            {\n                return v => f2(f1(v));\n            }\n        }\n\n        public int CopyTo(Span<TOut> span, int start)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n\n            var length = Math.Min(Count - start, span.Length);\n            var end = _offset + start + length;\n            for (int i = _offset + start, j = 0; i < end; i++, j++)\n                span[j] = _map(_innerList[i]);\n\n            return length;\n        }\n\n        public TOut this[int index]\n        {\n            get\n            {\n                if (index < 0 || index >= Count)\n                    throw new IndexOutOfRangeException();\n\n                return _map(_innerList[index + _offset]);\n            }\n        }\n\n        public static IReadOnlyListExt<TOut> Create(IReadOnlyList<TIn> list, Func<TIn, TOut> map, int offset, int count)\n        {\n            if (list is IReadOnlyListExt<TIn> readOnlyListExt)\n                return readOnlyListExt.Slice(offset, count).Map(map);\n\n            if (list is TIn[] array)\n                return new ReadOnlyMemoryList<TIn>(array).Slice(offset, count).Map(map);\n\n            return new MappedReadOnlyListSpan<TIn, TOut>(list, map, offset, count);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/MemoryCollectionList.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class MemoryCollectionList<T> : ICollectionList<T>\n    {\n        private readonly IReadOnlyList<Memory<T>> _list;\n\n        public MemoryCollectionList(IReadOnlyList<Memory<T>> list)\n            => _list = list;\n\n        public int Count => _list.Count;\n\n        public T this[int listIndex, int index]\n            => _list[listIndex].Span[index];\n\n        public IEnumerable<T> GetItems()\n            => _list.SelectMany(list => MemoryMarshal.ToEnumerable<T>(list));\n\n        public IEnumerable<int> GetListLengths()\n            => _list.Select(item => item.Length);\n\n        public int GetLength(int listIndex)\n            => _list[listIndex].Length;\n    }\n}"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/MultiDimensionalArrayReadOnlyListAdapter.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal static class MultiDimensionalArrayReadOnlyListAdapter\n    {\n        public static (Func<Array, object> createList, Type listElementType) Dispatch(Type arrayElementType, int arrayRank)\n        {\n            return TypeDispatcher.Dispatch(arrayElementType, new Dispatcher(arrayRank));\n        }\n\n        private sealed class Dispatcher : ITypeDispatcher<(Func<Array, object> createList, Type listElementType)>\n        {\n            private readonly int _arrayRank;\n\n            public Dispatcher(int arrayRank)\n            {\n                _arrayRank = arrayRank;\n            }\n\n            public (Func<Array, object> createList, Type listElementType) Dispatch<T>()\n            {\n                return Dispatch(\n                    _arrayRank - 1,\n                    (arr, indices, idx) =>\n                    {\n                        object? result;\n                        if (indices == null)\n                        {\n                            result = arr.GetValue(idx);\n                        }\n                        else\n                        {\n                            var idxCopy = new int[indices.Length + 1];\n                            Array.Copy(indices, idxCopy, indices.Length);\n                            idxCopy[^1] = idx;\n                            result = arr.GetValue(idxCopy);\n                        }\n\n                        // Actually the result can be null if T is nullable\n                        return (T) result!;\n                    });\n            }\n\n            private static (Func<Array, object> createList, Type listElementType) Dispatch<T>(int depth, Func<Array, int[]?, int, T> selector)\n            {\n                if (depth == 0)\n                    return (array => new Adapter<T>(array, null, selector), typeof(T));\n\n                return Dispatch(\n                    depth - 1,\n                    (arr, indices, idx) =>\n                    {\n                        int[] idxCopy;\n                        if (indices == null)\n                        {\n                            idxCopy = new[] {idx};\n                        }\n                        else\n                        {\n                            idxCopy = new int[indices.Length + 1];\n                            Array.Copy(indices, idxCopy, indices.Length);\n                            idxCopy[^1] = idx;\n                        }\n\n                        return new Adapter<T>(arr, idxCopy, selector);\n                    });\n            }\n        }\n\n        private sealed class Adapter<T> : IReadOnlyList<T>\n        {\n            private readonly Array _array;\n            private readonly int[]? _indices;\n            private readonly Func<Array, int[]?, int, T> _selector;\n\n            public int Count => _array.GetLength(_indices?.Length ?? 0);\n\n            public Adapter(Array array, int[]? indices, Func<Array, int[]?, int, T> selector)\n            {\n                _array = array;\n                _indices = indices;\n                _selector = selector;\n            }\n\n            public T this[int index] => _selector(_array, _indices, index);\n\n            public IEnumerator<T> GetEnumerator()\n            {\n                var count = _array.GetLength(_indices?.Length ?? 0);\n                for (int i = 0; i < count; i++)\n                    yield return _selector(_array, _indices, i);\n            }\n\n            IEnumerator IEnumerable.GetEnumerator()\n            {\n                return GetEnumerator();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/ReadOnlyCollectionList.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class ReadOnlyCollectionList<T> : ICollectionList<T>\n    {\n        private readonly IReadOnlyList<IReadOnlyList<T>?> _list;\n\n        public ReadOnlyCollectionList(IReadOnlyList<IReadOnlyList<T>?> list)\n            => _list = list;\n\n        public int Count => _list.Count;\n\n        public T this[int listIndex, int index]\n            => _list[listIndex]![index];\n\n        public IEnumerable<T> GetItems()\n            => _list.SelectMany(list => list ?? Enumerable.Empty<T>());\n\n        public IEnumerable<int> GetListLengths()\n            => _list.Select(item => item?.Count ?? 0);\n\n        public int GetLength(int listIndex)\n            => _list[listIndex]?.Count ?? 0;\n    }\n}"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/ReadOnlyListSpan.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class ReadOnlyListSpan<T> : IReadOnlyListExt<T>\n    {\n        private readonly IReadOnlyList<T> _innerList;\n        private readonly int _offset;\n\n        public int Count { get; }\n\n        private ReadOnlyListSpan(IReadOnlyList<T> innerList, int offset, int count)\n        {\n            if (innerList == null)\n                throw new ArgumentNullException(nameof(innerList));\n            if (offset < 0 || offset > innerList.Count)\n                throw new ArgumentOutOfRangeException(nameof(offset));\n            if (count < 0 || offset + count > innerList.Count)\n                throw new ArgumentOutOfRangeException(nameof(count));\n\n            _innerList = innerList;\n            _offset = offset;\n            Count = count;\n        }\n\n        public IEnumerator<T> GetEnumerator()\n        {\n            return _innerList.Skip(_offset).Take(Count).GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        public IReadOnlyListExt<T> Slice(int start, int length)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n            if (length < 0 || start + length > Count)\n                throw new ArgumentOutOfRangeException(nameof(length));\n\n            return Slice(_innerList, _offset + start, length);\n        }\n\n        public IReadOnlyListExt<TOut> Map<TOut>(Func<T, TOut> map)\n        {\n            return MappedReadOnlyListSpan<T, TOut>.Create(_innerList, map, _offset, Count);\n        }\n\n        public int CopyTo(Span<T> span, int start)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n\n            var length = Math.Min(Count - start, span.Length);\n            if (_innerList is T[] array)\n            {\n                new ReadOnlySpan<T>(array, _offset + start, length).CopyTo(span);\n            }\n            else\n            {\n                var end = _offset + start + length;\n                for (int i = _offset + start, j = 0; i < end; i++, j++)\n                    span[j] = _innerList[i];\n            }\n\n            return length;\n        }\n\n        public T this[int index]\n        {\n            get\n            {\n                if (index < 0 || index >= Count)\n                    throw new IndexOutOfRangeException();\n\n                return _innerList[index + _offset];\n            }\n        }\n\n        public static IReadOnlyListExt<T> Slice(IReadOnlyList<T> list, int offset, int count)\n        {\n            if (list is IReadOnlyListExt<T> readOnlyListExt)\n                return readOnlyListExt.Slice(offset, count);\n\n            return new ReadOnlyListSpan<T>(list, offset, count);\n        }        \n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/ReadOnlyMemoryCollectionList.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class ReadOnlyMemoryCollectionList<T> : ICollectionList<T>\n    {\n        private readonly IReadOnlyList<ReadOnlyMemory<T>> _list;\n\n        public ReadOnlyMemoryCollectionList(IReadOnlyList<ReadOnlyMemory<T>> list)\n            => _list = list;\n\n        public int Count => _list.Count;\n\n        public T this[int listIndex, int index]\n            => _list[listIndex].Span[index];\n\n        public IEnumerable<T> GetItems()\n            => _list.SelectMany(MemoryMarshal.ToEnumerable);\n\n        public IEnumerable<int> GetListLengths()\n            => _list.Select(item => item.Length);\n\n        public int GetLength(int listIndex)\n            => _list[listIndex].Length;\n    }\n}"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/ReadOnlyMemoryList.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class ReadOnlyMemoryList<T> : IReadOnlyListExt<T>\n    {\n        private readonly ReadOnlyMemory<T> _memory;\n\n        public int Count => _memory.Length;\n\n        public ReadOnlyMemoryList(ReadOnlyMemory<T> memory)\n        {\n            _memory = memory;\n        }\n\n        public ReadOnlyMemoryList(Memory<T> memory)\n        {\n            _memory = memory;\n        }\n\n        public IEnumerator<T> GetEnumerator()\n        {\n            for (int i = 0; i < _memory.Length; i++)\n                yield return _memory.Span[i];\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        public IReadOnlyListExt<T> Slice(int start, int length)\n        {\n            if (start < 0 || start > Count)\n                throw new ArgumentOutOfRangeException(nameof(start));\n            if (length < 0 || start + length > Count)\n                throw new ArgumentOutOfRangeException(nameof(length));\n\n            return new ReadOnlyMemoryList<T>(_memory.Slice(start, length));\n        }\n\n        public IReadOnlyListExt<TOut> Map<TOut>(Func<T, TOut> map)\n        {\n            return MappedReadOnlyList<T, TOut>.Map(_memory, map);\n        }\n\n        public int CopyTo(Span<T> span, int start)\n        {\n            if (start < 0 || start > _memory.Length)\n                throw new ArgumentOutOfRangeException(nameof(start));\n\n            var length = Math.Min(_memory.Length - start, span.Length);\n            _memory.Slice(start, length).Span.CopyTo(span);\n            return length;            \n        }\n\n        public T this[int index] => _memory.Span[index];\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/ReadWriteBuffer.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class ReadWriteBuffer\n    {\n        private readonly int _segmentSize;\n\n        private readonly List<(byte[] buffer, int length)> _segments;\n\n        private int _readSegmentIdx;\n        private int _readSegmentPosition;\n        private int _readLength;\n        private int _writeSegmentIdx;\n        private int _writeSegmentPosition;\n\n        private ReadOnlySequence<byte>? _readCache;\n\n        public ReadWriteBuffer(int segmentSize)\n        {\n            if (segmentSize <= 0)\n                throw new ArgumentOutOfRangeException(nameof(segmentSize));\n\n            _segmentSize = segmentSize;\n            _segments = new List<(byte[] buffer, int length)>();\n        }\n\n        public ReadOnlySequence<byte> Read()\n        {\n            if (_readCache != null)\n                return _readCache.Value;\n\n            if (_readLength == 0)\n                return ReadOnlySequence<byte>.Empty;\n\n            var readSegment = _segments[_readSegmentIdx];\n            if (readSegment.length - _readSegmentPosition >= _readLength)\n                return new ReadOnlySequence<byte>(readSegment.buffer, _readSegmentPosition, _readLength);\n\n            var memory = new List<ReadOnlyMemory<byte>> {new ReadOnlyMemory<byte>(readSegment.buffer, _readSegmentPosition, readSegment.length - _readSegmentPosition)};\n            for (int length = _readLength - memory[0].Length, segmentIdx = (_readSegmentIdx + 1) % _segments.Count; length > 0; segmentIdx = (segmentIdx + 1) % _segments.Count)\n            {\n                var segment = _segments[segmentIdx];\n                var memoryBlock = new ReadOnlyMemory<byte>(segment.buffer, 0, Math.Min(segment.length, length));\n                memory.Add(memoryBlock);\n                length -= memoryBlock.Length;\n            }\n\n            var sequenceSegment = new SimpleReadOnlySequenceSegment<byte>(memory);\n            _readCache = new ReadOnlySequence<byte>(sequenceSegment, 0, sequenceSegment.LastSegment, sequenceSegment.LastSegment.Memory.Length);\n            return _readCache.Value;\n        }\n\n        public void ConfirmRead(int length)\n        {\n            if (length < 0)\n                throw new ArgumentOutOfRangeException(nameof(length));\n\n            if (length > _readLength)\n                throw new ArgumentOutOfRangeException(nameof(length), \"Internal error. The length can't be greater than the size of the buffer.\");\n\n            if (length == 0)\n                return;\n\n            var segment = _segments[_readSegmentIdx];\n            if (length < segment.length - _readSegmentPosition)\n            {\n                _readSegmentPosition += length;\n            }\n            else\n            {\n                var currentLength = length - segment.length + _readSegmentPosition;\n                var currentIndex = _readSegmentIdx;\n                var currentSegment = _segments[currentIndex];\n\n                while (true)\n                {\n                    if (currentLength == 0 && currentIndex == _writeSegmentIdx && currentSegment.buffer.Length == _writeSegmentPosition)\n                    {\n                        // Read index is pointing to the beginning of the next segment. Write index is pointing to the end of\n                        // the current segment. Adjust write index.\n                        _writeSegmentIdx = _readSegmentIdx = (currentIndex + 1) % _segments.Count;\n                        _writeSegmentPosition = _readSegmentPosition = 0;\n\n                        currentSegment = _segments[_writeSegmentIdx];\n                        _segments[_writeSegmentIdx] = (currentSegment.buffer, currentSegment.buffer.Length);\n                        break;\n                    }\n\n                    currentIndex = (currentIndex + 1) % _segments.Count;\n                    Debug.Assert(currentIndex != _readSegmentIdx);\n                    currentSegment = _segments[currentIndex];\n\n                    if (currentLength < currentSegment.length)\n                    {\n                        _readSegmentIdx = currentIndex;\n                        _readSegmentPosition = currentLength;\n                        break;\n                    }\n\n                    currentLength -= currentSegment.length;\n                }\n            }\n\n            _readLength -= length;\n\n            if (_readCache != null)\n            {\n                _readCache = _readCache.Value.Slice(length);\n                Debug.Assert(_readCache.Value.Length == _readLength);\n            }\n        }\n\n        public Memory<byte> GetMemory()\n        {\n            if (_segments.Count > 0)\n            {\n                var writeSegment = _segments[_writeSegmentIdx];\n                if (writeSegment.buffer.Length > _writeSegmentPosition)\n                    return new Memory<byte>(writeSegment.buffer, _writeSegmentPosition, writeSegment.buffer.Length - _writeSegmentPosition);\n            }\n\n            return AcquireNextSegment(_segmentSize);\n        }\n\n        public Memory<byte> GetMemory(int sizeHint)\n        {\n            if (sizeHint < 0)\n                throw new ArgumentOutOfRangeException(nameof(sizeHint));\n\n            if (_segments.Count > 0)\n            {\n                var writeSegment = _segments[_writeSegmentIdx];\n                if (writeSegment.buffer.Length - _writeSegmentPosition >= sizeHint)\n                    return new Memory<byte>(writeSegment.buffer, _writeSegmentPosition, writeSegment.buffer.Length - _writeSegmentPosition);\n            }\n\n            var segmentSize = _segmentSize;\n            if (sizeHint > _segmentSize)\n                segmentSize = (sizeHint / _segmentSize + Math.Min(sizeHint % _segmentSize, 1)) * _segmentSize;\n\n            return AcquireNextSegment(segmentSize);\n        }\n\n        private Memory<byte> AcquireNextSegment(int size)\n        {\n            if (_segments.Count == 0)\n            {\n                _segments.Add((new byte[size], size));\n                return new Memory<byte>(_segments[_writeSegmentIdx].buffer);\n            }\n\n            _segments[_writeSegmentIdx] = (_segments[_writeSegmentIdx].buffer, _writeSegmentPosition);\n\n            if (_segments.Count > 1)\n            {\n                var firstFreeSegmentIdx = (_writeSegmentIdx + 1) % _segments.Count;\n                for (int i = firstFreeSegmentIdx; i != _readSegmentIdx; i = (i + 1) % _segments.Count)\n                {\n                    if (_segments[i].buffer.Length >= size)\n                    {\n                        var segment = _segments[i];\n                        if (i != firstFreeSegmentIdx)\n                            _segments[i] = _segments[firstFreeSegmentIdx];\n                        \n                        _segments[firstFreeSegmentIdx] = (segment.buffer, segment.buffer.Length);\n                        _writeSegmentIdx = firstFreeSegmentIdx;\n                        _writeSegmentPosition = 0;\n\n                        return new Memory<byte>(_segments[_writeSegmentIdx].buffer);\n                    }\n                }\n\n                if (firstFreeSegmentIdx != _readSegmentIdx)\n                {\n                    _segments[firstFreeSegmentIdx] = (new byte[size], size);\n                    _writeSegmentIdx = firstFreeSegmentIdx;\n                    _writeSegmentPosition = 0;\n\n                    return new Memory<byte>(_segments[_writeSegmentIdx].buffer);\n                }\n            }\n\n            if (_writeSegmentIdx < _readSegmentIdx)\n                ++_readSegmentIdx;\n\n            ++_writeSegmentIdx;\n            _writeSegmentPosition = 0;\n            _segments.Insert(_writeSegmentIdx, (new byte[size], size));\n            return new Memory<byte>(_segments[_writeSegmentIdx].buffer);\n        }\n\n        public void ConfirmWrite(int length)\n        {\n            if (length < 0)\n                throw new ArgumentOutOfRangeException(nameof(length));\n\n            var segment = _segments[_writeSegmentIdx];\n            if (length > segment.buffer.Length - _writeSegmentPosition)\n                throw new ArgumentOutOfRangeException(nameof(length), \"Internal error. The length can't be greater than the size of the buffer.\");\n\n            _writeSegmentPosition += length;\n            Debug.Assert(_writeSegmentPosition <= segment.length);\n        }\n\n        public void Flush()\n        {\n            if (_segments.Count == 0)\n                return;\n\n            int length;\n            if (_readSegmentIdx == _writeSegmentIdx)\n            {\n                length = _writeSegmentPosition - _readSegmentPosition;\n                Debug.Assert(length >= 0);\n            }\n            else\n            {\n                length = _segments[_readSegmentIdx].length - _readSegmentPosition;\n                for (int i = (_readSegmentIdx + 1) % _segments.Count; i != _writeSegmentIdx; i = (i + 1) % _segments.Count)\n                    length += _segments[i].length;\n\n                length += _writeSegmentPosition;\n            }\n\n            if (length == _readLength)\n                return;\n\n            Debug.Assert(length > _readLength);\n\n            _readCache = null;\n            _readLength = length;\n        }\n\n        public void Discard()\n        {\n            if (_segments.Count == 0)\n                return;\n\n            int length = _readLength + _readSegmentPosition;\n            int index = _readSegmentIdx;\n            while (true)\n            {\n                var segment = _segments[index];\n                if (length <= segment.length)\n                {\n                    _writeSegmentIdx = index;\n                    _writeSegmentPosition = length;\n                    _segments[index] = (segment.buffer, segment.buffer.Length);\n                    break;\n                }\n                \n                length -= _segments[index].length;\n                index = (index + 1) % _segments.Count;\n                Debug.Assert(index != _readSegmentIdx);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/SimpleReadOnlySequenceSegment.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal sealed class SimpleReadOnlySequenceSegment<T> : ReadOnlySequenceSegment<T>\n    {\n        private readonly SimpleReadOnlySequenceSegment<T>? _lastSegment;\n\n        public SimpleReadOnlySequenceSegment<T> LastSegment => _lastSegment ?? this;\n\n        public SimpleReadOnlySequenceSegment(ReadOnlyMemory<T> firstSegment, ReadOnlySequence<T> sequence)\n        {\n            var segments = new Stack<ReadOnlyMemory<T>>(2);\n            long runningIndex = firstSegment.Length;\n            if (firstSegment.Length > 0)\n                segments.Push(firstSegment);\n            \n            foreach (var segment in sequence)\n            {\n                if (segment.Length == 0)\n                    continue;\n\n                runningIndex += segment.Length;\n                segments.Push(segment);\n            }\n\n            SimpleReadOnlySequenceSegment<T>? nextSegment = null;\n            if (segments.Count == 0)\n            {\n                Memory = firstSegment;\n                Debug.Assert(runningIndex == 0);\n            }\n            else\n            {\n                while (segments.Count > 1)\n                {\n                    var segment = segments.Pop();\n                    runningIndex -= segment.Length;\n                    nextSegment = new SimpleReadOnlySequenceSegment<T>(segment, runningIndex, nextSegment);\n                }\n\n                Memory = segments.Pop();\n                Debug.Assert(runningIndex == Memory.Length);\n            }\n\n            RunningIndex = 0;\n            Next = nextSegment;\n            _lastSegment = nextSegment?.LastSegment;\n        }\n\n        public SimpleReadOnlySequenceSegment(IReadOnlyList<ReadOnlyMemory<T>> segments)\n        {\n            var runningIndex = segments.Aggregate((long)0, (v, s) => v + s.Length);\n\n            SimpleReadOnlySequenceSegment<T>? nextSegment = null;\n            for (int i = segments.Count - 1; i > 0; i--)\n            {\n                var segment = segments[i];\n                runningIndex -= segment.Length;\n                nextSegment = new SimpleReadOnlySequenceSegment<T>(segment, runningIndex, nextSegment);\n            }\n\n            if (segments.Count > 0)\n            {\n                var firstSegment = segments[0];\n                runningIndex -= firstSegment.Length;\n                \n                Memory = firstSegment;\n            }\n\n            Debug.Assert(runningIndex == 0);\n            RunningIndex = runningIndex;\n            Next = nextSegment;\n            _lastSegment = nextSegment?.LastSegment;\n        }\n\n        private SimpleReadOnlySequenceSegment(ReadOnlyMemory<T> memory, long runningIndex, SimpleReadOnlySequenceSegment<T>? nextSegment)\n        {\n            Memory = memory;\n            RunningIndex = runningIndex;\n            Next = nextSegment;\n            _lastSegment = nextSegment?.LastSegment;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/TaskHelper.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Runtime.ExceptionServices;\nusing System.Threading.Tasks;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal static class TaskHelper\n    {\n        /// <summary>\n        /// Call this method only for the task which is not async in any execution path\n        /// </summary>\n        internal static void WaitNonAsyncTask(ValueTask task)\n        {\n            try\n            {\n                task.AsTask().Wait();\n            }\n            catch (AggregateException aggrEx)\n            {\n                if (aggrEx.InnerExceptions.Count == 1)\n                    ExceptionDispatchInfo.Capture(aggrEx.InnerExceptions[0]).Throw();\n\n                throw;\n            }\n        }\n\n        /// <summary>\n        /// Call this method only for the task which is not async in any execution path\n        /// </summary>\n        internal static T WaitNonAsyncTask<T>(ValueTask<T> task)\n        {\n            try\n            {\n                return task.Result;\n            }\n            catch (AggregateException aggrEx)\n            {\n                if (aggrEx.InnerExceptions.Count == 1)\n                    ExceptionDispatchInfo.Capture(aggrEx.InnerExceptions[0]).Throw();\n\n                throw;\n            }\n        }\n\n        internal static T WaitSynchronously<T>(Func<Task<T>> taskFactory)\n        {\n            if (taskFactory == null)\n                throw new ArgumentNullException(nameof(taskFactory));\n\n            var innerTask = WaitInternal(taskFactory);\n            try\n            {\n                var result = innerTask.Result;\n                return result;\n            }\n            catch (AggregateException aggrEx)\n            {\n                if (aggrEx.InnerExceptions.Count == 1)\n                    ExceptionDispatchInfo.Capture(aggrEx.InnerExceptions[0]).Throw();\n\n                throw;\n            }\n        }\n\n        internal static void WaitSynchronously([NotNull] Func<Task> taskFactory)\n        {\n            if (taskFactory == null)\n                throw new ArgumentNullException(nameof(taskFactory));\n\n            var innerTask = WaitInternal(taskFactory);\n            try\n            {\n                innerTask.Wait();\n            }\n            catch (AggregateException aggrEx)\n            {\n                if (aggrEx.InnerExceptions.Count == 1)\n                    ExceptionDispatchInfo.Capture(aggrEx.InnerExceptions[0]).Throw();\n\n                throw;\n            }\n        }\n\n        private static async Task<T> WaitInternal<T>([NotNull] Func<Task<T>> taskFactory)\n        {\n            return await Task.Run(async () => await taskFactory().ConfigureAwait(false)).ConfigureAwait(false);\n        }\n\n        private static async Task WaitInternal([NotNull] Func<Task> taskFactory)\n        {\n            await Task.Run(async () => await taskFactory().ConfigureAwait(false)).ConfigureAwait(false);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/TimeZoneHelper.Net6.0.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021-2022 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if NET6_0_OR_GREATER\n\nusing System;\nusing System.Runtime.CompilerServices;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    partial class TimeZoneHelper\n    {\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        static partial void GetTimeZoneInfoImpl(string timeZone, ref TimeZoneInfo? timeZoneInfo)\n        {\n            timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        static partial void GetTimeZoneIdImpl(TimeZoneInfo timeZoneInfo, ref string? timeZoneCode)\n        {\n            if (timeZoneInfo.HasIanaId)\n            {\n                timeZoneCode = timeZoneInfo.Id;\n            }\n            else if (!TimeZoneInfo.TryConvertWindowsIdToIanaId(timeZoneInfo.Id, out timeZoneCode))\n            {\n                throw new TimeZoneNotFoundException($\"The IANA time zone identifier for the time zone '{timeZoneInfo.Id}' was not found.\");\n            }\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/TimeZoneHelper.NetCoreApp3.1.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\n#if NETCOREAPP3_1_OR_GREATER && !NET6_0_OR_GREATER\n\nusing System;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing TimeZoneConverter;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    partial class TimeZoneHelper\n    {\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        static partial void GetTimeZoneInfoImpl(string timeZone, ref TimeZoneInfo? timeZoneInfo)\n        {\n            timeZoneInfo = TZConvert.GetTimeZoneInfo(timeZone);\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        static partial void GetTimeZoneIdImpl(TimeZoneInfo timeZoneInfo, ref string? timeZoneCode)\n        {\n            timeZoneCode = timeZoneInfo.Id;\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n                timeZoneCode = TZConvert.WindowsToIana(timeZoneCode);\n        }\n    }\n}\n\n#endif"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/TimeZoneHelper.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal static partial class TimeZoneHelper\n    {\n        public static TimeZoneInfo GetTimeZoneInfo(string timeZone)\n        {\n            TimeZoneInfo? timeZoneInfo = null;\n            GetTimeZoneInfoImpl(timeZone, ref timeZoneInfo);\n            if (timeZoneInfo == null)\n                throw new NotImplementedException($\"Internal error. The method {nameof(GetTimeZoneInfoImpl)} is not implemented properly.\");\n\n            return timeZoneInfo;\n        }\n\n        public static string GetTimeZoneId(TimeZoneInfo timeZone)\n        {\n            string? timeZoneCode = null;\n            GetTimeZoneIdImpl(timeZone, ref timeZoneCode);\n            if (timeZoneCode == null)\n                throw new NotImplementedException($\"Internal error. The method {nameof(GetTimeZoneIdImpl)} is not implemented properly.\");\n\n            return timeZoneCode;\n        }\n\n        static partial void GetTimeZoneInfoImpl(string timeZone, ref TimeZoneInfo? timeZoneInfo);\n\n        static partial void GetTimeZoneIdImpl(TimeZoneInfo timeZoneInfo, ref string? timeZoneCode);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient/Utils/TypeDispatcher.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Utils\n{\n    internal static class TypeDispatcher\n    {\n        public static TOut Dispatch<TOut>(Type type, ITypeDispatcher<TOut> dispatcher)\n        {\n            var dispatcherType = typeof(Dispatcher<>).MakeGenericType(type);\n            var dsp = (ITypeDispatcher) Activator.CreateInstance(dispatcherType)!;\n            return dsp.Dispatch(dispatcher);\n        }\n\n        public static ITypeDispatcher Create(Type type)\n        {\n            var dispatcherType = typeof(Dispatcher<>).MakeGenericType(type);\n            var dsp = (ITypeDispatcher)Activator.CreateInstance(dispatcherType)!;\n            return dsp;\n        }\n\n        private class Dispatcher<TValue> : ITypeDispatcher\n        {\n            public T Dispatch<T>(ITypeDispatcher<T> dispatcher)\n            {\n                return dispatcher.Dispatch<TValue>();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Benchmarks/ColumnWriterBenchmarks.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing BenchmarkDotNet.Attributes;\n\nnamespace Octonica.ClickHouseClient.Benchmarks\n{\n    public class ColumnWriterBenchmarks\n    {\n        private ClickHouseConnectionSettings _connectionSettings;\n\n        private List<Guid> _id;\n        private List<string?> _str;\n        private List<DateTime> _dt;\n        private List<decimal> _val;\n\n        [Params(10_000, 50_000)]\n        public int Rows;\n\n        [Params(1, 100, 1000)]\n        public int BatchSize;\n\n        [GlobalSetup]\n        public void Setup()\n        {\n            _connectionSettings = ConnectionSettingsHelper.GetConnectionSettings();\n\n            using (var connection = new ClickHouseConnection(_connectionSettings))\n            {\n                connection.Open();\n\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS ColumnWriterInsertBenchmarks\");\n                cmd.ExecuteNonQuery();\n\n                cmd.CommandText = \"CREATE TABLE ColumnWriterInsertBenchmarks(id UUID, str Nullable(String), dt DateTime, val Decimal64(4)) ENGINE = Memory\";\n                cmd.ExecuteNonQuery();\n            }\n\n            _id = new List<Guid>(Rows);\n            _str = new List<string?>(Rows);\n            _dt = new List<DateTime>(Rows);\n            _val = new List<decimal>(Rows);\n\n            const string strSource =\n                \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\";\n\n            var now = DateTime.Now;\n            var rnd = new Random(2306);\n            for (int i = 0; i < Rows; i++)\n            {\n                _id.Add(Guid.NewGuid());\n                _str.Add(i % strSource.Length == 0 ? null : strSource.Substring(0, i % strSource.Length));\n                _dt.Add(now.AddSeconds(i));\n                _val.Add(Math.Round((decimal) (rnd.NextDouble() - 0.5) * 100_000, 4));\n            }\n        }\n\n        [Benchmark]\n        public async Task WriteBatсhes()\n        {\n            await using var connection = new ClickHouseConnection(_connectionSettings);\n            await connection.OpenAsync();\n\n            await using var writer = connection.CreateColumnWriter(\"INSERT INTO ColumnWriterInsertBenchmarks VALUES\");\n\n            var id = new List<Guid>(BatchSize);\n            var str = new List<string?>(BatchSize);\n            var dt = new List<DateTime>(BatchSize);\n            var val = new List<decimal>(BatchSize);\n\n            var columns = new object[4];\n            columns[writer.GetOrdinal(\"id\")] = id;\n            columns[writer.GetOrdinal(\"str\")] = str;\n            columns[writer.GetOrdinal(\"dt\")] = dt;\n            columns[writer.GetOrdinal(\"val\")] = val;\n\n            for (int i = 0; i < Rows; i += BatchSize)\n            {\n                id.Clear();\n                str.Clear();\n                dt.Clear();\n                val.Clear();\n\n                var len = Math.Min(BatchSize, Rows - i);\n                for (int j = 0; j < len; j++)\n                {\n                    id.Add(_id[i + j]);\n                    str.Add(_str[i + j]);\n                    dt.Add(_dt[i + j]);\n                    val.Add(_val[i + j]);\n                }\n\n                await writer.WriteTableAsync(columns, len, CancellationToken.None);\n            }\n\n            await writer.EndWriteAsync(CancellationToken.None);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Benchmarks/InsertRowBenchmark.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing BenchmarkDotNet.Attributes;\n\nnamespace Octonica.ClickHouseClient.Benchmarks\n{\n    public class InsertRowBenchmark\n    {\n        private ClickHouseConnectionSettings _connectionSettings;\n\n        private List<Guid> _id;\n        private List<string?> _str;\n        private List<DateTime> _dt;\n        private List<decimal> _val;\n\n        [Params(50, 200)]\n        public int Rows;\n\n        [Params(true, false)]\n        public bool KeepConnection;\n\n        [GlobalSetup]\n        public void Setup()\n        {\n            _connectionSettings = ConnectionSettingsHelper.GetConnectionSettings();\n\n            using (var connection = new ClickHouseConnection(_connectionSettings))\n            {\n                connection.Open();\n\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS ColumnWriterInsertBenchmarks\");\n                cmd.ExecuteNonQuery();\n\n                cmd.CommandText = \"CREATE TABLE ColumnWriterInsertBenchmarks(id UUID, str Nullable(String), dt DateTime, val Decimal64(4)) ENGINE = Memory\";\n                cmd.ExecuteNonQuery();\n            }\n\n            _id = new List<Guid>(Rows);\n            _str = new List<string?>(Rows);\n            _dt = new List<DateTime>(Rows);\n            _val = new List<decimal>(Rows);\n\n            const string strSource =\n                \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\";\n\n            var now = DateTime.Now;\n            var rnd = new Random(2306);\n            for (int i = 0; i < Rows; i++)\n            {\n                _id.Add(Guid.NewGuid());\n                _str.Add(i % strSource.Length == 0 ? null : strSource.Substring(0, i % strSource.Length));\n                _dt.Add(now.AddSeconds(i));\n                _val.Add(Math.Round((decimal)(rnd.NextDouble() - 0.5) * 100_000, 4));\n            }\n        }\n\n        [Benchmark]\n        public async Task InsertWithColumnWriter()\n        {\n            var id = new List<Guid>(1) {Guid.Empty};\n            var str = new List<string?>(1) {null};\n            var dt = new List<DateTime>(1) {DateTime.MinValue};\n            var val = new List<decimal>(1) {0};\n\n            if (KeepConnection)\n            {\n                await using var connection = new ClickHouseConnection(_connectionSettings);\n                await connection.OpenAsync();\n                for (int i = 0; i < Rows; i++)\n                {\n                    await using var writer = connection.CreateColumnWriter(\"INSERT INTO ColumnWriterInsertBenchmarks VALUES\");\n\n                    var columns = new object[4];\n                    columns[writer.GetOrdinal(\"id\")] = id;\n                    columns[writer.GetOrdinal(\"str\")] = str;\n                    columns[writer.GetOrdinal(\"dt\")] = dt;\n                    columns[writer.GetOrdinal(\"val\")] = val;\n\n\n                    id[0] = _id[i];\n                    str[0] = _str[i];\n                    dt[0] = _dt[i];\n                    val[0] = _val[i];\n\n                    await writer.WriteTableAsync(columns, 1, CancellationToken.None);\n                    await writer.EndWriteAsync(CancellationToken.None);\n                }\n            }\n            else\n            {\n                for (int i = 0; i < Rows; i++)\n                {\n                    await using var connection = new ClickHouseConnection(_connectionSettings);\n                    await connection.OpenAsync();\n\n                    await using var writer = connection.CreateColumnWriter(\"INSERT INTO ColumnWriterInsertBenchmarks VALUES\");\n\n                    var columns = new object[4];\n                    columns[writer.GetOrdinal(\"id\")] = id;\n                    columns[writer.GetOrdinal(\"str\")] = str;\n                    columns[writer.GetOrdinal(\"dt\")] = dt;\n                    columns[writer.GetOrdinal(\"val\")] = val;\n\n                    id[0] = _id[i];\n                    str[0] = _str[i];\n                    dt[0] = _dt[i];\n                    val[0] = _val[i];\n\n                    await writer.WriteTableAsync(columns, 1, CancellationToken.None);\n                    //await writer.EndWriteAsync(CancellationToken.None);\n                }\n            }\n        }\n\n        [Benchmark]\n        public async Task InsertFromSelect()\n        {\n            if (KeepConnection)\n            {\n                await using var connection = new ClickHouseConnection(_connectionSettings);\n                await connection.OpenAsync();\n\n                for (int i = 0; i < Rows; i++)\n                {\n                    await using var cmd = connection.CreateCommand(\"INSERT INTO ColumnWriterInsertBenchmarks(id, str, dt, val) SELECT {id:UUID}, {str}, {dt}, {val}\");\n\n                    var idParam = new ClickHouseParameter(\"id\") {Value = _id[i].ToString(), ClickHouseDbType = ClickHouseDbType.String};\n                    cmd.Parameters.Add(idParam);\n\n                    var strParam = new ClickHouseParameter(\"str\") {Value = _str[i], ClickHouseDbType = ClickHouseDbType.String};\n                    cmd.Parameters.Add(strParam);\n\n                    var dtParam = new ClickHouseParameter(\"dt\") {Value = _dt[i], ClickHouseDbType = ClickHouseDbType.DateTime};\n                    cmd.Parameters.Add(dtParam);\n\n                    var valParam = new ClickHouseParameter(\"val\") {Value = _val[i], ClickHouseDbType = ClickHouseDbType.Decimal};\n                    cmd.Parameters.Add(valParam);\n\n                    await cmd.ExecuteNonQueryAsync();\n                }\n            }\n            else\n            {\n                for (int i = 0; i < Rows; i++)\n                {\n                    await using var connection = new ClickHouseConnection(_connectionSettings);\n                    await connection.OpenAsync();\n\n                    await using var cmd = connection.CreateCommand(\"INSERT INTO ColumnWriterInsertBenchmarks(id, str, dt, val) SELECT {id:UUID}, {str}, {dt}, {val}\");\n\n                    var idParam = new ClickHouseParameter(\"id\") {Value = _id[i].ToString(), ClickHouseDbType = ClickHouseDbType.String};\n                    cmd.Parameters.Add(idParam);\n\n                    var strParam = new ClickHouseParameter(\"str\") {Value = _str[i], ClickHouseDbType = ClickHouseDbType.String};\n                    cmd.Parameters.Add(strParam);\n\n                    var dtParam = new ClickHouseParameter(\"dt\") {Value = _dt[i], ClickHouseDbType = ClickHouseDbType.DateTime};\n                    cmd.Parameters.Add(dtParam);\n\n                    var valParam = new ClickHouseParameter(\"val\") {Value = _val[i], ClickHouseDbType = ClickHouseDbType.Decimal};\n                    cmd.Parameters.Add(valParam);\n\n                    await cmd.ExecuteNonQueryAsync();\n\n                    if (!KeepConnection)\n                    {\n                        await cmd.DisposeAsync();\n                        await connection.DisposeAsync();\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Benchmarks/Octonica.ClickHouseClient.Benchmarks.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netcoreapp3.1;net6.0;net8.0</TargetFrameworks>\n    <OutputType>Exe</OutputType>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"BenchmarkDotNet\" Version=\"0.13.11\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Octonica.ClickHouseClient\\Octonica.ClickHouseClient.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Compile Include=\"..\\ConnectionSettingsHelper.cs\" Link=\"ConnectionSettingsHelper.cs\" />\n\n    <None Update=\"clickHouse.dbconfig\" Condition=\"Exists('clickHouse.dbconfig')\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Benchmarks/Program.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing BenchmarkDotNet.Running;\n\nnamespace Octonica.ClickHouseClient.Benchmarks\n{\n    internal static class Program\n    {\n        private static void Main()\n        {\n            BenchmarkRunner.Run(typeof(Program).Assembly);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/AsyncTestFibSequence.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    internal sealed class AsyncTestFibSequence : IAsyncEnumerable<decimal>\n    {\n        public IAsyncEnumerator<decimal> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())\n        {\n            return new Enumerator();\n        }\n\n        private sealed class Enumerator : IAsyncEnumerator<decimal>\n        {\n            private decimal _prev;\n            private decimal _next = 1;\n\n            public decimal Current { get; private set; }\n\n            public ValueTask DisposeAsync()\n            {\n                return default;\n            }\n\n            public ValueTask<bool> MoveNextAsync()\n            {\n                _prev = Current;\n                Current = _next;\n                _next = _prev + Current;\n\n                return new ValueTask<bool>(true);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/CityHashTests.Data.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    partial class CityHashTests\n    {\n        // Expected values were genereated with the reference implementation of CityHash\n        // https://github.com/ClickHouse/ClickHouse/blob/master/contrib/cityhash102/include/city.h\n        private static readonly UInt64[,] testdata = new UInt64[kTestSize, 16]\n        {\n            {\n                C(0x9ae16a3b2f90404f), C(0x75106db890237a4a), C(0x3feac5f636039766), C(0x3df09dfc64c09a2b), C(0x3cb540c392e51e29), C(0x6b56343feac0663), C(0x5b7bc50fd8e8ad92),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa4c09bd9f0e45f0), C(0xeb83988ef66c657c), C(0x6b9ccf8681a18aa1), C(0x535daa5e388d3a90), C(0x74836eeafb7f7102), C(0x2a57492128885367), C(0xebc8ac93ca0466d2),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3be5e76f9931c681), C(0xb06fdde338ed51e1), C(0x47b3ccffbf2f6d07), C(0x5e54cfca59ba7e38), C(0xea2779909528f985), C(0x6c161777d6a8023), C(0xd1f3c5fb9996bc00),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1e9cc7d232c7a33f), C(0x236de88d608d66b4), C(0x43352ab6411f608), C(0x6315f182053dd353), C(0x9c8d9f1e3e37158a), C(0x1136a36ed4a9ffd9), C(0x393f1316596cf0be),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x6f1d652a9aa9382e), C(0xd5abb3825f0f754), C(0x7f51135ccd215f9a), C(0x6425ead2a90c7a2b), C(0x44037551d679aee), C(0xce5d08d7367b62d1), C(0x4795915c38e590f8),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3bd60c14f66bb809), C(0xb16e895fd5f365ec), C(0x51075d0889c013d2), C(0x6ad28d3665a43295), C(0x7fc37ed943e66dc8), C(0xad299ebe643bfb48), C(0x5bd04fbf925f6cce),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xbcbf47b8a67165c2), C(0xe0675209accbb9be), C(0x7308de500106588b), C(0x352b4f3f0772b9c7), C(0x6792aebe7398c194), C(0x95204503ef64f633), C(0xb6e4a03d70c094fc),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe067868dc58fd396), C(0xc8473c58086a01ed), C(0x360edd83935faa24), C(0x93c93a1af22dc970), C(0x809dd5b41b66c5e6), C(0x9f1d9d910cf52399), C(0xdbe481a61d1e7afe),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x6479c218e3b4a905), C(0xb70e630314f69053), C(0x91fbca514877b918), C(0x14049ebb94229b31), C(0xc3077ff5341e2606), C(0x40225f5626645bc0), C(0x7f1e41d06b6617bf),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x18009929c10980cf), C(0x1228aeb4388c73ce), C(0x161bf0c40170a6cf), C(0x62b03ff682658646), C(0xe6980864fd5d231c), C(0x522896654f825861), C(0xe0e758f820a5469c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xddf30a6fdce5d271), C(0xef58ff5ce35daf9a), C(0xd7e9b5802f25800e), C(0xb499363261c302d), C(0x101d05196bfd326b), C(0x2b3bcb83b73cbe96), C(0x962ea2a5be845d42),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc949210e09de308), C(0xa3a3a00d5e0bb375), C(0xd35d45563d3f80ee), C(0xf1f641038e0144a), C(0x5683a2db245de92a), C(0xab52a459db446624), C(0x6b20fd105557de34),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xdf87f848054247cb), C(0x161ecfb8370fd8d8), C(0x370313fa6788665e), C(0xeb71b5269045213b), C(0x62fb1a8ff15dfce5), C(0x150a9394f3fdb96b), C(0xcc0d5d64a6e18f2c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x6a0cb058805e4acd), C(0x301106bab8fc482d), C(0x4cf37c5a1e5798cb), C(0xd356b4644394283d), C(0x571aae72bac35532), C(0xc850f195952feda6), C(0x78414943a6ae6f0c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9433ace8412f255f), C(0x25c9902d7dcfcf86), C(0x4c10ac88117aa0a8), C(0x8babd16f6370ed49), C(0x7a7c7fa1019e2ebb), C(0x67a5714b7b594c9b), C(0x22d631afd621c2fd),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x723768cf3fa7b3a8), C(0x268a28711d788ba1), C(0xfb3792ee19b2da32), C(0x63bbd31777fbc65d), C(0x1b313de6f5b010ab), C(0xcb3434ed701a4f15), C(0x54594ab884cc93ae),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xd3362226822b1b09), C(0x54f213c110832024), C(0x58b8afceea142bea), C(0x38a6adc421343bda), C(0xdeac2b566cc7b6c7), C(0xbd9562ad101d7afa), C(0xa5609ca846fd88e1),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1dc2612576924bbb), C(0x5cf79870310a596a), C(0x8f1cc739a9efcc7), C(0x818c48dc83dd96df), C(0x46a1ff36301aa443), C(0x23bbde43de2cb214), C(0xa8c333112a243c8c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x5ee23a15102f6d57), C(0x6504de0d98e50ad5), C(0x5be10f78492f612a), C(0x298a7ba24ea61876), C(0xce1a0c9da8aa0d75), C(0x6b5a9a8f64ee1da6), C(0x9f74e86c6da69421),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9744fe3e5efa3407), C(0xc57f2bce8c2900cf), C(0x3456d73508cd04d7), C(0x5079f7dfed35a42d), C(0x141901f72878884f), C(0x491e400491cd4ece), C(0x7c19d3530ea3547f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe91e4a718994579c), C(0x31ae7ead8f1e2a04), C(0x343568a4ee4195be), C(0xd466bd089dc336b7), C(0x90eb9e1272e261a6), C(0x5f8b04a15ae42361), C(0xfc193363336453dd),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x32fd044b0766c3db), C(0xcd99c8567fafb46), C(0xb690bcc63bd8e56f), C(0xc22e4983680d6d0a), C(0xfc9a888ff205bf15), C(0xeb6ecbb0b831d185), C(0xe0168df5fad0c670),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb410fced2be12203), C(0x81741151bc486ee0), C(0x345169c236f0b0fa), C(0x9c9e3686134f0592), C(0x6ceeb3c6dbebee95), C(0x4e7f425bfac67ca7), C(0x9461b911a1c6d589),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa6a3e76de568fb61), C(0x1e350f999671b7da), C(0xb16703528b8ac03d), C(0x8c414acd75ca5463), C(0xa65ea81aeb84b69), C(0x210c7500995aa0e6), C(0x6c13190557106457),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xd169c59771e4a833), C(0xb5611e98efa87d2c), C(0x6be952171b8dccd1), C(0xb64af6857280d273), C(0xbacead50d7f6acc2), C(0x6bc63c666b5100e2), C(0xe0b056f1821752af),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xf8eadfaca72b6370), C(0x2ff5cd51c2eb1ed4), C(0x4a16ce6ad0c8515a), C(0x7823b92e0863cb7e), C(0xd6db820d0e150186), C(0xaa24dc2a9573d5fe), C(0xeb136daa89da5110),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xafcba310d3f470c3), C(0x23ecd39cf152688c), C(0x16829a08db73ebb6), C(0x756b3a471adbdea5), C(0xd24646c267ea1f2), C(0xe81f4c4a1989036a), C(0xd0f8db365f9d7e00),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x546ad0dbfb9f685a), C(0xe26387c036f6cbf8), C(0x5150a1967b016512), C(0xd80611a6e84c890e), C(0xf05ba9be87e230fc), C(0xf0c6624c4b098fd3), C(0x1bae2053e41fa4d9),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1ea4b6086d96b2f4), C(0x8fba4a5ee7a4f9a1), C(0xd9ce86c50368f487), C(0xd20b16270e4be8e2), C(0xdfa63433183a5d9a), C(0x3a9c8ed5a399d0a9), C(0x951b8d084691d4e4),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc14a1b73f3773c8d), C(0x66b2d5049fc7acf5), C(0x23c4ab45908b1748), C(0x7a9c7215c9ea097d), C(0x143994542aa22f70), C(0x9c0178848321c97a), C(0x9d934f814f4d6a3c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa7e9b9f09d6bf7b7), C(0x32367c98d0f88cc4), C(0x4ee1d87a4c49c5a6), C(0x3b44c5438b9ff383), C(0x9105b5e1500c9647), C(0x42b192d71f414b7a), C(0x79692cef44fa0206),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb4927ae39d53bc5b), C(0xa6c6866d19840c22), C(0x92b6d63a63813e2b), C(0x3f3a64f52594b108), C(0xdab4250729d6b9d9), C(0xfa5d6461e768dda2), C(0xcb3ce74e8ec4f906),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xf3fd396b782f2a99), C(0x2f665744d98ffe47), C(0x81563b58d0920a8a), C(0x42a19c6c32827264), C(0xf9dc632849387dc3), C(0xfecfe11e13a2bdb4), C(0x6c4fa0273d7db08c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x8264c5cfcbffd0d9), C(0xdaaf578999048a43), C(0xd1bc39ac5de6f891), C(0xac67bdf0bb8d9f74), C(0xa0d09bacb7e76c0), C(0x7bb50ffc9fac74b3), C(0x477e70ab2b347db2),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x8da705e357584a5), C(0xe13ce37fda1ed7e4), C(0x5e4c185e3cff62f8), C(0x3023524ef4e8258e), C(0xb19af60cdb67b5d9), C(0x5f97b9750e365411), C(0xe8cde7f93af49a3),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1e58797a3777956e), C(0x150c9d0cba45eefd), C(0xd9bef89fc1700ad7), C(0x4924076cbd02bb1e), C(0x2495980a4c9235d3), C(0xda90fa7c28c37478), C(0x5e9a2eafc670a88a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe67dc878f9fd0b48), C(0x88074dce3568a650), C(0x720a3a380e613bd3), C(0x6a11a71062e89ad7), C(0xca9b3eec85c6c145), C(0xf4b70a971855e732), C(0x40c7695aa3662afd),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x43c18dc2183a85d5), C(0x35bcb5695b12d7fa), C(0xaacaa54f2dd24278), C(0x7b1feddee46d2ba), C(0x7aab0e0158f3b6e1), C(0xcd6da30530f3ea89), C(0xb7f8b9a704e6cea1),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x2684ddfe1bff5138), C(0xcf162753dc0cb180), C(0xd46dc59006960658), C(0x9f341c8724a85fa), C(0xc8d4b4b9b45cbe88), C(0x39bf5e5fec82dcca), C(0x8ade56388901a619),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc36fcbd65f684b36), C(0x8ed97ca915e6d0bb), C(0x6f4d23e2f86ca1bf), C(0xc329bdb4844f36b6), C(0x3376eab257f59f92), C(0x82f3503f636aef1), C(0x5f78a282378b6bb0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xfcee317c5dd8e661), C(0x8dc1d0d5859b3506), C(0xdef6cc9601da20d7), C(0xfd39c446cb170097), C(0xae73bfa47b001bf), C(0xa644aff716928297), C(0xdd46aee73824b4ed),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x5fbe0cc098e6253c), C(0x2f42d10f6d5052d4), C(0xff61398e9dcc3e7c), C(0x8a621e89c6e83bee), C(0xbb3df752ed2694ae), C(0xfb53ab03b9ad0855), C(0x3664026c8fc669d7),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x4640e5467e2f959a), C(0x36093aa74487aecf), C(0xd164c7865659ae98), C(0x27fd0c01df4a9c27), C(0x40260438c2466d0), C(0x8e959533e35a766), C(0x347b7c22b75ae65f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x75a34298f73e9fca), C(0xff8e75b212dd392e), C(0x3afb8d169a8e019d), C(0x56f4c707cd4840a2), C(0xb68e5ef023989468), C(0x89ef1afca81f7de8), C(0xb1857db11985d296),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1e12b3799eddf003), C(0x67c21d7ecb76b350), C(0x660ceab6bcbc1edd), C(0x2e368afca726f268), C(0x2a76d35deb67e18d), C(0x380fad1e288d57e5), C(0xbf7c7e8ef0e3b83a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa048588e4cc6625), C(0x95dd0cdf4bf74697), C(0x8f4a977b4e0c8b62), C(0xb1ba563883cfa53b), C(0xd89ec69bf787188a), C(0x6b9406ead64079bf), C(0x11b28e20a573b7bd),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x7c37833af2b3509c), C(0xcbe4f7c6c28ceb7b), C(0x79edb6891ea45050), C(0x1b6701e3e342b1a1), C(0x38fc9980ef01ea41), C(0x236542255b2ad8d9), C(0x595d201a2c19d5bc),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x10ceeb033d30d7fc), C(0xc2487e1df5c7f59a), C(0x5e998a919e50d6a9), C(0xe39cfa7607d81faa), C(0xe823f27b3c488883), C(0x6189dfba34ed656c), C(0x91658f95836e5206),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xf7802b66e6485ce8), C(0xf215c799a475d715), C(0xbc2d80a71f2b7bf4), C(0xe5df8bd4af890217), C(0xa75799d36c309ae7), C(0xa674d85812c7cf6), C(0x63538c0351049940),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3b7b4b79e9530d39), C(0xf7412431374c78bc), C(0xec6c5d44efe184b8), C(0x445aa9c46792ce64), C(0x6f9cdad707b71b28), C(0xbdd7353230eb2b38), C(0xfad31fced7abade5),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xaa463be8aa9397f9), C(0xc2fc420945c4110f), C(0x8dd5a41fd502ce93), C(0x450e6c4ff53debce), C(0xb5e917d78f0cafb7), C(0x434e824cb3e0cd11), C(0x431a4d382e39d16e),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc979a7e1e48cc1cf), C(0x9435e19d02cdfc5b), C(0x593e99cee88e282c), C(0x1f33ab45c93068ff), C(0xcac5bf84c51c2460), C(0x7c6bf01c60436075), C(0xfa55161e7d9030b2),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9a4c3956f7076999), C(0xafc6ca28d864f66f), C(0xac2d7a8a41c1efd2), C(0x45b0669c4147730b), C(0x6d36b7066a9e2fb4), C(0xc3f40a2f40b3b213), C(0x6a784de68794492d),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x48e30a1ea00756ec), C(0xc3902be98a47c18a), C(0xf50ec4db63014e93), C(0xa6add4bec1720542), C(0xea03976e5cc1a86), C(0xfea3af64a413d0b2), C(0xd64d1810e83520fe),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x82e03f3aa98aff75), C(0xa0292ce77b71e2ce), C(0x7d577756324c6bd6), C(0x4c958567626d38b9), C(0x29e371e785317087), C(0x1ce621fd700fe396), C(0x686450d7a346878a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe1e5c15418a7eeb5), C(0xd566cf8d17a1a9c7), C(0x7f9d80e52fb3d5f7), C(0xe5cd49142616aa69), C(0x381106b646d128d1), C(0x7cc06361b86d0559), C(0x119b617a8c2be199),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9884a8d98c51bae1), C(0x3cb348ea1b3b54bc), C(0x152395417bb7ad30), C(0xed2fb9c7ced0af65), C(0xa8f4a145e9da5beb), C(0xdeb85613995c06ed), C(0xcbe1d957485a3ccd),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x74520d7ecc269ddd), C(0xf92219f5fe0a6758), C(0xee26f3756ceadd13), C(0x998bd660601f89e8), C(0x53268902d428ff32), C(0x84f80c832d71979c), C(0x229310f3ffbbf4c6),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc39671165e8b2bd9), C(0xb6c216f6f70a4132), C(0x78c47b6653f9ef0e), C(0xc035ce3bc0818dbf), C(0x3c2f0333e7b7dcea), C(0x8dddfbab930f6494), C(0x2ccf4b08f5d417a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3b7282fb96edc5f8), C(0xe566ebeb8b834ecb), C(0xd422702f7053db59), C(0x60dcc9edbf2d2956), C(0xaa1e49bcda347c20), C(0xfa722d4f243b4964), C(0x25f15800bffdd122),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x5cc607671cc54a0e), C(0xc4ba56cea4b9772a), C(0xc831d7b72fc7d3ee), C(0xd3da99c67e5df166), C(0x6ca2dae0b688e65a), C(0xa96dcc4d1f4782a7), C(0x102b62a82309dde5),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x34bb51ab07b5f566), C(0x17f6266960e96415), C(0x282ca656ad1b652c), C(0xdec0691980d9c025), C(0x7ce80f93472ee4a5), C(0x2d553ffbff3be99d), C(0xc91c4ee0cb563182),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x72622f2b10de6d6b), C(0x2767d831a05ff08a), C(0x33d5002e23da6ff2), C(0x54569b470cfed9eb), C(0x3b1ae2a607451568), C(0xa5c550166b3a142b), C(0x2f482b4e35327287),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb624e9707ea879f), C(0x912c7ccd5c4c8327), C(0xaa975b6fbbfa8a20), C(0x71565fb68cc48592), C(0xea86f9ff62cac9b0), C(0x9653efeed5897681), C(0xf5367ff83e9ebbb3),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x89eececb3a3883a4), C(0xb607c348c4742672), C(0x96565545dd9205f9), C(0x74565f3e108efa3c), C(0x29532b1d9c27ce0f), C(0xc0ca86b360746e96), C(0xaa679cc066a8040b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xbd23872256dbf216), C(0x8913bded7e0ef012), C(0x6a1fb4504ab0c356), C(0xef1b4e6e851c8139), C(0xb11db03ae4debfd5), C(0x814aadfacd217f1d), C(0x2754e3def1c405a9),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x432dc78e38b2b88c), C(0x339aef22ae2840f1), C(0xa9b553de119dd498), C(0xec2bdbf3b7569cf0), C(0x46a08ad7635bc4b9), C(0xfcc09198bb90bf9f), C(0xc5e077e41a65ba91),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x70c3594fcc5804ee), C(0x28e270597aee14d1), C(0x6f277e69c5feade3), C(0x510f304b3b59322a), C(0x4b9c45919dc0b3d6), C(0x308bd616d5460239), C(0x4fd33269f76783ea),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x64e124d478c1f963), C(0xcbd254de16ad2e91), C(0x2f07aa0b9f9502e7), C(0xc406f98356586912), C(0xaed868be6ada8078), C(0x24dc06833bf193a9), C(0x3c23308ba8e99d7e),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xab74ee59c2f9fd97), C(0xbca2dbba9276c039), C(0x2ec3d8c1d7f98f1f), C(0x406293ac4639aa79), C(0x42dc9622f9ed209a), C(0x301b11bf8a4d8ce8), C(0x73126fd45ab75de9),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x5fe253681540a9f9), C(0xfb27f3755950447d), C(0x9b2f2b84495d27ff), C(0xb2bdfa7552f0eeae), C(0xeddc2c0855761fe3), C(0x48f4ab74a35e95f2), C(0xcc1afcfd99a180e7),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x131598d20f7177d2), C(0x4dfa1ed83fc96ec5), C(0x8a8cf453d42a9703), C(0x807a4a531dc44c53), C(0xac569a40e2c6d83a), C(0xf2bc948cc4fc027c), C(0x8a8000c6066772a3),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc2865c947d4a3767), C(0x47454b1e8b9bfbb9), C(0xe0d26745d5e3100f), C(0xd8b7a5ad22fe9c66), C(0x951248494fc50d8b), C(0x178b4059e1a0afe5), C(0x6e2c96b7f58e5178),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xd6e457ce8f99dac7), C(0xf5887d014261a774), C(0xc0b87384ce98da79), C(0xd2a5b45192785dbe), C(0x26f3b91ba038588c), C(0x5f3b792b22f07297), C(0xfd64061f8be86811),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x7085f9a27f5c35f5), C(0x538e7930fad7e653), C(0xa0028f1e3c3317f0), C(0xab4dea70451fed36), C(0xa319425d4e740342), C(0x9fc3c4764037c3c9), C(0x2890c42fc0d972cf),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x40fde0167608e3c3), C(0x4bc14d90dd50da02), C(0x2b85e31097978d87), C(0x804139c438c8d74d), C(0xd3c1f9ed22af3414), C(0xf169e1f0b835279d), C(0x7498e432f9619b27),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe6690fd7fedd557d), C(0x1b2ecb79f8ff3d72), C(0x779ef63892ce8777), C(0xcc287c8eb296265), C(0x660d43c157382a4f), C(0xaa6cb5c4bafae741), C(0x739699951ca8c713),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa48744daddaf84c9), C(0xe4577cea3c5abc39), C(0x5a52424e98ddd071), C(0x9e1b8b7e452c588b), C(0x7ecf0a7a00893386), C(0xdd86c4d4cb6258e2), C(0xefa9857afd046c7f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x6b13a0100dedec79), C(0x6b90824ea06d6932), C(0xef3b744321d4cdd8), C(0x7cb7925c3a184c78), C(0x44ac1e5e256787b6), C(0x96d5c91970f2cb12), C(0x40fd28c43506c95d),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x8b2a0d2702916340), C(0xb1d422021f5263a6), C(0x4e6b643f71d8251b), C(0xa83ed5ce742683f5), C(0xe007bcc532d794c8), C(0x1fbe30982e78e6f0), C(0xa460a15dcf327e44),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x720b6ffba2808792), C(0x5a1eeb32e165345b), C(0xd94a75b38cbfb4cf), C(0x879f5a90d7acc458), C(0x42ffaf4b159b454), C(0xf1bf910d44bd84cb), C(0xb32c24c6a40272),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x5b9450322bc68263), C(0x19d28db5b1d30e00), C(0x9803fd62efa15d50), C(0xe87d8247a4150e38), C(0xe6963ebc11f4f246), C(0x30b078e76b0214e2), C(0x42954e6ad721b920),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe195aa10d7cfe8f5), C(0x9d95d76ab979902), C(0x157a8c5a2bd91648), C(0x39a41ecf8b3171cf), C(0x7c7da0e00f19072), C(0xaa0fe8d12f808f83), C(0x443e31d70873bb6b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x4aef776746299e4e), C(0xd0a4b408cf306ba0), C(0x1f143ad71cf8a24f), C(0xf1e08906155b04c0), C(0xc787e8e3cb94bd54), C(0x18e002679217c405), C(0xbd6d66e85332ae9f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc7846db10d5b0801), C(0xf633cb6792a68091), C(0x7085c4319a78feb3), C(0x4bc29b36ff94405), C(0x4e8d8a9e86c00ba8), C(0x89b563996d3a0b78), C(0x39b02413b23c3f08),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x468758d7c6dbea94), C(0x674a74eeaf00dbc5), C(0xd33a0ddcaf78c6b2), C(0x87dec3bd8bb1105b), C(0xad212ca93bdd2655), C(0x9723a9f4c08ad93a), C(0x5309596f48ab456b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3f161ad3ccda8159), C(0x31cbf83a3b530240), C(0x76287b00836006e4), C(0xf9bb65b54081f31f), C(0x152492732c55c78e), C(0x4f3db1c53eca2952), C(0xd24d69b3e9ef10f3),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x21445292adbf3c2a), C(0xe2f3840b2aa15aea), C(0x812f5a840916e28c), C(0x7b06f5b212130dad), C(0x9e5372da69654871), C(0x37b2cbdd973a3ac9), C(0x7b3223cd9c9497be),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x6fa4782a0971ddb3), C(0x9cab66441fa0f232), C(0xed6dc7aa143b3c52), C(0x5abbfa60974dede9), C(0x54f5af2e26435486), C(0x2b9f07f93a6c25b9), C(0x96f24ede2bdc0718),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x7fdd5b47ace106db), C(0xa5eb83de169efbdf), C(0x5180932a48b8613d), C(0xd37a588f65df8483), C(0x95ab6f21206b720d), C(0xc77dd1f881df2c54), C(0x62eac298ec226dc3),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc8cdb83ec1dda280), C(0x14befeb18c713b61), C(0x71171b5c6d90629e), C(0xbc0013b518146b1), C(0x5608cd39a26e2a17), C(0xd92f7ba9928a4ffe), C(0x53f56babdcae96a6),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc73d49846863238f), C(0x367a591a5dbe36bf), C(0x83028fb3929c300a), C(0xb4a028f94bb8694), C(0x337b48e93e007d93), C(0x7bab08fdd26ba0a4), C(0x7587743c18fe2475),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xbf929720d27d4e74), C(0xe08e048b4c1b938d), C(0x8733fb996da6284a), C(0x460773a58d947074), C(0x3cb4720c4e3c9db0), C(0xf4f12a5b1ac11f29), C(0x7db8bad81249dee4),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x316385a97273d36c), C(0x689329d08b30496f), C(0x33c2053bda670550), C(0x73d889808efeea48), C(0x362c95419f53fba0), C(0x8257a30062cb66f), C(0x6786f9b2dc1ff18a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa35d327b6158a633), C(0xdeba8db534b3b9fb), C(0x792c657844290eed), C(0x5a1bf5910ac5b207), C(0x8eb3a88f4bfeeb47), C(0x9e89ece0712db1c0), C(0x101d8274a711a54b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3091bf084dcca587), C(0xec98b7e268cbe95a), C(0x4eda4c50a24f3d41), C(0x38c0fbf61e9ebf87), C(0x298e3f20be872352), C(0x2140cec706b9d406), C(0x7b22429b131e9c72),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9a77f98e1bbce2cc), C(0xe66c9812b7e116c2), C(0x9216eac2bdaa1fbb), C(0x834c803b0e7b07fe), C(0x745287427a0dea5c), C(0x5ac8ca76a357eb1b), C(0x32b58308625661fb),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x43cb1634a58af4a5), C(0xad41864d25a9dedd), C(0x1dbbda7f2d79d2a8), C(0x4a0747dedfccfb03), C(0x2ce3ddcdb423f3de), C(0x4ad276b249a5d5dd), C(0x549a22a17c0cde12),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb3feb31e7021d1a3), C(0xf77d22f7e2d35ceb), C(0x1c51da411b82f89c), C(0xd61146c9f2c64d04), C(0x4fe0c67064eda9f1), C(0x8ebc520c227206fe), C(0xda3f861490f5d291),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xaf3cab5151956f32), C(0x368a869c04453884), C(0xb8ac4f4a15926e27), C(0x6201341ac742d736), C(0xb96d17e1c467f2b5), C(0xe42693d5b34e63ab), C(0x2f4ef2be67f62104),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x104c71fd5a44a02e), C(0x9ea3a815b5b10b6a), C(0x81d9e5892438948b), C(0xcf51207ad3d9feda), C(0x81bf069a1afdd68), C(0x37e9dfd950e7b692), C(0x80673be6a7888b87),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x7615abf4800d03de), C(0x3fbf88dba9021e20), C(0xf94c95c95d28d1c1), C(0x5a031558c7eefc15), C(0x8bc38c58f9e39b14), C(0x4438bae88ae28bf9), C(0xaa7eae72c9244a0d),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x2c54f6cab59038f0), C(0x981ef1f94d2d824f), C(0xc84d1476c2cfb3e4), C(0xd6aaa399a0834d4c), C(0xcb3b491b31150982), C(0xbfe279aed5cb4bc8), C(0x2a62508a467a22ff),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9660da1239deea6e), C(0xf60422e527d9863f), C(0xaf8371a3e5dd29a7), C(0x526b279848ce5ea3), C(0x5791d3f9afccd1ff), C(0x679c204ad3d9e766), C(0xb28e788878488dc1),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xf69cfc86f522ec99), C(0x36be168cf6a167e8), C(0x1b09ccc92f7f8a3c), C(0xa7f7eaf8470fc07b), C(0x5bd419533de91276), C(0xede23fb9a251771), C(0xbd617f2643324590),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x97baf65105fc2685), C(0x638fd69ae4a0b881), C(0x90dd12767578057c), C(0x1032c6fe2cdd44cb), C(0xc57077534741f5d0), C(0xc7c1eec455217145), C(0x6adfdc6e07602d42),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x99df889c7e3b45ef), C(0x74a8d52d38deaa58), C(0x9d4de60f6980b894), C(0xfa00e9b866b954a1), C(0x36bc9ad6e9e94ced), C(0xfcd6da5e5fae833a), C(0x51ed3c41f87f9118),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xf822e66918469e1d), C(0xadb1585d362485fc), C(0xe9bcedd28277901d), C(0x2f6ff1b1bf6c70a5), C(0x88ca571198019725), C(0x9841bf66d0462cd), C(0x79140c1c18536aeb),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1c2b96f94ec3addf), C(0x7dd1e7de52fae722), C(0x3fd087d70d3d1379), C(0x6a70f0bdd7b3d161), C(0x889bf55dfa6ac252), C(0xf6915c1562c7d82f), C(0xe4071d82a6dd71db),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x5b0eee1eb438ba44), C(0x837e8f053b8df92), C(0x96a9566d665a01bd), C(0xa7810b71a02a9d51), C(0xaf403849304bad56), C(0xcdfd34ba7d7b03eb), C(0x5061812ce6c88499),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe140edd9491c8219), C(0x53c05140c7343562), C(0xc5eaeede1cc02b2c), C(0xb8c077da249cacf2), C(0x947ee3d8f79a2063), C(0x6de42ba8672b9640), C(0xd0bccdb72c51c18),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa43b3b1cafcc5179), C(0x9585f7a3735259bf), C(0x17fe8f5f41d9da0a), C(0xef34535e2b79889), C(0x1bfcad698ab4f8af), C(0x345b793ccfa93055), C(0x932160fe802ca975),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x83dfc41bff95a7e), C(0x78744a43f096eb39), C(0xe20949ec0319e6c3), C(0x98e2d802c1ca71ac), C(0x66a515e92a5fe4ae), C(0xe30e4b2372171bdf), C(0xf3db986c4156f3cb),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x53c8cca8585ee88b), C(0x471a2284a3ffd625), C(0x77d248379356c42f), C(0x856aa76713e0a34b), C(0xe78aed261854142f), C(0xe9cc71ae64e3f09e), C(0xbef634bc978bac31),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x58022d172edc977a), C(0x6840d36b2c902ab4), C(0x1971575ebcf95124), C(0x61237b5567b9d77f), C(0xeefa2a1e718fa37), C(0xed186122d71bcc9f), C(0x8620017ab5f3ba3b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa3835b1aa4cb303c), C(0xf870fcd77b9c071e), C(0xa56dd5cafe5abe24), C(0x80e057b3e6bc4277), C(0x1e8ae5d04c7c0f25), C(0xcb1a9e85de5e4b8d), C(0xd4d12afb67a27659),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x617b2e32ad11dd34), C(0x1b4b6b7b4f7d0666), C(0x6842f84c8992fb54), C(0xec51051eb6767dac), C(0xd7bd6fd9ff408414), C(0xea3040bc0c717ef8), C(0x7617ab400dfadbc),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x33b7bee7e4424a18), C(0x1851732a22285037), C(0x355f95f09d27d3d6), C(0xa883e54b3868ce53), C(0x100a6a5bd79480d1), C(0x4f1cf4006e613b78), C(0x57c40c4db32bec3b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x893de34989d8236a), C(0x5e499786cade7880), C(0xd060a6e1bb81dfed), C(0x260c87edbb85731b), C(0x249e83d79b92cff3), C(0x4383a9236f8b5a2b), C(0x7bc1a64641d803a4),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb158d322a0072315), C(0x24ccefc413d23302), C(0xe7f509c413975392), C(0x8c70cbdb9ccef69a), C(0xb4499ee47fe49a01), C(0xfd9bd8d397abcfa3), C(0x8ccf0004aa86b795),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x5b2029e1ef27808d), C(0x94c7496316b6db04), C(0x8472b9f196d0b2ec), C(0xd5f47d8dbe8af661), C(0xe25368388c156618), C(0x8eccc7e4f3af3b51), C(0x381e54c3c8f1c7d0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xac9e5787db2f0335), C(0xd5ec72af78d37332), C(0xb4daded474f2df55), C(0x8863854124d5bcdb), C(0x3c3cf93aa88cf091), C(0xdbb71106cdbfea36), C(0x785239a742c6d26d),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xd21806e08aac3b7c), C(0xb450b7e770b216ee), C(0x987a7377bf634988), C(0x4c669adab9c2e6fb), C(0xfbfb5b46fda6e343), C(0x2e329a5be2c011b), C(0x73161c93331b14f9),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xbc9db4eea4997698), C(0x1236095365b1f3b5), C(0xe5b16b7e7e9fcc7a), C(0x63932710f6d1aa14), C(0x79d3852adadbf992), C(0xc46f0a7847f60c1d), C(0xaf1579c5797703cc),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x67bdfa2af2feba27), C(0x463f49d88fe9ef4c), C(0xebc7ce72b30feca2), C(0x51b0a1a586b23426), C(0xb2dd8b8908425506), C(0x1c842a07abab30cd), C(0xcd8124176bac01ac),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x132ef8b26a1af44c), C(0x31d0570b0c9172e6), C(0xdfa6a38b5281d9ce), C(0xe669872a046eb92e), C(0x2b4e1d0a3f12aa85), C(0xf18c7fcf34d1df47), C(0xdfb043419ecf1fa9),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x286163c54897c913), C(0x5296ee61f3aa836d), C(0x752341b4678f4b9b), C(0xeebb5e79fed4a57), C(0x181be485284dc229), C(0x65e9c1fd885aa932), C(0x354d4bc034ba8cbe),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xde8e77b45e031e8c), C(0x664b779ec74208ce), C(0xa1cfd23a083f2cfc), C(0x43ad050e96a213d9), C(0x6fec16412f85b3f2), C(0xc77f131cca38f761), C(0xc56ac3cf275be121),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x43991ac2dafb5583), C(0x2dd1fc0424ee9b3), C(0xb23ef52163a01c76), C(0x27162f4a83100f16), C(0x9795c377db26a7b3), C(0x429f935fba7a0e8e), C(0xe991298919233781),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x2367c7674f9b4186), C(0xd74629f1b793f794), C(0x87d41cb421bf7ce6), C(0x584f04ed0a17afd2), C(0xcd03dd8c237e355b), C(0x80a832b0db0aeaf4), C(0x9c3bbfb275fe9ae0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x4f0ccd886aeaa45e), C(0x19c6084bccd82397), C(0xecd5a9898d3f1f84), C(0x78751baea194bf90), C(0x9046ccb3aa41c895), C(0x4c3e06dc260108b1), C(0x7b638b432b07348a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3f93960447b0f41e), C(0x5af0021cd8c9190b), C(0xa6bb529625479c11), C(0x58c04fa3216cb06f), C(0x59352346e13a1e0d), C(0xfd3f257f00325a25), C(0x9654a1ba577d7e9d),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x655c4693fea0ebd8), C(0xf50ed27abf3ee825), C(0xd7d122541c5d7ce9), C(0x808ac6db23d2eed1), C(0x5175a45d64db90de), C(0xb36c6c4e0635e97e), C(0x91398aca83518c2),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xdc713b0c1df2e130), C(0x276b7a79bc6b0ced), C(0x72e523771a4d04a0), C(0x7422b3dc34b2d24b), C(0x7cebf518e224d3f4), C(0x18dee526144c3773), C(0x30a7fbac39654376),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1da8742a924ad91d), C(0xe4c71d72610e20c4), C(0x71478d6343b645c3), C(0x625a064671caf18c), C(0x63937950fcb40747), C(0x972f4978becff208), C(0x73e7c92ed536728f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa65e93532318d21f), C(0x55c1181234a06fa6), C(0xd173f8f5583d911a), C(0x7db04180e3d35ff8), C(0x6f320c7c9e9b4a7c), C(0x33173bc5f2e3126d), C(0x8dc4edca4f0e75bf),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xd33358c9d79d375c), C(0x17a29d209cf46ede), C(0xba4fdc94b2058549), C(0x401ea4f2f4153e7), C(0x37a5bce6c5293893), C(0xfc49fdea809e6a5f), C(0x8d11a0cf694bcd13),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x65ecc61fee720200), C(0x9b3bdd718af21376), C(0xc5822d258b317021), C(0x334b96c19026238e), C(0x7a53c4d15f609bd9), C(0xa24089e51527eef8), C(0x93db966ce70acd4f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x6eaf317841fb2460), C(0x8280182820128065), C(0x95c825fcca607219), C(0x58ccf9673d904af3), C(0xbfedaa65fb40e6a), C(0xc45db1f558aa82f4), C(0x97c2ced4c2b4ab74),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3b15db8385bd540), C(0x6f0b1039aed94e1e), C(0xcae94c7f7a26eac4), C(0x41173b7d6b9288f0), C(0x172219e1c0f497e6), C(0xd402acbff69d734d), C(0xcfdd50534210adb8),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x4d5b182ed7504c53), C(0xdf9bc0ce68d47f20), C(0x7295d33e7755e364), C(0x41b08efe0ddbe05c), C(0x3d3050d504800767), C(0x176d142d00a57fd5), C(0xa2555ad65af9306a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xd4148382ed0c1a7e), C(0xb14cb62bea7ac5f2), C(0xffdf43df3ac7bc00), C(0x96f811cd8969e73d), C(0x7d97d002cfd1227a), C(0xfec3f26f85392330), C(0x873830deaedc5c70),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xf941f66ef3b3adeb), C(0x77cfe23a5fa3bea), C(0xaa1f4494f8b91364), C(0x62c1e3cf3f15cf6c), C(0xb0c1eb21e2a8c03f), C(0x7e108b3a8f5ec19f), C(0x1e308430746f3cb1),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc4b133d694367236), C(0x6fb7ae799d346afb), C(0x67ad79a760ae67ad), C(0x2a145dd3c792d176), C(0xe1fe89ffd04e5da3), C(0xc4da2b0ed7d58249), C(0xb3e53011bf0871b9),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe891cf68fd2bd413), C(0xd9bb1af1cb4d5db8), C(0xd129d95cb295b5c8), C(0x6701ce79dccf629c), C(0x48b7f1bbc78e99b7), C(0x40db60c51e9b8163), C(0x16de75584883f55e),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x170066a856def440), C(0x9dffa440da4d7422), C(0x1fb0c45005f59f38), C(0xf9e0b220a4cc3ae3), C(0x10d6699ed3a4632f), C(0x760daefee72daf58), C(0xe9083d145ab2e0bc),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x799ddba2fa54a6ef), C(0xe97e8f8f25c9f229), C(0x798218d1c4282ff3), C(0x100c06ff2b6fa888), C(0xbf32d2f55480c3b9), C(0xb7b777930cd4bfb5), C(0xa77f5d1fc82e6f6d),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x884ac60dfb4dd572), C(0x11f66c9202ceff52), C(0x17499a442342f26f), C(0x9c70be20d262a3a0), C(0x2684adc62f176a50), C(0x6afadb8b1a75e546), C(0x3afc5789c5af9d7a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x204f1439f7fe19d), C(0x1a6adc9f21ab233b), C(0x85c15c91074f47c2), C(0xa7e43f22f693f0ab), C(0x64831b7dd7307ca6), C(0xf8272c673bda5d08), C(0xeeb94e53f2a14082),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x55636cd0d4b16c04), C(0xe5604c4e9276b03c), C(0x83056280084d66fe), C(0x1bf3971cc8e712d), C(0xcc256d2baabe8ab1), C(0x204a53ad0b713713), C(0x7e63318b704ac9dd),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xf9f64d26f7c8c01d), C(0x9da658b75222cdeb), C(0xbaffdc245a52c3b8), C(0x7e23d5127db80ff6), C(0x7005f02670bd673b), C(0xca806427f80d8854), C(0xa4ec224cb8f0cc6),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x18ec9cd25fa8119d), C(0x554102d88213f5bd), C(0xe39dd9dcd9c6d24d), C(0x7f8d11a8fc15894e), C(0xb2fe009f5acd820e), C(0x75ed64126bb12911), C(0xe521d97a3b2288f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x63cde81bc96b4d6d), C(0xe343ece780b0e7a), C(0x3b733bdb410e2d3), C(0x9d5022b97e51139a), C(0x5e06b3d4187b1fc0), C(0x44d63c9c8d363e73), C(0x81046b5941ed0960),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9cb75c87f14ceed9), C(0x9b3c0e13e9027232), C(0xc77fa6a661377d23), C(0x96dee94ab0958bba), C(0x5e8f414669d94369), C(0xee690496a6baf1fc), C(0xaafe74baabc01c56),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa4117387529414c7), C(0xeb14672cbf3eb81c), C(0x41748712f045f98b), C(0xf9734d66d84b3de9), C(0x15ec4f373a48f7fa), C(0x523ffc8d341e8b7), C(0x9f10cfb6f3927f30),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x772a7376f56a0090), C(0xc8dbbd6b446de59e), C(0xe204c2d56f7873ed), C(0x3850c4f19307486a), C(0xca0e073417d3b866), C(0xfc3929e65c2a4e7d), C(0x214774db5ead288c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xbbddfc834e0cad4b), C(0x269040ec2bf67759), C(0xc0f0b69b55d4a102), C(0xc9399463297b4910), C(0x330057f8dbec3aa6), C(0x23695fdf0cc4de7a), C(0x9aa0dda74cb06516),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xbe396d864e19a93c), C(0x63c3a33a58c673ef), C(0xed581cead350466b), C(0xee17af910951df8), C(0x1e95be7050c18ed9), C(0x4bb8288ed7ea8ea5), C(0xc7b5d22fd2db5bca),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x7098302f4b41637e), C(0x8d2d305464240bfd), C(0xe96dd76823e6d604), C(0x14023b6c2f6d77f0), C(0x5ef44110e4625b39), C(0x8287bfb7cfff5278), C(0x1365fc6bbd09f931),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1cf48de0dd627a04), C(0xf33a18f5b7706bd2), C(0xf42699a833e2dcfc), C(0x9d173eb3f4df3c3c), C(0xa42f4d6257186336), C(0x3fc50520484d054), C(0x7b53dbfa9e033583),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xd789d2adbc232f40), C(0x8cd5d3d39ef7108e), C(0xebb2e0810a61c118), C(0xe91166bf8596d49e), C(0xee7038b517b2d113), C(0xb710fdad0e8963be), C(0x85a664af226c21be),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x2494f64ad14c0de9), C(0x66202e9f9b552c1b), C(0xba704fbe82ff6c30), C(0x320a1fe6262695a5), C(0x3dfdf1cfbb185765), C(0x6fff46fa1f314a4), C(0xcd39f69512847bc5),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa4d5907f8da27d23), C(0xc22d8413497ad864), C(0xca32d49cc6a02cf3), C(0x190fc8fcf0a491d3), C(0xad97d786e3da3ad8), C(0x30c3ce2265fa97d0), C(0xbf0b97ec9afeb926),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3ed3ca4a7f7f58f7), C(0x33e65c2f5818c873), C(0xf0ce287d6353bf5e), C(0x1b6f34538beff4d3), C(0x3a3afa3fa25cdd95), C(0xfe45852346bb2c5b), C(0xeefbf422b4516e81),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x95d59381f9ac7ecd), C(0xd72c56d6ea3bac4a), C(0xc897016da43933d5), C(0x82752018b97573c1), C(0x32185472c1f99edf), C(0xf4675061253439f7), C(0x41d00e823c47ff59),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc502e5c7e7565602), C(0xe582dbb67fdcdcc6), C(0x456a76f7b8e3e664), C(0x8807bfa60b10aba0), C(0x1556cd5e29e43419), C(0x3582aa2014befce2), C(0x4a92bd41e14487a9),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x83a7b1eb536d182b), C(0xec1d9ac0ec0f4e40), C(0xf025d18aae2dac26), C(0x3a80953eb090e014), C(0xd3e89d4323d5de6d), C(0xc1907bb9794a9890), C(0x2e5832dae7816503),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x4c6f3ed4f750e475), C(0x5cc52e1a502f3238), C(0x6c50d400281b9e10), C(0xa01c7d96d49b3f65), C(0x6d8aafadaf0c81f), C(0xb13f37c3f0e42022), C(0xd2f498ce7efc1e5d),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x2bf900474df2148f), C(0xfe66283eef7f11d6), C(0x396aea9faad76eeb), C(0xe1304147104f04cf), C(0xecd289597aa47970), C(0x5493e823f3091de9), C(0x59019e4dcffe4299),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x15977d178f3686c), C(0xbd4ac1ec7122a2c4), C(0x1c0f7da89e71d818), C(0x99bbd042a39d5631), C(0x7f9ac288a488b5d0), C(0xa285cca06bf5faae), C(0xa789c39564df1d18),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x2ceac71488c8abef), C(0x35d30a7c89f5dc8c), C(0x14bf5ceb06edc93e), C(0x9ba08c30d1567e5c), C(0xa1eccd705e24ec37), C(0xb47749eefa75ef99), C(0x7a62f0237cf02f1e),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9c8106bddd57df2), C(0x1e6f04161fb31572), C(0x5b58a315c61f3f6a), C(0xf7cff9d97f92d2c6), C(0x65db7f7a131f1c94), C(0xf9587e5b26073810), C(0x13f29a82e559b126),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x8d2ad91d07da6228), C(0x9ffa877455e4aa7f), C(0x10479a69f55da0d6), C(0x3b852de9c74a84ee), C(0x8eacf7e1c2fe20ac), C(0xa690163b925dd001), C(0xeed3a1b49ad7fbbd),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x151ca7e559d107ea), C(0x8aa73fcb41287428), C(0xef95cb4dd0fc40bb), C(0x319876fc2d2aa105), C(0xc962292fc6df879f), C(0x1f1814ca73b99ab5), C(0x596942b5857ebec4),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1d5569440c345122), C(0x579b663380a10a3c), C(0xfeed5ff4329135bb), C(0x4a0fbf7f7e677fcc), C(0x4e1d74cfdb06c30b), C(0x4c38806477ec4456), C(0x48c228e8366d3f78),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb0d6c01aaa2de542), C(0x7a2e6a216b0b8478), C(0xabef48df3524ec19), C(0x9270d5f585fb2e21), C(0x7fcca364d4230e36), C(0xf947ec047f5e1e7e), C(0x5b6b5edb12fc801e),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x69a449fa00de6a79), C(0x5bfc50cfbb6888c5), C(0xab266dbcfa2af154), C(0x5164406923ad24de), C(0xa7e8189bf1af223e), C(0xb43e0625bec5a5), C(0xbfb5b7f4f9e19cc6),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xcaa8062e3fbbb7b3), C(0xd3355d19d1811ec3), C(0x66ca010b5f872aba), C(0x1cd04ae709d23c85), C(0x71a2ad83debd6d1), C(0xf8c864f7ed7546f1), C(0xa53bcec522b02457),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x22f0de7c1cd5bfa0), C(0x8bd50ce5180d1f20), C(0xf34491fe7c987cb9), C(0xcb4c22b0e31adbae), C(0x5e42a7e5aee0bbb2), C(0xb78ca4da20bb8b47), C(0xfe89d33c8b876e9a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc66c28b40515996b), C(0x6f6f5b71f263f0f1), C(0x78cba0e7a232f349), C(0xc8a5490a3bb1573b), C(0x79d269de8bc29c3a), C(0x646e7d85143d228), C(0xe87448cdcf9d66e4),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xccd4d37712f19e9f), C(0xe94ac54dc5903759), C(0x7b321166c18c6176), C(0x24535314244b7330), C(0xc89a22a4007ea1d8), C(0xe5d0ab7de64069d9), C(0xcdc548c002993e85),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x4b2af9244d1c3a29), C(0x816186c635f6e5e5), C(0xa5ecf748dd9a4dcc), C(0x500bff0f2d0ce317), C(0xb353b5d20a7bccd4), C(0x2231f510bf2dd8d7), C(0x291a42aec2551270),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x8bb2928cae20007f), C(0xca45e9108aa731d), C(0xac86a408946655f8), C(0xff86daf78607c033), C(0xfe6b88eed2ac0e9a), C(0x29794de866eeed6), C(0x2638b96181f9e332),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3573397bd4bec419), C(0x4daca2cec69e9f8b), C(0xddf59b60358edf53), C(0xcda340ea129ae7d4), C(0xcefe209561023c51), C(0x3a0dc7e70d015b41), C(0xa566caeda87afc3b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x82297f8ddec5c381), C(0x7783128b39eba8f2), C(0x48332d7a26e9fddd), C(0x199bc68457698731), C(0x92212c6630522124), C(0xedfc8b0cbe3fab0f), C(0xc41454201541567f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3cf70f6203baddaa), C(0x6c02d1f6faf9f909), C(0xb858f32b01eb298c), C(0xcf15dc0c3ebd8e28), C(0xb7c8fd6b297b25b2), C(0x3e57a9c015a6caac), C(0xbce97c88db4c44fb),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x7890a569eb2ea772), C(0xa074e9a5ed63e57b), C(0xe5a32c23f6f67c6a), C(0x446b99785ca87df1), C(0x60c29fef2146fb9c), C(0xd22f7a3f46aec10f), C(0xcbab77eb46cc43d0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x254ac030cadcdc14), C(0xe50aa7e239a22eb0), C(0xcc85d892d1af3d9e), C(0xd50850453fa6651f), C(0xab7a36f4758b2215), C(0x35008ea5e18ae602), C(0x4b0cead88935d49b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x939e1dfd562a6981), C(0xa09335ab24c30193), C(0x7834a74d024adc32), C(0x72a98a3030d22550), C(0xf09c825e5c7c04b7), C(0xef2b78d033ff9cc), C(0xfbc4febf8f3ea589),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x18191c1a357bfeb6), C(0xc29e4e6f7bbb967f), C(0xc9dc2ed80945c8c7), C(0x5d8b0f3dd89a8b0c), C(0x1a43985acb3a7d50), C(0x1d0dede22c8e58d9), C(0x781cb48e6c9e964f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x7a9db4f50f213fa9), C(0xb34a14db788ae496), C(0xc83c1a90f6718d94), C(0xf319edcaf1b23c5a), C(0x9585fb122b8c8989), C(0xb461bedc029bb60f), C(0xbe801390ce7453dc),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x808f618bfa395418), C(0xdbdbc68430a1b714), C(0x31c0c4c1612fefa6), C(0x9aa99ef81ac4dafa), C(0x4f255d17d83cf70b), C(0x8c77ed178655ac1e), C(0x2e074bf2d8d18a08),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3194b00cd82b5b35), C(0x6367748d8f89c37), C(0x1b8d71b40d656e55), C(0xb2cd3a5faba3a90f), C(0xd056eefce9c27e51), C(0x462ee8d7ecab34f1), C(0x6c2b8fc186ff5e02),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa2c5d544e7a5e908), C(0x8e52d92e13f6cfb6), C(0x5b272ba78306c66b), C(0xc8ab2f7b09b1b21f), C(0x4da41552813cd9a1), C(0x6ccefa934832edee), C(0x59d87c4c949cd0ae),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x45cc30d80cecd783), C(0x599caf75bf03555a), C(0xb947d88f7377e2d5), C(0xe5906c916ad3989b), C(0x9603034c10d98ab9), C(0xb7d0fe57a70a6c59), C(0xa5177e4ee6ae2552),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x6ae17204aac7b4e3), C(0x1ba2e4ae9b8b6387), C(0xbd90222e8700c2b8), C(0x8811a8980f471c15), C(0x92adad7ce8eb7e5c), C(0xacb058bad34deff4), C(0xbfe2ba89a7d773ad),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb5539d23f4c5d7f5), C(0x1242146bb85cf446), C(0xf804110b944ce9e7), C(0xb472b50e27d1e06b), C(0xab215f4721a7366c), C(0xcda2aed65ffb8124), C(0x1c2d88370353e208),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x825f26c1fff59ffb), C(0xfa8d944735bd0280), C(0x9d10a7c0b29e743e), C(0x2676c70f75f1c90b), C(0x6ebee46303b577d), C(0x3bb1c25892ec4519), C(0x62372ca16f756025),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x86bdd42a12554204), C(0x7b511c7c41915e7f), C(0x59e88745055b07a4), C(0xb8a2ff293ff6a169), C(0xd23b113b9a29f), C(0x44d37484a10cd134), C(0x7cabdab7e19fcbd5),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x2cb0a5225b886f60), C(0x5e40fead4f8b68af), C(0xec07e360a55c1ab8), C(0x1a9a1d50ba8747b3), C(0xfa33927aa2589e0), C(0x64e857e411303d8e), C(0xec65e619dd9a3ff),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x408125504020e0f6), C(0xde3a6a8f46b12fdc), C(0x9a3cd19b6436ff15), C(0x139153392a3d3c0e), C(0xaa7073e62e1320c7), C(0xbf78b34efcef8d78), C(0x84489c6bf230039),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x707a475edc0daeb8), C(0x29f8aed3cdcee18c), C(0x3e1a048f2ac2411f), C(0xee68213edbb058c7), C(0x8a7b7e337ea21548), C(0x64095a066fcaf625), C(0xdb5da1fd34c007aa),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe9984cee385a5a35), C(0xbbdc22f9228537ff), C(0x9aeceefe4da32a83), C(0xf5e5d4162f3ec779), C(0x223e46bd893a0ae), C(0x19ea31f459d65113), C(0xa180e41d8318798d),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x6496e6fcee023508), C(0xb26ee80260c15488), C(0x5207b2e806c78692), C(0xa8202cce61225e6b), C(0x2d2cf89ca32359a8), C(0xba8a59f7d9008e05), C(0x69fc1c16d4be8c58),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb6d5e5bf8bb2f964), C(0x732235aade21519c), C(0x12a58d17f8546b47), C(0x5666396be37f4a69), C(0x98e5b57f2ac97936), C(0xeb8788b72696127f), C(0x71c2989a8a53ebb4),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xdbb102307eed35fb), C(0xb3484a09d80aa994), C(0xcd100da053d56073), C(0xd19270dac980ced6), C(0x7b892e4650a7df0b), C(0x4e4d8950a28365b1), C(0x6f0cd7b6ba61d5e2),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x323629d0bd9d465f), C(0x310dd105d03a8d5b), C(0x531bce95d8bb0eeb), C(0x6e0ddcb9cc6d3937), C(0x753d3d390c2d9a32), C(0xcfb08f1795529489), C(0x7eaf598850ceda3),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe52af374f1d158be), C(0x1540d7337486830b), C(0x4e1a59660ad6e3f3), C(0x2191b4a5266f1740), C(0x41e3efa52d8dd39), C(0xc57887bb6ec91ed4), C(0x80d13a10713ec646),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x374c4ee36437b688), C(0x7d2ea1220d78ade0), C(0x4684b93b97de5ee6), C(0x65be14a8566e0643), C(0x6d444e179494fccb), C(0xd1141be601ee41b4), C(0xac76dc9245fc0837),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x13cec65c8d8c594c), C(0xbc9e42b6308f5df0), C(0x10726cad24db8336), C(0x10aa206327ab1e22), C(0xce43bf74dbdd04ef), C(0x9f82e94c161ea20c), C(0xe76c4bc30fcfa261),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xa6ef8d534eccf277), C(0xe88119e921bef254), C(0x2e0ec50e6fe979dc), C(0xf2ac5c3e12b9f171), C(0x2790e5110ce0524f), C(0x894b70e3c6e4afb3), C(0xa05105ad1319b8aa),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x821844d03da733d3), C(0xe557d6470b8f72e1), C(0x1deb4d3209b8d910), C(0x260d962023a628d7), C(0x184f8586b6fd7d81), C(0x25b98cb4875a4a67), C(0xd94535c207360a39),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x48566f12bee42c39), C(0x73656b26eea7e0c6), C(0x828ed07d0df800f2), C(0x3cf2b5f42d49d4ac), C(0x908e6cbb900b418), C(0x48cc84df6de8ccca), C(0xb993dbe71c0f6483),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb507ace18c7cca7b), C(0x2d061684043d6fd9), C(0xef1ef01d4ef75971), C(0x70d3bc91a638d3ec), C(0x6d68a1b462809c73), C(0x76f7eb2e29ee3a9d), C(0xb80d88b56a67ca36),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xf1253a63d880e37), C(0x7f2e1644d1d3357c), C(0xc1965b600a8df257), C(0x99bdbcf20507d18a), C(0xeb9009144434ff3a), C(0x38d4e4846157a928), C(0x4aea39d3ae8cd0c1),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x4a8340e68817da1e), C(0x137c6930cf9d0c04), C(0xd91cf5b059b6cd83), C(0x1c68d57e8bddb0f9), C(0xc38434f1d79fda98), C(0xbd754a99bcd8570c), C(0xb8ae64ca5bbcc99a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x59f6e03f588123ef), C(0x20fec99e9b33e2b0), C(0x85112fc684babffc), C(0x75e0ecb17e8a20a1), C(0xc9bb1eceaa6af6cb), C(0xb1b17b903b449aa0), C(0x91ba2c376af7ac61),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x76dca7203e5a5b5b), C(0xaa7adac414ebf447), C(0x731b56fa9fde24ae), C(0x346d3eebb7384335), C(0x242743d1a683240), C(0x324e97d0881c73d5), C(0xb9543da36e81325c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3a3beca1e183b2c7), C(0xe0699a97e3e8f816), C(0x84f22445fdc824d), C(0x6f45bcb999f800d3), C(0x534ce0b0a43511ef), C(0x254dd4f7fb14d078), C(0xe6140575c20d4632),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xd0edbab00bb3b980), C(0x49202c3135bc6708), C(0xb44476275e7f5bd9), C(0x66648f4f22c62dc3), C(0x79ba17f561e2c0c3), C(0x35f877a56be052e9), C(0x37aaf15f89e71c97),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xf2fcebfde8df2670), C(0x85e149139c7acb6), C(0x34a39ac3cef68ea7), C(0x3f8b01fd6ac57ca2), C(0x2b1f8573886d22c), C(0xdcf96c8ae5655697), C(0x240e3f7b77d260b3),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x66e01c952e00b5a3), C(0x2c6772345fe7a505), C(0xce3c61ac41f0cf28), C(0xb380f6a78e765366), C(0xf17f6b377a788913), C(0x697fff4db9b53e14), C(0x43492d9c8b5f505c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x20e2597df93b1d82), C(0x4c72c0d07d634086), C(0x84e90170550c8c87), C(0xc50422963ab45fea), C(0xa0d6ef7e925e011e), C(0x1814011b139fceb1), C(0xfda187586fcde26c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x41e3d38b169f527), C(0x2db5c0fbfb5678b3), C(0xbb6240e6b7ae0db1), C(0x94c3658f08ecc6df), C(0x95945b99d1e01baa), C(0x61d90373b9976206), C(0x3f6ded31a41fe6e3),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xcd413b22363ce08a), C(0xd03aab9932f8b25c), C(0xa85bc04a7044ae7e), C(0xcc4775e95f98e5b0), C(0x747a77374bda0ede), C(0x75599f9f86046db7), C(0x36ee979420c7fd55),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1a40632178de533b), C(0xa53d9a96304c96ae), C(0xb4f85409add3e762), C(0xb3bc1b4898b31850), C(0x45a5f9af894df43f), C(0x8d8d51189542e147), C(0xa6a30fc863d6cc9b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x333def8cc13e6e5c), C(0xaede2b5a56d901db), C(0xb0dc2f7bd8605a93), C(0x3facfd9eb3180cc2), C(0x3f9010d5547aca38), C(0xd9b64964a562025c), C(0xcca5a239a7c11eb5),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x150a02c0af1d77f3), C(0xb9c0d3220c4bcb9e), C(0xccff413f0f13edd6), C(0xc4dde59bbf5e76ee), C(0x907e2c5e26fffc4c), C(0x6f8ce9bccf73980), C(0xb357053ef46b2411),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x95371497e074c817), C(0xa4151bbfb3341906), C(0xe5eef2a8eb18f5ee), C(0x67421b8a2b3454a3), C(0xf023fb99e6ff786a), C(0x8bbf1e63ae4555bb), C(0x1cd62fc126da4919),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb2bf9ed25d4c8309), C(0x19b620024f4a300f), C(0x1c6302f562942154), C(0xceba65bd88bf1d54), C(0xfbcc06f5fc6aa58), C(0xfef6b08a85f0c99f), C(0xf90d5788e574596f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x43391fb6d18c5538), C(0x7726591f70780aeb), C(0x1d3c6d82429ef36a), C(0x9932919799803aa5), C(0xcb1df42ecdb266c4), C(0xf83b3e9b3b4ec8d8), C(0x11320a5b32947007),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x2e1ea34669165696), C(0xf279aba13156ee65), C(0x84df8da7a137b346), C(0xce472980208f4301), C(0x9636492ebc6fcece), C(0xdfa3c0b96ce21c53), C(0xb165b931de25ad56),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x7f86624c2d4e6042), C(0x912c902f503e14f3), C(0x5a29bf6d021a8760), C(0xbad24f8b9e93f147), C(0xbde72955da1efb39), C(0xbd7de638c72bc20b), C(0x40dc53e2c1ae326),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x49682b3f2dbb2029), C(0x394cda9bd2137e30), C(0x1488f36480c8ae8e), C(0x427dbf4126eeb4bc), C(0xa6e6f536feed3543), C(0xc811a91f3c069063), C(0x2ae528cd45dae0da),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xf5120254c6f3254e), C(0x431c28f66c32c920), C(0xc352ef16416589e2), C(0xad2af2b85df04ebf), C(0xf1d9b1458e2c52d9), C(0xe13de9a1fbd1aa2), C(0x17ff3a50753968b0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x32322576dfa25afc), C(0xa0242f6aef829b4e), C(0xc7ebeef52ccbcb25), C(0x16d62a1715528f92), C(0x2d4db8f008ad36a7), C(0xf994e1e405664c82), C(0x4b26e6adb4a6b556),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x66093fe4d2cad02), C(0x21cbad2d7a679fc), C(0xfa8df1e26e9df524), C(0x3b2f255a374ef002), C(0x6e3d77bc6ff5b217), C(0x888b6f6e6a5a9945), C(0x7d3324c13fb3e7ba),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x710ae7b1967dd256), C(0x1b51927b9f4dca3), C(0x9c24f65fce7de3b8), C(0xacd81f0b50814169), C(0xe34bee74572aa8b5), C(0xfe341050acd3b0b0), C(0x4599e7919722d842),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xbfa94295a75a74a9), C(0x428e96e3140ab83), C(0xebb411d3f9efaaa6), C(0xa4ca6e0ce09e138), C(0x504fb7336b9494d4), C(0xcb4253998a2e4c44), C(0xb16341c5e3853c02),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x4c6bc11b3e283546), C(0xc183162862a2693c), C(0x378a84cd0a2c2bec), C(0xe1d5ec1998052c75), C(0x7d9ef7b1088f7234), C(0xb66554281b0a1d31), C(0x85973ab849e93af0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x6b453210638ed039), C(0x6942a00e246bdfb1), C(0xf31dfc9986556060), C(0xb0f5e75947e76a6d), C(0x90c05b62642d3a24), C(0x930777028ebdfd58), C(0x25e83fb4e5bf341),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xfc6f39364fea6c32), C(0x585f983084da141d), C(0x3163e554aaf2c567), C(0x7d08a14d8d4c9317), C(0x42587a14e16f4fdb), C(0x47569062b450f6d7), C(0x98c4f6b7fe765461),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc6cde2e1487fa19d), C(0xe37fcf98fba7a664), C(0xe8d34907bb461ad), C(0x12a36abff6b29f29), C(0x3a5316a8502a35c2), C(0x7c93aaa35da06f5a), C(0xb32d0a314d7741ae),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc8e2e79ae7aeb0f1), C(0x10a274633805b953), C(0xe65c22a227bf96d7), C(0xd12dc763af6d81ea), C(0x17c9ad9f93280391), C(0x4f7c819724884dee), C(0x80f21ba5ffe98951),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x473b58475b95f2c3), C(0x49304f84cb5189d6), C(0x415bc1c0f72c1248), C(0x78c99f6b8046eebc), C(0x819b61aa6d979caf), C(0xe2bd7e31bce1bd8b), C(0x7e5f16df540ee87f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x23db4825404759e7), C(0x54b75cfed0eabd78), C(0xdda7dad48f449392), C(0xe2e68edd6ae8f1f7), C(0x8474c9e6a68ac6f5), C(0x186a8e05ac60cbc2), C(0xdd3b2f844b5a9ab3),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x94a66d5551fa7da8), C(0x6271508fddd43aa0), C(0xcc43556efe0c881b), C(0x71d8ea7a4e2da192), C(0x5cc61c50808b92b0), C(0xb370ae85d547e21b), C(0xc7738839125270a2),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xbfd6a834e74cba20), C(0x40c58c15b662ae35), C(0xbe483f266619c98d), C(0x4436f48df59b3103), C(0x4b59e29a30dc3b2e), C(0xe23c6f7a4ed2eec1), C(0x96598d8a6ec3d3a0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3b94c1621611b5d3), C(0x40218d960cebc668), C(0x88bb9de9f21d8439), C(0xb1d7cc8c9d5489e7), C(0xbb8073866bca352f), C(0xa9bdbd31ed2bc54f), C(0xea07ca18ff70af),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc2f345bf5ffea6a8), C(0xb323b0956324cca3), C(0x96c0d47cc60e6dbc), C(0xdb71521a37f2c1da), C(0x5a92d6e935d78fb7), C(0x6d212cafd181957a), C(0x95a3e096ca96c289),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x2168600d492821a), C(0x3200f838d8d40893), C(0x2262f28374632720), C(0xa4b40a910cf02d0e), C(0xe0069718e4c0fb04), C(0x64418037c4dc60c0), C(0xb997faf4958c451c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9a4042e5e515051c), C(0xeba8e08b0eb2cf5e), C(0xe9d8adf2dc14126b), C(0x9ff9a47ac9506d80), C(0xdc52ddd79d5cc38e), C(0xe6c1ce8393e7e850), C(0xe114cd1ccecda312),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x45e534343b36b57d), C(0x1d66a3c528c0d9cd), C(0x41eb1cdf565297b6), C(0x537df3a47050fc25), C(0xfc197ef3283f78ee), C(0x6b893f5021b46292), C(0x95aca821e70a362b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc168e1c665ad91ac), C(0x114759de60c852f5), C(0x205c9226b8ac4edc), C(0xb7c3f2183f96752), C(0x8cc3ebc780253db1), C(0x460e4a4949028369), C(0x824baf2a2d8250b4),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xd67661e9194d8215), C(0xda86d09543ad2d65), C(0x1cc96449839fd9d0), C(0x46818057af60d930), C(0x5991e521b7791255), C(0x10f85eb83e7f3513), C(0xf792a79849fd7f9a),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x755fde5bdfc44bfe), C(0xdb9ea1766c5f9c2e), C(0x718982318d3a1c64), C(0x847089cb841ff845), C(0x2307b8de874ed911), C(0xe12b3cf7cb7ddf01), C(0xc0f88460363c7c82),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x5ea8f71a8db73a43), C(0x5db74aceab3217ad), C(0x571b6e5308de4096), C(0x70745db0fcab1747), C(0xd5760bf37e70a616), C(0x148b292497695a17), C(0x7f25e8fe3207381d),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x469cdd38580cb319), C(0xce17f9268976005e), C(0x6fe0eb74927886b6), C(0x8c2c8f28aca17175), C(0x19c2f98273f000e6), C(0x5a3b34e0716f4702), C(0x349689e263bc91de),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x597c55972df98998), C(0x63d385818b67740e), C(0x800f53ebf8d939ee), C(0x9a467f399df6431f), C(0x8611b0947c4b03f2), C(0x32ab7af66286ba81), C(0xe2892f675cd6c5ad),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x8568c8479cb896f5), C(0x681dbd2a958dbd59), C(0xe949c6e65ff97daa), C(0x62b5dafed1fe308e), C(0x10e16cb6caa9d77a), C(0xcdd57f0442340fd3), C(0x4523f3ba5cef14d0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x1fc87401a28fff8a), C(0x7b36a64698569a26), C(0x4345702479af8cca), C(0xa1d036b397702b2d), C(0xd42a5a91d9d5a575), C(0x88ca9d88ba6ddd4c), C(0x5f71f09e5b451226),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x928460ee79e97cc9), C(0x8910f01505cee9e2), C(0x2d596829ea99f3ae), C(0xee49a43efc63f6dc), C(0xa5d3e38b71c4dc3a), C(0x39f4900072cebead), C(0xa751e82b791dddc2),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x7e16adbb5d07ddbe), C(0x370ec6b51fe542a4), C(0xb1f18b985a06e694), C(0x25ae06f58136cdb7), C(0xf0e900e340023ac8), C(0x3d93c688274b90ce), C(0x8c43f98eb39744e7),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9b5d61b2b6d05a1c), C(0xcb1a5c97c3e83a04), C(0x38baf280e96fdad4), C(0x2615071835299ffd), C(0xdc3e6454981dd9f6), C(0x59c57d05d906916b), C(0xd217d52efd005b07),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xac364c5d44555598), C(0xb95b3aafe7c0f61), C(0x241928765076a8c5), C(0x15286f1de007cdcd), C(0xebdcf20a1e42822c), C(0x878c39e0c90cad81), C(0xf0f25302dbd3002),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x98c9764114d06cb), C(0xb929b5bd1f88ec7e), C(0xea0fdb316076c5f6), C(0x4a6d9608b0c0cd06), C(0xacd1afb5eaac7db5), C(0xff2be69a7769cb56), C(0xaba05bacfdd04f51),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xcb45892c5b62dccd), C(0x26244e8009a6757b), C(0xe10a86fc4738645e), C(0xa96a92a4a99e3d44), C(0x29150038e3a4da0b), C(0x379850aa8e09e651), C(0x64df693f3e228f16),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x84b4b8081de6c1d7), C(0xd5cca7fd2698e028), C(0x21dc6f2e60fa15f), C(0xdca769b8d56826e2), C(0xa999ab399268d1b1), C(0x70a703feba7c33c), C(0x53703405c1760a5),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9c80d0fb3c9a55d0), C(0x63fc3876a8f722a5), C(0xc108163a0da25f67), C(0x6fb56535b583ff1c), C(0x644dc8bc73364103), C(0x61a547684a3608ef), C(0xf3b120221dd9697d),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x540dabc6272e4b41), C(0x4c7508c2ba64b41d), C(0xeb52dd0e7e952501), C(0xf8d9a627dfd4d84d), C(0x4ea474e6bf766969), C(0xf916e2bcaeb83cf0), C(0x423f790d186051da),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x9b419d153385d259), C(0xb43c3bf5f33c12ee), C(0x5ccbcb21c6f085f3), C(0x99dc2fe7d318170), C(0x7a683261d7060fa8), C(0x350ad306bb7ccaac), C(0xbc3058d6e9921bcf),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xdd486d89bbb2a036), C(0x994c8cad09d1cc27), C(0xb86b6543fc6276ed), C(0x8cc69f3ac9ad58a6), C(0x6ef789683080f21f), C(0xb0a76477cc76407c), C(0xb5d634fd4ff0b9ce),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x6e02793fe8655e19), C(0xb49df281baf622da), C(0x5daf444b08f1724a), C(0x606fb7bd9de22458), C(0xc57c540c38311bf4), C(0xff533ef545b26999), C(0xc8ff449ebbd7da07),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb70edd368850e8cf), C(0xe4cb09362bf7cbf8), C(0xe1a21bd2de10df3c), C(0xbdc731127a12b873), C(0x4deb186e645edf8b), C(0x22c45afcfc3735ad), C(0x74ba9d334bb2e45c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xea6cf19f08e837d8), C(0xbfb7bc0773ca0972), C(0x7562496d26ba4e1c), C(0x309d751439f8cf4d), C(0x5d2583b4ed252c0), C(0xc12c6bf49806e465), C(0x71c6f349f7fb9236),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x4d197ae8f8c3523c), C(0x229dbe986014142d), C(0xc4dfa4c84d7f1125), C(0x5ec6683b7b2d1ee), C(0x2aed9f9cb5551531), C(0xd808e0a60d103428), C(0x162ba9dd2b749c63),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xddc514c5873048e1), C(0x24f6bbf3f0e63fde), C(0x29eac02af97de173), C(0xe628431493cb1598), C(0x7b8a889067a30d77), C(0xac25a0d190e03a14), C(0x6ade3e5cc83dfee7),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x49d4f98cb205cc9c), C(0xf3a3c7276ad923a4), C(0xf4a950ed9fe81ea9), C(0xe3c9c727815b69b7), C(0x2e62aaa96aea8969), C(0x69a2a38440a6d73c), C(0x1efcab6a109b6869),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x43c06116da2c6bd5), C(0x93e15b613f57f894), C(0x685ef99f855158aa), C(0xa69cd689d8f2724c), C(0x543186f1b59c7f0f), C(0xb818680983e7557), C(0x7659de18b274cbc8),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe5fe62e8b9a71c7a), C(0xb1f728ceda69d96f), C(0xfb916ec8ffd3c2b3), C(0x95f969dce1309381), C(0x18114e0511a57ce3), C(0x1ab2bf825a1d7e46), C(0x1eaabde6e5ff4962),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x164bb7f1d4e95803), C(0xc018bac02cf6023e), C(0x3001945c3463af44), C(0x8e5139fa1ef6b699), C(0x9b39a84475f0452), C(0xc4dc645e63193f61), C(0x402ff137e1021713),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xc4854498c31ab511), C(0xd3679668fa3978d4), C(0x5c9da1b987ca9d15), C(0xb4c8e8ba61b3ec04), C(0xfd894032e835fa18), C(0xd96f29fc7749c1a9), C(0x2c256fa1cc416ef7),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x2737d887d16334ee), C(0x39c454733b947f38), C(0xe8d56c31c29c82b0), C(0x4ab06a5c101ed75d), C(0xc265a403c3743bc2), C(0xe6bfa65c3681e7f5), C(0x44022a8bbe431c8e),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x638135132965ee3b), C(0x58e998c0c0198517), C(0xb8427ac4983ff0f6), C(0x250c492e3fe6014a), C(0xe508f7260312b192), C(0xd922903ae658b136), C(0x6b837dcd9bc8371e),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x22fa4df1fc7d7c1e), C(0x5d9eb9560a6830be), C(0xb6510889a4c6c199), C(0x48b36e190eb1e880), C(0x5b0981b3baca8559), C(0xb38d0a3946c6ba5a), C(0x288b25eaeba9c4c6),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x3b3363ce879dea68), C(0x7bc4a7bc73c61123), C(0xf4945426df80733a), C(0x3e9a8e4a49281c53), C(0x3159f8b713632101), C(0x9e1df1ae59800ff2), C(0xa6b20cc3eea56a8c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x5954b10428c23976), C(0xd58548f56e31e115), C(0x9fefeb60f97b3375), C(0x1c287cd84f8a50d1), C(0xd7ac67389fe5c511), C(0x3168f7bf076a315f), C(0xa9871b5f116c5d2c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x76b9a43060ef413e), C(0x25f326310feb2c7d), C(0xca96a690b8d0ea42), C(0x98a7e59d5ceb10a2), C(0xae4ec395375f4375), C(0x2e7265f625a6bc33), C(0xa51bc362dbf05fbb),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xae8372ddf645b30d), C(0x7356f94bb11f6549), C(0x75da652315d1a09c), C(0xe509a1f9480f34c), C(0x2e9fa5c3a84733ec), C(0x8063c65a7a1d4b90), C(0xf3554f5b50d443e3),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xd657c0473ec3119), C(0x99924c332a698786), C(0x17a95f45db8cac70), C(0x7fd31d283d81edc6), C(0xed8e4bb93b5157a3), C(0x26232db6f91c32d6), C(0xd4a772807fa51b3e),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x72bb8dc446ce6a6a), C(0xc345f676420a2ac2), C(0xfa49460681f4ee11), C(0x2e6e0409152ebe85), C(0x73cb78ab751c370), C(0x8ff3d5c4440d0ce0), C(0x7ffedc68ebf9b662),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x68562a0f5145373a), C(0xbb476fe63e2b4f56), C(0xce59beee3118ad3c), C(0x3ec3955403c766dc), C(0x1f5ce88203477753), C(0x79bc267699ae2f7), C(0x88400bfa09690bb2),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x37f7641651e95480), C(0x3fd5f20886756d1b), C(0x23ef7d5b02f7962b), C(0xc28da0cf67fbc584), C(0x13766e5700129c14), C(0x37402d371512a872), C(0xd6ea21778b77dcb2),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x57fd773d6afab1c2), C(0x4b859c43da103b95), C(0x9cced0296e31f1c7), C(0xb2c88e6b067decb4), C(0x39c6cf857fc1d673), C(0xfc3de39b07ea8bbf), C(0xfda8915492cb2f2b),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xb94e8a87235bcc93), C(0xcb9a8812c82f44f7), C(0x8760581f4501637e), C(0xc59cf7334dd9ecf4), C(0x8bb4d19143d61efc), C(0xceb885ff6a84154a), C(0x97674a15d034ea9f),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xdd611b418ed944af), C(0xc1b61857bbbddcb7), C(0x55da3f71b4f8de2), C(0xac548553d784b332), C(0xbcad57e9fd394e9), C(0xedb5dbf34cf563a), C(0x5c8b09dedbd2006),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x995d1a2e823ec116), C(0x202feba5224a0d9d), C(0xf1a57c9f687135e0), C(0xa93e11ef9795623c), C(0x3db7eb94f4bf9d2a), C(0x9cd41eff9189ea31), C(0xba884150d2b4aa21),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0xe86d78f841f3946f), C(0x901ddc4adbc5c875), C(0xd3605ac52698b68b), C(0x8308c55d9f34fd39), C(0x43fa8e525ff8c46f), C(0x811c48869b053a91), C(0x816a4f0731366a2e),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x90aa9f8973cb1f60), C(0xf839f47021d2fb8b), C(0xc1b24260abc8913c), C(0x3038c5915608c16c), C(0x51c1f94d3d1cf33b), C(0x9aec5642526a9065), C(0xa2e0b9c0eeb73b3c),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n            {\n                C(0x201188a03a94bc94), C(0xcb76199fe78b5322), C(0xdb0e67ae3ab570e1), C(0x1e19a3882d1b408b), C(0x2353bd7982090d22), C(0xdb1a97e53cea4262), C(0x8c29ad1f6339693e),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0), C(0x0), C(0x0), C(0x0),\n                C(0x0)\n            },\n        };\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/CityHashTests.cs",
    "content": "﻿#region License\n// Copyright (c) 2011 Google, Inc.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n#endregion\n\nusing System;\nusing System.Buffers;\nusing Octonica.ClickHouseClient.Protocol;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public partial class CityHashTests\n    {\n        const UInt64 k0 = 0xc3a5c85c97cb3127UL;\n        const UInt64 kSeed0 = 1234567;\n        const UInt64 kSeed1 = k0;\n        static readonly UInt128 kSeed128 = new UInt128(kSeed1, kSeed0);\n        const int kDataSize = 1 << 20;\n        const int kTestSize = 300;\n\n        byte[]? data;\n\n        // Initialize data to pseudorandom values.\n        void setup()\n        {\n            data = new byte[kDataSize];\n            UInt64 a = 9;\n            UInt64 b = 777;\n            for (int i = 0; i < kDataSize; i++)\n            {\n                a += b;\n                b += a;\n                a = (a ^ (a >> 41)) * k0;\n                b = (b ^ (b >> 41)) * k0 + (UInt64) i;\n                var u = (byte)(b >> 37);\n                data[i] = u; //memcpy(data + i, &u, 1); // uint8 -> char\n            }\n        }\n\n        //#define C(x) 0x ## x ## ULL\n        private static UInt64 C(UInt64 v)\n        {\n            return v;\n        }\n\n        void Test(int index, int offset, int len)\n        {\n            var seq = new ReadOnlySequence<byte>(data, offset, len);\n\n            UInt128 u = CityHash.CityHash128(seq);\n            UInt128 v = CityHash.CityHash128WithSeed(seq, kSeed128);\n#if NET8_0_OR_GREATER\n            Assert.Equal(new UInt128(testdata[index, 4], testdata[index, 3]), u);\n            Assert.Equal(new UInt128(testdata[index, 6], testdata[index, 5]), v);\n#else\n            Assert.Equal(testdata[index, 3], u.Low);\n            Assert.Equal(testdata[index, 4], u.High);\n            Assert.Equal(testdata[index, 5], v.Low);\n            Assert.Equal(testdata[index, 6], v.High);\n#endif\n        }\n\n        [Fact]\n        public void main()\n        {\n            setup();\n            int i = 0;\n            for (; i < kTestSize - 1; i++)\n            {\n                Test(i, i * i, i);\n            }\n            Test(i, 0, kDataSize);\n        }\n    }\n}"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ClickHouseColumnWriterTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Globalization;\nusing System.Linq;\nusing System.Net;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ClickHouseColumnWriterTests : ClickHouseTestsBase, IClassFixture<ClickHouseColumnWriterTests.TableFixture>, IClassFixture<EncodingFixture>\n    {\n        private const string TestTableName = \"stream_insert_test\";\n\n        private readonly TableFixture _tableFixture;\n\n        public ClickHouseColumnWriterTests(TableFixture tableFixture)\n        {\n            _tableFixture = tableFixture;\n        }\n\n        [Fact]\n        public async Task InsertValues()\n        {\n            await using var con = await OpenConnectionAsync();\n\n            var rangeStart = _tableFixture.ReserveRange(100);\n            await using (var writer = await con.CreateColumnWriterAsync($\"INSERT INTO {TestTableName} VALUES\", CancellationToken.None))\n            {\n                var columns = new object?[writer.FieldCount];\n                columns[writer.GetOrdinal(\"id\")] = new[] { rangeStart, rangeStart + 1 };\n                columns[writer.GetOrdinal(\"num\")] = new List<decimal?> {49999.99m, -999999.99999m};\n\n                await writer.WriteTableAsync(columns, 2, CancellationToken.None);\n            }\n\n            var cmd = con.CreateCommand($\"SELECT id, str, num FROM {TestTableName} WHERE id>={rangeStart} AND id<{rangeStart + 2}\");\n            int count = 0;\n            await using (var reader = await cmd.ExecuteReaderAsync())\n            {\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    var str = reader.GetFieldValue(1, \"NULL\");\n                    var num = reader.GetDecimal(2);\n\n                    Assert.Equal(\"NULL\", str);\n                    switch (id - rangeStart)\n                    {\n                        case 0:\n                            Assert.Equal(49999.99m, num);\n                            break;\n                        case 1:\n                            Assert.Equal(-999999.99999m, num);\n                            break;\n                        default:\n                            Assert.False(true, $\"Unexpected id: {id}\");\n                            break;\n                    }\n\n                    ++count;\n                }\n            }\n\n            Assert.Equal(2, count);\n        }\n\n        [Fact]\n        public async Task InsertValuesFromGeneratedColumns()\n        {\n            await using var con = await OpenConnectionAsync();\n\n            var rangeStart = _tableFixture.ReserveRange(10_000);\n            await using (var writer = await con.CreateColumnWriterAsync($\"INSERT INTO {TestTableName} VALUES\", CancellationToken.None))\n            {\n                var columns = new Dictionary<string, object>\n                {\n                    [\"id\"] = Enumerable.Range(rangeStart, int.MaxValue - rangeStart),\n                    [\"str\"] = Enumerable.Range(0, int.MaxValue).Select(i => i % 3 == 0 ? i % 5 == 0 ? \"FizzBuzz\" : \"Fizz\" : i % 5 == 0 ? \"Buzz\" : i.ToString())\n                };\n\n                await writer.WriteTableAsync(columns, 100, CancellationToken.None);\n            }\n\n            var cmd = con.CreateCommand($\"SELECT id, str, num FROM {TestTableName} WHERE id>={rangeStart} AND id<{rangeStart + 10_000}\");\n            int count = 0;\n            await using (var reader = await cmd.ExecuteReaderAsync())\n            {\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    var str = reader.GetString(1);\n                    var num = reader.GetFieldValue<decimal>(2, null);\n                    Assert.Null(num);\n\n                    var i = id - rangeStart;\n                    switch (str)\n                    {\n                        case \"Fizz\":\n                            Assert.True(i % 3 == 0);\n                            break;\n\n                        case \"Buzz\":\n                            Assert.True(i % 5 == 0);\n                            break;\n\n                        case \"FizzBuzz\":\n                            Assert.True(i % 3 == 0);\n                            Assert.True(i % 5 == 0);\n                            break;\n\n                        default:\n                            Assert.NotNull(str);\n                            Assert.True(int.TryParse(str, out var parsedStr));\n                            Assert.Equal(i, parsedStr);\n                            break;\n                    }\n\n                    ++count;\n                }\n            }\n\n            Assert.Equal(100, count);\n        }\n\n        [Fact]\n        public async Task InsertValuesFromAsyncEnumerableColumn()\n        {\n            await using var con = await OpenConnectionAsync();\n\n            var rangeStart = _tableFixture.ReserveRange(10_000);\n            await using (var writer = await con.CreateColumnWriterAsync($\"INSERT INTO {TestTableName} VALUES\", CancellationToken.None))\n            {\n                var columns = new object?[writer.FieldCount];\n                columns[writer.GetOrdinal(\"id\")] = Enumerable.Range(rangeStart, int.MaxValue - rangeStart);\n                columns[writer.GetOrdinal(\"num\")] = new AsyncTestFibSequence();\n\n                await writer.WriteTableAsync(columns, 63, CancellationToken.None);\n            }\n\n            var cmd = con.CreateCommand($\"SELECT id, str, num FROM {TestTableName} WHERE id>={rangeStart} AND id<{rangeStart + 10_000} ORDER BY id\");\n            int count = 0;\n            await using (var reader = await cmd.ExecuteReaderAsync())\n            {\n                decimal? previous = null, current = null;\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    var str = reader.GetString(1, null);\n                    var num = reader.GetFieldValue<decimal>(2, null);\n                    Assert.Null(str);\n\n                    var i = id - rangeStart;\n                    Assert.Equal(count, i);\n\n                    if (current == null)\n                        current = 1;\n                    else if (previous == null)\n                        previous = 1;\n                    else\n                    {\n                        var next = previous + current;\n                        previous = current;\n                        current = next;\n                    }\n\n                    Assert.Equal(current, num);\n                    ++count;\n                }\n            }\n\n            Assert.Equal(63, count);\n        }\n\n        [Fact]\n        public async Task InsertStringsWithEncoding()\n        {\n            await using var con = await OpenConnectionAsync();\n\n            var values = new List<string>\n                {\"ноль\", \"один\", \"два\", \"три\", \"четыре\", \"пять\", \"шесть\", \"семь\", \"восемь\", \"девять\", \"десять\", \"одиннадцать\", \"двенадцать\", \"тринадцать\", \"четырнадцать\", \"пятнадцать\"};\n\n            var rangeStart = _tableFixture.ReserveRange(10_000);\n            await using (var writer = await con.CreateColumnWriterAsync($\"INSERT INTO {TestTableName} VALUES\", CancellationToken.None))\n            {\n                writer.ConfigureColumn(\"str\", new ClickHouseColumnSettings(Encoding.UTF7));\n                var columns = new object?[writer.FieldCount];\n                columns[writer.GetOrdinal(\"id\")] = Enumerable.Range(rangeStart, int.MaxValue - rangeStart);\n                columns[writer.GetOrdinal(\"str\")] = values;\n\n                await writer.WriteTableAsync(columns, values.Count, CancellationToken.None);\n            }\n\n            var cmd = con.CreateCommand($\"SELECT CAST(id - {rangeStart} AS Int32), convertCharset(str, 'UTF-7', 'cp1251'), num FROM {TestTableName} WHERE id>={rangeStart} AND id<{rangeStart + 10_000} ORDER BY id\");\n            int count = 0;\n            await using (var reader = await cmd.ExecuteReaderAsync())\n            {\n                reader.ConfigureColumn(1, new ClickHouseColumnSettings(Encoding.GetEncoding(\"windows-1251\")));\n\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    var str = reader.GetString(1, null);\n                    var num = reader.GetFieldValue<decimal>(2, null);\n                    Assert.Null(num);\n\n                    var originalValue = values[id];\n                    Assert.Equal(originalValue, str);\n\n                    ++count;\n                }\n            }\n\n            Assert.Equal(values.Count, count);\n        }\n\n        [Fact]\n        public async Task InsertSingleRow()\n        {\n            await using var con = await OpenConnectionAsync();\n\n            var rangeStart = _tableFixture.ReserveRange(100);\n            await using (var writer = await con.CreateColumnWriterAsync($\"INSERT INTO {TestTableName}(num, id, str) VALUES\", CancellationToken.None))\n            {\n                await writer.WriteRowAsync(new List<object?> {42m, rangeStart, \"Hello\"}, CancellationToken.None);\n                writer.WriteRow(null, rangeStart + 1, \"world!\");\n                writer.WriteRow(new List<object?> { 42.5m, rangeStart + 2, DBNull.Value });\n\n                await writer.EndWriteAsync(CancellationToken.None);\n            }\n\n            var cmd = con.CreateCommand($\"SELECT cast(T.id - {rangeStart} AS Int32) id, T.str, T.num FROM {TestTableName} AS T WHERE T.id>={rangeStart} AND T.id<{rangeStart + 100}\");\n            int count = 0;\n            await using (var reader = await cmd.ExecuteReaderAsync())\n            {\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    var str = reader.GetFieldValue(1, (string?) null);\n                    var num = reader.GetFieldValue(2, (decimal?) null);\n\n                    switch (id)\n                    {\n                        case 0:\n                            Assert.Equal(\"Hello\", str);\n                            Assert.Equal(42m, num);\n                            break;\n                        case 1:\n                            Assert.Equal(\"world!\", str);\n                            Assert.Null(num);\n                            break;\n                        case 2:\n                            Assert.Null(str);\n                            Assert.Equal(42.5m, num);\n                            break;\n                        default:\n                            Assert.True(id >= 0 && id <= 3, \"Id is out of range.\");\n                            break;\n                    }\n\n                    ++count;\n                }\n            }\n\n            Assert.Equal(3, count);\n        }\n\n        [Fact]\n        public async Task InsertArrayValues()\n        {\n            try\n            {\n                await using var cn = await OpenConnectionAsync();\n\n                var cmd = cn.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}_arr\");\n                cmd.ExecuteNonQuery();\n\n                cmd = cn.CreateCommand($\"CREATE TABLE {TestTableName}_arr(id Int32, arr Array(Nullable(String)), multi_arr Array(Array(Nullable(Decimal64(4))))) ENGINE=Memory\");\n                cmd.ExecuteNonQuery();\n\n                var arr = new List<List<string?>>\n                    {new List<string?> {\"foo\", null, \"bar\"}, new List<string?> {null, null, null, null}, new List<string?>(0), new List<string?> {\"1\", \"2\", \"Lorem ipsum\"}};\n                var multiArr = new[]\n                {\n                    new List<decimal?[]>(0),\n                    new List<decimal?[]>\n                    {\n                        new decimal?[] {1, 2, 3, null, 4, 5},\n                        new decimal?[0],\n                        new decimal?[] {null, null, 6, null}\n                    },\n                    new List<decimal?[]>\n                    {\n                        new decimal?[0]\n                    },\n                    new List<decimal?[]>\n                    {\n                        new decimal?[0],\n                        new decimal?[0],\n                        new decimal?[0],\n                        new decimal?[] {7, 8, 9, 10}\n                    }\n                };\n\n                await using (var writer = await cn.CreateColumnWriterAsync($\"INSERT INTO {TestTableName}_arr VALUES\", CancellationToken.None))\n                {\n                    var columns = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase)\n                    {\n                        [\"ID\"] = Enumerable.Range(0, int.MaxValue),\n                        [\"ARR\"] = arr,\n                        [\"multi_ARR\"] = multiArr\n                    };\n\n                    await writer.WriteTableAsync(columns, Math.Max(arr.Count, multiArr.Length), CancellationToken.None);\n\n                    Assert.False(writer.IsClosed);\n                    writer.EndWrite();\n                    Assert.True(writer.IsClosed);\n                }\n\n                cmd = cn.CreateCommand($\"SELECT id, multi_arr, arr FROM {TestTableName}_arr\");\n                int count = 0;\n                await using (var reader = await cmd.ExecuteReaderAsync())\n                {\n                    while (await reader.ReadAsync())\n                    {\n                        var id = reader.GetInt32(0);\n                        var resultMultiArr = reader.GetFieldValue<decimal?[][]>(1);\n                        var resultArr = reader.GetFieldValue<string?[]>(2);\n\n                        var expectedMultiArr = multiArr[id];\n                        Assert.Equal(expectedMultiArr.Count, resultMultiArr.Length);\n\n                        for (int i = 0; i < expectedMultiArr.Count; i++)\n                            Assert.Equal(expectedMultiArr[i], resultMultiArr[i]);\n\n                        var expectedArr = arr[id];\n                        Assert.Equal(expectedArr, resultArr);\n\n                        ++count;\n                    }\n                }\n\n                Assert.Equal(count, arr.Count);\n            }\n            finally\n            {\n                await using var cn = await OpenConnectionAsync();\n\n                var cmd = cn.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}_arr\");\n                cmd.ExecuteNonQuery();\n            }\n        }\n\n        [Fact]\n        public async Task InsertTupleValues()\n        {\n            var tuples = new[]\n            {\n                new Tuple<int, string?, ulong, decimal?, string, DateTime?, byte, Tuple<short?, ulong?, decimal>>(\n                    0,\n                    null,\n                    4,\n                    3434.35m,\n                    \"0123456\",\n                    null,\n                    1,\n                    new Tuple<short?, ulong?, decimal>(19, 3838838383, 53356.2343m)),\n\n                new Tuple<int, string?, ulong, decimal?, string, DateTime?, byte, Tuple<short?, ulong?, decimal>>(\n                    1,\n                    \"foo_bar\",\n                    534343434343434678,\n                    8949679.5555m,\n                    \"5432106\",\n                    new DateTime(2020, 1, 1, 11, 11, 11),\n                    255,\n                    new Tuple<short?, ulong?, decimal>(-6598, 8984, 85760704949.4567m)),\n\n                new Tuple<int, string?, ulong, decimal?, string, DateTime?, byte, Tuple<short?, ulong?, decimal>>(\n                    2,\n                    null,\n                    0,\n                    34987134.35m,\n                    \"6543210\",\n                    null,\n                    128,\n                    new Tuple<short?, ulong?, decimal>(-19, 38383, 0.2343m)),\n\n                new Tuple<int, string?, ulong, decimal?, string, DateTime?, byte, Tuple<short?, ulong?, decimal>>(\n                    3,\n                    \"one more value\",\n                    42,\n                    -0.6548m,\n                    \"0246135\",\n                    new DateTime(2019, 12, 31, 23, 59, 59),\n                    15,\n                    new Tuple<short?, ulong?, decimal>(null, null, -1234.567m)),\n\n                new Tuple<int, string?, ulong, decimal?, string, DateTime?, byte, Tuple<short?, ulong?, decimal>>(\n                    4,\n                    null,\n                    ulong.MinValue,\n                    null,\n                    \"1352460\",\n                    null,\n                    99,\n                    new Tuple<short?, ulong?, decimal>(null, null, -0.0001m)),\n\n                new Tuple<int, string?, ulong, decimal?, string, DateTime?, byte, Tuple<short?, ulong?, decimal>>(\n                    5,\n                    \"Five!\",\n                    ulong.MaxValue,\n                    1_000_000.0001m,\n                    \"2350146\",\n                    null,\n                    127,\n                    new Tuple<short?, ulong?, decimal>(null, 2, -0.0042m))\n            };\n\n            try\n            {\n                await using var cn = new ClickHouseConnection(GetDefaultConnectionSettings());\n                cn.Open();\n\n                var cmd = cn.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}_tuple\");\n                cmd.ExecuteNonQuery();\n\n                cmd = cn.CreateCommand($@\"CREATE TABLE {TestTableName}_tuple(ten Tuple(\n                                                                                        Int32,\n                                                                                        Nullable(String),\n                                                                                        UInt64,\n                                                                                        Nullable(Decimal64(4)),\n                                                                                        FixedString(7),\n                                                                                        Nullable(DateTime),\n                                                                                        UInt8,\n                                                                                        Nullable(Int16),\n                                                                                        Nullable(UInt64),\n                                                                                        Decimal64(4)\n                                                                                        )) ENGINE = Memory\");\n                cmd.ExecuteNonQuery();\n\n                await using (var writer = await cn.CreateColumnWriterAsync($\"INSERT INTO {TestTableName}_tuple VALUES\", CancellationToken.None))\n                {\n                    await writer.WriteTableAsync(new[] {tuples}, tuples.Length, CancellationToken.None);\n\n                    Assert.False(writer.IsClosed);\n                    await writer.EndWriteAsync(CancellationToken.None);\n                    Assert.True(writer.IsClosed);\n                }\n\n                cmd = cn.CreateCommand($\"SELECT * FROM {TestTableName}_tuple\");\n                int count = 0;\n                await using (var reader = await cmd.ExecuteReaderAsync())\n                {\n                    while (await reader.ReadAsync())\n                    {\n                        var value = reader.GetFieldValue<Tuple<int, string?, ulong, decimal?, string, DateTime?, byte, Tuple<short?, ulong?, decimal>>>(0);\n                        Assert.Equal(tuples[value.Item1], value);\n\n                        ++count;\n                    }\n                }\n\n                Assert.Equal(count, tuples.Length);\n            }\n            finally\n            {\n                await using var cn = new ClickHouseConnection(GetDefaultConnectionSettings());\n                cn.Open();\n                var cmd = cn.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}_tuple\");\n                cmd.ExecuteNonQuery();\n            }\n        }\n\n        [Fact]\n        public async Task InsertLowCardinalityValues()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n\n                var cmd = connection.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}_low_cardinality\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand($\"CREATE TABLE {TestTableName}_low_cardinality(id Int32, str LowCardinality(Nullable(String)), strNotNull LowCardinality(String)) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                var idEnumerable = Enumerable.Range(0, 1000);\n                var strEnumerable = Enumerable.Range(0, 1000).Select(NumToString);\n                var strNotNullEnumerable = Enumerable.Range(0, 1000).Select(n => NumToString(n) ?? string.Empty);\n                await using (var writer = connection.CreateColumnWriter($\"INSERT INTO {TestTableName}_low_cardinality(id, str, strNotNull) VALUES\"))\n                {\n                    var source = new object[] {idEnumerable, strEnumerable, strNotNullEnumerable};\n\n                    await writer.WriteTableAsync(source, 250, CancellationToken.None);\n                    await writer.WriteTableAsync(source, 250, CancellationToken.None);\n                    await writer.WriteTableAsync(source, 500, CancellationToken.None);\n                    await writer.EndWriteAsync(CancellationToken.None);\n                }\n\n                cmd.CommandText = $\"SELECT id, str, strNotNull FROM {TestTableName}_low_cardinality\";\n                int count = 0;\n                await using (var reader = cmd.ExecuteReader())\n                {\n                    while (await reader.ReadAsync())\n                    {\n                        var id = reader.GetInt32(0);\n                        var str = reader.GetString(1, null);\n                        var strNotNull = reader.GetString(2);\n\n                        var expectedStr = NumToString(id);\n                        Assert.Equal(expectedStr, str);\n                        Assert.Equal(expectedStr ?? string.Empty, strNotNull);\n\n                        ++count;\n                    }\n                }\n\n                Assert.Equal(1000, count);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}_low_cardinality\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n\n            static string? NumToString(int num) => num % 15 == 0 ? (num % 2 == 0 ? null : string.Empty) : num % 3 == 0 ? \"foo\" : num % 5 == 0 ? \"bar\" : num % 2 == 0 ? \"true\" : \"false\";\n        }\n\n        [Fact]\n        public async Task InsertEnumValues()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n\n                var cmd = connection.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}_enums\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand($\"CREATE TABLE {TestTableName}_enums(id Int16, e8 Enum8('min' = -128, 'zero' = 0, 'max' = 127), e16 Enum16('unknown value' = 0, 'well known value' = 42, 'foo' = -1024, 'bar' = 108)) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                await using (var writer = connection.CreateColumnWriter($\"INSERT INTO {TestTableName}_enums(id, e16, e8) VALUES\"))\n                {\n                    var source = new object[]\n                    {\n                        Enumerable.Range(0, 1000).Select(num => (short) num),\n                        Enumerable.Range(-500, 1000).Select(num => num % 108 == 0 ? \"bar\" : num < 0 ? \"foo\" : num == 42 ? \"well known value\" : \"unknown value\"),\n                        Enumerable.Range(0, 1000).Select(num => num % 3 == 0 ? sbyte.MinValue : num % 3 == 1 ? (sbyte) 0 : sbyte.MaxValue)\n                    };\n\n                    await writer.WriteTableAsync(source, 1000, CancellationToken.None);\n                    await writer.EndWriteAsync(CancellationToken.None);\n                }\n\n                cmd.CommandText = $\"SELECT e8, e16 FROM {TestTableName}_enums ORDER BY id\";\n\n                int count = 0;\n                await using var reader = await cmd.ExecuteReaderAsync();\n\n                while (reader.Read())\n                {\n                    var e8 = reader.GetValue(0);\n                    var e16 = reader.GetInt16(1);\n\n                    Assert.Equal(count % 3 == 0 ? \"min\" : count % 3 == 1 ? \"zero\" : \"max\", e8);\n                    Assert.Equal((count - 500) % 108 == 0 ? 108 : count - 500 < 0 ? -1024 : count - 500 == 42 ? 42 : 0, e16);\n                    ++count;\n                }\n\n                Assert.Equal(1000, count);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}_enums\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Fact]\n        public async Task InsertNullableEnumValues()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n\n                var cmd = connection.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}_null_enums\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand($\"CREATE TABLE {TestTableName}_null_enums(id Int16, e8 Nullable(Enum8('min' = -128, 'zero' = 0, 'max' = 127)), e16 Nullable(Enum16('unknown value' = 0, 'well known value' = 42, 'foo' = -1024, 'bar' = 108))) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                await using (var writer = connection.CreateColumnWriter($\"INSERT INTO {TestTableName}_null_enums(id, e16, e8) VALUES\"))\n                {\n                    var source = new object[]\n                    {\n                        Enumerable.Range(0, 1000).Select(num => (short) num),\n                        Enumerable.Range(-500, 1000).Select(num => num % 108 == 0 ? num % 5 == 0 ? null : \"bar\" : num < 0 ? \"foo\" : num == 42 ? \"well known value\" : \"unknown value\"),\n                        Enumerable.Range(0, 1000).Select(num => num % 3 == 0 ? num % 7 == 0 ? (sbyte?) null : sbyte.MinValue : num % 3 == 1 ? (sbyte) 0 : sbyte.MaxValue)\n                    };\n\n                    await writer.WriteTableAsync(source, 1000, CancellationToken.None);\n                    await writer.EndWriteAsync(CancellationToken.None);\n                }\n\n                cmd.CommandText = $\"SELECT e8, e16 FROM {TestTableName}_null_enums ORDER BY id\";\n\n                int count = 0;\n                await using var reader = await cmd.ExecuteReaderAsync();\n\n                while (reader.Read())\n                {\n                    var e8 = reader.GetValue(0);\n                    var e16 = reader.GetFieldValue(1, (short?)null);\n\n                    Assert.Equal(count % 3 == 0 ? count % 7 == 0 ? (object)DBNull.Value : \"min\" : count % 3 == 1 ? \"zero\" : \"max\", e8);\n                    Assert.Equal((count - 500) % 108 == 0 ? (count - 500) % 5 == 0 ? (short?)null : 108 : count - 500 < 0 ? -1024 : count - 500 == 42 ? (short?)42 : 0, e16);\n                    ++count;\n                }\n\n                Assert.Equal(1000, count);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}_null_enums\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Theory]\n        [InlineData(0)]\n        [InlineData(null)]\n        [InlineData(2111)]\n        [InlineData(999_990)]\n        public async Task InsertLargeTable(int? maxBlockSize)\n        {\n            // \"Large\" means that the size of the table is greater than the size of the buffer\n            const int rowCount = 100_000;\n            var startId = _tableFixture.ReserveRange(rowCount);\n\n            var settings = new ClickHouseConnectionStringBuilder(GetDefaultConnectionSettings()) {BufferSize = 4096};\n\n            await using var connection = new ClickHouseConnection(settings);\n            await connection.OpenAsync();\n\n            await using (var writer = connection.CreateColumnWriter($\"INSERT INTO {TestTableName}(id, str) VALUES\"))\n            {\n                // Leave a default value intact when maxBlockSize == 0\n                if (maxBlockSize == null || maxBlockSize > 0)\n                    writer.MaxBlockSize = maxBlockSize;                \n\n                var table = new object[] {Enumerable.Range(startId, rowCount), Enumerable.Range(startId, rowCount).Select(num => num.ToString())};\n                await writer.WriteTableAsync(table, rowCount, CancellationToken.None);\n                await writer.EndWriteAsync(CancellationToken.None);\n            }\n\n            await using var cmd = connection.CreateCommand($\"SELECT id, str FROM {TestTableName} WHERE id >= {{startId}} AND id < {{endId}} ORDER BY id\");\n            cmd.Parameters.AddWithValue(\"startId\", startId);\n            cmd.Parameters.AddWithValue(\"endId\", startId + rowCount);\n\n            await using var reader = cmd.ExecuteReader();\n            int expectedId = startId;\n            while (await reader.ReadAsync())\n            {\n                var id = reader.GetInt32(0);\n                var str = reader.GetString(1);\n\n                Assert.Equal(expectedId, id);\n                Assert.Equal(expectedId.ToString(), str);\n\n                ++expectedId;\n            }\n\n            Assert.Equal(startId + rowCount, expectedId);\n        }\n\n        [Fact]\n        public async Task InsertFromSecondaryInterfaces()\n        {\n            const int rowCount = 100;\n            var startId = _tableFixture.ReserveRange(rowCount);\n\n            // Each wrapper implements only one interface\n            var ids = new EnumerableListWrapper<int>(Enumerable.Range(startId, rowCount).ToList());\n            var strings = new GenericEnumerableListWrapper<string>(Enumerable.Range(1, rowCount).Select(num => $\"Str #{num}\").ToList());\n            var numbers = new ListWrapper<decimal?>(\n                Enumerable.Range(0, rowCount).Select(num => num % 17 == 0 ? (decimal?) null : Math.Round(num / (decimal) 17, 6)).ToList());\n\n            var connection = await OpenConnectionAsync();\n\n            var writeCount = (int) (rowCount * 0.9);\n            await using (var writer = connection.CreateColumnWriter($\"INSERT INTO {TestTableName}(id, str, num) VALUES\"))\n            {\n                var table = new object[] {ids, strings, numbers};\n                await writer.WriteTableAsync(table, writeCount, CancellationToken.None);\n                await writer.EndWriteAsync(CancellationToken.None);\n            }\n\n            await using var cmd = connection.CreateCommand($\"SELECT id, str, num FROM {TestTableName} WHERE id >= {{startId}} AND id < {{endId}} ORDER BY id\");\n            cmd.Parameters.AddWithValue(\"startId\", startId);\n            cmd.Parameters.AddWithValue(\"endId\", startId + rowCount);\n\n            await using var reader = cmd.ExecuteReader();\n            int count = 0;\n            while (await reader.ReadAsync())\n            {\n                var id = reader.GetInt32(0);\n                var str = reader.GetString(1);\n                var num = reader.GetFieldValue<decimal>(2, null);\n\n                Assert.Equal(ids.List[count], id);\n                Assert.Equal(strings.List[count], str);\n                Assert.Equal(numbers.List[count], num);\n\n                ++count;\n            }\n\n            Assert.Equal(writeCount, count);\n        }\n\n        [Fact]\n        public async Task InsertValuesWithLowRowCount()\n        {\n            const int rowCount = 100, rowCap = 71;\n            var startId = _tableFixture.ReserveRange(rowCount);\n\n            await using var con = await OpenConnectionAsync();\n\n            var ids = Enumerable.Range(startId, rowCount).ToList();\n            var nums = Enumerable.Range(0, rowCount).Select(v => -100 + Math.Round(200m / (rowCount - 1) * v, 6)).ToArray();\n            await using (var writer = await con.CreateColumnWriterAsync($\"INSERT INTO {TestTableName} VALUES\", CancellationToken.None))\n            {\n                var columns = new object?[writer.FieldCount];\n                columns[writer.GetOrdinal(\"id\")] = ids;\n                columns[writer.GetOrdinal(\"num\")] = nums;\n\n                await writer.WriteTableAsync(columns, rowCap, CancellationToken.None);\n            }\n\n            await using var cmd = con.CreateCommand($\"SELECT id, str, num FROM {TestTableName} WHERE id >= {{startId}} AND id < {{endId}} ORDER BY id\");\n            cmd.Parameters.AddWithValue(\"startId\", startId);\n            cmd.Parameters.AddWithValue(\"endId\", startId + rowCount);\n\n            int count = 0;\n            await using (var reader = await cmd.ExecuteReaderAsync())\n            {\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    var str = reader.GetFieldValue(1, \"NULL\");\n                    var num = reader.GetDecimal(2);\n\n                    Assert.Equal(ids[count], id);\n                    Assert.Equal(\"NULL\", str);\n                    Assert.Equal(nums[count], num);\n\n                    ++count;\n                }\n            }\n\n            Assert.Equal(rowCap, count);\n        }\n\n        [Fact]\n        public async Task InsertArrayFromMemory()\n        {\n            var ints = Enumerable.Range(0, 1000).ToArray();\n            var nums = ints.Select(v => -100 + Math.Round(200m / (ints.Length - 1) * v, 6)).ToArray();\n\n            var intsArr = new List<Memory<int>>();\n            var numsArr = new List<ReadOnlyMemory<decimal>>();\n            int expectedCount = 0;\n            for (int i = 0; i < ints.Length;)\n            {\n                var size = i % 13 == 0 ? 13 : i % 7;\n                if (size == 0)\n                    size = 3;\n\n                if (i + size > ints.Length)\n                    break;\n                \n                var intsMem = new Memory<int>(ints, i, size);\n                var numsMem = new Memory<decimal>(nums, i, size);\n\n                intsArr.Add(intsMem);\n                numsArr.Add(numsMem);\n\n                i += size;\n                expectedCount = i;\n            }\n\n            await WithTemporaryTable(\"mem\", \"idx Int32, id Array(Int32), num Array(Decimal64(6))\", RunTest);\n\n            async Task RunTest(ClickHouseConnection connection, string tableName)\n            {\n                await using (var writer = connection.CreateColumnWriter($\"INSERT INTO {tableName}(num, id, idx) VALUES\"))\n                {\n                    var source = new object[] { numsArr, intsArr, Enumerable.Range(0, numsArr.Count).ToArray() };\n\n                    await writer.WriteTableAsync(source, numsArr.Count, CancellationToken.None);\n                    await writer.EndWriteAsync(CancellationToken.None);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT id, num, idx FROM {tableName}\");\n\n                int count = 0;\n                await using var reader = await cmd.ExecuteReaderAsync();\n\n                while (reader.Read())\n                {\n                    var idArr = reader.GetFieldValue<int[]>(0);\n                    var numArr = reader.GetFieldValue<decimal[]>(1);\n                    var idx = reader.GetInt32(2);\n\n                    Assert.Equal(idArr.Length, numArr.Length);\n\n                    for (int i = 0; i < idArr.Length; i++)\n                    {\n                        Assert.Equal(nums[idArr[i]], numArr[i]);\n                        Assert.Equal(intsArr[idx].Span[i], idArr[i]);\n                        Assert.Equal(numsArr[idx].Span[i], numArr[i]);\n                    }\n\n                    count += idArr.Length;\n                }\n\n                Assert.Equal(expectedCount, count);\n            }\n        }\n\n        [Fact]\n        public async Task InsertStringFromMemory()\n        {\n            const int rowCount = 400;\n            var startId = _tableFixture.ReserveRange(rowCount);\n\n            const string someText =\n    \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \" +\n    \"Donec varius tortor iaculis sapien malesuada, nec eleifend risus ultrices. \" +\n    \"Suspendisse ac ligula nec nunc finibus lobortis sed ac ipsum. \" +\n    \"Curabitur rutrum ligula feugiat, finibus enim id, vulputate purus. \" +\n    \"Aliquam facilisis sem vel mattis fringilla. \" +\n    \"Nullam in mauris feugiat, pulvinar nibh id, pretium quam. \" +\n    \"Suspendisse hendrerit sapien et nisi rutrum, eu vestibulum magna convallis. \" +\n    \"Nam sed turpis vulputate, volutpat augue eget, pulvinar sapien.\";\n\n            var ids = new List<int>(Enumerable.Range(0, 100));\n            var mem = new List<Memory<char>>();\n            var roMem = new List<ReadOnlyMemory<char>>();\n            var bytes = new List<Memory<byte>>();\n            var roBytes = new List<ReadOnlyMemory<byte>>();\n\n            var someTextChars = someText.ToCharArray();\n            var someTextBytes = Encoding.ASCII.GetBytes(someText);\n            Assert.Equal(someTextChars.Length, someTextBytes.Length);\n\n            int position = 0;\n            char[] separators = { ' ', ',', '.' };\n            for (int i = 0; i < ids.Count; i++)\n            {\n                while (separators.Contains(someText[position]))\n                    position = (position + 1) % someText.Length;\n\n                var idx = someText.IndexOfAny(separators, position);\n                var len = idx < 0 ? someText.Length - position : idx - position;\n\n                mem.Add(new Memory<char>(someTextChars, position, len));\n                roMem.Add(someText.AsMemory(position, len));\n                var bytesMem = new Memory<byte>(someTextBytes, position, len);\n                bytes.Add(bytesMem);\n                roBytes.Add(bytesMem);\n\n                position = (position + len) % someText.Length;\n            }\n\n            var connection = await OpenConnectionAsync();\n\n            await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {TestTableName} VALUES\", CancellationToken.None))\n            {\n                var columns = new object?[writer.FieldCount];\n                columns[writer.GetOrdinal(\"id\")] = ids.Select(id => id + startId);\n                columns[writer.GetOrdinal(\"str\")] = mem;\n\n                await writer.WriteTableAsync(columns, ids.Count, CancellationToken.None);\n\n                columns[writer.GetOrdinal(\"id\")] = ids.Select(id => id + startId + ids.Count);\n                columns[writer.GetOrdinal(\"str\")] = roMem;\n\n                await writer.WriteTableAsync(columns, ids.Count, CancellationToken.None);\n\n                columns[writer.GetOrdinal(\"id\")] = ids.Select(id => id + startId + ids.Count * 2);\n                columns[writer.GetOrdinal(\"str\")] = bytes;\n\n                await writer.WriteTableAsync(columns, ids.Count, CancellationToken.None);\n\n                columns[writer.GetOrdinal(\"id\")] = ids.Select(id => id + startId + ids.Count * 3);\n                columns[writer.GetOrdinal(\"str\")] = roBytes;\n\n                await writer.WriteTableAsync(columns, ids.Count, CancellationToken.None);\n            }\n\n            await using var cmd = connection.CreateCommand($\"SELECT id, str, num FROM {TestTableName} WHERE id >= {{startId}} AND id < {{endId}} ORDER BY id\");\n            cmd.Parameters.AddWithValue(\"startId\", startId);\n            cmd.Parameters.AddWithValue(\"endId\", startId + rowCount);\n\n            int count = 0;\n            await using (var reader = await cmd.ExecuteReaderAsync())\n            {\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    var str = reader.GetString(1);\n                    var num = reader.GetFieldValue(2, (decimal?) null);\n\n                    Assert.Equal(mem[(id - startId) % ids.Count].ToString(), str);\n                    Assert.Null(num);\n\n                    ++count;\n                }\n            }\n\n            Assert.Equal(rowCount, count);\n        }\n\n        [Fact]\n        public async Task InsertFixedStringFromMemory()\n        {\n            const string someText =\n                \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \" +\n                \"Donec varius tortor iaculis sapien malesuada, nec eleifend risus ultrices. \" +\n                \"Suspendisse ac ligula nec nunc finibus lobortis sed ac ipsum. \" +\n                \"Curabitur rutrum ligula feugiat, finibus enim id, vulputate purus. \" +\n                \"Aliquam facilisis sem vel mattis fringilla. \" +\n                \"Nullam in mauris feugiat, pulvinar nibh id, pretium quam. \" +\n                \"Suspendisse hendrerit sapien et nisi rutrum, eu vestibulum magna convallis. \" +\n                \"Nam sed turpis vulputate, volutpat augue eget, pulvinar sapien.\";\n\n            var id = new List<int>(Enumerable.Range(0, 100));\n            var mem = new List<Memory<char>>();\n            var roMem = new List<ReadOnlyMemory<char>>();\n            var bytes = new List<Memory<byte>>();\n            var roBytes = new List<ReadOnlyMemory<byte>>();\n\n            var someTextChars = someText.ToCharArray();\n            var someTextBytes = Encoding.ASCII.GetBytes(someText);\n            Assert.Equal(someTextChars.Length, someTextBytes.Length);\n\n            int position = 0;\n            int maxLength = 0;\n            char[] separators = {' ', ',', '.'};\n            for (int i = 0; i < id.Count; i++)\n            {\n                while (separators.Contains(someText[position]))\n                    position = (position + 1) % someText.Length;\n\n                var idx = someText.IndexOfAny(separators, position);\n                var len = idx < 0 ? someText.Length - position : idx - position;\n\n                mem.Add(new Memory<char>(someTextChars, position, len));\n                roMem.Add(someText.AsMemory(position, len));\n                var bytesMem = new Memory<byte>(someTextBytes, position, len);\n                bytes.Add(bytesMem);\n                roBytes.Add(bytesMem);\n\n                maxLength = Math.Max(maxLength, len);\n                position = (position + len) % someText.Length;\n            }\n\n            await WithTemporaryTable(\"fsm\", $\"id Int32, str FixedString({maxLength})\", RunTest);\n\n            async Task RunTest(ClickHouseConnection connection, string tableName)\n            {\n                await using (var writer = connection.CreateColumnWriter($\"INSERT INTO {tableName}(id, str) VALUES\"))\n                {\n                    writer.ConfigureColumn(\"str\", new ClickHouseColumnSettings(Encoding.ASCII));\n                    await writer.WriteTableAsync(new object[] {id, mem}, id.Count, CancellationToken.None);\n                    await writer.WriteTableAsync(new object[] {id.Select(i => i + id.Count), roMem}, id.Count, CancellationToken.None);\n                    await writer.WriteTableAsync(new object[] {id.Select(i => i + id.Count*2), bytes}, id.Count, CancellationToken.None);\n                    await writer.WriteTableAsync(new object[] {id.Select(i => i + id.Count*3), roBytes}, id.Count, CancellationToken.None);\n                    await writer.EndWriteAsync(CancellationToken.None);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT id, str FROM {tableName}\");\n\n                int count = 0;\n                await using var reader = await cmd.ExecuteReaderAsync();\n                reader.ConfigureColumn(\"str\", new ClickHouseColumnSettings(Encoding.ASCII));\n\n                while (reader.Read())\n                {\n                    var idVal = reader.GetFieldValue<int>(0);\n                    var strVal = reader.GetFieldValue<string>(1);\n\n                    Assert.Equal(roMem[idVal % id.Count].ToString(), strVal);\n                    count++;\n                }\n\n                Assert.Equal(id.Count * 4, count);\n            }\n        }\n\n        [Fact]\n        public async Task InsertFromCollectionOfObjects()\n        {\n            const int rowCount = 200;\n            var startId = _tableFixture.ReserveRange(rowCount);\n\n            // Each wrapper implements only one interface\n            var ids = new EnumerableListWrapper<object>(Enumerable.Range(startId, rowCount / 2).Cast<object>().ToList());\n            var strings = new GenericEnumerableListWrapper<object>(Enumerable.Range(1, rowCount / 2).Select(num => $\"Str #1_{num}\").ToList<object>());\n            var numbers = new ListWrapper<object?>(\n                Enumerable.Range(0, rowCount / 2).Select(num => num % 19 == 0 ? (object?)null : Math.Round(num / (decimal)19, 6)).ToList());\n\n            var ids2 = new AsyncEnumerableListWrapper<object>(Enumerable.Range(startId + rowCount/2, rowCount / 2).Cast<object>().ToList());\n            var strings2 = Enumerable.Range(1, rowCount / 2).Select(num => $\"Str #2_{num}\").ToArray<object>();\n            var numbers2 = Enumerable.Range(rowCount / 2, rowCount / 2).Select(num => num % 19 == 0 ? (object?)null : Math.Round(num / (decimal)19, 6)).ToList();\n\n            await using var connection = await OpenConnectionAsync();\n\n            await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {TestTableName}(id, str, num) VALUES\", CancellationToken.None))\n            {\n                await writer.WriteTableAsync(new object[] { ids, strings, numbers }, rowCount / 2, CancellationToken.None);\n                await writer.WriteTableAsync(new object[] { ids2, strings2, numbers2 }, rowCount / 2, CancellationToken.None);                \n            }\n\n            await using var cmd = connection.CreateCommand($\"SELECT id, str, num FROM {TestTableName} WHERE id >= {{startId}} AND id < {{endId}} ORDER BY id\");\n            cmd.Parameters.AddWithValue(\"startId\", startId);\n            cmd.Parameters.AddWithValue(\"endId\", startId + rowCount);\n\n            await using var reader = cmd.ExecuteReader();\n            int count = 0;\n            while (await reader.ReadAsync())\n            {\n                var id = reader.GetInt32(0);\n                var str = reader.GetString(1);\n                var num = reader.GetFieldValue<decimal>(2, null);\n\n                Assert.Equal(count + startId, id);\n\n                if (id - startId < rowCount / 2)\n                {\n                    Assert.Equal(strings.List[count], str);\n                    Assert.Equal(numbers.List[count], num);\n                }\n                else\n                {\n                    Assert.Equal((string)strings2[count - rowCount / 2], str);\n                    Assert.Equal((decimal?)numbers2[count - rowCount / 2], num);\n                }\n\n                ++count;\n            }\n\n            Assert.Equal(rowCount, count);\n        }\n\n        [Fact]\n        public async Task InsertValuesOfSpecifiedType()\n        {\n            const int rowCount = 200;\n            var startId = _tableFixture.ReserveRange(rowCount);\n\n            var ids = Enumerable.Range(startId, 123).ToList();\n            var list = new Int32ToUInt32MappedListWrapper(ids, v => (uint)v * 7);\n            var table = new object?[] { list, list, null };\n\n            await using var connection = await OpenConnectionAsync();\n\n            await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {TestTableName}(id, num, str) VALUES\", CancellationToken.None))\n            {\n                Assert.Equal(typeof(int), writer.GetFieldType(0));\n                Assert.Equal(typeof(decimal), writer.GetFieldType(1));\n                Assert.Equal(typeof(string), writer.GetFieldType(2));\n\n                writer.ConfigureColumn(\"id\", new ClickHouseColumnSettings(typeof(int)));\n                writer.ConfigureColumn(\"num\", new ClickHouseColumnSettings(typeof(uint)));\n\n                Assert.Equal(typeof(int), writer.GetFieldType(0));\n                Assert.Equal(typeof(uint), writer.GetFieldType(1));\n                Assert.Equal(typeof(string), writer.GetFieldType(2));\n\n                await writer.WriteTableAsync(table, ids.Count, CancellationToken.None);\n            }\n\n            await using var cmd = connection.CreateCommand($\"SELECT id, num FROM {TestTableName} WHERE id >= {{startId}} AND id < {{endId}} ORDER BY id\");\n            cmd.Parameters.AddWithValue(\"startId\", startId);\n            cmd.Parameters.AddWithValue(\"endId\", startId + rowCount);\n\n            await using var reader = cmd.ExecuteReader();\n            int count = 0;\n            while (await reader.ReadAsync())\n            {\n                var id = reader.GetInt32(0);\n                var num = reader.GetFieldValue<decimal>(1);\n\n                Assert.Equal(count + startId, id);\n                Assert.Equal(id * 7, num);\n\n                ++count;\n            }\n\n            Assert.Equal(ids.Count, count);\n        }\n\n        [Fact]\n        public async Task InsertValuesFromObjectArrays()\n        {\n            var tableData = new object?[][]\n            {\n                new object[]{1, 2, 3, 4, 5, 6, 7},\n                new object?[]{\"one\", null, null, null, \"five\", \"six\", \"seven\"},\n                new object?[]{\"192.168.121.0\", DBNull.Value, \"127.0.0.1\", DBNull.Value, DBNull.Value, \"10.0.0.1\", \"8.8.8.8\"},\n                new object?[]{null, DBNull.Value, 12.34m, 12324.57m, 2195.99m, DBNull.Value, null},\n                new object[]{(sbyte)-1, (sbyte)0, (sbyte)0, (sbyte)1, (sbyte)1, (sbyte)-1, (sbyte)0}\n\n            };\n\n            var expectedData = new object?[][]\n            {\n                tableData[0].Cast<int>().Select(v=>(object)(long)v).ToArray(),\n                tableData[1],\n                new object?[]{IPAddress.Parse(\"192.168.121.0\"), null, IPAddress.Parse(\"127.0.0.1\"), null, null, IPAddress.Parse(\"10.0.0.1\"), IPAddress.Parse(\"8.8.8.8\")},\n                tableData[3],\n                new object[]{\"minus\", \"zero\", \"zero\", \"plus\", \"plus\", \"minus\", \"zero\"}\n            };\n\n            await WithTemporaryTable(\"obj_arr\", \"id Int64, str Nullable(String), ip Nullable(IPv4), num Nullable(Decimal32(2)), sign Enum8('minus'=-1, 'zero'=0, 'plus'=1)\", RunTest);\n\n            async Task RunTest(ClickHouseConnection connection, string tableName)\n            {\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id, str, ip, num, sign) VALUES\", CancellationToken.None))\n                {\n                    writer.ConfigureColumn(0, new ClickHouseColumnSettings(tableData[0][0]!.GetType()));\n                    writer.ConfigureColumn(1, new ClickHouseColumnSettings(tableData[1][0]!.GetType()));\n                    writer.ConfigureColumn(2, new ClickHouseColumnSettings(tableData[2][0]!.GetType()));\n                    writer.ConfigureColumn(4, new ClickHouseColumnSettings(tableData[4][0]!.GetType()));\n\n                    await writer.WriteTableAsync(tableData, tableData[0].Length, CancellationToken.None);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT id, str, ip, num, sign FROM {tableName}\");\n                await using var reader = await cmd.ExecuteReaderAsync();\n                \n                int count = 0;\n                while (await reader.ReadAsync())\n                {\n                    for (int i = 0; i < tableData.Length; i++)\n                    {\n                        var expectedValue = expectedData[i][count];\n                        bool isNull = expectedValue == null || expectedValue == DBNull.Value;\n                        Assert.Equal(isNull, reader.IsDBNull(i));\n\n                        if (isNull)\n                            continue;\n\n                        var value = reader.GetValue(i);\n                        Assert.Equal(expectedValue, value);\n                    }\n\n                    ++count;\n                }\n\n                Assert.Equal(tableData[0].Length, count);\n            }\n        }\n\n        [Fact]\n        public async Task InsertMapValues()\n        {\n            var map1 = new Dictionary<string, int> { [\"a\"] = 1, [\"b\"] = 2 };\n            var map2 = new KeyValuePair<string, int>[] { new KeyValuePair<string, int>(\"c\", 3) };\n            var map3 = new List<Tuple<string, int>> { new Tuple<string, int>(\"d\", 5), new Tuple<string, int>(\"e\", 6) };\n            var map4 = new List<(string key, int value)> { (\"f\", 7), (\"g\", 8), (\"h\", 9), (\"i\", 10) };\n\n            await WithTemporaryTable(\"map\", \"id Int32, map Map(String, Int32)\", Test);\n\n            async Task Test(ClickHouseConnection cn, string tableName)\n            {\n                await using (var writer = await cn.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id, map) VALUES\", CancellationToken.None))\n                {\n                    await writer.WriteTableAsync(new object[] { AsListOfOne(1), AsListOfOne(map1) }, 1, CancellationToken.None);\n                    await writer.WriteTableAsync(new object[] { AsListOfOne(2), AsListOfOne(map2) }, 1, CancellationToken.None);\n                    await writer.WriteTableAsync(new object[] { AsListOfOne(3), AsListOfOne(map3) }, 1, CancellationToken.None);\n                    await writer.WriteTableAsync(new object[] { AsListOfOne(4), AsListOfOne(map4) }, 1, CancellationToken.None);\n                }\n\n                var cmd = cn.CreateCommand($\"SELECT map FROM {tableName} ORDER BY id\");\n\n                await using var reader = await cmd.ExecuteReaderAsync();\n\n                int count = 0;\n                while(await reader.ReadAsync())\n                {\n                    KeyValuePair<string, int>[] expected;\n                    switch (++count)\n                    {\n                        case 1:\n                            expected = map1.ToArray();\n                            break;\n                        case 2:\n                            expected = map2;\n                            break;\n                        case 3:\n                            expected = map3.Select(t => new KeyValuePair<string, int>(t.Item1, t.Item2)).ToArray();\n                            break;\n                        case 4:\n                            expected = map4.Select(t => new KeyValuePair<string, int>(t.key, t.value)).ToArray();\n                            break;\n                        default:\n                            Assert.True(false, \"Too many rows.\");\n                            throw new InvalidOperationException();\n                    }\n\n                    var actual = reader.GetFieldValue<KeyValuePair<string, int>[]>(0);\n                    Assert.Equal(expected, actual);\n                }\n\n                Assert.Equal(4, count);\n            }\n\n            static IReadOnlyList<T> AsListOfOne<T>(T value)\n            {\n                return new[] { value };\n            }\n        }\n\n        [Fact]\n        public async Task InsertArrayLowCardinality()\n        {\n            var columns = new Dictionary<string, object?>()\n            {\n                [\"id\"] = Enumerable.Range(1, 10).ToList(),\n                [\"data\"] = Enumerable.Range(1, 10).Select(o => new[] { $\"test{ 1 + o * 2 % 3}\", $\"test{ 1 + (1 + o * 2) % 3}\" }),\n            };\n\n            await WithTemporaryTable(\"arrlc\", \"id Int32, data Array(LowCardinality(String))\", Test);\n\n            async Task Test(ClickHouseConnection cn, string tableName)\n            {\n                await using (var writer = await cn.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id, data) VALUES\", CancellationToken.None))\n                {\n                    await writer.WriteTableAsync(columns, 10, CancellationToken.None);\n                }\n\n                var cmd = cn.CreateCommand($\"SELECT id, data FROM {tableName} ORDER BY id\");\n                await using var reader = await cmd.ExecuteReaderAsync();\n                int count = 0;\n                while(await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    var data = reader.GetFieldValue<string[]>(1);\n\n                    Assert.Equal(((List<int>?)columns[\"id\"])?[count], id);\n                    Assert.Equal(new[] { $\"test{ 1 + (1 + count) * 2 % 3}\", $\"test{ 1 + (1 + (1 + count) * 2) % 3}\" }, data);\n                    ++count;\n                }\n\n                Assert.Equal(10, count);\n            }\n        }\n\n        [Fact]\n        public Task InsertBoolValues()\n        {\n            var columns = new Dictionary<string, object?>\n            {\n                [\"id\"] = Enumerable.Range(1, 100).ToList(),\n                [\"b1\"] = Enumerable.Range(1, 100).Select(i => i % 2 == 0).ToList(),\n                [\"b2\"] = Enumerable.Range(1, 100).Select(i => i % 3 == 0 ? (bool?)null : i % 4 == 0).ToList(),\n                [\"b3\"] = Enumerable.Range(1, 100).Select(i => (byte)(i % 8)).ToList(),\n                [\"b4\"] = Enumerable.Range(1, 100).Select(i => i % 5 == 0 ? null : (byte?)(i % 16))\n            };\n\n            return WithTemporaryTable(\"bool\", \"id Int32, b1 Boolean, b2 Nullable(Boolean), b3 Boolean, b4 Nullable(Boolean)\", Test);\n\n            async Task Test(ClickHouseConnection cn, string tableName)\n            {\n                await using (var writer = await cn.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id, b1, b2, b3, b4) VALUES\", CancellationToken.None))\n                {\n                    await writer.WriteTableAsync(columns, 100, CancellationToken.None);\n                }\n\n                var cmd = cn.CreateCommand($\"SELECT id, b1, b2, b3, b4 data FROM {tableName} ORDER BY id\");\n                await using var reader = await cmd.ExecuteReaderAsync();\n                int count = 0;\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n\n                    // Bool value can be true or false.\n                    // Byte value can be 1 or 0, even if inserted value was different.\n\n                    var b1 = reader.GetBoolean(1);\n                    var b1Byte = reader.GetByte(1);\n                    Assert.Equal(b1 ? 1 : 0, b1Byte);\n\n                    var b2 = reader.GetFieldValue(2, (bool?)null);\n                    var b2Byte = reader.GetFieldValue(2, (byte?)null);\n                    Assert.Equal(b2 == null ? null : b2.Value ? (byte?)1 : 0, b2Byte);\n\n                    var b3 = reader.GetValue(3);\n                    var b3Byte = reader.GetByte(3);\n                    var b3Bool = Assert.IsType<bool>(b3);\n                    Assert.Equal(b3Bool ? 1 : 0, b3Byte);\n\n                    var b4 = reader.GetValue(4);\n                    var b4Byte = reader.GetFieldValue(4, (byte?)null);\n                    var b4Bool = b4 == DBNull.Value ? (bool?)null : Assert.IsType<bool>(b4);\n                    Assert.Equal(b4Bool == null ? null : b4Bool.Value ? (byte?)1 : 0, b4Byte);\n\n                    Assert.Equal(count + 1, id);\n                    Assert.Equal(id % 2 == 0, b1);\n                    Assert.Equal(id % 3 == 0 ? (bool?)null : id % 4 == 0, b2);\n                    Assert.Equal(id % 8 != 0, b3);\n                    Assert.Equal(id % 5 == 0 ? DBNull.Value : (object)(id % 16 != 0), b4);\n\n                    ++count;\n                }\n\n                Assert.Equal(100, count);\n            }\n        }\n\n        [Fact]\n        public Task TransactionModeBlock()\n        {\n            return WithTemporaryTable(\"tran_block\", \"id Int32\", Test);\n\n            async Task Test(ClickHouseConnection connection, string tableName, CancellationToken ct)\n            {\n                var list = MappedReadOnlyList<int, int>.Map(Enumerable.Range(0, 48).ToList(), i => i < 47 ? i : throw new IndexOutOfRangeException(\"Too long!\"));\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id) VALUES\", ct))\n                {\n                    // There will be three blocks in the list. The last block will produce an error, but first two blocks must be commited.\n                    writer.MaxBlockSize = 16;\n\n                    var ex = await Assert.ThrowsAnyAsync<ClickHouseHandledException>(() => writer.WriteTableAsync(new[] { list }, list.Count, ClickHouseTransactionMode.Block, ct));\n                    Assert.NotNull(ex.InnerException);\n                    Assert.IsType<IndexOutOfRangeException>(ex.InnerException);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT * FROM {tableName} ORDER BY id\");\n                await using var reader = await cmd.ExecuteReaderAsync();\n                int expected = 0;\n\n                while (await reader.ReadAsync(ct))\n                {\n                    var id = reader.GetInt32(0);\n                    Assert.Equal(expected++, id);\n                }\n\n                Assert.Equal(32, expected);\n            }\n        }\n\n        [Fact]\n        public Task TransactionModeManual()\n        {\n            return WithTemporaryTable(\"tran_manual\", \"id Int32\", Test);\n\n            static async Task Test(ClickHouseConnection connection, string tableName, CancellationToken ct)\n            {\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id) VALUES\", ct))\n                {\n                    await writer.WriteTableAsync(new[] { Enumerable.Range(0, 10) }, 10, ClickHouseTransactionMode.Manual, ct);\n                    await writer.RollbackAsync(ct);\n                    await writer.WriteTableAsync(new[] { Enumerable.Range(10, 10) }, 10, ClickHouseTransactionMode.Manual, ct);\n                    await writer.CommitAsync(ct);\n                    await writer.WriteTableAsync(new[] { Enumerable.Range(20, 10) }, 10, ClickHouseTransactionMode.Manual, ct);\n                }\n\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id) VALUES\", ct))\n                {\n                    await writer.WriteRowAsync(new object?[] { 18 }, false, ct);\n                    await writer.WriteRowAsync(new object?[] { 19 }, false, ct);\n                    await writer.RollbackAsync(ct);\n                    await writer.WriteRowAsync(new object?[] { 20 }, false, ct);\n                    await writer.WriteRowAsync(new object?[] { 21 }, true, ct);\n                    await writer.CommitAsync(ct);\n                    await writer.WriteRowAsync(new object?[] { 22 }, false, ct);\n                    await writer.WriteRowAsync(new object?[] { 23 }, false, ct);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT * FROM {tableName} ORDER BY id\");\n                await using var reader = await cmd.ExecuteReaderAsync();\n                int expected = 10;\n\n                while (await reader.ReadAsync(ct))\n                {\n                    var id = reader.GetInt32(0);\n                    Assert.Equal(expected++, id);\n                }\n\n                Assert.Equal(22, expected);\n            }\n        }\n\n        [Theory]\n        [InlineData(ClickHouseTransactionMode.Default)]\n        [InlineData(ClickHouseTransactionMode.Auto)]\n        public Task TransactionModeAuto(ClickHouseTransactionMode mode)\n        {\n            return WithTemporaryTable(\"tran_auto\", \"id Int32\", Test);\n\n            async Task Test(ClickHouseConnection connection, string tableName, CancellationToken ct)\n            {\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id) VALUES\", ct))\n                {\n                    await writer.WriteTableAsync(new[] { Enumerable.Range(0, 10) }, 10, mode, ct);\n                    await writer.RollbackAsync(ct);\n                    await writer.WriteTableAsync(new[] { Enumerable.Range(10, 10) }, 10, mode, ct);\n                    await writer.CommitAsync(ct);\n                    await writer.WriteTableAsync(new[] { Enumerable.Range(20, 10) }, 10, mode, ct);\n                }\n\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id) VALUES\", ct))\n                {\n                    await writer.WriteRowAsync(new object?[] { 30 }, true, ct);\n                    await writer.RollbackAsync(ct);\n                    await writer.WriteRowAsync(new object?[] { 31 }, true, ct);\n                    await writer.CommitAsync(ct);\n                    await writer.WriteRowAsync(new object?[] { 32 }, true, ct);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT * FROM {tableName} ORDER BY id\");\n                await using var reader = await cmd.ExecuteReaderAsync();\n                int expected = 0;\n\n                while (await reader.ReadAsync(ct))\n                {\n                    var id = reader.GetInt32(0);\n                    Assert.Equal(expected++, id);\n                }\n\n                Assert.Equal(33, expected);\n            }\n        }\n\n        [Fact]\n        public Task InsertMapLowCardinalityValues()\n        {\n            return WithTemporaryTable(\"map_lc\", \"id Int32, data Map(LowCardinality(String), Int32)\", Test);\n\n            static async Task Test(ClickHouseConnection connection, string tableName, CancellationToken ct)\n            {\n                var ids = Enumerable.Range(0, 1000).ToList();\n                var values = ids.Select(\n                    id =>\n                        id % 2 == 0\n                        ? new[] { KeyValuePair.Create(\"key1\", id * 3), KeyValuePair.Create(\"key2\", id * 3 + 1) }\n                        : new[] { KeyValuePair.Create(\"key2\", id * 3 - 1) })\n                    .ToList();\n\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id, data) VALUES\", ct))\n                    await writer.WriteTableAsync(new object[] { ids, values }, ids.Count, ct);\n\n                var cmd = connection.CreateCommand($\"SELECT id, data FROM {tableName} ORDER BY id\");\n                await using (var reader = await cmd.ExecuteReaderAsync(ct))\n                {\n                    int counter = 0;\n                    while (await reader.ReadAsync(ct))\n                    {\n                        var id = reader.GetInt32(0);\n                        var data = reader.GetFieldValue<KeyValuePair<string, int>[]>(1);\n\n                        Assert.Equal(counter++, id);\n                        Assert.Equal(values[id], data);\n                    }\n                    Assert.Equal(1000, counter);\n                }\n\n                // Skipping values\n                await cmd.ExecuteNonQueryAsync(ct);\n            }\n        }\n\n        [Fact]\n        public Task TransactionModeAutoBackwardCompatibility()\n        {\n            // Check that the default transaction mode is 'Auto' when not specified\n            return WithTemporaryTable(\"tran_auto_bc\", \"id Int32\", Test);\n\n            async Task Test(ClickHouseConnection connection, string tableName, CancellationToken ct)\n            {\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id) VALUES\", ct))\n                {\n                    await writer.WriteTableAsync(new[] { Enumerable.Range(0, 10) }, 10, ct);\n                    await writer.RollbackAsync(ct);\n                    await writer.WriteTableAsync(new[] { Enumerable.Range(10, 10) }, 10, ct);\n                    await writer.CommitAsync(ct);\n                    await writer.WriteTableAsync(new[] { Enumerable.Range(20, 10) }, 10, ct);\n                }\n\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id) VALUES\", ct))\n                {\n                    await writer.WriteRowAsync(new object?[] { 30 }, ct);\n                    await writer.RollbackAsync(ct);\n                    await writer.WriteRowAsync(new object?[] { 31 }, ct);\n                    await writer.CommitAsync(ct);\n                    await writer.WriteRowAsync(new object?[] { 32 }, ct);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT * FROM {tableName} ORDER BY id\");\n                await using var reader = await cmd.ExecuteReaderAsync();\n                int expected = 0;\n\n                while (await reader.ReadAsync(ct))\n                {\n                    var id = reader.GetInt32(0);\n                    Assert.Equal(expected++, id);\n                }\n\n                Assert.Equal(33, expected);\n            }\n        }\n\n        [Fact]\n        public Task InsertIPv6Values()\n        {\n            return WithTemporaryTable(\"ipv6\", \"id Int32, ip IPv6\", Test, csb => csb.BufferSize = 33);\n\n            static async Task Test(ClickHouseConnection connection, string tableName, CancellationToken ct)\n            {\n                var ids = Enumerable.Range(0, 255).ToList();\n                var ips = ids.Select(id => string.Format(CultureInfo.InvariantCulture, \"192.168.0.{0}\", id)).ToList();\n\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id, ip) VALUES\", ct))\n                    await writer.WriteTableAsync(new object[] { ids, ips }, ids.Count, ct);\n\n                var cmd = connection.CreateCommand($\"SELECT id, ip FROM {tableName}\");\n                await using (var reader = await cmd.ExecuteReaderAsync(ct))\n                {\n                    int counter = 0;\n                    while (await reader.ReadAsync(ct))\n                    {\n                        var id = reader.GetInt32(0);\n                        var ip = reader.GetFieldValue<IPAddress>(1);\n\n                        Assert.Equal(counter++, id);\n                        Assert.Equal(IPAddress.Parse(\"::ffff:\" + ips[id]), ip);\n                    }\n\n                    Assert.Equal(255, counter);\n                }\n            }\n        }\n\n        [Fact]\n        public Task InsertEmptyArrayLowCardinality()\n        {\n            return WithTemporaryTable(\"lc_array\", \"id Int32, strings Array(LowCardinality(String)), tuples Array(Array(Tuple(LowCardinality(String), LowCardinality(String))))\", Test);\n\n            static async Task Test(ClickHouseConnection cn, string tableName, CancellationToken ct)\n            {\n                var columns = new Dictionary<string, object?>\n                {\n                    { \"id\", new[] { 123 } },\n                    { \"strings\", new[] { Array.Empty<string>() } },\n                    { \"tuples\", new[] { new[] { Array.Empty<(string, string)>(), Array.Empty<(string, string)>() } } }\n                };\n\n                await using (var writer = await cn.CreateColumnWriterAsync($\"insert into {tableName} values\", ct))\n                {\n                    await writer.WriteTableAsync(columns, 1, ct);\n                }\n\n                await using var reader = await cn.CreateCommand($\"select * from {tableName}\").ExecuteReaderAsync(ct);\n\n                Assert.True(await reader.ReadAsync(ct));\n                var obj1 = reader.GetValue(0);\n                var obj2 = reader.GetValue(1);\n                var obj3 = reader.GetValue(2);\n\n                Assert.Equal(123, obj1);\n                Assert.Equal(Array.Empty<string>(), obj2);\n                var arr3 = Assert.IsType<Tuple<string, string>[][]>(obj3);\n                Assert.Equal(2, arr3.Length);\n                Assert.Equal(Array.Empty<Tuple<string, string>>(), arr3[0]);\n                Assert.Equal(Array.Empty<Tuple<string, string>>(), arr3[1]);\n\n                Assert.False(await reader.ReadAsync(ct));\n            }\n        }\n\n        [Fact]\n        public Task InsertVariant()\n        {\n            return WithTemporaryTable(\"variant\", \"id Int32, v Variant(UInt64, LowCardinality(String), Array(Int32))\", Test, afterOpen: (cn, ct) => cn.CreateCommand(\"SET allow_experimental_variant_type = 1\").ExecuteNonQueryAsync(ct));\n\n            static async Task Test(ClickHouseConnection cn, string tableName, CancellationToken ct)\n            {\n                var columns = new Dictionary<string, object?>\n                {\n                    [\"id\"] = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 },\n                    [\"v\"] = new object?[] { DBNull.Value, \"foo\", \"bar\", Array.Empty<int>(), \"foo\", null, \"bar\", new[] { 1, 2, 3, 4, 5 }, 42ul }\n                };\n\n                await using (var writer = await cn.CreateColumnWriterAsync($\"INSERT INTO {tableName} VALUES\", ct))\n                    await writer.WriteTableAsync(columns, 9, ct);\n\n                var cmd = cn.CreateCommand();\n                cmd.CommandText = $\"SELECT id, v FROM {tableName}\";\n                await using var reader = await cmd.ExecuteReaderAsync(ct);\n\n                var expected = (object?[])columns[\"v\"]!;\n                int count = 0;\n                while(await reader.ReadAsync(ct))\n                {\n                    var id = reader.GetInt32(0);\n                    var val = reader.GetValue(1);\n\n                    bool isNull = (expected[id - 1] ?? DBNull.Value) == DBNull.Value;\n                    Assert.Equal(isNull, reader.IsDBNull(1));\n\n                    Assert.Equal(expected[id - 1] ?? DBNull.Value, val);\n                    ++count;\n                }\n\n                Assert.Equal(9, count);\n            }\n        }\n\n        protected override string GetTempTableName(string tableNameSuffix)\n        {\n            return $\"{TestTableName}_{tableNameSuffix}\";\n        }\n\n        public class TableFixture : ClickHouseTestsBase, IDisposable\n        {\n            private int _identity;\n\n            public TableFixture()\n            {\n                using var cn = OpenConnection();\n\n                var cmd = cn.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}\");\n                cmd.ExecuteNonQuery();\n\n                cmd = cn.CreateCommand($\"CREATE TABLE {TestTableName}(id Int32, str Nullable(String), num Nullable(Decimal64(6))) ENGINE=Memory\");\n                cmd.ExecuteNonQuery();\n            }\n\n            /// <summary>\n            /// Reserves a range of unique sequential identifiers\n            /// </summary>\n            /// <param name=\"length\">The length of a range</param>\n            /// <returns>The first identifier of the reserved range</returns>\n            public int ReserveRange(int length)\n            {\n                Assert.True(length > 0);\n\n                var identity = _identity;\n                while (true)\n                {\n                    var nextIdentity = identity + length;\n                    var originalValue = Interlocked.CompareExchange(ref _identity, nextIdentity, identity);\n                    if (originalValue == identity)\n                        return identity;\n\n                    identity = originalValue;\n                }\n            }\n\n            public void Dispose()\n            {\n                using var cn = OpenConnection();\n                var cmd = cn.CreateCommand($\"DROP TABLE IF EXISTS {TestTableName}\");\n                cmd.ExecuteNonQuery();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ClickHouseCommandTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Globalization;\nusing System.IO;\nusing System.Linq;\nusing System.Net;\nusing System.Net.Sockets;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ClickHouseCommandTests : ClickHouseTestsBase\n    {\n        [Fact]\n        public async Task SimpleExecuteScalar()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand();\n            cmd.CommandText = \"select 42\";\n            var value = await cmd.ExecuteScalarAsync(CancellationToken.None);\n            Assert.Equal((byte)42, value);\n        }\n\n        [Fact]\n        public async Task ExecuteNonQuery()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand();\n            cmd.CommandText = \"select 42 as answer union all select 43 as not_an_answer\";\n\n            var rowCount = await cmd.ExecuteNonQueryAsync(CancellationToken.None);\n            Assert.Equal(2, rowCount);\n\n            cmd.CommandText = \"select 44\";\n            var value = cmd.ExecuteScalar();\n\n            var byteValue = Assert.IsType<byte>(value);\n            Assert.Equal<byte>(44, byteValue);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task Params(ClickHouseParameterMode parameterMode)\n        {\n            await using var cn = await OpenConnectionAsync(parameterMode);\n            await using var cmd = cn.CreateCommand();\n\n            cmd.CommandText =\n                \"select {a:UUID} a, {b} b, {long_parameter_name} /*+{_}*/ c, {d} d, {e123_456_789e} e, {_} f, {g} g, '{e123_456_789e}' h, '/v\\\\\\\\'`/v\\\\\\\\`, \\\"{a}\\\" i--{g} should not be replaced\" +\n                Environment.NewLine +\n                \"from (select 'some real value' as `{a}`)\";\n\n            var now = DateTime.Now;\n            now = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, DateTimeKind.Local);\n            var id = Guid.NewGuid();\n\n            cmd.Parameters.Add(new ClickHouseParameter(\"{a}\") { DbType = DbType.String, Value = id.ToString(\"D\") });\n            cmd.Parameters.Add(new ClickHouseParameter(\"{B}\") { DbType = DbType.Guid, Value = DBNull.Value });\n            cmd.Parameters.Add(new ClickHouseParameter(\"LONG_parameter_NAME\") { DbType = DbType.Int32 });\n            cmd.Parameters.Add(new ClickHouseParameter(\"D\") { Value = 42m });\n            cmd.Parameters.Add(new ClickHouseParameter(\"{e123_456_789E}\") { Value = \"e123_456_789e\" });\n            cmd.Parameters.Add(new ClickHouseParameter(\"_\"));\n            cmd.Parameters.Add(new ClickHouseParameter(\"g\") { Value = now });\n\n            await using var reader = cmd.ExecuteReader();\n            Assert.True(await reader.ReadAsync());\n\n            Assert.Equal(id, reader.GetGuid(reader.GetOrdinal(\"a\")));\n            Assert.True(reader.IsDBNull(reader.GetOrdinal(\"b\")));\n            Assert.True(reader.IsDBNull(reader.GetOrdinal(\"c\")));\n            Assert.Equal(42m, reader.GetDecimal(reader.GetOrdinal(\"d\")));\n            Assert.Equal(\"e123_456_789e\", reader.GetString(reader.GetOrdinal(\"e\")));\n            Assert.True(reader.IsDBNull(reader.GetOrdinal(\"f\")));\n            Assert.Equal(now, reader.GetDateTime(reader.GetOrdinal(\"g\")));\n            Assert.Equal(\"{e123_456_789e}\", reader.GetString(reader.GetOrdinal(\"h\")));\n            Assert.Equal(\"some real value\", reader.GetString(reader.GetOrdinal(\"i\")));\n            Assert.Equal(@\"/v\\\", reader.GetString(reader.GetOrdinal(@\"/v\\\")));\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task MsSqlLikeParams(ClickHouseParameterMode parameterMode)\n        {\n            await using var cn = await OpenConnectionAsync(parameterMode);\n            await using var cmd = cn.CreateCommand();\n\n            cmd.CommandText =\n                \"select cast(@a as UUID) a, @b b, @long_parameter_name /*+@_*/ c, @d d, {e123_456_789e} e, @_ f, {g} g, '@e123_456_789e' h, '/v\\\\\\\\\\'`/v\\\\\\\\`, \\\"@a\\\" i--@g should not be replaced\" +\n                Environment.NewLine +\n                \"from (select 'some real value' as `@a`)\";\n\n            var now = DateTime.Now;\n            now = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, DateTimeKind.Local);\n            var id = Guid.NewGuid();\n\n            cmd.Parameters.Add(new ClickHouseParameter(\"{a}\") { DbType = DbType.String, Value = id.ToString(\"D\") });\n            cmd.Parameters.Add(new ClickHouseParameter(\"@B\") { DbType = DbType.Guid, Value = DBNull.Value });\n            cmd.Parameters.Add(new ClickHouseParameter(\"@LONG_parameter_NAME\") { DbType = DbType.Int32 });\n            cmd.Parameters.Add(new ClickHouseParameter(\"@D\") { Value = 42m });\n            cmd.Parameters.Add(new ClickHouseParameter(\"e123_456_789E\") { Value = \"e123_456_789e\" });\n            cmd.Parameters.Add(new ClickHouseParameter(\"@_\"));\n            cmd.Parameters.Add(new ClickHouseParameter(\"g\") { Value = now });\n\n            await using var reader = cmd.ExecuteReader();\n            Assert.True(await reader.ReadAsync());\n\n            Assert.Equal(id, reader.GetGuid(reader.GetOrdinal(\"a\")));\n            Assert.True(reader.IsDBNull(reader.GetOrdinal(\"b\")));\n            Assert.True(reader.IsDBNull(reader.GetOrdinal(\"c\")));\n            Assert.Equal(42m, reader.GetDecimal(reader.GetOrdinal(\"d\")));\n            Assert.Equal(\"e123_456_789e\", reader.GetString(reader.GetOrdinal(\"e\")));\n            Assert.True(reader.IsDBNull(reader.GetOrdinal(\"f\")));\n            Assert.Equal(now, reader.GetDateTime(reader.GetOrdinal(\"g\")));\n            Assert.Equal(\"@e123_456_789e\", reader.GetString(reader.GetOrdinal(\"h\")));\n            Assert.Equal(\"some real value\", reader.GetString(reader.GetOrdinal(\"i\")));\n            Assert.Equal(@\"/v\\\", reader.GetString(reader.GetOrdinal(@\"/v\\\")));\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task EmptyStringParam(ClickHouseParameterMode parameterMode)\n        {\n            const string query = @\"select {url}, {data}\";\n\n            await using var cn = await OpenConnectionAsync(parameterMode);\n            await using var cmd = cn.CreateCommand(query);\n\n            cmd.Parameters.AddWithValue(\"url\", \"\");\n            cmd.Parameters.AddWithValue(\"data\", \"{\\\"value\\\":107}\");\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            Assert.True(await reader.ReadAsync());\n\n            Assert.Equal((string)cmd.Parameters[\"url\"].Value!, reader.GetString(0));\n            Assert.Equal((string)cmd.Parameters[\"data\"].Value!, reader.GetString(1));\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task TypedParams(ClickHouseParameterMode parameterMode)\n        {\n            await using var cn = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = cn.CreateCommand();\n\n            cmd.CommandText = \"SELECT {param:UInt32} intVal, {PaRaM:Decimal128(9)} decVal, {pArAm:String} strVal\";\n\n            cmd.Parameters.Add(new ClickHouseParameter(\"{Param}\") { Value = \"42\" });\n\n            await using var reader = cmd.ExecuteReader();\n            Assert.True(await reader.ReadAsync());\n\n            Assert.Equal<uint>(42, reader.GetFieldValue<uint>(0));\n            Assert.Equal(42, reader.GetDecimal(1));\n            Assert.Equal(\"42\", reader.GetString(2));\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task UseConnectionInParallel()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            var res = await Task.WhenAll(Sleep(cn, 1), Sleep(cn, 2), Sleep(cn, 3));\n\n            Assert.Equal(new[] { 1, 2, 3 }, res);\n\n            async Task<int> Sleep(ClickHouseConnection connection, int value)\n            {\n                await using var cmd = connection.CreateCommand(string.Format(CultureInfo.InvariantCulture, \"SELECT sleep(0.2)+{0}\", value));\n                return await cmd.ExecuteScalarAsync<int>();\n            }\n        }\n\n        [Fact]\n        public async Task ReadScalarFromLargeResultSet()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand(\"SELECT number+42 FROM system.numbers\");\n\n            var cts = new CancellationTokenSource();\n\n            var expectedTask = cmd.ExecuteScalarAsync<ulong>(cts.Token);\n            var task = await Task.WhenAny(expectedTask, Task.Delay(2000, CancellationToken.None));\n\n            if (!ReferenceEquals(expectedTask, task))\n                cts.Cancel(true);\n\n            var value = await expectedTask;\n            Assert.Equal<ulong>(42, value);\n        }\n\n        [Fact]\n        public async Task UseCommandInParallel()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand(\"SELECT sleep(0.2) + 42\");\n\n            var result = await Task.WhenAll(cmd.ExecuteScalarAsync<uint>(), cmd.ExecuteScalarAsync<uint>(), cmd.ExecuteScalarAsync<uint>());\n            Assert.Equal(new uint[] { 42, 42, 42 }, result);\n        }\n\n#if DEBUG\n        [Fact]\n        public async Task CancelOnSocketTimeout()\n        {\n            // Attempt to emulate a network error. It doesn't work sometimes because ClickHouse server sends Progress messages\n\n            var csb = new ClickHouseConnectionStringBuilder(GetDefaultConnectionSettings()) { ReadWriteTimeout = 25 };\n            await using (var cn = new ClickHouseConnection(csb))\n            {\n                await cn.OpenAsync();\n                await using var cmd = cn.CreateCommand(\"SELECT sleep(3)\");\n\n                var ioEx = Assert.Throws<IOException>(() => cmd.ExecuteNonQuery());\n                var socketEx = Assert.IsType<SocketException>(ioEx.InnerException);\n                Assert.Equal(SocketError.TimedOut, socketEx.SocketErrorCode);\n            }\n        }\n#endif\n\n        [Fact]\n        public async Task CancelOnCommandTimeout()\n        {\n            var csb = new ClickHouseConnectionStringBuilder(GetDefaultConnectionSettings()) { CommandTimeout = 2 };\n            await using (var cn = new ClickHouseConnection(csb))\n            {\n                await cn.OpenAsync();\n                await using var cmd = cn.CreateCommand(\"SELECT sleep(3)\");\n\n                Assert.Throws<OperationCanceledException>(() => cmd.ExecuteNonQuery());\n            }\n        }\n\n        [Fact]\n        public async Task CancelOnTokenTimeout()\n        {\n            await using (var cn = await OpenConnectionAsync())\n            {\n                await using var cmd = cn.CreateCommand(\"SELECT sleep(3)\");\n\n                var tokenSource = new CancellationTokenSource();\n                tokenSource.CancelAfter(500);\n                var ex = await Assert.ThrowsAnyAsync<OperationCanceledException>(() => cmd.ExecuteNonQueryAsync(tokenSource.Token));\n                Assert.Equal(tokenSource.Token, ex.CancellationToken);\n            }\n        }\n\n        [Fact]\n        public void ShouldUnwrapCommandExecuteScalar()\n        {\n            using var conn = OpenConnection();\n            Assert.Throws<ClickHouseServerException>(() => conn.CreateCommand(\"select nothing\").ExecuteScalar());\n        }\n\n        [Fact]\n        public async Task ExecuteScalarShouldReturnSingleResult()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmdDrop = connection.CreateCommand(\"select 2+2\");\n\n            var result = Convert.ToInt32(await cmdDrop.ExecuteScalarAsync());\n            Assert.Equal(4, result);\n        }\n\n        [Fact]\n        public async Task CommandBehaviorCloseConnection()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand(\"SELECT * FROM system.numbers LIMIT 100\");\n            await using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))\n            {\n                Assert.True(await reader.ReadAsync());\n                Assert.Equal(ConnectionState.Open, cn.State);\n            }\n\n            Assert.Equal(ConnectionState.Closed, cn.State);\n\n            await cn.OpenAsync();\n            await using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))\n            {\n                Assert.True(await reader.ReadAsync());\n                Assert.Equal(ConnectionState.Open, cn.State);\n\n                await reader.CloseAsync();\n                Assert.Equal(ConnectionState.Closed, cn.State);\n            }\n\n            await cn.OpenAsync();\n            await using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))\n            {\n                int count = 0;\n                while (await reader.ReadAsync())\n                {\n                    Assert.Equal(ConnectionState.Open, cn.State);\n                    ++count;\n                }\n\n                Assert.Equal(100, count);\n                Assert.Equal(ConnectionState.Closed, cn.State);\n\n                Assert.False(await reader.ReadAsync());\n            }\n\n            cmd.CommandText = \"select x, sum(y) as v from (SELECT number%3 + 1 as x, number as y FROM numbers(10)) group by x with totals;\";\n            await cn.OpenAsync();\n\n            await using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))\n            {\n                int count = 0;\n                while (await reader.ReadAsync())\n                {\n                    Assert.Equal(ConnectionState.Open, cn.State);\n                    ++count;\n                }\n\n                Assert.Equal(3, count);\n                Assert.Equal(ConnectionState.Open, cn.State);\n\n                Assert.True(await reader.NextResultAsync());\n                Assert.True(await reader.ReadAsync());\n                Assert.Equal(ConnectionState.Open, cn.State);\n                Assert.False(await reader.ReadAsync());\n                Assert.Equal(ConnectionState.Closed, cn.State);\n\n                Assert.False(await reader.ReadAsync());\n                Assert.False(await reader.NextResultAsync());\n            }\n\n            await cn.OpenAsync();\n\n            cmd.CommandText = \"It IS NOT a query...\";\n            await Assert.ThrowsAsync<ClickHouseServerException>(() => cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection));\n            Assert.Equal(ConnectionState.Closed, cn.State);\n        }\n\n        [Fact]\n        public async Task TableParameterSingleColumn()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            var cmd = cn.CreateCommand(\"SELECT toInt32(number) FROM numbers(100000) WHERE number IN param_table\");\n            \n            var tableProvider = new ClickHouseTableProvider(\"param_table\", 100);\n            tableProvider.AddColumn(Enumerable.Range(500, int.MaxValue / 2));\n\n            cmd.TableProviders.Add(tableProvider);\n\n            var expectedValues = new HashSet<int>(Enumerable.Range(500, 100));\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            while(await reader.ReadAsync())\n            {\n                var value = reader.GetInt32(0);\n                Assert.True(expectedValues.Remove(value));\n            }\n\n            Assert.Empty(expectedValues);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task TableParameterAndScalarParameter(ClickHouseParameterMode parameterMode)\n        {\n            await using var cn = await OpenConnectionAsync(parameterMode);\n\n            var cmd = cn.CreateCommand(\"SELECT toInt32(number) FROM numbers(100000) WHERE number IN (SELECT {val}*val FROM param_table)\");\n\n            var tableProvider = new ClickHouseTableProvider(\"param_table\", 100);\n            tableProvider.AddColumn(\"val\", Enumerable.Range(500, int.MaxValue / 2));\n\n            cmd.TableProviders.Add(tableProvider);\n\n            cmd.Parameters.AddWithValue(\"val\", 3);\n\n            var expectedValues = new HashSet<int>(Enumerable.Range(500, 100).Select(v => v * 3));\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            while (await reader.ReadAsync())\n            {\n                var value = reader.GetInt32(0);\n                Assert.True(expectedValues.Remove(value));\n            }\n\n            Assert.Empty(expectedValues);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task MultipleTableParameters(ClickHouseParameterMode parameterMode)\n        {\n            await using var cn = await OpenConnectionAsync(parameterMode);\n\n            var cmd = cn.CreateCommand(\"SELECT T2.id AS id, T1.id AS user_id, T1.value AS user, T2.value AS ip FROM q_user AS T1 LEFT JOIN q_addr AS T2 ON T1.id = T2.user_id WHERE T2.id%{param} != 0 ORDER BY T2.id\");\n\n            var users = new (int id, string name)[] { (1, \"user1\"), (2, \"user2\"), (3, \"admin1\"), (4, \"admin2\") };\n            var addr = new (int id, string? ip)[] { (1, \"8.8.8.8\"), (2, \"9.9.9.9\"), (3, null), (4, null), (3, \"127.0.0.1\"), (4, \"127.0.0.1\"), (1, \"2001:0db8:0000:0000:0000:ff00:0042:8329\"), (4, \"::ffff:192.0.2.1\"), (2, \"fe80::883b:771b:71c6:7c31\") };\n\n            var usersTable = new ClickHouseTableProvider(\"q_user\", users.Length);\n            usersTable.AddColumn(\"id\", users.Select(u => u.id));\n            usersTable.AddColumn(\"value\", users.Select(u => u.name));\n\n            cmd.TableProviders.Add(usersTable);\n\n            var addrTable = new ClickHouseTableProvider(\"q_addr\", addr.Length);\n            addrTable.AddColumn(\"id\", Enumerable.Range(0, addr.Length));\n            addrTable.AddColumn(\"user_id\", addr.Select(a => a.id));\n            var ipColumn = addrTable.AddColumn(\"value\", addr.Select(a => a.ip));\n            ipColumn.ClickHouseDbType = ClickHouseDbType.IpV6;\n            ipColumn.IsNullable = true;\n\n            cmd.TableProviders.Add(addrTable);\n\n            cmd.Parameters.AddWithValue(\"param\", 7);\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            int expectedId = 1;\n            while(await reader.ReadAsync())\n            {\n                var id = reader.GetInt32(0);\n                Assert.Equal(expectedId, id);\n\n                var userId = reader.GetInt32(1);\n                var user = reader.GetString(2);\n                var ip = reader.GetFieldValue<IPAddress>(3, null);\n\n                Assert.Equal(addr[expectedId].id, userId);\n                var expectedUser = users.Single(u => u.id == userId).name;\n                Assert.Equal(expectedUser, user);\n\n                var expectedIp = addr[expectedId].ip == null ? null : IPAddress.Parse(addr[expectedId].ip).MapToIPv6();\n                Assert.Equal(expectedIp, ip);\n\n                if (++expectedId % 7 == 0)\n                    ++expectedId;\n            }\n\n            Assert.Equal(addr.Length, expectedId);\n        }\n\n        [Fact]\n        public async Task InsertWithParameters()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync(builder => builder.ParametersMode = ClickHouseParameterMode.Interpolate);\n\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS insert_with_parameters_test\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand(\"CREATE TABLE insert_with_parameters_test(str_val String) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"INSERT INTO insert_with_parameters_test(str_val) VALUES (@str_param)\";\n                var p = cmd.CreateParameter();\n                p.ParameterName = \"str_param\";\n                var insertedValue = \"IZyy8d\\\\'\\\"\\n\\t\\v\\b\\rLsVeTtdfk6MjJl\";\n                p.Value = insertedValue;\n                cmd.Parameters.Add(p);\n                await cmd.ExecuteNonQueryAsync();\n                cmd.Parameters.Clear();\n\n                cmd.CommandText = \"SELECT str_val FROM insert_with_parameters_test\";\n                var selectedValue = (string?)await cmd.ExecuteScalarAsync();\n                Assert.Equal(insertedValue, selectedValue);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS insert_with_parameters_test\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Fact]\n        public async Task DeleteWithParameters()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync(builder => builder.ParametersMode = ClickHouseParameterMode.Interpolate);\n\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS delete_with_parameters_test\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand(\"CREATE TABLE delete_with_parameters_test(str_val String) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"alter table delete_with_parameters_test delete where str_val = @str_param\";\n                var p = cmd.CreateParameter();\n                p.ParameterName = \"str_param\";\n                var insertedValue = \"IZyy8d\\\\'\\\"\\n\\t\\v\\b\\rLsVeTtdfk6MjJl\";\n                p.Value = insertedValue;\n                cmd.Parameters.Add(p);\n                await cmd.ExecuteNonQueryAsync();\n                // Assert: Not Throws\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS delete_with_parameters_test\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Fact]\n        public async Task UpdateWithParameters()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS update_with_parameters_test\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand(\"CREATE TABLE update_with_parameters_test(str_val String) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"alter table update_with_parameters_test update str_val = @str_param where str_val = @str_param\";\n                var p = cmd.CreateParameter();\n                p.ParameterName = \"str_param\";\n                var insertedValue = \"IZyy8d\\\\'\\\"\\n\\t\\v\\b\\rLsVeTtdfk6MjJl\";\n                p.Value = insertedValue;\n                ((ClickHouseParameter)p).ParameterMode = ClickHouseParameterMode.Interpolate;\n                cmd.Parameters.Add(p);\n                await cmd.ExecuteNonQueryAsync();\n                // Assert: Not Throws\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS update_with_parameters_test\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Fact]\n        public async Task SelectWithOffsetLimitParameters()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"select cast(42 as UInt64) limit @Limit offset @Offset\");\n            cmd.ParametersMode = ClickHouseParameterMode.Interpolate;\n            var p_limit = cmd.CreateParameter();\n            var p_offset = cmd.CreateParameter();\n            p_limit.ParameterName = \"Limit\";\n            p_limit.Value = 1;\n            p_offset.ParameterName = \"Offset\";\n            p_offset.Value = 0;\n            cmd.Parameters.Add(p_limit);\n            cmd.Parameters.Add(p_offset);\n            var result = await cmd.ExecuteScalarAsync();\n            Assert.IsType<ulong>(result);\n            Assert.Equal(42UL, result);\n        }\n\n        [Fact]\n        public async Task CreateTableWithCommentParameter()\n        {\n            var tableName = GetTempTableName(\"with_comment\");\n\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n\n                var cmd = connection.CreateCommand($\"DROP TABLE IF EXISTS {tableName}\");\n                await cmd.ExecuteNonQueryAsync();\n\n                const string commentText = \"The comment: \\\\'\\\"\\n\\t\\v\\b\\rals;kdjang\";\n                cmd = connection.CreateCommand($\"CREATE TABLE {tableName}(id Int32) ENGINE = Memory COMMENT {{comment}}\");\n                cmd.Parameters.AddWithValue(\"comment\", commentText).ParameterMode = ClickHouseParameterMode.Interpolate;\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand(\"SELECT comment FROM system.tables WHERE name = {table}\");\n                cmd.Parameters.AddWithValue(\"table\", tableName).ParameterMode = ClickHouseParameterMode.Serialize;\n                var result = await cmd.ExecuteScalarAsync();\n                Assert.IsType<string>(result);\n                Assert.Equal(commentText, result);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand($\"DROP TABLE IF EXISTS {tableName}\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Fact]\n        public async Task WithQueryId()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            var cmd = connection.CreateCommand();\n            cmd.CommandText = \"SELECT * FROM system.processes WHERE query_id = {qid}\";\n            cmd.QueryId = \"ABSOLUTELY NOT a UUID\";\n            cmd.Parameters.AddWithValue(\"qid\", cmd.QueryId);\n\n            await using var reader = cmd.ExecuteReader();\n            Assert.True(await reader.ReadAsync());\n\n            var isInitialIdx = reader.GetOrdinal(\"is_initial_query\");\n            Assert.True(isInitialIdx >= 0);\n            var queryIdIdx = reader.GetOrdinal(\"query_id\");\n            Assert.True(queryIdIdx >= 0);\n            var initialQueryIdIdx = reader.GetOrdinal(\"initial_query_id\");\n            Assert.True(initialQueryIdIdx >= 0);\n\n            bool isInitialQuery = reader.GetBoolean(isInitialIdx);\n            Assert.True(isInitialQuery);\n\n            var queryId = reader.GetString(queryIdIdx);\n            var initialQueryId = reader.GetString(initialQueryIdIdx);\n\n            Assert.Equal(queryId, initialQueryId);\n            Assert.Equal(queryId, cmd.QueryId);\n\n            Assert.False(await reader.ReadAsync());\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ClickHouseConnectionStringBuilderTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ClickHouseConnectionStringBuilderTests\n    {\n        [Fact]\n        public void FromString()\n        {\n            var builder = new ClickHouseConnectionStringBuilder(\n                \"host=ClickHouse.example.com;port=65500;compress = off;DataBase=\\\"don't; connect\\\"\\\" to me :)\\\";user=root; password=123456;\\r\\n bufferSize=1337; ReadWriteTimeout=42; clientname=ClickHouse.NetCore Tests;clientversion=3.2.1\");\n\n            var settings = builder.BuildSettings();\n\n            Assert.Equal(\"ClickHouse.example.com\", settings.Host);\n            Assert.Equal(65500, settings.Port);\n            Assert.Equal(\"don't; connect\\\" to me :)\", settings.Database);\n            Assert.Equal(\"root\", settings.User);\n            Assert.Equal(\"123456\", settings.Password);\n            Assert.Equal(1337, settings.BufferSize);\n            Assert.Equal(42, settings.ReadWriteTimeout);\n            Assert.Equal(\"ClickHouse.NetCore Tests\", settings.ClientName);\n            Assert.Equal(new ClickHouseVersion(3, 2, 1), settings.ClientVersion);\n            Assert.Equal(false, settings.Compress);\n        }\n\n        [Fact]\n        public void Remove()\n        {\n            var builder = new ClickHouseConnectionStringBuilder {Host = \"some_instance.example.com\", Port = ClickHouseConnectionStringBuilder.DefaultPort *2, ClientName = \"Test!\"};\n\n            Assert.Equal(3, builder.Count);\n\n            Assert.True(builder.Remove(\"pORT\"));\n            Assert.Equal(2, builder.Count);\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultPort, builder.Port);\n\n            Assert.False(builder.Remove(\"Port\"));\n            \n            Assert.True(builder.Remove(\"ClientName\"));\n            Assert.Equal(1, builder.Count);\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultClientName, builder.ClientName);\n        }\n\n        [Fact]\n        public void SetConnectionString()\n        {\n            var builder = new ClickHouseConnectionStringBuilder(\n                \"host=ClickHouse.example.com;port=65500;DataBase=\\\"don't; connect\\\"\\\" to me :)\\\";user=root; password=123456;\\r\\n bufferSize=1337; ReadWriteTimeout=42; clientname=ClickHouse.NetCore Tests;clientversion=3.2.1\");\n\n            Assert.Equal(9, builder.Count);\n\n            builder.ConnectionString = \"host=localhost; port=31337\";\n            Assert.Equal(2, builder.Count);\n        }\n\n        [Fact]\n        public void Default()\n        {\n            var builder = new ClickHouseConnectionStringBuilder();\n\n            Assert.Equal(string.Empty, builder.ConnectionString);\n            Assert.Empty(builder);\n\n            // The host is required\n            Assert.Throws<ArgumentException>(() => builder.BuildSettings());\n\n            builder.Host = \"localhost\";\n            Assert.Equal(\"Host=localhost\", builder.ConnectionString);\n\n            var settings = builder.BuildSettings();\n\n            var checkedPropertiesCount = 0;\n            Assert.Equal(\"localhost\", settings.Host);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultPort, settings.Port);\n            ++checkedPropertiesCount;\n\n            Assert.Null(settings.Database);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultUser, settings.User);\n            ++checkedPropertiesCount;\n\n            Assert.Null(settings.Password);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultBufferSize, settings.BufferSize);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultReadWriteTimeout, settings.ReadWriteTimeout);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultClientName, settings.ClientName);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultClientVersion, settings.ClientVersion);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultCompress, settings.Compress);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultCommandTimeout, settings.CommandTimeout);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultTlsMode, settings.TlsMode);\n            ++checkedPropertiesCount;\n\n            Assert.Null(settings.RootCertificate);\n            ++checkedPropertiesCount;\n\n            Assert.True(settings.ServerCertificateHash.IsEmpty);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseConnectionStringBuilder.DefaultParametersMode, settings.ParametersMode);\n            ++checkedPropertiesCount;\n\n            Assert.Null(settings.QuotaKey);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(checkedPropertiesCount, settings.GetType().GetProperties().Length);\n        }\n\n        [Fact]\n        public void Clone()\n        {\n            var builder = new ClickHouseConnectionStringBuilder(\n                \"host=ClickHouse.example.com;\" +\n                \"port=65500;\" +\n                \"DataBase=\\\"don't; connect\\\";\" +\n                \"user=root; \" +\n                \"password=123456;\\r\\n \" +\n                \"bufferSize=1337; \" +\n                \"ReadWriteTimeout=42; \" +\n                \"clientname=ClickHouse.NetCore Tests;\" +\n                \"clientversion=3.2.1;\" +\n                $\"COMPRESS={!ClickHouseConnectionStringBuilder.DefaultCompress};\" +\n                \"CommandTimeout=123;\" +\n                \"TLSMode=rEqUIrE;\" +\n                \"RootCertificate=/usr/local/share/ca-certificates/Yandex/YandexInternalRootCA.pem;\" +\n                \"ServerCertificateHash=1234-5678 9abc-def0;\" +\n                \"ParametersMode=Interpolate;\" +\n                \"QuotaKey='unlimited'\");\n\n            var settings = builder.BuildSettings();\n\n            var builderCopy = new ClickHouseConnectionStringBuilder(settings);\n            settings = builderCopy.BuildSettings();\n\n            var checkedPropertiesCount = 0;\n\n            Assert.Equal(\"ClickHouse.example.com\", settings.Host);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(65500, settings.Port);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(\"don't; connect\", settings.Database);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(\"root\", settings.User);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(\"123456\", settings.Password);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(1337, settings.BufferSize);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(42, settings.ReadWriteTimeout);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(\"ClickHouse.NetCore Tests\", settings.ClientName);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(new ClickHouseVersion(3, 2, 1), settings.ClientVersion);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(!ClickHouseConnectionStringBuilder.DefaultCompress, settings.Compress);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(123, settings.CommandTimeout);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseTlsMode.Require, settings.TlsMode);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(\"/usr/local/share/ca-certificates/Yandex/YandexInternalRootCA.pem\", settings.RootCertificate);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(new byte[] { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 }, settings.ServerCertificateHash.ToArray());\n            ++checkedPropertiesCount;\n\n            Assert.Equal(ClickHouseParameterMode.Interpolate, settings.ParametersMode);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(\"unlimited\", settings.QuotaKey);\n            ++checkedPropertiesCount;\n\n            Assert.Equal(checkedPropertiesCount, settings.GetType().GetProperties().Length);\n        }\n\n        [Fact]\n        public void InvalidConnectionString()\n        {\n            Assert.Throws<ArgumentException>(() => new ClickHouseConnectionStringBuilder(\"Host=127.0.0.1;User=anonymous;Timeout:1337\"));\n        }\n\n        [Fact]\n        public void ValidConnectionStringWithInvalidProperty()\n        {\n            var ex = Assert.Throws<ArgumentException>(() => new ClickHouseConnectionStringBuilder(\"Host=127.0.0.1;User=anonymous;Timeout=1337;Server=clickhouse.example.com\"));\n            Assert.Equal(\"keyword\", ex.ParamName);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ClickHouseConnectionTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2021, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Data;\nusing System.Linq;\nusing System.Net.Sockets;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Protocol;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ClickHouseConnectionTests : ClickHouseTestsBase\n    {\n        [Fact]\n        public async Task Probe()\n        {\n            // This test checks the version of the ClickHouse server specified in the connection string.\n            // Its only purpose is to check if the CI pipeline has passed suitable connection settings.\n            await using var cn = await OpenConnectionAsync();\n            Assert.NotNull(cn.ServerInfo);\n            Assert.InRange(cn.ServerInfo.ServerRevision, ClickHouseProtocolRevisions.CurrentRevision, int.MaxValue);\n        }\n\n        [Fact]\n        public void ShouldUnwrapConnectionOpenExceptions()\n        {\n            var sb = new ClickHouseConnectionStringBuilder { Host = \"none.example.com\" };\n            using var conn = new ClickHouseConnection(sb);\n            var exception = Assert.ThrowsAny<SocketException>(() => conn.Open());\n            Assert.Equal(SocketError.HostNotFound, exception.SocketErrorCode);\n        }\n\n        [Fact]\n        public async Task CanConnectWithUserAndPassword()\n        {\n            var settings = GetDefaultConnectionSettings();\n            settings = new ClickHouseConnectionStringBuilder(settings) { Database = null }.BuildSettings();\n\n            Assert.False(string.IsNullOrEmpty(settings.User));\n            Assert.Null(settings.Database);\n\n            await using var conn = new ClickHouseConnection(settings);\n            await conn.OpenAsync();\n            Assert.Equal(string.Empty, conn.Database);\n\n            var currentUser = await conn.CreateCommand(\"select currentUser()\").ExecuteScalarAsync<string>();\n            var currentDb = await conn.CreateCommand(\"select currentDatabase()\").ExecuteScalarAsync<string>();\n\n            Assert.Equal(settings.User, currentUser);\n            Assert.False(string.IsNullOrEmpty(currentDb));\n        }\n\n        [Fact]\n        public async Task TryPing()\n        {\n            await using var cn = new ClickHouseConnection(GetDefaultConnectionSettings());\n\n            var ex = await Assert.ThrowsAnyAsync<ClickHouseException>(() => cn.TryPingAsync());\n            Assert.Equal(ClickHouseErrorCodes.ConnectionClosed, ex.ErrorCode);\n\n            await cn.OpenAsync();\n            Assert.True(await cn.TryPingAsync());\n\n            var cmd = cn.CreateCommand(\"SELECT * FROM system.one\");\n            Assert.True(await cn.TryPingAsync());\n            await using (var reader = await cmd.ExecuteReaderAsync())\n            {\n                Assert.False(await cn.TryPingAsync());\n                Assert.True(await reader.ReadAsync());\n                Assert.False(await cn.TryPingAsync());\n                Assert.False(await reader.ReadAsync());\n                Assert.True(await cn.TryPingAsync());\n            }\n\n            await using (await cmd.ExecuteReaderAsync())\n            {\n                Assert.False(await cn.TryPingAsync());\n            }\n\n            Assert.True(await cn.TryPingAsync());\n\n            await WithTemporaryTable(\n                \"ping\",\n                \"id Int32\",\n                async (_, tableName) =>\n                {\n                    await using (var writer = await cn.CreateColumnWriterAsync($\"INSERT INTO {tableName} VALUES\", CancellationToken.None))\n                    {\n                        Assert.False(await cn.TryPingAsync());\n                        await writer.EndWriteAsync(CancellationToken.None);\n                        Assert.True(await cn.TryPingAsync());\n                    }\n\n                    Assert.True(await cn.TryPingAsync());\n                    await using (await cn.CreateColumnWriterAsync($\"INSERT INTO {tableName} VALUES\", CancellationToken.None))\n                    {\n                        Assert.False(await cn.TryPingAsync());\n                    }\n\n                    Assert.True(await cn.TryPingAsync());\n                });\n        }\n\n        [Fact]\n        public async Task OpenConnectionInParallel()\n        {\n            // Only one thread should open the connection and other threads should fail\n\n            int counter = 0;\n            var settings = GetDefaultConnectionSettings();\n            await using var connection = new ClickHouseConnection(settings);\n            connection.StateChange += OnStateChanged;\n\n            var tcs = new TaskCompletionSource<bool>();\n\n            var tasks = Enumerable.Range(0, 32).Select(_ => Task.Run(Open)).ToList();\n\n            tcs.SetResult(true);\n            await Assert.ThrowsAnyAsync<Exception>(() => Task.WhenAll(tasks));\n\n            int notFailedTaskCount = 0;\n            foreach (var task in tasks)\n            {\n                if (task.Exception == null)\n                {\n                    ++notFailedTaskCount;\n                    continue;\n                }\n\n                var aggrEx = Assert.IsAssignableFrom<AggregateException>(task.Exception);\n                Assert.Single(aggrEx.InnerExceptions);\n                var ex = Assert.IsAssignableFrom<ClickHouseException>(aggrEx.InnerExceptions[0]);\n                Assert.Equal(ClickHouseErrorCodes.InvalidConnectionState, ex.ErrorCode);\n            }\n\n            // There should be at least one completed task and several failed tasks\n            Assert.True(notFailedTaskCount > 0 && notFailedTaskCount < tasks.Count);\n            Assert.Equal(ConnectionState.Open, connection.State);\n            Assert.Equal(0x10001, counter);\n\n            void OnStateChanged(object sender, StateChangeEventArgs e)\n            {\n                Assert.Same(sender, connection);\n\n                if (e.OriginalState == ConnectionState.Closed && e.CurrentState == ConnectionState.Connecting)\n                    Interlocked.Increment(ref counter);\n\n                if (e.OriginalState == ConnectionState.Connecting && e.CurrentState == ConnectionState.Open)\n                    Interlocked.Add(ref counter, 0x10000);\n            }\n\n            async Task Open()\n            {\n                await tcs.Task;\n                await connection.OpenAsync();\n            }\n        }\n\n        [Theory]\n        [InlineData(ConnectionState.Connecting, ConnectionState.Closed)]\n        [InlineData(ConnectionState.Open, ConnectionState.Open)]\n        [InlineData(ConnectionState.Closed, ConnectionState.Closed)]\n        public async Task HandleCallbackException(ConnectionState throwOnState, ConnectionState expectedState)\n        {\n            var settings = GetDefaultConnectionSettings();\n            await using var connection = new ClickHouseConnection(settings);\n            connection.StateChange += OnStateChanged;\n\n            var ex = await Assert.ThrowsAnyAsync<ClickHouseException>(async () =>\n            {\n                await connection.OpenAsync();\n                await connection.CloseAsync();\n            });\n\n            Assert.Equal(expectedState, connection.State);\n            Assert.Equal(ClickHouseErrorCodes.CallbackError, ex.ErrorCode);\n            Assert.NotNull(ex.InnerException);\n            Assert.Equal(\"You shall not pass!\", ex.InnerException!.Message);\n\n            void OnStateChanged(object sender, StateChangeEventArgs e)\n            {\n                Assert.Same(sender, connection);\n\n                if (e.CurrentState == throwOnState)\n                    throw new Exception(\"You shall not pass!\");\n            }\n        }\n\n        [Fact]\n        public async Task HandleDoubleCallbackException()\n        {\n            var settings = GetDefaultConnectionSettings();\n            await using var connection = new ClickHouseConnection(settings);\n            connection.StateChange += OnStateChanged;\n\n            var ex = await Assert.ThrowsAnyAsync<AggregateException>(() => connection.OpenAsync());\n\n            Assert.Equal(ConnectionState.Closed, connection.State);\n            Assert.Equal(2, ex.InnerExceptions.Count);\n            Assert.Equal(\"You shall not pass!\", ex.InnerExceptions[0].Message);\n            Assert.Equal(\"How dare you!\", ex.InnerExceptions[1].Message);\n\n            void OnStateChanged(object sender, StateChangeEventArgs e)\n            {\n                Assert.Same(sender, connection);\n\n                if (e.CurrentState == ConnectionState.Closed)\n                    throw new Exception(\"How dare you!\");\n\n                if (e.CurrentState == ConnectionState.Connecting)\n                    throw new Exception(\"You shall not pass!\");\n            }\n        }\n\n        [Fact]\n        public async Task DisposeCallback()\n        {\n            bool disposed = false;\n            await using(var connection=await OpenConnectionAsync())\n            {\n                connection.Disposed += OnDisposed;\n            }\n\n            Assert.True(disposed);\n\n            void OnDisposed(object? sender, EventArgs eventArgs)\n            {\n                disposed = true;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ClickHouseDataReaderTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2024, 2026 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Types;\nusing System;\nusing System.Data;\nusing System.Globalization;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ClickHouseDataReaderTests : ClickHouseTestsBase\n    {\n        [Fact]\n        public async Task SimpleReader()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand();\n            cmd.CommandText = \"select 42 as answer\";\n\n            await using var reader = await cmd.ExecuteReaderAsync(CancellationToken.None);\n            var columnIndex = reader.GetOrdinal(\"answer\");\n            Assert.True(await reader.ReadAsync(CancellationToken.None));\n            var value = reader.GetByte(columnIndex);\n            Assert.False(await reader.ReadAsync());\n\n            Assert.Equal<byte>(42, value);\n        }\n\n        [Fact]\n        public async Task CloseReaderWithoutReading()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using (var cmd = cn.CreateCommand(\"SELECT * FROM system.numbers\"))\n            {\n                await using (var reader = await cmd.ExecuteReaderAsync())\n                {\n                    ulong value = 0;\n                    while (value < 100 && await reader.ReadAsync())\n                        value = reader.GetFieldValue<ulong>(0);\n\n                    Assert.Equal<ulong>(100, value);\n                }\n\n                await using var reader2 = await cmd.ExecuteReaderAsync(CancellationToken.None);\n                Assert.True(await reader2.ReadAsync());\n\n                var value2 = reader2.GetFieldValue<ulong>(0);\n                Assert.Equal<ulong>(0, value2);\n            }\n\n            await using var cmd2 = cn.CreateCommand(\"SELECT * FROM system.one\");\n\n            await (await cmd2.ExecuteReaderAsync()).DisposeAsync();\n\n            cmd2.CommandText = \"SELECT * FROM system.numbers LIMIT 10000 OFFSET 42\";\n\n            await using var reader3 = await cmd2.ExecuteReaderAsync();\n            Assert.True(await reader3.ReadAsync());\n            var value3 = reader3.GetFieldValue<ulong>(0);\n\n            Assert.Equal<ulong>(42, value3);\n        }\n\n        [Fact]\n        public async Task TotalsWithNextResult()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand();\n            cmd.CommandText = \"select x, sum(y) as v from (SELECT number%2 + 1 as x, number as y FROM numbers(10)) group by x with totals;\";\n\n            using var reader = await cmd.ExecuteReaderAsync();\n            Assert.Equal(ClickHouseDataReaderState.Data, reader.State);\n\n            ulong rowsTotal = 0;\n            while (reader.Read())\n            {\n                Assert.Equal(ClickHouseDataReaderState.Data, reader.State);\n                rowsTotal += reader.GetFieldValue<ulong>(1);\n            }\n\n            Assert.Equal(ClickHouseDataReaderState.NextResultPending, reader.State);\n            var hasTotals = reader.NextResult();\n            Assert.True(hasTotals);\n            Assert.Equal(ClickHouseDataReaderState.Totals, reader.State);\n\n            Assert.True(reader.Read());\n\n            Assert.Equal(ClickHouseDataReaderState.Totals, reader.State);\n            var total = reader.GetFieldValue<ulong>(1);\n            Assert.Equal(rowsTotal, total);\n\n            Assert.False(reader.Read());\n\n            Assert.Equal(ClickHouseDataReaderState.Closed, reader.State);\n        }\n\n        [Fact]\n        public async Task ExtremesWithNextResult()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand();\n            cmd.Extremes = true;\n            cmd.CommandText = \"select x, sum(y) as v from (SELECT number%2 + 1 as x, number as y FROM numbers(10)) group by x;\";\n\n            using var reader = await cmd.ExecuteReaderAsync();\n            Assert.Equal(ClickHouseDataReaderState.Data, reader.State);\n\n            ulong minX = ulong.MaxValue, maxX = ulong.MinValue, minSum = ulong.MaxValue, maxSum = ulong.MinValue;\n            while (reader.Read())\n            {\n                Assert.Equal(ClickHouseDataReaderState.Data, reader.State);\n                var x = reader.GetFieldValue<ulong>(0);\n                var sum = reader.GetFieldValue<ulong>(1);\n\n                minX = Math.Min(minX, x);\n                maxX = Math.Max(maxX, x);\n\n                minSum = Math.Min(minSum, sum);\n                maxSum = Math.Max(maxSum, sum);\n            }\n\n            Assert.Equal(ClickHouseDataReaderState.NextResultPending, reader.State);\n\n            Assert.True(reader.NextResult());\n\n            Assert.True(reader.Read());\n            var extremeX = reader.GetFieldValue<ulong>(0);\n            var extremeSum = reader.GetFieldValue<ulong>(1);\n            Assert.Equal(minX, extremeX);\n            Assert.Equal(minSum, extremeSum);\n\n            Assert.True(reader.Read());\n            extremeX = reader.GetFieldValue<ulong>(0);\n            extremeSum = reader.GetFieldValue<ulong>(1);\n            Assert.Equal(maxX, extremeX);\n            Assert.Equal(maxSum, extremeSum);\n\n            Assert.False(reader.Read());\n\n            Assert.Equal(ClickHouseDataReaderState.Closed, reader.State);\n        }\n\n        [Fact]\n        public async Task TotalsAndExtremes()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand();\n            cmd.Extremes = true;\n            cmd.CommandText = \"select x, sum(y) as v from (SELECT number%2 + 1 as x, number as y FROM numbers(10)) group by x with totals;\";\n\n            using var reader = await cmd.ExecuteReaderAsync();\n            Assert.Equal(ClickHouseDataReaderState.Data, reader.State);\n\n            ulong rowsTotal = 0;\n            ulong minX = ulong.MaxValue, maxX = ulong.MinValue, minSum = ulong.MaxValue, maxSum = ulong.MinValue;\n            while (reader.Read())\n            {\n                Assert.Equal(ClickHouseDataReaderState.Data, reader.State);\n                var x = reader.GetFieldValue<ulong>(0);\n                var sum = reader.GetFieldValue<ulong>(1);\n\n                rowsTotal += sum;\n\n                minX = Math.Min(minX, x);\n                maxX = Math.Max(maxX, x);\n\n                minSum = Math.Min(minSum, sum);\n                maxSum = Math.Max(maxSum, sum);\n            }\n\n            Assert.Equal(ClickHouseDataReaderState.NextResultPending, reader.State);\n            var hasTotals = reader.NextResult();\n            Assert.True(hasTotals);\n            Assert.Equal(ClickHouseDataReaderState.Totals, reader.State);\n\n            Assert.True(reader.Read());\n\n            Assert.Equal(ClickHouseDataReaderState.Totals, reader.State);\n            var total = reader.GetFieldValue<ulong>(1);\n            Assert.Equal(rowsTotal, total);\n\n            Assert.False(reader.Read());\n\n            Assert.Equal(ClickHouseDataReaderState.NextResultPending, reader.State);\n\n            Assert.True(reader.NextResult());\n\n            Assert.True(reader.Read());\n            var extremeX = reader.GetFieldValue<ulong>(0);\n            var extremeSum = reader.GetFieldValue<ulong>(1);\n            Assert.Equal(minX, extremeX);\n            Assert.Equal(minSum, extremeSum);\n\n            Assert.True(reader.Read());\n            extremeX = reader.GetFieldValue<ulong>(0);\n            extremeSum = reader.GetFieldValue<ulong>(1);\n            Assert.Equal(maxX, extremeX);\n            Assert.Equal(maxSum, extremeSum);\n\n            Assert.False(reader.Read());\n\n            Assert.Equal(ClickHouseDataReaderState.Closed, reader.State);\n        }\n\n        [Theory]\n        [InlineData(ClickHouseDataReaderState.Data, 3)]\n        [InlineData(ClickHouseDataReaderState.Extremes, 2)]\n        [InlineData(ClickHouseDataReaderState.Totals, 1)]\n        public async Task SkipNextResult(ClickHouseDataReaderState readBlock, int expectedCount)\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand();\n            cmd.Extremes = true;\n            cmd.CommandText = \"select x, sum(y) as v from (SELECT number%3 + 1 as x, number as y FROM numbers(10)) group by x with totals;\";\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            Assert.Equal(ClickHouseDataReaderState.Data, reader.State);\n\n            do\n            {\n                switch (reader.State)\n                {\n                    case ClickHouseDataReaderState.Data:\n                    case ClickHouseDataReaderState.Totals:\n                    case ClickHouseDataReaderState.Extremes:\n                        if (reader.State == readBlock)\n                            break;\n\n                        continue;\n\n                    default:\n                        Assert.True(false, $\"Unexpected state: {reader.State}.\");\n                        break;\n                }\n\n                int count = 0;\n                while (await reader.ReadAsync())\n                    ++count;\n\n                Assert.False(await reader.ReadAsync());\n                Assert.Equal(expectedCount, count);\n                Assert.True(reader.State == ClickHouseDataReaderState.NextResultPending || reader.State == ClickHouseDataReaderState.Closed);\n\n            } while (await reader.NextResultAsync());\n\n            Assert.Equal(ClickHouseDataReaderState.Closed, reader.State);\n        }\n\n        [Fact]\n        public async Task CommandBehaviorSchemaOnly()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand(\"SELECT * FROM system.numbers\");\n            await using var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SchemaOnly);\n\n            Assert.False(await reader.ReadAsync());\n            Assert.False(await reader.ReadAsync());\n            Assert.False(await reader.NextResultAsync());\n            Assert.False(await reader.ReadAsync());\n\n            Assert.Equal(1, reader.FieldCount);\n        }\n\n        [Fact]\n        public async Task CommandBehaviorSingleRow()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand(\"SELECT * FROM system.numbers\");\n            await using var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SingleRow);\n\n            Assert.Equal(1, reader.FieldCount);\n            Assert.True(await reader.ReadAsync());\n\n            Assert.False(await reader.ReadAsync());\n            Assert.False(await reader.ReadAsync());\n            Assert.False(await reader.NextResultAsync());\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task CommandBehaviorSingleResult()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand(\"select x, sum(y) as v from (SELECT number%7 + 1 as x, number as y FROM numbers(100)) group by x with totals;\");\n            cmd.Extremes = true;\n\n            await using var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SingleResult);\n\n            int count = 0;\n            while (await reader.ReadAsync())\n                ++count;\n\n            Assert.Equal(7, count);\n            Assert.False(reader.Read());\n            Assert.NotEqual(ClickHouseDataReaderState.NextResultPending, reader.State);\n\n            Assert.False(reader.NextResult());\n            Assert.False(reader.Read());\n        }\n\n        [Fact]\n        public async Task CommandBehaviorSequentialAccess()\n        {\n            // SequentialAccess is ignored\n\n            await using var cn = await OpenConnectionAsync();\n            await using var cmd = cn.CreateCommand(\"SELECT 41,42,43\");\n            await using var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess);\n\n            Assert.True(await reader.ReadAsync());\n\n            Assert.Equal(41, reader.GetInt32(0));\n            Assert.Equal(42, reader.GetInt32(1));\n            Assert.Equal(43, reader.GetInt32(2));\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task CommandBehaviorKeyInfo()\n        {\n            // KeyInfo is not supported\n\n            await using var cn = await OpenConnectionAsync();\n            await using var cmd = cn.CreateCommand(\"SELECT 42\");\n            var ex = await Assert.ThrowsAsync<ArgumentException>(() => cmd.ExecuteReaderAsync(CommandBehavior.KeyInfo));\n            Assert.Equal(\"behavior\", ex.ParamName);\n        }\n\n        [Fact (Skip = \"This test is flaky. The server doesn't always respond with profile events.\")]\n        public async Task ProfileEvents()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            const int expectedCount = 200_000;\n            await using var cmd = cn.CreateCommand(string.Format(CultureInfo.InvariantCulture, \"SELECT number as n, addSeconds('2000-01-01 00:00:00'::DateTime, n) as d FROM numbers({0})\", expectedCount));\n            cmd.IgnoreProfileEvents = false;\n\n            int count = 0, eventTableCount = 0;\n            long selectedRows = 0;\n            await using var reader = await cmd.ExecuteReaderAsync();\n            reader.ConfigureColumn(\"d\", new ClickHouseColumnSettings(columnType: typeof(DateTime)));\n\n            var startDate = new DateTimeOffset(2000, 1, 1, 0, 0, 0, -cn.GetServerTimeZone().GetUtcOffset(new DateTime(2000, 1, 1)));\n\n            while(true)\n            {\n                Assert.Equal(ClickHouseDataReaderState.Data, reader.State);\n\n                while (await reader.ReadAsync())\n                {\n                    var number = reader.GetUInt64(0);\n                    var dateObj = reader.GetValue(1);\n\n                    // Check that column settings applied\n                    var date = Assert.IsType<DateTime>(dateObj);\n\n                    Assert.Equal(startDate.AddSeconds(number).DateTime, date);\n                    ++count;\n                }\n\n                if (!await reader.NextResultAsync())\n                    break;\n\n                Assert.Equal(ClickHouseDataReaderState.ProfileEvents, reader.State);\n\n                var typeColumnIdx = reader.GetOrdinal(\"type\");\n                var nameColumnIdx = reader.GetOrdinal(\"name\");\n                var valueColumnIdx = reader.GetOrdinal(\"value\");\n\n                ++eventTableCount;\n                while(await reader.ReadAsync())\n                {\n                    var name = reader.GetString(nameColumnIdx);\n                    if (name != \"SelectedRows\")\n                        continue;\n\n                    var type = reader.GetString(typeColumnIdx);\n                    var value = reader.GetInt64(valueColumnIdx);\n                    switch (type)\n                    {\n                        case \"increment\":\n                            selectedRows = value;\n                            break;\n                        default:\n                            Assert.True(false, $\"Unexpected event type: {type}\");\n                            continue;\n                    }\n                }\n\n                if (!await reader.NextResultAsync())\n                    break;\n            };\n\n            Assert.True(eventTableCount > 1);\n            Assert.Equal(expectedCount, count);\n            Assert.True(selectedRows >= expectedCount);\n        }\n\n        [Fact]\n        public async Task DateTimeKindForDate()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand();\n            cmd.CommandText = \"select cast('2022-01-01' as Date)\";\n\n            await using var reader = await cmd.ExecuteReaderAsync(CancellationToken.None);\n            Assert.True(await reader.ReadAsync(CancellationToken.None));\n            var value = reader.GetDateTime(0);\n            Assert.False(await reader.ReadAsync());\n\n            Assert.Equal(2022, value.Year);\n            Assert.Equal(1, value.Month);\n            Assert.Equal(1, value.Day);\n            Assert.Equal(0, value.Hour);\n            Assert.Equal(0, value.Minute);\n            Assert.Equal(0, value.Second);\n            Assert.Equal(DateTimeKind.Unspecified, value.Kind);\n        }\n\n        [Fact]\n        public async Task DateTimeKindForDate32()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            await using var cmd = cn.CreateCommand();\n            cmd.CommandText = \"select cast('2022-01-01' as Date32)\";\n\n            await using var reader = await cmd.ExecuteReaderAsync(CancellationToken.None);\n            Assert.True(await reader.ReadAsync(CancellationToken.None));\n            var value = reader.GetDateTime(0);\n            Assert.False(await reader.ReadAsync());\n\n            Assert.Equal(2022, value.Year);\n            Assert.Equal(1, value.Month);\n            Assert.Equal(1, value.Day);\n            Assert.Equal(0, value.Hour);\n            Assert.Equal(0, value.Minute);\n            Assert.Equal(0, value.Second);\n            Assert.Equal(DateTimeKind.Unspecified, value.Kind);\n        }\n\n        [Fact]\n        public Task SparseSerializedColumns()\n        {\n            return WithTemporaryTable(\"sparse\", Query, Test);\n\n            static string Query(string tableName) =>\n$@\"CREATE TABLE {tableName}\n(\n    id UInt64,\n    s Nullable(String),\n    t Tuple(bool, String)\n)\nENGINE = MergeTree\nORDER BY id\nSETTINGS ratio_of_defaults_for_sparse_serialization = 0.95\";\n\n            static async Task Test(ClickHouseConnection cn, string tableName, CancellationToken ct)\n            {\n                var cmd = cn.CreateCommand();\n                const int expectedCount = 5_000_000;\n\n                cmd.CommandText = $@\"INSERT INTO {tableName}\nSELECT\n    number,\n    number % 25 = 0 ? toString(number) : '',\n    tuple(number % 33 = 0, number % 99 = 0 ? toString(number) : '')\nFROM\n    numbers({expectedCount})\";\n\n                await cmd.ExecuteNonQueryAsync(ct);\n\n                cmd.CommandText = $\"SELECT id, s, t FROM {tableName}\";\n\n                await using (var reader = await cmd.ExecuteReaderAsync(ct))\n                {\n                    int counter = 0;\n                    while (await reader.ReadAsync(ct))\n                    {\n                        var id = reader.GetUInt64(0);\n                        var strVal = reader.GetString(1);\n                        var tuple = reader.GetFieldValue<(bool boolVal, string strVal)>(2);\n\n                        Assert.Equal(id % 25 == 0 ? id.ToString() : string.Empty, strVal);\n                        Assert.Equal(id % 33 == 0, tuple.boolVal);\n                        Assert.Equal(id % 99 == 0 ? id.ToString() : string.Empty, tuple.strVal);\n\n                        ++counter;\n                    }\n\n                    Assert.Equal(expectedCount, counter);\n                }\n\n                // Test skipping sparse values\n                await cmd.ExecuteNonQueryAsync(ct);\n\n                // Checking if the channel is in a valid state after skipping\n                cmd.CommandText = \"SELECT 21*2\";\n                var answer = await cmd.ExecuteScalarAsync<int>(ct);\n                Assert.Equal(42, answer);\n            }\n        }\n\n        [Fact]\n        public async Task CustomColumnCast()\n        {\n            await using var cn = await OpenConnectionAsync();\n            var cmd = cn.CreateCommand(\"SELECT 42::Float32\");\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            reader.ConfigureColumnReader(0, (double v) => new TestBox<decimal>((decimal)v));\n\n            Assert.True(await reader.ReadAsync());\n            Assert.Equal(typeof(TestBox<decimal>), reader.GetFieldType(0));\n\n            var res = reader.GetValue(0);\n            Assert.IsType<TestBox<decimal>>(res);\n            Assert.Equal(42m, ((TestBox<decimal>)res).Unbox());\n\n            var box = reader.GetFieldValue<TestBox<decimal>>(0);\n            Assert.Equal(42m, box.Unbox());\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task CustomNullableColumnCast()\n        {\n            await using var cn = await OpenConnectionAsync();\n            var cmd = cn.CreateCommand(\"SELECT if(number=1, NULL, (number + 40)::Nullable(Float32)) AS c, c AS c_copy FROM numbers(1, 2)\");\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            reader.ConfigureColumnReader(0, (double v) => (decimal)v);\n            reader.ConfigureColumnReader(\"c_copy\", (float? v) => v == null ? (TestBox<int>?)null : new TestBox<int>((int)v));\n\n            Assert.Equal(typeof(decimal), reader.GetFieldType(0));\n            Assert.Equal(typeof(TestBox<int>), reader.GetFieldType(1));\n\n            Assert.True(await reader.ReadAsync());\n\n            Assert.True(reader.IsDBNull(0));\n            Assert.True(reader.IsDBNull(1));\n\n            var res = reader.GetValue(0);\n            Assert.IsType<DBNull>(res);\n\n            var resCopy = reader.GetValue(1);\n            Assert.IsType<DBNull>(resCopy);\n\n            var box = reader.GetFieldValue<TestBox<int>>(1, null);\n            Assert.Null(box);\n\n            Assert.True(await reader.ReadAsync());\n\n            Assert.False(reader.IsDBNull(0));\n            Assert.False(reader.IsDBNull(1));\n\n            res = reader.GetValue(0);\n            Assert.IsType<decimal>(res);\n            Assert.Equal(42m, (decimal)res);\n\n            var altReinterpreted = reader.GetDouble(1);\n            Assert.Equal(42, altReinterpreted);\n\n            resCopy = reader.GetValue(1);\n            Assert.IsType<TestBox<int>>(resCopy);\n            Assert.Equal(42, ((TestBox<int>)resCopy).Unbox());\n\n            box = reader.GetFieldValue<TestBox<int>?>(1);\n            Assert.NotNull(box);\n            Assert.Equal(42, box.Value.Unbox());\n\n            box = reader.GetFieldValue<TestBox<int>>(1);\n            Assert.NotNull(box);\n            Assert.Equal(42, box.Value.Unbox());\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task CustomObjecctColumnCast()\n        {\n            await using var cn = await OpenConnectionAsync();\n            var cmd = cn.CreateCommand(\"SELECT 42::Float32\");\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            reader.ConfigureColumnReader(0, (object _) => 24);\n\n            Assert.Equal(typeof(int), reader.GetFieldType(0));\n\n            Assert.True(await reader.ReadAsync());\n\n            var res = reader.GetValue(0);\n            Assert.IsType<int>(res);\n            Assert.Equal(24, (int)res);\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task ValidColumnReconfiguration()\n        {\n            await using var cn = await OpenConnectionAsync();\n            var cmd = cn.CreateCommand(\"SELECT 42::Float32 AS col0, 42::Int32 AS col1, 42 AS col2\");\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            reader.ConfigureColumn(0, new ClickHouseColumnSettings(Encoding.UTF8));\n            reader.ConfigureColumnReader(\"col0\", (float f) => (double)f);\n\n            reader.ConfigureColumnReader(2, (byte b) => (double)b);\n            reader.ConfigureColumn(\"col2\", new ClickHouseColumnSettings(enumConverter: new ClickHouseEnumConverter<TestEnum>()));\n\n            Assert.True(await reader.ReadAsync());\n\n            var result = new object[3];\n            Assert.Equal(3, reader.GetValues(result));\n\n            Assert.Equal(42d, result[0]);\n            Assert.Equal(42, result[1]);\n            Assert.Equal(42d, result[2]);\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task NullableObjectColumnReconfiguration()\n        {\n            await using var cn = await OpenConnectionAsync();\n            var cmd = cn.CreateCommand(\"SELECT number AS n, multiIf(number%15==0, 'fizzbuzz', number%5==0, 'buzz', number%3==0, 'fizz', NULL) AS s FROM numbers(1, 15)\");\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            reader.ConfigureColumnReader(\"s\", (string s) => s.ToUpperInvariant());\n\n            Assert.Equal(typeof(ulong), reader.GetFieldType(0));\n            Assert.Equal(typeof(string), reader.GetFieldType(1));\n\n            int count = 0;\n            var result = new object[2];\n            while(await reader.ReadAsync())\n            {\n                Assert.Equal(2, reader.GetValues(result));\n                var n = Assert.IsType<ulong>(result[0]);\n                string? expected = null;\n                if (n % 3 == 0)\n                    expected = \"FIZZ\";\n                if (n % 5 ==0)\n                    expected = expected == null ? \"BUZZ\" : \"FIZZBUZZ\";\n\n                if (expected == null)\n                {\n                    Assert.IsType<DBNull>(result[1]);\n                    Assert.True(reader.IsDBNull(1));\n\n                    var value = reader.GetValue(1);\n                    Assert.IsType<DBNull>(value);\n\n                    var strNullable = reader.GetFieldValue(1, \"NONE\");\n                    Assert.Equal(\"NONE\", strNullable);\n                }\n                else\n                {\n                    var str = Assert.IsType<string>(result[1]);\n                    Assert.Equal(expected, str);\n                    Assert.False(reader.IsDBNull(1));\n\n                    str = reader.GetString(1);\n                    Assert.Equal(expected, str);\n\n                    var strNullable = reader.GetFieldValue(1, \"NONE\");\n                    Assert.Equal(expected, strNullable);\n                }\n\n                count++;\n            }\n\n            Assert.Equal(15, count);\n        }\n\n        [Fact]\n        public async Task InavlidColumnReconfiguration()\n        {\n            await using var cn = await OpenConnectionAsync();\n            var cmd = cn.CreateCommand(\"SELECT 42::Float32 AS col0, 42::Float64 AS col1, 42 AS col2\");\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            reader.ConfigureDataReader(new ClickHouseColumnSettings(typeof(double)));\n            var err = Assert.Throws<ClickHouseException>(() => reader.ConfigureColumnReader(0, (double v) => v + 1));\n            Assert.Equal(ClickHouseErrorCodes.InvalidColumnSettings, err.ErrorCode);\n\n            reader.ConfigureColumn(\"col2\", new ClickHouseColumnSettings());\n\n            err = Assert.Throws<ClickHouseException>(() => reader.ConfigureColumnReader(\"col2\", (byte v) => (string?)null));\n            Assert.Equal(ClickHouseErrorCodes.CallbackError, err.ErrorCode);\n\n            reader.ConfigureColumnReader(\"col2\", (byte? v) => v / 2);\n\n            err = Assert.Throws<ClickHouseException>(() => reader.ConfigureColumn(2, new ClickHouseColumnSettings(typeof(int))));\n            Assert.Equal(ClickHouseErrorCodes.InvalidColumnSettings, err.ErrorCode);\n\n            err = Assert.Throws<ClickHouseException>(() => reader.ConfigureDataReader(new ClickHouseColumnSettings(typeof(int))));\n            Assert.Equal(ClickHouseErrorCodes.InvalidColumnSettings, err.ErrorCode);\n\n            Assert.True(await reader.ReadAsync());\n\n            err = Assert.Throws<ClickHouseException>(() => reader.ConfigureColumnReader(\"col0\", (float v) => (int)v));\n            Assert.Equal(ClickHouseErrorCodes.DataReaderError, err.ErrorCode);\n\n            var result = new object[3];\n            Assert.Equal(3, reader.GetValues(result));\n\n            Assert.Equal(42d, result[0]);\n            Assert.Equal(42d, result[1]);\n            Assert.Equal(21, result[2]);\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task ExecuteDdlQuery()\n        {\n            await WithTemporaryTable(\"reader_drop\", \"dummy Int32\", Test);\n\n            static async Task Test(ClickHouseConnection cn, string tableName)\n            {\n                var cmd = cn.CreateCommand($\"DROP TABLE {tableName}\");\n                await using var reader = await cmd.ExecuteReaderAsync(CancellationToken.None);\n\n                Assert.True(reader.IsClosed);\n                Assert.Equal(ClickHouseDataReaderState.Closed, reader.State);\n                Assert.Equal(0, reader.FieldCount);\n                Assert.False(await reader.ReadAsync());\n            }\n        }\n\n        [Fact]\n        public async Task ExecuteCreateInsertFromSelect()\n        {\n            await WithTemporaryTable(\"reader_from_select\", \"dummy Int32\", Test);\n\n            static async Task Test(ClickHouseConnection cn, string tableName)\n            {\n                var cmd = cn.CreateCommand($\"INSERT INTO {tableName} SELECT number FROM system.numbers LIMIT 42\");\n                await using (var reader = await cmd.ExecuteReaderAsync(CancellationToken.None))\n                {\n                    Assert.True(reader.IsClosed);\n                    Assert.Equal(ClickHouseDataReaderState.Closed, reader.State);\n                    Assert.Equal(0, reader.FieldCount);\n                    Assert.False(await reader.ReadAsync());\n                }\n\n                cmd.CommandText = $\"DROP TABLE {tableName}\";\n                await using (var reader = await cmd.ExecuteReaderAsync(CancellationToken.None))\n                {\n                    Assert.True(reader.IsClosed);\n                    Assert.Equal(ClickHouseDataReaderState.Closed, reader.State);\n                    Assert.Equal(0, reader.FieldCount);\n                    Assert.False(await reader.ReadAsync());\n                }\n\n                cmd.CommandText = $\"CREATE TABLE {tableName} ENGINE = Memory AS SELECT number FROM system.numbers LIMIT 100\";\n                await using (var reader = await cmd.ExecuteReaderAsync(CancellationToken.None))\n                {\n                    Assert.True(reader.IsClosed);\n                    Assert.Equal(ClickHouseDataReaderState.Closed, reader.State);\n                    Assert.Equal(0, reader.FieldCount);\n                    Assert.False(await reader.ReadAsync());\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ClickHouseDbProviderFactoryTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020, 2023 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Data;\nusing System.Data.Common;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ClickHouseDbProviderFactoryTests\n    {\n        private static readonly DbProviderFactory Factory = new ClickHouseDbProviderFactory();\n\n        [Fact]\n        public void SupportedFeatures()\n        {\n            Assert.False(Factory.CanCreateCommandBuilder);\n            Assert.False(Factory.CanCreateDataAdapter);\n            Assert.False(Factory.CanCreateDataSourceEnumerator);\n\n            var p = Factory.CreateParameter();\n            Assert.IsAssignableFrom<ClickHouseParameter>(p);\n\n            var cmd = Factory.CreateCommand();\n            Assert.NotNull(cmd);\n            Assert.IsAssignableFrom<ClickHouseCommand>(cmd);\n            cmd.Dispose();\n\n            var cn = Factory.CreateConnection();\n            Assert.NotNull(cn);\n            Assert.IsAssignableFrom<ClickHouseConnection>(cn);\n            cn.Dispose();\n\n            var sb = Factory.CreateConnectionStringBuilder();\n            Assert.IsAssignableFrom<ClickHouseConnectionStringBuilder>(sb);\n        }\n\n        [Theory]\n        [MemberData(nameof(ClickHouseTestsBase.ParameterModes), MemberType = typeof(ClickHouseTestsBase))]\n        public void CreateCommand(ClickHouseParameterMode parameterMode)\n        {\n            var connectionString = ConnectionSettingsHelper.GetConnectionString(csb => csb.ParametersMode = parameterMode);\n            var sb = Factory.CreateConnectionStringBuilder();\n            Assert.NotNull(sb);\n            sb.ConnectionString = connectionString;\n\n            using var connection = Factory.CreateConnection();\n            Assert.NotNull(connection);\n\n            using var cmd = Factory.CreateCommand();\n            Assert.NotNull(cmd);\n\n            var parameterName = cmd.CreateParameter().ParameterName;\n            cmd.Parameters.RemoveAt(parameterName);\n            Assert.Empty(cmd.Parameters);\n\n            var parameter = Factory.CreateParameter();\n            Assert.NotNull(parameter);\n            \n            cmd.CommandText = $\"SELECT * FROM system.one WHERE dummy < {parameterName}\";\n            cmd.Parameters.Add(parameter);\n\n            parameter.ParameterName = parameterName;\n            parameter.DbType = DbType.Int32;\n            parameter.Value = int.MaxValue;\n\n            connection.ConnectionString = sb.ToString();\n            connection.Open();\n\n            cmd.Connection = connection;\n\n            var result = cmd.ExecuteScalar();\n            Assert.Equal((byte) 0, result);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ClickHouseParameterCollectionTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Data;\nusing System.Linq;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ClickHouseParameterCollectionTests\n    {\n        [Fact]\n        public void AddParameter()\n        {\n            var parameterNames = new[] {\"abc\", \"{abc}\", \"@abc\"};\n\n            foreach (var name in parameterNames)\n            {\n                var collection = new ClickHouseParameterCollection();\n                var parameter = new ClickHouseParameter(name);\n                collection.Add(parameter);\n\n                Assert.Single(collection);\n                Assert.Same(parameter, collection[0]);\n\n                foreach (var altName in parameterNames)\n                {\n                    Assert.True(collection.Contains(altName));\n                    Assert.True(collection.TryGetValue(altName, out var collectionParameter));\n                    Assert.Same(parameter, collectionParameter);\n                    Assert.Same(collection, parameter.Collection);\n                }\n            }\n        }\n\n        [Fact]\n        public void ChangeParameterName()\n        {\n            var collection = new ClickHouseParameterCollection();\n            var parameter = new ClickHouseParameter(\"p1\");\n\n            Assert.Null(parameter.Collection);\n            collection.Add(parameter);\n            Assert.Same(collection, parameter.Collection);\n\n            parameter.ParameterName = \"{p1}\";\n            Assert.Same(collection, parameter.Collection);\n            Assert.True(collection.TryGetValue(parameter.ParameterName, out var collectionParameter));\n            Assert.Same(parameter, collectionParameter);\n\n            parameter.ParameterName = \"param123\";\n            Assert.Same(collection, parameter.Collection);\n            Assert.True(collection.TryGetValue(parameter.ParameterName, out collectionParameter));\n            Assert.Same(parameter, collectionParameter);\n\n            parameter.ParameterName = \"p1\";\n            Assert.Same(collection, parameter.Collection);\n            Assert.True(collection.TryGetValue(parameter.ParameterName, out collectionParameter));\n            Assert.Same(parameter, collectionParameter);\n\n            parameter.ParameterName = \"P1\";\n            Assert.Same(collection, parameter.Collection);\n            Assert.True(collection.TryGetValue(parameter.ParameterName, out collectionParameter));\n            Assert.Same(parameter, collectionParameter);\n        }\n\n        [Fact]\n        public void RemoveParameter()\n        {\n            var collection = new ClickHouseParameterCollection();\n            var parameters = new[] {new ClickHouseParameter(\"p1\"), new ClickHouseParameter(\"p2\"), new ClickHouseParameter(\"p3\")};\n\n            collection.AddRange(parameters);\n            Assert.Equal(parameters.Length, collection.Count);\n\n            Assert.False(collection.Remove(\"p4\", out var removedParameter));\n            Assert.Null(removedParameter);\n            Assert.Equal(parameters.Length, collection.Count);\n            \n            Assert.False(collection.Remove(\"p4\"));\n            Assert.Equal(parameters.Length, collection.Count);\n\n            collection.RemoveAt(\"p4\");\n            Assert.Equal(parameters.Length, collection.Count);\n\n            Assert.True(collection.Remove(\"p1\"));\n            Assert.Equal(2, collection.Count);\n            Assert.Null(parameters[0].Collection);\n            Assert.Same(parameters[1], collection[0]);\n            Assert.Same(parameters[2], collection[1]);\n\n            Assert.True(collection.Remove(\"p3\", out removedParameter));\n            Assert.Equal(1, collection.Count);\n            Assert.Null(parameters[2].Collection);\n            Assert.Same(parameters[2], removedParameter);\n            Assert.Same(collection[0], parameters[1]);\n\n            collection.RemoveAt(\"p2\");\n            Assert.Equal(0, collection.Count);\n            Assert.Null(parameters[1].Collection);\n\n            collection.AddRange(parameters.Reverse().ToArray());\n            Assert.Equal(parameters.Length, collection.Count);\n\n            collection.RemoveAt(0);\n            Assert.Equal(2, collection.Count);\n            Assert.Null(parameters[2].Collection);\n            Assert.Same(parameters[1], collection[0]);\n            Assert.Same(parameters[0], collection[1]);\n\n            collection.Remove((object)parameters[2]);\n            Assert.Equal(2, collection.Count);\n\n            Assert.False(collection.Remove(parameters[2]));\n            Assert.Equal(2, collection.Count);\n\n            Assert.True(collection.Remove(parameters[1]));\n            Assert.Equal(1, collection.Count);\n            Assert.Null(parameters[1].Collection);\n            Assert.Same(collection[0], parameters[0]);\n        }\n\n        [Fact]\n        public void InsertParameter()\n        {\n            var collection = new ClickHouseParameterCollection();\n\n            collection.Insert(0, (object) new ClickHouseParameter(\"p1\"));\n            Assert.Equal(1, collection.Count);\n            Assert.Equal(\"p1\", collection[0].ParameterName);\n            Assert.Same(collection, collection[0].Collection);\n\n            collection.Insert(0, new ClickHouseParameter(\"{p2}\"));\n            Assert.Equal(2, collection.Count);\n            Assert.Equal(\"{p2}\", collection[0].ParameterName);\n            Assert.Same(collection, collection[0].Collection);\n            Assert.Equal(\"p1\", collection[1].ParameterName);\n            Assert.Same(collection, collection[1].Collection);\n\n            collection.Insert(2, (object) new ClickHouseParameter(\"@p3\"));\n            Assert.Equal(3, collection.Count);\n            Assert.Equal(\"{p2}\", collection[0].ParameterName);\n            Assert.Same(collection, collection[0].Collection);\n            Assert.Equal(\"p1\", collection[1].ParameterName);\n            Assert.Same(collection, collection[1].Collection);\n            Assert.Equal(\"@p3\", collection[2].ParameterName);\n            Assert.Same(collection, collection[2].Collection);\n\n            collection.Insert(1, new ClickHouseParameter(\"p4\"));\n            Assert.Equal(4, collection.Count);\n            Assert.Equal(\"{p2}\", collection[0].ParameterName);\n            Assert.Same(collection, collection[0].Collection);\n            Assert.Equal(\"p4\", collection[1].ParameterName);\n            Assert.Same(collection, collection[1].Collection);\n            Assert.Equal(\"p1\", collection[2].ParameterName);\n            Assert.Same(collection, collection[2].Collection);\n            Assert.Equal(\"@p3\", collection[3].ParameterName);\n            Assert.Same(collection, collection[3].Collection);\n        }\n\n        [Fact]\n        public void ReplaceParameter()\n        {\n            var collection = new ClickHouseParameterCollection();\n\n            var parameters = new[] {\"p1\", \"p2\", \"p3\", \"p4\"}.Select(name => collection.AddWithValue(name, null, DbType.String)).ToArray();\n            Assert.Equal(4, collection.Count);\n\n            var newParam = new ClickHouseParameter(\"p5\");\n            collection[2] = newParam;\n            Assert.Equal(4, collection.Count);\n            Assert.Equal(\"p1\", collection[0].ParameterName);\n            Assert.Same(collection, collection[0].Collection);\n            Assert.Equal(\"p2\", collection[1].ParameterName);\n            Assert.Same(collection, collection[1].Collection);\n            Assert.Same(newParam, collection[2]);\n            Assert.Same(collection, collection[2].Collection);\n            Assert.Equal(\"p4\", collection[3].ParameterName);\n            Assert.Same(collection, collection[3].Collection);\n            Assert.Null(parameters[2].Collection);\n\n            collection[\"{p5}\"] = parameters[2];\n            Assert.Equal(4, collection.Count);\n            for (int i = 0; i < parameters.Length; i++)\n            {\n                Assert.Same(parameters[i], collection[i]);\n                Assert.Same(collection, parameters[i].Collection);\n            }\n\n            Assert.Null(newParam.Collection);\n\n            collection[\"p1\"] = parameters[0];\n            Assert.Equal(4, collection.Count);\n            for (int i = 0; i < parameters.Length; i++)\n            {\n                Assert.Same(parameters[i], collection[i]);\n                Assert.Same(collection, parameters[i].Collection);\n            }\n\n            collection[\"{p5}\"] = newParam;\n            Assert.Equal(5, collection.Count);\n            for (int i = 0; i < parameters.Length; i++)\n            {\n                Assert.Same(parameters[i], collection[i]);\n                Assert.Same(collection, parameters[i].Collection);\n            }\n\n            Assert.Same(newParam, collection[4]);\n            Assert.Same(collection, newParam.Collection);\n\n            var ex = Assert.Throws<ArgumentException>(() => collection[\"p6\"] = new ClickHouseParameter(\"p7\"));\n            Assert.Equal(5, collection.Count);\n            Assert.Equal(\"parameterName\", ex.ParamName);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ClickHouseParameterTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021-2022 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Data;\nusing System.Linq.Expressions;\nusing System.Net;\nusing System.Reflection;\nusing System.Text;\nusing Octonica.ClickHouseClient.Utils;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ClickHouseParameterTests\n    {\n        private static readonly ReadOnlyDictionary<string, Action<ClickHouseParameter, ClickHouseParameter>> ParameterPublicProperties;\n\n        static ClickHouseParameterTests()\n        {\n            var properties = new Dictionary<string, Action<ClickHouseParameter, ClickHouseParameter>>();\n            foreach (var property in typeof(ClickHouseParameter).GetProperties(BindingFlags.Public | BindingFlags.Instance))\n            {\n                if (property.GetMethod == null || property.SetMethod == null || !property.GetMethod.IsPublic || !property.SetMethod.IsPublic)\n                    continue;\n\n                var comparer = TypeDispatcher.Dispatch(property.PropertyType, new PropertyComparerDispatcher(property));\n                properties.Add(property.Name, comparer);\n            }\n\n            ParameterPublicProperties = new ReadOnlyDictionary<string, Action<ClickHouseParameter, ClickHouseParameter>>(properties);\n        }\n\n        [Fact]\n        public void Clone()\n        {\n            var p1 = new ClickHouseParameter(\"p1\") { Value = new[] { 42 }, ClickHouseDbType = ClickHouseDbType.VarNumeric, Precision = 19, Scale = 7, ParameterMode = ClickHouseParameterMode.Interpolate };\n\n            var p2 = p1.Clone();\n\n            Assert.NotSame(p1, p2);\n            AssertParametersEqual(p1, p2);\n            Assert.Equal(\"p1\", p1.ParameterName);\n            Assert.Equal(p1.Value, p2.Value);\n            Assert.Equal(ClickHouseDbType.VarNumeric, p2.ClickHouseDbType);\n            Assert.Equal(19, p2.Precision);\n            Assert.Equal(7, p2.Scale);\n            Assert.Equal(ClickHouseParameterMode.Interpolate, p2.ParameterMode);\n\n            p2.TimeZone = TimeZoneInfo.Local;\n            p2.Size = 35;\n            p2.ArrayRank = 3;\n            p2.StringEncoding = Encoding.ASCII;\n            p2.IsNullable = true;\n\n            var collection = new ClickHouseParameterCollection { p2 };\n\n            var p3 = p2.Clone();\n            Assert.NotSame(p2, p3);\n            Assert.Null(p3.Collection);\n            Assert.Same(collection, p2.Collection);\n\n            AssertParametersEqual(p2, p3);\n            Assert.Equal(TimeZoneInfo.Local, p3.TimeZone);\n            Assert.Equal(35, p3.Size);\n            Assert.Equal(3, p3.ArrayRank);\n            Assert.Equal(Encoding.ASCII, p3.StringEncoding);\n            Assert.True(p3.IsNullable);\n\n            p2.SourceColumn = \"some value\";\n            p2.SourceColumnNullMapping = true;\n\n            var p4 = p2.Clone();\n\n            Assert.NotSame(p2, p4);\n            Assert.Null(p4.Collection);\n\n            AssertParametersEqual(p2, p4);\n            Assert.Equal(\"some value\", p4.SourceColumn);\n            Assert.True(p2.SourceColumnNullMapping);\n        }\n\n        [Fact]\n        public void CopyTo()\n        {\n            var p1 = new ClickHouseParameter(\"p1\")\n            {\n                ArrayRank = 150,\n                ClickHouseDbType = ClickHouseDbType.IpV6,\n                IsArray = true,\n                IsNullable = true,\n                Precision = 24,\n                Scale = 255,\n                Size = 123456,\n                SourceColumn = \"aaaaa\",\n                SourceColumnNullMapping = true,\n                StringEncoding = Encoding.BigEndianUnicode,\n                TimeZone = TimeZoneInfo.Utc,\n                Value = 123.456m\n            };\n\n            var collection = new ClickHouseParameterCollection { p1, new ClickHouseParameter(\"p2\") };\n            var p2 = collection[\"p2\"];\n            p1.CopyTo(p2);\n\n            AssertParametersEqual(p1, p2, false);\n        }\n\n        [Fact]\n        public void NotSupportedProperties()\n        {\n            var p = new ClickHouseParameter();\n\n            Assert.Throws<NotSupportedException>(() => p.Direction = ParameterDirection.InputOutput);\n            Assert.Throws<NotSupportedException>(() => p.Direction = ParameterDirection.Output);\n            Assert.Throws<NotSupportedException>(() => p.Direction = ParameterDirection.ReturnValue);\n\n            // Inherited behaviour\n            p.SourceVersion = DataRowVersion.Current;\n            Assert.Equal(DataRowVersion.Default, p.SourceVersion);\n\n            p.SourceVersion = DataRowVersion.Original;\n            Assert.Equal(DataRowVersion.Default, p.SourceVersion);\n\n            p.SourceVersion = DataRowVersion.Proposed;\n            Assert.Equal(DataRowVersion.Default, p.SourceVersion);\n        }\n\n        [Fact]\n        public void TypeDetection()\n        {\n            var testData = new (object? value, ClickHouseDbType expectedType, bool expectedNullable, int expectedArrayRank)[]\n            {\n                (null, ClickHouseDbType.Nothing, true, 0),\n                (DBNull.Value, ClickHouseDbType.Nothing, true, 0),\n                (new[] {DBNull.Value}, ClickHouseDbType.Nothing, true, 1),\n                \n                (true, ClickHouseDbType.Boolean, false, 0),\n\n                ((byte) 1, ClickHouseDbType.Byte, false, 0),\n                ((sbyte) 1, ClickHouseDbType.SByte, false, 0),\n                ((short) 1, ClickHouseDbType.Int16, false, 0),\n                ((ushort) 1, ClickHouseDbType.UInt16, false, 0),\n                ((int) 1, ClickHouseDbType.Int32, false, 0),\n                ((uint) 1, ClickHouseDbType.UInt32, false, 0),\n                ((long) 1, ClickHouseDbType.Int64, false, 0),\n                ((ulong) 1, ClickHouseDbType.UInt64, false, 0),\n\n                ((float) 1, ClickHouseDbType.Single, false, 0),\n                ((double) 1, ClickHouseDbType.Double, false, 0),\n                ((decimal) 1, ClickHouseDbType.Decimal, false, 0),\n\n                (DateTime.Now, ClickHouseDbType.DateTime, false, 0),\n                (DateTimeOffset.Now, ClickHouseDbType.DateTime, false, 0),\n\n                (Guid.Empty, ClickHouseDbType.Guid, false, 0),\n\n                (new IPAddress(new byte[] {127, 0, 0, 1}), ClickHouseDbType.IpV4, false, 0),\n                (IPAddress.Parse(\"2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d\"), ClickHouseDbType.IpV6, false, 0),\n\n                (string.Empty, ClickHouseDbType.String, false, 0),\n                (new[] {'!'}, ClickHouseDbType.String, false, 0),\n                (string.Empty.AsMemory(), ClickHouseDbType.String, false, 0),\n                (new[] {'!'}.AsMemory(), ClickHouseDbType.String, false, 0),\n\n                (new byte[0], ClickHouseDbType.Byte, false, 1),\n                (new byte[0].AsMemory(), ClickHouseDbType.Byte, false, 1),\n                ((ReadOnlyMemory<byte>) new byte[0].AsMemory(), ClickHouseDbType.Byte, false, 1),\n\n                (new Guid?[0, 0, 0], ClickHouseDbType.Guid, true, 3),\n            };\n\n            var p = new ClickHouseParameter(\"p\");\n            foreach(var testCase in testData)\n            {\n                p.Value = testCase.value;\n\n                Assert.Equal(testCase.expectedType, p.ClickHouseDbType);\n                Assert.Equal(testCase.expectedArrayRank, p.ArrayRank);\n                Assert.Equal(testCase.expectedArrayRank > 0, p.IsArray);\n                Assert.Equal(testCase.expectedNullable, p.IsNullable);\n            }\n        }\n\n        private static void AssertParametersEqual(ClickHouseParameter expected, ClickHouseParameter actual, bool compareName = true)\n        {\n            foreach (var pair in ParameterPublicProperties)\n            {\n                if (pair.Key == nameof(ClickHouseParameter.ParameterName) && !compareName)\n                    continue;\n\n                pair.Value(expected, actual);\n            }\n        }\n\n        private class PropertyComparerDispatcher : ITypeDispatcher<Action<ClickHouseParameter, ClickHouseParameter>>\n        {\n            private readonly PropertyInfo _propertyInfo;\n\n            public PropertyComparerDispatcher(PropertyInfo propertyInfo)\n            {\n                _propertyInfo = propertyInfo;\n            }\n\n            public Action<ClickHouseParameter, ClickHouseParameter> Dispatch<T>()\n            {\n                var expParam = Expression.Variable(typeof(ClickHouseParameter), \"p\");\n                var expGetValue = Expression.Property(expParam, _propertyInfo);\n                var expLambda = Expression.Lambda<Func<ClickHouseParameter, T>>(expGetValue, expParam);\n\n                var getValue = expLambda.Compile();\n                return (expected, actual) =>\n                {\n                    var expectedValue = getValue(expected);\n                    var actualValue = getValue(actual);\n                    Assert.Equal(expectedValue, actualValue);\n                };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ClickHouseTestsBase.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public abstract class ClickHouseTestsBase\n    {\n        private ClickHouseConnectionSettings? _settings;\n\n        public static readonly IReadOnlyCollection<object[]> ParameterModes = new[]\n        {\n            new object[] { ClickHouseParameterMode.Binary },\n            new object[] { ClickHouseParameterMode.Interpolate },\n            new object[] { ClickHouseParameterMode.Serialize }\n        };\n\n        public ClickHouseConnectionSettings GetDefaultConnectionSettings(Action<ClickHouseConnectionStringBuilder>? updateSettings = null)\n        {\n            if (_settings != null)\n            {\n                return _settings;\n            }\n\n            _settings = ConnectionSettingsHelper.GetConnectionSettings(updateSettings);\n            return _settings;\n        }\n\n        public async Task<ClickHouseConnection> OpenConnectionAsync(ClickHouseConnectionSettings settings, CancellationToken cancellationToken)\n        {\n            ClickHouseConnection connection = new ClickHouseConnection(settings);\n            await connection.OpenAsync(cancellationToken);\n\n            return connection;\n        }\n\n        public Task<ClickHouseConnection> OpenConnectionAsync(ClickHouseParameterMode parameterMode, CancellationToken cancellationToken = default)\n        {\n            return OpenConnectionAsync(builder => builder.ParametersMode = parameterMode, cancellationToken);\n        }\n\n        public async Task<ClickHouseConnection> OpenConnectionAsync(Action<ClickHouseConnectionStringBuilder>? updateSettings = null, CancellationToken cancellationToken = default)\n        {\n            return await OpenConnectionAsync(GetDefaultConnectionSettings(updateSettings), cancellationToken);\n        }\n\n        public ClickHouseConnection OpenConnection(Action<ClickHouseConnectionStringBuilder>? updateSettings = null)\n        {\n            ClickHouseConnection connection = new ClickHouseConnection(GetDefaultConnectionSettings(updateSettings));\n            connection.Open();\n\n            return connection;\n        }\n\n        protected Task WithTemporaryTable(string tableNameSuffix, string columns, Func<ClickHouseConnection, string, Task> runTest, Action<ClickHouseConnectionStringBuilder>? updateSettings = null)\n        {\n            return WithTemporaryTable(tableNameSuffix, columns, (cn, tableName, _) => runTest(cn, tableName), updateSettings);\n        }\n\n        protected Task WithTemporaryTable(\n            string tableNameSuffix,\n            string columns,\n            Func<ClickHouseConnection, string, CancellationToken, Task> runTest,\n            Action<ClickHouseConnectionStringBuilder>? updateSettings = null,\n            Func<ClickHouseConnection, CancellationToken, Task>? afterOpen = null,\n            CancellationToken ct = default)\n        {\n            return WithTemporaryTable(tableNameSuffix, tableName => $\"CREATE TABLE {tableName}({columns}) ENGINE=Memory\", runTest, updateSettings, afterOpen, ct);\n        }\n\n        protected async Task WithTemporaryTable(\n            string tableNameSuffix,\n            Func<string, string> makeCreateTableQuery, Func<ClickHouseConnection, string, CancellationToken, Task> runTest,\n            Action<ClickHouseConnectionStringBuilder>? updateSettings = null,\n            Func<ClickHouseConnection, CancellationToken, Task>? configure = null,\n            CancellationToken ct = default)\n        {\n            var tableName = GetTempTableName(tableNameSuffix);\n            try\n            {\n                await using var connection = await OpenConnectionAsync(updateSettings, ct);\n\n                if (configure != null)\n                    await configure(connection, ct);\n\n                var cmd = connection.CreateCommand($\"DROP TABLE IF EXISTS {tableName}\");\n                await cmd.ExecuteNonQueryAsync(ct);\n\n                var createTableQuery = makeCreateTableQuery(tableName);\n                cmd = connection.CreateCommand(createTableQuery);\n                await cmd.ExecuteNonQueryAsync(ct);\n\n                await runTest(connection, tableName, ct);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync(cancellationToken: CancellationToken.None);\n                var cmd = connection.CreateCommand($\"DROP TABLE IF EXISTS {tableName}\");\n                await cmd.ExecuteNonQueryAsync(CancellationToken.None);\n            }\n        }\n\n        protected virtual string GetTempTableName(string tableNameSuffix)\n        {\n            return $\"clickhouse_client_test_{tableNameSuffix}\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ClickHouseTypeInfoTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Octonica.ClickHouseClient.Types;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ClickHouseTypeInfoTests\n    {\n        [Theory]\n        [InlineData(\"Nullable(Nothing)\", \"Nothing\")]\n        [InlineData(\"Nullable( String )\", \"String\")]\n        [InlineData(\"Nullable ( DateTime( 'Asia/Yekaterinburg' ) )\", \"DateTime('Asia/Yekaterinburg')\")]\n        public void NullableGenericArguments(string typeName, string baseTypeName)\n        {\n            var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(typeName);\n            \n            Assert.Equal(1, typeInfo.GenericArgumentsCount);\n            Assert.Equal(1, typeInfo.TypeArgumentsCount);\n\n            Assert.Equal(baseTypeName, typeInfo.GetGenericArgument(0).ComplexTypeName);\n            Assert.Equal(baseTypeName, Assert.IsAssignableFrom<IClickHouseTypeInfo>(typeInfo.GetTypeArgument(0)).ComplexTypeName);\n        }\n\n        [Theory]\n        [InlineData(\"LowCardinality(String)\", \"String\")]\n        [InlineData(\"LowCardinality( Decimal ( 28, 10 ))\", \"Decimal(28, 10)\")]\n        public void LowCardinalityGenericArguments(string typeName, string baseTypeName)\n        {\n            var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(typeName);\n\n            Assert.Equal(1, typeInfo.GenericArgumentsCount);\n            Assert.Equal(1, typeInfo.TypeArgumentsCount);\n\n            Assert.Equal(baseTypeName, typeInfo.GetGenericArgument(0).ComplexTypeName);\n            Assert.Equal(baseTypeName, Assert.IsAssignableFrom<IClickHouseTypeInfo>(typeInfo.GetTypeArgument(0)).ComplexTypeName);\n        }\n\n        [Fact]\n        public void TupleGenericArguments()\n        {\n            var typeNames = new[] {\"Decimal(19, 6)\", \"String\", \"Nullable(String)\", \"DateTime64(5, 'Europe/Prague')\", \"UInt8\", \"Int32\", \"Float32\", \"Enum8('a'=10, 'b'=20)\", \"UInt64\"};\n\n            for (int i = 1; i <= typeNames.Length; i++)\n            {\n                var typeName = \"Tuple(\" + string.Join(',', typeNames.Take(i)) + ')';\n                var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(typeName);\n\n                Assert.Equal(i, typeInfo.GenericArgumentsCount);\n                Assert.Equal(i, typeInfo.TypeArgumentsCount);\n                for (int j = 0; j < i; j++)\n                {\n                    IClickHouseTypeInfo baseType = typeInfo.GetGenericArgument(j);\n                    Assert.Equal(typeNames[j], baseType.ComplexTypeName);\n                    \n                    var typeArgument = typeInfo.GetTypeArgument(j);\n                    baseType = Assert.IsAssignableFrom<IClickHouseTypeInfo>(typeArgument);\n                    Assert.Equal(typeNames[j], baseType.ComplexTypeName);\n                }\n            }\n        }\n\n        [Theory]\n        [InlineData(\"Array(Int32)\", \"Int32\", null)]\n        [InlineData(\"Array(Nullable(Int32))\", \"Nullable(Int32)\", \"Int32\")]\n        [InlineData(\"Array(Array(Nothing))\", \"Array(Nothing)\", \"Nothing\")]\n        public void ArrayGenericArguments(string typeName, string baseTypeName, string? baseBaseTypeName)\n        {\n            var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(typeName);\n\n            Assert.Equal(1, typeInfo.GenericArgumentsCount);\n            Assert.Equal(1, typeInfo.TypeArgumentsCount);\n\n            var baseType = typeInfo.GetGenericArgument(0);\n\n            Assert.Equal(baseTypeName, baseType.ComplexTypeName);\n            Assert.Equal(baseTypeName, Assert.IsAssignableFrom<IClickHouseTypeInfo>(typeInfo.GetTypeArgument(0)).ComplexTypeName);\n\n            if (baseBaseTypeName == null)\n            {\n                Assert.Equal(0, baseType.GenericArgumentsCount);\n                return;\n            }\n\n            Assert.Equal(1, baseType.GenericArgumentsCount);\n            Assert.Equal(1, baseType.TypeArgumentsCount);\n\n            Assert.Equal(baseBaseTypeName, baseType.GetGenericArgument(0).ComplexTypeName);\n            Assert.Equal(baseBaseTypeName, Assert.IsAssignableFrom<IClickHouseTypeInfo>(baseType.GetTypeArgument(0)).ComplexTypeName);\n        }\n\n        [Theory]\n        [InlineData(\"Decimal32(5)\", \"Decimal32\", 5, null)]\n        [InlineData(\"Decimal64(1)\", \"Decimal64\", 1, null)]\n        [InlineData(\"Decimal128(9)\", \"Decimal128\", 9, null)]\n        [InlineData(\"Decimal(35, 10)\", \"Decimal\", 35, 10)]\n        public void DecimalTypeArguments(string typeName, string expectedTypeName, int firstArgument, int? secondArgument)\n        {\n            var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(typeName);\n\n            int expectedCount = 1 + (secondArgument == null ? 0 : 1);\n            Assert.Equal(0, typeInfo.GenericArgumentsCount);\n            Assert.Equal(expectedTypeName, typeInfo.TypeName);\n            Assert.Equal(expectedCount, typeInfo.TypeArgumentsCount);\n\n            Assert.Equal(firstArgument, typeInfo.GetTypeArgument(0));\n\n            if (secondArgument != null)\n                Assert.Equal(secondArgument, typeInfo.GetTypeArgument(1));\n        }\n\n        [Fact]\n        public void DateTimeTypeArguments()\n        {\n            var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(\"DateTime('Asia/Macau')\");\n\n            Assert.Equal(\"DateTime\", typeInfo.TypeName);\n            Assert.Equal(0, typeInfo.GenericArgumentsCount);\n            Assert.Equal(1, typeInfo.TypeArgumentsCount);\n\n            Assert.Equal(\"Asia/Macau\", typeInfo.GetTypeArgument(0));\n\n            typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(\"DateTime\");\n\n            Assert.Equal(\"DateTime\", typeInfo.TypeName);\n            Assert.Equal(0, typeInfo.GenericArgumentsCount);\n            Assert.Equal(0, typeInfo.TypeArgumentsCount);\n        }\n\n        [Fact]\n        public void DateTime64TypeArguments()\n        {\n            var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(\"DateTime64(3, 'Africa/Addis_Ababa')\");\n\n            Assert.Equal(\"DateTime64\", typeInfo.TypeName);\n            Assert.Equal(0, typeInfo.GenericArgumentsCount);\n            Assert.Equal(2, typeInfo.TypeArgumentsCount);\n\n            Assert.Equal(3, typeInfo.GetTypeArgument(0));\n            Assert.Equal(\"Africa/Addis_Ababa\", typeInfo.GetTypeArgument(1));\n\n            typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(\"DateTime64(5)\");\n\n            Assert.Equal(\"DateTime64\", typeInfo.TypeName);\n            Assert.Equal(0, typeInfo.GenericArgumentsCount);\n            Assert.Equal(1, typeInfo.TypeArgumentsCount);\n\n            Assert.Equal(5, typeInfo.GetTypeArgument(0));\n        }\n\n        [Theory]\n        [InlineData(\"Enum8('a' = 42)\", new[] { \"a\" }, new sbyte[] { 42 })]\n        [InlineData(\"Enum8('a' = 2, 'C' = -3,'b'=1)\", new[] { \"a\", \"C\", \"b\" }, new sbyte[] { 2, -3, 1 })]\n        [InlineData(\"Enum8('\\\\'a\\\\'' = -5, ' \\\\tescaped \\\\'value\\\\' ({[ ' = -9,'\\\\r\\\\n\\\\t\\\\d\\\\\\\\'= 18)\", new[] { \"'a'\", \" \\tescaped 'value' ({[ \", \"\\r\\n\\t\\\\d\\\\\" }, new sbyte[] { -5, -9, 18 })]\n        public void Enum8TypeArguments(string typeName, string[] expectedKeys, sbyte[] expectedValues)\n        {\n            Assert.Equal(expectedKeys.Length, expectedValues.Length);\n\n            var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(typeName);\n\n            Assert.Equal(\"Enum8\", typeInfo.TypeName);\n            Assert.Equal(0, typeInfo.GenericArgumentsCount);\n            Assert.Equal(expectedKeys.Length, typeInfo.TypeArgumentsCount);\n\n            for (int i = 0; i < expectedKeys.Length; i++)\n            {\n                var typeArgumentObj = typeInfo.GetTypeArgument(i);\n                var typeArgument = Assert.IsType<KeyValuePair<string, sbyte>>(typeArgumentObj);\n                Assert.Equal(typeArgument.Key, expectedKeys[i]);\n                Assert.Equal(typeArgument.Value, expectedValues[i]);\n            }\n        }\n\n        [Theory]\n        [InlineData(\"Enum16('a' = 1024)\", new[] { \"a\" }, new short[] { 1024 })]\n        [InlineData(\"Enum16('a' = 8965, 'C' = 5,'b'=-3256)\", new[] { \"a\", \"C\", \"b\" }, new short[] { 8965, 5, -3256 })]\n        [InlineData(\"Enum16('\\\"a\\\"' = 31000 , '\\\\'\\\\\\\\e\\\\s\\\\c\\\\\\\\a\\\\p\\\\e\\\\d\\\\'' = -31000, '}])' = 42)\", new[] { \"\\\"a\\\"\", @\"'\\e\\s\\c\\a\\p\" + \"\\x1b\" + @\"\\d'\", \"}])\" }, new short[] { 31000, -31000, 42 })]\n        public void Enum16TypeArguments(string typeName, string[] expectedKeys, short[] expectedValues)\n        {\n            Assert.Equal(expectedKeys.Length, expectedValues.Length);\n\n            var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(typeName);\n\n            Assert.Equal(\"Enum16\", typeInfo.TypeName);\n            Assert.Equal(0, typeInfo.GenericArgumentsCount);\n            Assert.Equal(expectedKeys.Length, typeInfo.TypeArgumentsCount);\n\n            for (int i = 0; i < expectedKeys.Length; i++)\n            {\n                var typeArgumentObj = typeInfo.GetTypeArgument(i);\n                var typeArgument = Assert.IsType<KeyValuePair<string, short>>(typeArgumentObj);\n                Assert.Equal(typeArgument.Key, expectedKeys[i]);\n                Assert.Equal(typeArgument.Value, expectedValues[i]);\n            }\n        }\n\n        [Fact]\n        public void FixedStringTypeArguments()\n        {\n            var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(\"FixedString(42)\");\n\n            Assert.Equal(\"FixedString\", typeInfo.TypeName);\n            Assert.Equal(0, typeInfo.GenericArgumentsCount);\n            Assert.Equal(1, typeInfo.TypeArgumentsCount);\n\n            Assert.Equal(42, typeInfo.GetTypeArgument(0));\n        }\n\n        [Fact]\n        public void NamedTupleArguments()\n        {\n            var typeNames = new[] { \"UInt32\", \"Int64\", \"Nullable(String)\", \"Enum16('ok'=0, 'notOk'=8096)\", \"UInt16\", \"String\", \"Float64\", \"DateTime64(2, 'America/Los_Angeles')\", \"Nullable(Decimal(28, 4))\" };\n            var itemNames = new KeyValuePair<string, string>[] {\n                new KeyValuePair<string, string>(\"A\",\"A \"),\n                new KeyValuePair<string, string>(\"second_item\",\" second_item \"),\n                new KeyValuePair<string, string>(\"B\",\"`B` \"),                \n                new KeyValuePair<string, string>(\"_4\",\"_4 \"),\n                new KeyValuePair<string, string>(\"escaped `C` with \\\\` :)\",\" `escaped \\\\`C\\\\` with \\\\\\\\\\\\` :)` \"),\n                new KeyValuePair<string, string>(\" ([{some other name \",\"` ([{some other name ` \"),\n                new KeyValuePair<string, string>(\"_O_O_\",\"   _O_O_    \"),\n                new KeyValuePair<string, string>(\"8\",\" \\t`8`\\t\"),\n                new KeyValuePair<string, string>(\"OMEGA\",\"\\t OMEGA \\t\")\n            };\n\n            Assert.Equal(typeNames.Length, itemNames.Length);\n\n            for (int i = 1; i <= typeNames.Length; i++)\n            {\n                var tupleItems = Enumerable.Range(0, i).Select(j => itemNames[j].Value + typeNames[j]);\n                var typeName = \"Tuple(\" + string.Join(',',  tupleItems) + ')';\n                var typeInfo = DefaultTypeInfoProvider.Instance.GetTypeInfo(typeName);\n\n                ValidateTypeInfo(typeInfo, i);\n\n                var typeInfoCopy = DefaultTypeInfoProvider.Instance.GetTypeInfo(typeInfo.ComplexTypeName);\n                ValidateTypeInfo(typeInfoCopy, i);\n            }\n\n            void ValidateTypeInfo(IClickHouseColumnTypeInfo typeInfo, int expectedArgCount)\n            {\n                Assert.Equal(\"Tuple\", typeInfo.TypeName);\n                Assert.Equal(expectedArgCount, typeInfo.GenericArgumentsCount);\n                Assert.Equal(expectedArgCount, typeInfo.TypeArgumentsCount);\n                for (int i = 0; i < expectedArgCount; i++)\n                {\n                    IClickHouseTypeInfo baseType = typeInfo.GetGenericArgument(i);\n                    Assert.Equal(typeNames[i], baseType.ComplexTypeName);\n\n                    var typeArgument = typeInfo.GetTypeArgument(i);\n                    var namedType = Assert.IsAssignableFrom<KeyValuePair<string, IClickHouseTypeInfo>>(typeArgument);\n                    Assert.Equal(itemNames[i].Key, namedType.Key);\n                    Assert.Equal(typeNames[i], namedType.Value.ComplexTypeName);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/CommonUtilsTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing Octonica.ClickHouseClient.Protocol;\nusing Octonica.ClickHouseClient.Types;\nusing Octonica.ClickHouseClient.Utils;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class CommonUtilsTests\n    {\n        [Fact]\n        public void GetColumnIndex()\n        {\n            var type = DefaultTypeInfoProvider.Instance.GetTypeInfo(\"Int32\");\n            var colInfo = new List<ColumnInfo> {new ColumnInfo(\"COL\", type), new ColumnInfo(\"col\", type), new ColumnInfo(\"another_col\", type)}.AsReadOnly();\n\n            var idx = CommonUtils.GetColumnIndex(colInfo, \"COL\");\n            Assert.Equal(0, idx);\n\n            idx = CommonUtils.GetColumnIndex(colInfo, \"col\");\n            Assert.Equal(1, idx);\n\n            idx = CommonUtils.GetColumnIndex(colInfo, \"AnOtHeR_cOl\");\n            Assert.Equal(2, idx);\n\n            Assert.Throws<IndexOutOfRangeException>(() => CommonUtils.GetColumnIndex(colInfo, \"there_is_no_column\"));\n            Assert.Throws<IndexOutOfRangeException>(() => CommonUtils.GetColumnIndex(colInfo, \"Col\"));\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/EncodingFixture.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System.Text;\n\nnamespace Octonica.ClickHouseClient.Tests\n{ \n    internal class EncodingFixture\n    {\n        static EncodingFixture()\n        {\n            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/IndexedCollectionTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class IndexedCollectionTests\n    {\n        [Fact]\n        public void Add()\n        {\n            var list = new[] {\n                new KeyValuePair<uint, string>(1, \"one\"),\n                new KeyValuePair<uint, string>(2, \"two\"),\n                new KeyValuePair<uint, string>(3, \"three\"),\n                new KeyValuePair<uint, string>(4, \"four\"),\n                new KeyValuePair<uint, string>(5, \"five\"),\n                new KeyValuePair<uint, string>(6, \"six\"),\n                new KeyValuePair<uint, string>(7, \"seven\"),\n                new KeyValuePair<uint, string>(8, \"eight\"),\n                new KeyValuePair<uint, string>(9, \"nine\"),\n                new KeyValuePair<uint, string>(0, \"zero\")\n            };\n\n            var collection = new TestIndexedCollection<uint, string>();\n            Assert.Empty(collection);\n            for (int i = 0; i < list.Length; i++)\n            {\n                collection.Clear();\n                Assert.Empty(collection);\n\n                for (int j = 0; j <= i; j++)\n                    collection.Add(list[j]);\n\n                Assert.Equal(i + 1, collection.Count);\n                \n                // Test IEnumerable\n                Assert.Equal(list.Take(i + 1), collection);\n\n                // Test indexer\n                for (int j = 0; j <= i; j++)\n                    Assert.Equal(list[j], collection[j]);\n\n                // Test key indexer\n                for (int j = 0; j <= i; j++)\n                    Assert.Equal(list[j], collection[list[j].Key]);\n\n                for (int j = 0; j < list.Length; j++)\n                {\n                    bool shouldContain = j <= i;\n\n                    Assert.Equal(shouldContain, collection.Contains(list[j]));\n                    Assert.Equal(shouldContain, collection.ContainsKey(list[j].Key));\n                    Assert.Equal(shouldContain, collection.TryGetValue(list[j].Key, out var actualValue));\n\n                    if (shouldContain)\n                        Assert.Equal(list[j], actualValue);\n\n                    var expectedIndex = shouldContain ? j : -1;\n                    Assert.Equal(expectedIndex, collection.IndexOf(list[j]));\n                    Assert.Equal(expectedIndex, collection.IndexOf(list[j].Key));                   \n                }\n            }\n        }\n\n        [Fact]\n        public void Insert()\n        {\n            var list = new List<KeyValuePair<string, int>>\n            {\n                new KeyValuePair<string, int>(\"one\", 1),\n                new KeyValuePair<string, int>(\"two\", 2),\n                new KeyValuePair<string, int>(\"three\", 3),\n                new KeyValuePair<string, int>(\"four\", 4),\n                new KeyValuePair<string, int>(\"five\", 5)                \n            };\n\n            var collection = new TestIndexedCollection<string, int>(StringComparer.Ordinal);\n            Assert.Empty(collection);\n\n            for (int i = 0; i <= list.Count; i++)\n            {\n                collection.Clear();\n                Assert.Empty(collection);\n\n                for (int j = list.Count - 1; j >= 0; j--)\n                    collection.Insert(0, list[j]);\n\n                Assert.Equal(list.Count, collection.Count);\n\n                var expectedList = list.ToList();\n                collection.Insert(i, new KeyValuePair<string, int>(\"many\", 42));\n                expectedList.Insert(i, new KeyValuePair<string, int>(\"many\", 42));\n\n                // Test IEnumerable\n                Assert.Equal(expectedList, collection);\n\n                // Test indexer\n                for (int j = 0; j < expectedList.Count; j++)\n                    Assert.Equal(expectedList[j], collection[j]);\n\n                // Test key indexer\n                for (int j = 0; j < expectedList.Count; j++)\n                    Assert.Equal(expectedList[j], collection[expectedList[j].Key]);\n\n                for (int j = 0; j < expectedList.Count; j++)\n                {\n                    Assert.Contains(expectedList[j], collection);\n                    Assert.True(collection.ContainsKey(expectedList[j].Key));\n                    Assert.True(collection.TryGetValue(expectedList[j].Key, out var actualValue));\n                    Assert.Equal(expectedList[j], actualValue);\n\n                    Assert.Equal(j, collection.IndexOf(expectedList[j]));\n                    Assert.Equal(j, collection.IndexOf(expectedList[j].Key));\n                }\n            }\n        }\n\n        [Theory]\n        [InlineData(\"RemoveAt\")]\n        [InlineData(\"RemoveKey\")]\n        [InlineData(\"RemoveItem\")]\n        public void Remove(string removeMethod)\n        {\n            var list = new List<KeyValuePair<string, int>>\n            {\n                new KeyValuePair<string, int>(\"a\", 2),\n                new KeyValuePair<string, int>(\"b\", 4),\n                new KeyValuePair<string, int>(\"c\", 8),\n                new KeyValuePair<string, int>(\"d\", 16),\n                new KeyValuePair<string, int>(\"e\", 32),\n                new KeyValuePair<string, int>(\"f\", 64),\n            };\n\n            var collection = new TestIndexedCollection<string, int>(StringComparer.Ordinal);\n            Assert.Empty(collection);\n            for (int range = 1; range <= list.Count; range++)\n            {\n                for (int start = 0; start <= list.Count - range; start++)\n                {\n                    collection.Clear();\n                    Assert.Empty(collection);\n\n                    var expectedList = list.ToList();\n                    for (int i = 0; i < list.Count; i++)\n                        collection.Add(list[i]);\n\n                    for (int i = range - 1; i >= 0; i--)\n                    {\n                        switch (removeMethod)\n                        {\n                            case \"RemoveKey\":\n                                var key = expectedList[start + i].Key;\n                                Assert.True(collection.Remove(key));\n                                Assert.False(collection.Remove(key));\n                                break;\n\n                            case \"RemoveItem\":\n                                var item = expectedList[start + i];\n                                Assert.True(collection.Remove(item));\n                                Assert.False(collection.Remove(item));\n                                break;\n\n                            case \"RemoveAt\":\n                                collection.RemoveAt(start + i);\n                                break;\n\n                            default:\n                                Assert.True(false, $\"Unknown method: {removeMethod}\");\n                                break;\n                        }\n\n                        expectedList.RemoveAt(start + i);\n                    }\n\n                    // Test IEnumerable\n                    Assert.Equal(expectedList, collection);\n\n                    // Test indexer\n                    for (int i = 0; i < expectedList.Count; i++)\n                        Assert.Equal(expectedList[i], collection[i]);\n\n                    // Test key indexer\n                    for (int i = 0; i < expectedList.Count; i++)\n                        Assert.Equal(expectedList[i], collection[expectedList[i].Key]);\n\n                    for (int i = 0; i < list.Count; i++)\n                    {\n                        bool shouldContain = i < start || i >= start + range;\n\n                        Assert.Equal(shouldContain, collection.Contains(list[i]));\n                        Assert.Equal(shouldContain, collection.ContainsKey(list[i].Key));\n                        Assert.Equal(shouldContain, collection.TryGetValue(list[i].Key, out var actualValue));\n\n                        if (shouldContain)\n                            Assert.Equal(list[i], actualValue);\n\n                        var expectedIndex = shouldContain ? (i < start ? i : i - range) : -1;\n                        Assert.Equal(expectedIndex, collection.IndexOf(list[i]));\n                        Assert.Equal(expectedIndex, collection.IndexOf(list[i].Key));\n                    }\n                }\n            }                \n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ListExtensionsTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Utils;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ListExtensionsTests\n    {\n        [Fact]\n        public void SliceArray()\n        {\n            var arrayAsList = (IList<int>)Enumerable.Range(50, 200).ToArray();\n\n            TestSlice(Enumerable.Range(50, 100).ToList(), arrayAsList.Slice(0, 100));\n            TestSlice(Enumerable.Range(200, 50).ToList(), arrayAsList.Slice(150));\n            TestSlice(Enumerable.Range(100, 100).ToList(), arrayAsList.Slice(50, 100));\n\n            var expectedList = Enumerable.Range(102, 7).ToList();\n            TestSlice(expectedList, arrayAsList.Slice(50).Slice(0, 100).Slice(2, 7));            \n            TestSlice(expectedList, arrayAsList.Slice(50).Slice(2, 7));\n            TestSlice(expectedList, arrayAsList.Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(Enumerable.Range(52, 7).ToList(), arrayAsList.Slice(0, 100).Slice(2, 7));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => arrayAsList.Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => arrayAsList.Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => arrayAsList.Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => arrayAsList.Slice(0, 100).Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => arrayAsList.Slice(50).Slice(2, 7).Slice(0, 100));\n\n            var arrayAsRoList = (IReadOnlyList<int>)arrayAsList;\n\n            TestSlice(Enumerable.Range(50, 100).ToList(), arrayAsRoList.Slice(0, 100));\n            TestSlice(Enumerable.Range(200, 50).ToList(), arrayAsRoList.Slice(150));\n            TestSlice(Enumerable.Range(100, 100).ToList(), arrayAsRoList.Slice(50, 100));\n\n            TestSlice(expectedList, arrayAsRoList.Slice(50).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, arrayAsRoList.Slice(50).Slice(2, 7));\n            TestSlice(expectedList, arrayAsRoList.Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(Enumerable.Range(52, 7).ToList(), arrayAsRoList.Slice(0, 100).Slice(2, 7));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => arrayAsRoList.Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => arrayAsRoList.Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => arrayAsRoList.Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => arrayAsRoList.Slice(0, 100).Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => arrayAsRoList.Slice(50).Slice(2, 7).Slice(0, 100));\n        }\n\n        [Fact]\n        public void SliceReadOnlyList()\n        {\n            var list = (IReadOnlyList<int>)Enumerable.Range(50, 200).ToList();\n\n            TestSlice(Enumerable.Range(50, 100).ToList(), list.Slice(0, 100));\n            TestSlice(Enumerable.Range(200, 50).ToList(), list.Slice(150));\n            TestSlice(Enumerable.Range(100, 100).ToList(), list.Slice(50, 100));\n\n            var expectedList = Enumerable.Range(102, 7).ToList();\n            TestSlice(expectedList, list.Slice(50).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, list.Slice(50).Slice(2, 7));\n            TestSlice(expectedList, list.Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(Enumerable.Range(52, 7).ToList(), list.Slice(0, 100).Slice(2, 7));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.Slice(0, 100).Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.Slice(50).Slice(2, 7).Slice(0, 100));\n        }\n\n        [Fact]\n        public void SliceList()\n        {\n            var list = new ListWrapper<int>(Enumerable.Range(50, 200).ToList());\n\n            TestSlice(Enumerable.Range(50, 100).ToList(), list.Slice(0, 100));\n            TestSlice(Enumerable.Range(200, 50).ToList(), list.Slice(150));\n            TestSlice(Enumerable.Range(100, 100).ToList(), list.Slice(50, 100));\n\n            var expectedList = Enumerable.Range(102, 7).ToList();\n            TestSlice(expectedList, list.Slice(50).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, list.Slice(50).Slice(2, 7));\n            TestSlice(expectedList, list.Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(Enumerable.Range(52, 7).ToList(), list.Slice(0, 100).Slice(2, 7));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.Slice(0, 100).Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.Slice(50).Slice(2, 7).Slice(0, 100));\n        }\n\n        [Fact]\n        public void MapArray()\n        {\n            var mappedArray = ((IReadOnlyList<int>)Enumerable.Range(-50, 100).ToArray()).Map(v => v + 10);\n            TestSlice(Enumerable.Range(-40, 100).ToList(), mappedArray);\n            TestSlice(Enumerable.Range(-40, 100).Select(v => v * 2).ToList(), mappedArray.Map(v => v * 2));\n\n            mappedArray = ((IList<int>)Enumerable.Range(-50, 100).ToArray()).Map(v => v - 10);\n            TestSlice(Enumerable.Range(-60, 100).ToList(), mappedArray);\n            TestSlice(Enumerable.Range(-60, 100).Select(v => v * 2).ToList(), mappedArray.Map(v => v * 2));\n        }\n\n        [Fact]\n        public void MapReadOnlyList()\n        {\n            var list = ((IReadOnlyList<int>)Enumerable.Range(-50, 100).ToList()).Map(v => v + 10);\n            TestSlice(Enumerable.Range(-40, 100).ToList(), list);\n            TestSlice(Enumerable.Range(-40, 100).Select(v => v * 2).ToList(), list.Map(v => v * 2));\n        }\n\n        [Fact]\n        public void MapList()\n        {\n            var list = new ListWrapper<int>(Enumerable.Range(-50, 100).ToList()).Map(v => v - 10);\n            TestSlice(Enumerable.Range(-60, 100).ToList(), list);\n            TestSlice(Enumerable.Range(-60, 100).Select(v => v * 2).ToList(), list.Map(v => v * 2));\n        }\n\n        [Fact]\n        public void SliceMapArray()\n        {\n            TestMappedArray(((IReadOnlyList<int>)Enumerable.Range(50, 200).ToArray()).Map(v => v + 10));\n            TestMappedArray(((IList<int>)Enumerable.Range(50, 200).ToArray()).Map(v => v + 10));\n\n            static void TestMappedArray(IReadOnlyList<int> mappedArray)\n            {\n                TestSlice(Enumerable.Range(60, 200).ToList(), mappedArray);\n\n                TestSlice(Enumerable.Range(60, 100).ToList(), mappedArray.Slice(0, 100));\n                TestSlice(Enumerable.Range(50, 100).ToList(), mappedArray.Map(v => v - 10).Slice(0, 100));\n                TestSlice(Enumerable.Range(50, 100).ToList(), mappedArray.Slice(0, 100).Map(v => v - 10));\n\n                TestSlice(Enumerable.Range(210, 50).ToList(), mappedArray.Slice(150));\n                TestSlice(Enumerable.Range(200, 50).ToList(), mappedArray.Map(v => v - 10).Slice(150));\n                TestSlice(Enumerable.Range(200, 50).ToList(), mappedArray.Slice(150).Map(v => v - 10));\n\n                TestSlice(Enumerable.Range(110, 100).ToList(), mappedArray.Slice(50, 100));\n                TestSlice(Enumerable.Range(100, 100).ToList(), mappedArray.Map(v => v - 10).Slice(50, 100));\n                TestSlice(Enumerable.Range(100, 100).ToList(), mappedArray.Slice(50, 100).Map(v => v - 10));\n\n                var expectedList = Enumerable.Range(102, 7).ToList();\n                TestSlice(expectedList.Select(v => v + 10).ToList(), mappedArray.Slice(50).Slice(0, 100).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Map(v => v - 10).Slice(50).Slice(0, 100).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Slice(50).Map(v => v - 10).Slice(0, 100).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Slice(50).Slice(0, 100).Map(v => v - 10).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Slice(50).Slice(0, 100).Slice(2, 7).Map(v => v - 10));\n\n                TestSlice(expectedList.Select(v => v + 10).ToList(), mappedArray.Slice(50).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Map(v => v - 10).Slice(50).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Slice(50).Map(v => v - 10).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Slice(50).Slice(2, 7).Map(v => v - 10));\n\n                TestSlice(expectedList.Select(v => v + 10).ToList(), mappedArray.Slice(0, 100).Slice(50).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Map(v => v - 10).Slice(0, 100).Slice(50).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Slice(0, 100).Map(v => v - 10).Slice(50).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Slice(0, 100).Slice(50).Map(v => v - 10).Slice(2, 7));\n                TestSlice(expectedList, mappedArray.Slice(0, 100).Slice(50).Slice(2, 7).Map(v => v - 10));\n\n                TestSlice(Enumerable.Range(62, 7).ToList(), mappedArray.Slice(0, 100).Slice(2, 7));\n\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(50).Slice(2, 7).Slice(0, 100));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Map(v => v - 10).Slice(50).Slice(2, 7).Slice(0, 100));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(2, 7).Slice(50));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Map(v => v - 10).Slice(2, 7).Slice(50));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(2, 7).Map(v => v - 10).Slice(50));\n\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(2, 7).Slice(0, 100));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(0, 100).Slice(2, 7).Slice(50));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Map(v => v - 10).Slice(0, 100).Slice(2, 7).Slice(50));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(0, 100).Map(v => v - 10).Slice(2, 7).Slice(50));\n\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(50).Slice(2, 7).Slice(0, 100));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Map(v => v - 10).Slice(50).Slice(2, 7).Slice(0, 100));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n                Assert.Throws<ArgumentOutOfRangeException>(() => mappedArray.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n            }\n\n            var array = (IReadOnlyList<int>)Enumerable.Range(60, 200).ToArray();\n\n            TestSlice(Enumerable.Range(60, 100).ToList(), array.Slice(0, 100).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(Enumerable.Range(50, 100).ToList(), array.Map(v => v - 10).Slice(0, 100));\n            TestSlice(Enumerable.Range(50, 100).ToList(), array.Slice(0, 100).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(210, 50).ToList(), array.Slice(150).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(Enumerable.Range(200, 50).ToList(), array.Map(v => v - 10).Slice(150));\n            TestSlice(Enumerable.Range(200, 50).ToList(), array.Slice(150).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(110, 100).ToList(), array.Slice(50, 100).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(Enumerable.Range(100, 100).ToList(), array.Map(v => v - 10).Slice(50, 100));\n            TestSlice(Enumerable.Range(100, 100).ToList(), array.Slice(50, 100).Map(v => v - 10));\n\n            var expectedList = Enumerable.Range(102, 7).ToList();\n            TestSlice(expectedList.Select(v => v + 10).ToList(), array.Slice(50).Map(v => v - 10).Map(v => v + 10).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), array.Slice(50).Map(v => v - 10).Slice(0, 100).Map(v => v + 10).Slice(2, 7));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), array.Slice(50).Map(v => v - 10).Slice(0, 100).Slice(2, 7).Map(v => v + 10));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), array.Slice(50).Slice(0, 100).Map(v => v - 10).Map(v => v + 10).Slice(2, 7));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), array.Slice(50).Slice(0, 100).Map(v => v - 10).Slice(2, 7).Map(v => v + 10));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), array.Slice(50).Slice(0, 100).Slice(2, 7).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(expectedList, array.Map(v => v - 10).Slice(50).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, array.Slice(50).Map(v => v - 10).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, array.Slice(50).Slice(0, 100).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, array.Slice(50).Slice(0, 100).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(expectedList, array.Map(v => v - 10).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, array.Slice(50).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, array.Slice(50).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(expectedList, array.Map(v => v - 10).Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, array.Slice(0, 100).Map(v => v - 10).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, array.Slice(0, 100).Slice(50).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, array.Slice(0, 100).Slice(50).Slice(2, 7).Map(v => v - 10));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => array.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => array.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => array.Slice(2, 7).Map(v => v - 10).Slice(50));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => array.Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => array.Slice(0, 100).Map(v => v - 10).Slice(2, 7).Slice(50));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => array.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => array.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n        }\n\n        [Fact]\n        public void SliceMapReadOnlyList()\n        {\n            var mappedRoList = ((IReadOnlyList<int>)Enumerable.Range(50, 200).ToList()).Map(v => v + 10);\n\n            TestSlice(Enumerable.Range(60, 200).ToList(), mappedRoList);\n\n            TestSlice(Enumerable.Range(60, 100).ToList(), mappedRoList.Slice(0, 100));\n            TestSlice(Enumerable.Range(50, 100).ToList(), mappedRoList.Map(v => v - 10).Slice(0, 100));\n            TestSlice(Enumerable.Range(50, 100).ToList(), mappedRoList.Slice(0, 100).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(210, 50).ToList(), mappedRoList.Slice(150));\n            TestSlice(Enumerable.Range(200, 50).ToList(), mappedRoList.Map(v => v - 10).Slice(150));\n            TestSlice(Enumerable.Range(200, 50).ToList(), mappedRoList.Slice(150).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(110, 100).ToList(), mappedRoList.Slice(50, 100));\n            TestSlice(Enumerable.Range(100, 100).ToList(), mappedRoList.Map(v => v - 10).Slice(50, 100));\n            TestSlice(Enumerable.Range(100, 100).ToList(), mappedRoList.Slice(50, 100).Map(v => v - 10));\n\n            var expectedList = Enumerable.Range(102, 7).ToList();\n            TestSlice(expectedList.Select(v => v + 10).ToList(), mappedRoList.Slice(50).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Map(v => v - 10).Slice(50).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Slice(50).Map(v => v - 10).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Slice(50).Slice(0, 100).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Slice(50).Slice(0, 100).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(expectedList.Select(v => v + 10).ToList(), mappedRoList.Slice(50).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Map(v => v - 10).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Slice(50).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Slice(50).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(expectedList.Select(v => v + 10).ToList(), mappedRoList.Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Map(v => v - 10).Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Slice(0, 100).Map(v => v - 10).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Slice(0, 100).Slice(50).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, mappedRoList.Slice(0, 100).Slice(50).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(62, 7).ToList(), mappedRoList.Slice(0, 100).Slice(2, 7));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Map(v => v - 10).Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Map(v => v - 10).Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(2, 7).Map(v => v - 10).Slice(50));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(0, 100).Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Map(v => v - 10).Slice(0, 100).Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(0, 100).Map(v => v - 10).Slice(2, 7).Slice(50));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Map(v => v - 10).Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedRoList.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            var roList = (IReadOnlyList<int>)Enumerable.Range(60, 200).ToList().AsReadOnly();\n\n            TestSlice(Enumerable.Range(60, 100).ToList(), roList.Slice(0, 100).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(Enumerable.Range(50, 100).ToList(), roList.Map(v => v - 10).Slice(0, 100));\n            TestSlice(Enumerable.Range(50, 100).ToList(), roList.Slice(0, 100).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(210, 50).ToList(), roList.Slice(150).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(Enumerable.Range(200, 50).ToList(), roList.Map(v => v - 10).Slice(150));\n            TestSlice(Enumerable.Range(200, 50).ToList(), roList.Slice(150).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(110, 100).ToList(), roList.Slice(50, 100).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(Enumerable.Range(100, 100).ToList(), roList.Map(v => v - 10).Slice(50, 100));\n            TestSlice(Enumerable.Range(100, 100).ToList(), roList.Slice(50, 100).Map(v => v - 10));\n\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Map(v => v - 10).Map(v => v + 10).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Map(v => v - 10).Slice(0, 100).Map(v => v + 10).Slice(2, 7));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Map(v => v - 10).Slice(0, 100).Slice(2, 7).Map(v => v + 10));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Slice(0, 100).Map(v => v - 10).Map(v => v + 10).Slice(2, 7));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Slice(0, 100).Map(v => v - 10).Slice(2, 7).Map(v => v + 10));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Slice(0, 100).Slice(2, 7).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(expectedList, roList.Map(v => v - 10).Slice(50).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(50).Map(v => v - 10).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(50).Slice(0, 100).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(50).Slice(0, 100).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(expectedList, roList.Map(v => v - 10).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(50).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(50).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(expectedList, roList.Map(v => v - 10).Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(0, 100).Map(v => v - 10).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(0, 100).Slice(50).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(0, 100).Slice(50).Slice(2, 7).Map(v => v - 10));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(2, 7).Map(v => v - 10).Slice(50));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(0, 100).Map(v => v - 10).Slice(2, 7).Slice(50));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n        }\n\n        [Fact]\n        public void SliceMapList()\n        {\n            var mappedList = new ListWrapper<int>(Enumerable.Range(50, 200).ToList()).Map(v => v + 10);\n\n            TestSlice(Enumerable.Range(60, 200).ToList(), mappedList);\n\n            TestSlice(Enumerable.Range(60, 100).ToList(), mappedList.Slice(0, 100));\n            TestSlice(Enumerable.Range(50, 100).ToList(), mappedList.Map(v => v - 10).Slice(0, 100));\n            TestSlice(Enumerable.Range(50, 100).ToList(), mappedList.Slice(0, 100).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(210, 50).ToList(), mappedList.Slice(150));\n            TestSlice(Enumerable.Range(200, 50).ToList(), mappedList.Map(v => v - 10).Slice(150));\n            TestSlice(Enumerable.Range(200, 50).ToList(), mappedList.Slice(150).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(110, 100).ToList(), mappedList.Slice(50, 100));\n            TestSlice(Enumerable.Range(100, 100).ToList(), mappedList.Map(v => v - 10).Slice(50, 100));\n            TestSlice(Enumerable.Range(100, 100).ToList(), mappedList.Slice(50, 100).Map(v => v - 10));\n\n            var expectedList = Enumerable.Range(102, 7).ToList();\n            TestSlice(expectedList.Select(v => v + 10).ToList(), mappedList.Slice(50).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Map(v => v - 10).Slice(50).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Slice(50).Map(v => v - 10).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Slice(50).Slice(0, 100).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Slice(50).Slice(0, 100).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(expectedList.Select(v => v + 10).ToList(), mappedList.Slice(50).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Map(v => v - 10).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Slice(50).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Slice(50).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(expectedList.Select(v => v + 10).ToList(), mappedList.Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Map(v => v - 10).Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Slice(0, 100).Map(v => v - 10).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Slice(0, 100).Slice(50).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, mappedList.Slice(0, 100).Slice(50).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(62, 7).ToList(), mappedList.Slice(0, 100).Slice(2, 7));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Map(v => v - 10).Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Map(v => v - 10).Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(2, 7).Map(v => v - 10).Slice(50));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(0, 100).Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Map(v => v - 10).Slice(0, 100).Slice(2, 7).Slice(50));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(0, 100).Map(v => v - 10).Slice(2, 7).Slice(50));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Map(v => v - 10).Slice(50).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => mappedList.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            var roList = new ListWrapper<int>(Enumerable.Range(60, 200).ToList());\n\n            TestSlice(Enumerable.Range(60, 100).ToList(), roList.Slice(0, 100).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(Enumerable.Range(50, 100).ToList(), roList.Map(v => v - 10).Slice(0, 100));\n            TestSlice(Enumerable.Range(50, 100).ToList(), roList.Slice(0, 100).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(210, 50).ToList(), roList.Slice(150).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(Enumerable.Range(200, 50).ToList(), roList.Map(v => v - 10).Slice(150));\n            TestSlice(Enumerable.Range(200, 50).ToList(), roList.Slice(150).Map(v => v - 10));\n\n            TestSlice(Enumerable.Range(110, 100).ToList(), roList.Slice(50, 100).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(Enumerable.Range(100, 100).ToList(), roList.Map(v => v - 10).Slice(50, 100));\n            TestSlice(Enumerable.Range(100, 100).ToList(), roList.Slice(50, 100).Map(v => v - 10));\n\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Map(v => v - 10).Map(v => v + 10).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Map(v => v - 10).Slice(0, 100).Map(v => v + 10).Slice(2, 7));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Map(v => v - 10).Slice(0, 100).Slice(2, 7).Map(v => v + 10));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Slice(0, 100).Map(v => v - 10).Map(v => v + 10).Slice(2, 7));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Slice(0, 100).Map(v => v - 10).Slice(2, 7).Map(v => v + 10));\n            TestSlice(expectedList.Select(v => v + 10).ToList(), roList.Slice(50).Slice(0, 100).Slice(2, 7).Map(v => v - 10).Map(v => v + 10));\n            TestSlice(expectedList, roList.Map(v => v - 10).Slice(50).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(50).Map(v => v - 10).Slice(0, 100).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(50).Slice(0, 100).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(50).Slice(0, 100).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(expectedList, roList.Map(v => v - 10).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(50).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(50).Slice(2, 7).Map(v => v - 10));\n\n            TestSlice(expectedList, roList.Map(v => v - 10).Slice(0, 100).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(0, 100).Map(v => v - 10).Slice(50).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(0, 100).Slice(50).Map(v => v - 10).Slice(2, 7));\n            TestSlice(expectedList, roList.Slice(0, 100).Slice(50).Slice(2, 7).Map(v => v - 10));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(2, 7).Map(v => v - 10).Slice(50));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(0, 100).Map(v => v - 10).Slice(2, 7).Slice(50));\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(50).Map(v => v - 10).Slice(2, 7).Slice(0, 100));\n            Assert.Throws<ArgumentOutOfRangeException>(() => roList.Slice(50).Slice(2, 7).Map(v => v - 10).Slice(0, 100));\n        }\n\n        [Fact]\n        public void ArrayCopyTo()\n        {\n            var array = Enumerable.Range(0, 99).ToArray();\n            TestCopyTo(array);         \n        }\n\n        [Fact]\n        public void ListCopyTo()\n        {\n            var list = Enumerable.Range(0, 99).ToList();\n            TestCopyTo(list);\n        }\n\n        private static void TestSlice<T>(IReadOnlyList<T> expected, IReadOnlyList<T> slice)\n        {\n            Assert.Equal(expected.Count, slice.Count);\n\n            var ex = Assert.ThrowsAny<Exception>(() => slice[-1]);\n            if(!(ex is IndexOutOfRangeException))\n            {\n                var argumentEx = Assert.IsAssignableFrom<ArgumentException>(ex);\n                Assert.Equal(\"index\", argumentEx.ParamName);\n            }\n\n            ex = Assert.ThrowsAny<Exception>(() => slice[expected.Count]);\n            if (!(ex is IndexOutOfRangeException))\n            {\n                var argumentEx = Assert.IsAssignableFrom<ArgumentException>(ex);\n                Assert.Equal(\"index\", argumentEx.ParamName);\n            }\n\n            // IEnumerable implementation\n            Assert.Equal<T>(expected, slice);\n\n            // IReadOnlyList implementation\n            for (int i = 0; i < expected.Count; i++)\n                Assert.Equal(expected[i], slice[i]);\n\n            TestCopyTo(slice);\n        }\n\n        private static void TestCopyTo<T>(IReadOnlyList<T> list)\n        {\n            var span = new Span<T>(new T[list.Count + 16]);\n\n            var length = list.CopyTo(span, list.Count);\n            Assert.Equal(0, length);\n\n            length = list.CopyTo(default, 0);\n            Assert.Equal(0, length);\n\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.CopyTo(new Span<T>(new T[2], 1, 1), -1));\n            Assert.Throws<ArgumentOutOfRangeException>(() => list.CopyTo(new Span<T>(new T[1]), list.Count + 1));\n\n            length = list.CopyTo(span, 0);\n            Assert.Equal(list.Count, length);\n            for (int i = 0; i < length; i++)\n                Assert.Equal(list[i], span[i]);\n\n            var halfLength = list.Count / 2;\n            length = list.CopyTo(span.Slice(halfLength, halfLength), halfLength / 2);\n            Assert.Equal(Math.Min(list.Count - halfLength / 2, halfLength), length);\n            for (int i = halfLength / 2, j = halfLength; i < halfLength / 2 + length; i++, j++)\n                Assert.Equal(list[i], span[j]);\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/Octonica.ClickHouseClient.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netcoreapp3.1;net6.0;net8.0</TargetFrameworks>\n\n    <IsPackable>false</IsPackable>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.8.0\" />\n    <PackageReference Include=\"xunit\" Version=\"2.6.4\" Condition=\"'$(TargetFramework)' != 'netcoreapp3.1'\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.5.6\" Condition=\"'$(TargetFramework)' != 'netcoreapp3.1'\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"xunit\" Version=\"2.4.2\" Condition=\"'$(TargetFramework)' == 'netcoreapp3.1'\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.5\" Condition=\"'$(TargetFramework)' == 'netcoreapp3.1'\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Octonica.ClickHouseClient\\Octonica.ClickHouseClient.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Compile Include=\"..\\ConnectionSettingsHelper.cs\" Link=\"ConnectionSettingsHelper.cs\" />\n    <None Update=\"clickHouse.dbconfig\" Condition=\"Exists('clickHouse.dbconfig')\">\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\n    </None>\n    <None Update=\"xunit.runner.json\">\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\n    </None>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/ReadWriteBufferTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Buffers;\nusing System.Linq;\nusing Octonica.ClickHouseClient.Utils;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class ReadWriteBufferTests\n    {\n        [Fact]\n        public void WriteBlocks()\n        {\n            var source = Enumerable.Range(0, 1000).Select(n => unchecked((byte) n)).ToArray();\n            for (int i = 0; i < 10; i++)\n            {\n                var buffer = new ReadWriteBuffer(7);\n                var rnd = new Random(i);\n\n                var position = 0;\n                while (position < source.Length)\n                {\n                    var blockSize = Math.Min(rnd.Next(1, 18), source.Length - position);\n\n                    var memory = buffer.GetMemory(blockSize);\n                    new ReadOnlySpan<byte>(source, position, blockSize).CopyTo(memory.Span);\n                    buffer.ConfirmWrite(blockSize);\n\n                    position += blockSize;\n                }\n\n                var empty = buffer.Read();\n                Assert.True(empty.IsEmpty);\n\n                buffer.Flush();\n\n                var firstPart = buffer.Read();\n                var array = firstPart.Slice(0, 500).ToArray();\n                buffer.ConfirmRead(array.Length);\n\n                Assert.Equal(source.Take(500), array);\n\n                var secondPart = buffer.Read();\n                secondPart.CopyTo(array);\n                buffer.ConfirmRead(array.Length);\n\n                Assert.Equal(source.Skip(500), array);\n\n                empty = buffer.Read();\n                Assert.True(empty.IsEmpty);\n            }\n        }\n\n        [Fact]\n        public void WriteExactThreeBlocks()\n        {\n            var source = Enumerable.Range(0, 7 * 3).Select(n => (byte) n).ToArray();\n            var buffer = new ReadWriteBuffer(7);\n            for (int i = 0; i < 3; i++)\n            {\n                for (int j = 0; j < 3; j++)\n                {\n                    var block = buffer.GetMemory(7);\n                    new Span<byte>(source, j * 7, 7).CopyTo(block.Span);\n                    buffer.ConfirmWrite(7);\n                }\n\n                buffer.Flush();\n\n                var result = buffer.Read().ToArray();\n                Assert.Equal(source, result);\n\n                buffer.ConfirmRead(result.Length);\n                var empty = buffer.Read();\n                Assert.True(empty.IsEmpty);\n            }\n        }\n\n        [Fact]\n        public void ReadWriteSequentialWithoutSegmentOverflow()\n        {\n            const int segmentSize = 13;\n\n            var source = Enumerable.Range(0, 1000).Select(n => unchecked((byte) n)).ToArray();\n            var target = new byte[source.Length];\n            var buffer = new ReadWriteBuffer(segmentSize);\n            for (int i = 0; i < 100; i++)\n            {\n                var rnd = new Random(i);\n                \n                Array.Clear(target, 0, target.Length);\n                int sourcePosition = 0;\n                int targetPosition = 0;\n\n                int readWriteFactor = i % 3 + 2;\n                while (sourcePosition != source.Length)\n                {\n                    var read = rnd.Next(readWriteFactor) == 0;\n                    if (read)\n                    {\n                        var availableBytes = buffer.Read();\n                        if (!availableBytes.IsEmpty)\n                        {\n                            var availableLen = Math.Min((int) availableBytes.Length, Math.Min(rnd.Next(segmentSize * 3) + 1, target.Length - targetPosition));\n                            var targetSlice = new Span<byte>(target).Slice(targetPosition, availableLen);\n                            availableBytes.Slice(0, availableLen).CopyTo(targetSlice);\n\n                            targetPosition += availableLen;\n                            buffer.ConfirmRead(availableLen);\n                            continue;\n                        }\n                    }\n\n                    var len = Math.Min(source.Length - sourcePosition, rnd.Next(segmentSize * 3) + 1);\n                    while (len != 0)\n                    {\n                        var memory = buffer.GetMemory();\n                        var currentLen = Math.Min(len, memory.Length);\n                        new ReadOnlySpan<byte>(source).Slice(sourcePosition, currentLen).CopyTo(memory.Span.Slice(0, currentLen));\n\n                        buffer.ConfirmWrite(currentLen);\n                        sourcePosition += currentLen;\n                        len -= currentLen;\n                    }\n\n                    buffer.Flush();\n                }\n\n                var lastBlock = buffer.Read();\n                Assert.Equal(target.Length - targetPosition, lastBlock.Length);\n\n                lastBlock.CopyTo(new Span<byte>(target, targetPosition, target.Length - targetPosition));\n                Assert.Equal(source, target);\n\n                buffer.ConfirmRead((int) lastBlock.Length);\n            }\n        }\n\n        [Fact]\n        public void ReadWriteSequentialWithSegmentOverflow()\n        {\n            const int segmentSize = 13;\n\n            var source = Enumerable.Range(0, 1000).Select(n => unchecked((byte) n)).ToArray();\n            var target = new byte[source.Length];\n            var buffer = new ReadWriteBuffer(segmentSize);\n            for (int i = 0; i < 100; i++)\n            {\n                var rnd = new Random(i);\n\n                Array.Clear(target, 0, target.Length);\n                int sourcePosition = 0;\n                int targetPosition = 0;\n\n                int readWriteFactor = i % 3 + 2;\n                while (sourcePosition != source.Length)\n                {\n                    var read = rnd.Next(readWriteFactor) == 0;\n                    if (read)\n                    {\n                        var availableBytes = buffer.Read();\n                        if (!availableBytes.IsEmpty)\n                        {\n                            var availableLen = Math.Min((int) availableBytes.Length, Math.Min(rnd.Next(segmentSize * 3) + 1, target.Length - targetPosition));\n                            var targetSlice = new Span<byte>(target).Slice(targetPosition, availableLen);\n                            availableBytes.Slice(0, availableLen).CopyTo(targetSlice);\n\n                            targetPosition += availableLen;\n                            buffer.ConfirmRead(availableLen);\n                            continue;\n                        }\n                    }\n\n                    var len = Math.Min(source.Length - sourcePosition, rnd.Next(segmentSize * 3) + 1);\n                    var memory = buffer.GetMemory(len);\n\n                    new ReadOnlySpan<byte>(source).Slice(sourcePosition, len).CopyTo(memory.Span.Slice(0, len));\n\n                    buffer.ConfirmWrite(len);\n                    sourcePosition += len;\n\n                    buffer.Flush();\n                }\n\n                var lastBlock = buffer.Read();\n                Assert.Equal(target.Length - targetPosition, lastBlock.Length);\n\n                lastBlock.CopyTo(new Span<byte>(target, targetPosition, target.Length - targetPosition));\n                Assert.Equal(source, target);\n\n                buffer.ConfirmRead((int) lastBlock.Length);\n            }\n        }\n\n        [Fact]\n        public void FlushDiscardWithoutSegmentOverflow()\n        {\n            const int segmentSize = 13;\n\n            var source = Enumerable.Range(0, 1000).Select(n => unchecked((byte)n)).ToArray();\n            var target = new byte[source.Length];\n            var buffer = new ReadWriteBuffer(segmentSize);\n            for (int i = 0; i < 100; i++)\n            {\n                var rnd = new Random(i);\n\n                Array.Clear(target, 0, target.Length);\n                int sourcePosition = 0;\n                int targetPosition = 0;\n                int discardPosition = 0;\n\n                int readWriteFactor = i % 3 + 2;\n                while (discardPosition != source.Length)\n                {\n                    var read = rnd.Next(readWriteFactor) == 0;\n                    if (read)\n                    {\n                        var availableBytes = buffer.Read();\n                        if (!availableBytes.IsEmpty)\n                        {\n                            var availableLen = Math.Min((int)availableBytes.Length, Math.Min(rnd.Next(segmentSize * 3) + 1, target.Length - targetPosition));\n                            var targetSlice = new Span<byte>(target).Slice(targetPosition, availableLen);\n                            availableBytes.Slice(0, availableLen).CopyTo(targetSlice);\n\n                            targetPosition += availableLen;\n                            buffer.ConfirmRead(availableLen);\n                            continue;\n                        }\n                    }\n\n                    var len = Math.Min(source.Length - sourcePosition, rnd.Next(segmentSize * 3) + 1);\n                    while (len != 0)\n                    {\n                        var memory = buffer.GetMemory();\n                        var currentLen = Math.Min(len, memory.Length);\n                        new ReadOnlySpan<byte>(source).Slice(sourcePosition, currentLen).CopyTo(memory.Span.Slice(0, currentLen));\n\n                        buffer.ConfirmWrite(currentLen);\n                        sourcePosition += currentLen;\n                        len -= currentLen;\n                    }\n\n                    if (rnd.Next(4) == 0)\n                    {\n                        buffer.Flush();\n                        discardPosition = sourcePosition;\n                    }\n                    else if (rnd.Next(2) == 0)\n                    {\n                        buffer.Discard();\n                        sourcePosition = discardPosition;\n                    }\n                }\n\n                var lastBlock = buffer.Read();\n                Assert.Equal(target.Length - targetPosition, lastBlock.Length);\n\n                lastBlock.CopyTo(new Span<byte>(target, targetPosition, target.Length - targetPosition));\n                Assert.Equal(source, target);\n\n                buffer.ConfirmRead((int)lastBlock.Length);\n            }\n        }\n\n        [Fact]\n        public void FlushDiscardWithSegmentOverflow()\n        {\n            const int segmentSize = 13;\n\n            var source = Enumerable.Range(0, 1000).Select(n => unchecked((byte)n)).ToArray();\n            var target = new byte[source.Length];\n            var buffer = new ReadWriteBuffer(segmentSize);\n            for (int i = 0; i < 100; i++)\n            {\n                var rnd = new Random(i);\n\n                Array.Clear(target, 0, target.Length);\n                int sourcePosition = 0;\n                int targetPosition = 0;\n                int discardPosition = 0;\n\n                int readWriteFactor = i % 3 + 2;\n                while (discardPosition != source.Length)\n                {\n                    var read = rnd.Next(readWriteFactor) == 0;\n                    if (read)\n                    {\n                        var availableBytes = buffer.Read();\n                        if (!availableBytes.IsEmpty)\n                        {\n                            var availableLen = Math.Min((int)availableBytes.Length, Math.Min(rnd.Next(segmentSize * 3) + 1, target.Length - targetPosition));\n                            var targetSlice = new Span<byte>(target).Slice(targetPosition, availableLen);\n                            availableBytes.Slice(0, availableLen).CopyTo(targetSlice);\n\n                            targetPosition += availableLen;\n                            buffer.ConfirmRead(availableLen);\n                            continue;\n                        }\n                    }\n\n                    var len = Math.Min(source.Length - sourcePosition, rnd.Next(segmentSize * 3) + 1);\n                    var memory = buffer.GetMemory(len);\n\n                    new ReadOnlySpan<byte>(source).Slice(sourcePosition, len).CopyTo(memory.Span.Slice(0, len));\n\n                    buffer.ConfirmWrite(len);\n                    sourcePosition += len;\n\n                    if (rnd.Next(4) == 0)\n                    {\n                        buffer.Flush();\n                        discardPosition = sourcePosition;\n                    }\n                    else if (rnd.Next(2) == 0)\n                    {\n                        buffer.Discard();\n                        sourcePosition = discardPosition;\n                    }\n                }\n\n                var lastBlock = buffer.Read();\n                Assert.Equal(target.Length - targetPosition, lastBlock.Length);\n\n                lastBlock.CopyTo(new Span<byte>(target, targetPosition, target.Length - targetPosition));\n                Assert.Equal(source, target);\n\n                buffer.ConfirmRead((int)lastBlock.Length);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/TestBox.cs",
    "content": "﻿#region License Apache 2.0\r\n/* Copyright 2024 Octonica\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n#endregion\r\n\r\nnamespace Octonica.ClickHouseClient.Tests\r\n{\r\n    internal readonly struct TestBox<T>\r\n    {\r\n        private readonly T _value;\r\n\r\n        public TestBox(T value)\r\n        {\r\n            _value = value;\r\n        }\r\n\r\n        public T Unbox() => _value;\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/TestEnum.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    internal enum TestEnum\n    {\n        None = 0,\n        Value1 = 42,\n        Value2 = -134,\n        Value3 = 32000,\n        Value4 = int.MaxValue\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/TestIndexedCollection.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing Octonica.ClickHouseClient.Utils;\nusing System.Collections.Generic;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    internal sealed class TestIndexedCollection<TKey, TValue> : IndexedCollectionBase<TKey, KeyValuePair<TKey, TValue>>\n        where TKey : notnull\n    {\n        public TestIndexedCollection(IEqualityComparer<TKey>? comparer = null)\n            : base(comparer)\n        {\n        }\n\n        protected override TKey GetKey(KeyValuePair<TKey, TValue> item)\n        {\n            return item.Key;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/TestListWrappers.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2020-2021 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    // Wrappers for List<T>. Each wrapper implements only one of interfaces implemented by List<T>.\n\n    internal abstract class ListWrapperBase<T>\n    {\n        public List<T> List { get; }\n\n        public int Count => List.Count;\n\n        public bool IsReadOnly => ((IList<T>) List).IsReadOnly;\n\n        protected ListWrapperBase(List<T> list)\n        {\n            List = list;\n        }\n\n        public IEnumerator<T> GetEnumerator()\n        {\n            return List.GetEnumerator();\n        }\n\n        public void Add(T item)\n        {\n            List.Add(item);\n        }\n\n        public void Clear()\n        {\n            List.Clear();\n        }\n\n        public bool Contains(T item)\n        {\n            return List.Contains(item);\n        }\n\n        public void CopyTo(T[] array, int arrayIndex)\n        {\n            List.CopyTo(array, arrayIndex);\n        }\n\n        public bool Remove(T item)\n        {\n            return List.Remove(item);\n        }\n\n        public int IndexOf(T item)\n        {\n            return List.IndexOf(item);\n        }\n\n        public void Insert(int index, T item)\n        {\n            List.Insert(index, item);\n        }\n\n        public void RemoveAt(int index)\n        {\n            List.RemoveAt(index);\n        }\n\n        public T this[int index]\n        {\n            get => List[index];\n            set => List[index] = value;\n        }\n    }\n\n    internal sealed class EnumerableListWrapper<T> : ListWrapperBase<T>, IEnumerable\n    {\n        public EnumerableListWrapper(List<T> list)\n            : base(list)\n        {\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n    }\n\n    internal sealed class GenericEnumerableListWrapper<T> : ListWrapperBase<T>, IEnumerable<T>\n    {\n        public GenericEnumerableListWrapper(List<T> list)\n            : base(list)\n        {\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n    }\n\n    internal sealed class ListWrapper<T> : ListWrapperBase<T>, IList<T>\n    {\n        public ListWrapper(List<T> list)\n            : base(list)\n        {\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n    }\n\n    internal sealed class AsyncEnumerableListWrapper<T> : IAsyncEnumerable<T>\n    {\n        private readonly IReadOnlyList<T> _list;\n\n        public AsyncEnumerableListWrapper(IReadOnlyList<T> list)\n        {\n            _list = list;\n        }\n\n        public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)\n        {\n            return new Enumerator(_list);\n        }\n\n        private class Enumerator : IAsyncEnumerator<T>\n        {\n            private readonly IReadOnlyList<T> _list;\n\n            private int _index = -1;\n\n            public T Current => _list[_index];\n\n            public Enumerator(IReadOnlyList<T> list)\n            {\n                _list = list;\n            }\n\n            public ValueTask DisposeAsync()\n            {\n                return default;\n            }\n\n            public ValueTask<bool> MoveNextAsync()\n            {\n                if (_index == _list.Count || ++_index == _list.Count)\n                    return new ValueTask<bool>(false);\n\n                return new ValueTask<bool>(true);\n            }\n        }\n    }\n\n    internal sealed class Int32ToUInt32MappedListWrapper : ListWrapperBase<int>, IReadOnlyList<int>, IReadOnlyList<uint>\n    {\n        private readonly Func<int, uint> _map;\n\n        public Int32ToUInt32MappedListWrapper(List<int> list, Func<int, uint> map)\n            : base(list)\n        {\n            _map = map;\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        IEnumerator<uint> IEnumerable<uint>.GetEnumerator()\n        {\n            return this.Select(_map).GetEnumerator();\n        }\n\n        uint IReadOnlyList<uint>.this[int index] => _map(this[index]);\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/TypeTests.cs",
    "content": "﻿#region License Apache 2.0\n/* Copyright 2019-2024 Octonica\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#endregion\n\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Data.Common;\nusing System.Globalization;\nusing System.Linq;\nusing System.Net;\nusing System.Numerics;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Octonica.ClickHouseClient.Exceptions;\nusing Octonica.ClickHouseClient.Types;\nusing Octonica.ClickHouseClient.Utils;\nusing Xunit;\n\nnamespace Octonica.ClickHouseClient.Tests\n{\n    public class TypeTests : ClickHouseTestsBase, IClassFixture<EncodingFixture>\n    {\n        [Fact]\n        public async Task ReadFixedStringScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT cast('1234йё' AS FixedString(10))\");\n            \n            var result = await cmd.ExecuteScalarAsync();\n            var resultBytes = Assert.IsType<byte[]>(result);\n\n            Assert.Equal(10, resultBytes.Length);\n            Assert.Equal(0, resultBytes[^1]);\n            Assert.Equal(0, resultBytes[^2]);\n\n            var resultString = Encoding.UTF8.GetString(resultBytes, 0, 8);\n            Assert.Equal(\"1234йё\", resultString);\n        }\n\n        [Fact]\n        public async Task ReadFixedStringWithEncoding()\n        {\n            const string str = \"аaбbсc\";\n            var encoding = Encoding.GetEncoding(\"windows-1251\");\n\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand($\"SELECT cast(convertCharset('{str}', 'UTF8', 'windows-1251') AS FixedString(10))\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultBytes = Assert.IsType<byte[]>(result);\n\n            Assert.Equal(10, resultBytes.Length);\n            Assert.Equal(0, resultBytes[^1]);\n            Assert.Equal(0, resultBytes[^2]);\n            Assert.Equal(0, resultBytes[^3]);\n            Assert.Equal(0, resultBytes[^4]);\n            Assert.NotEqual(0, resultBytes[^5]);\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            reader.ConfigureDataReader(new ClickHouseColumnSettings(encoding));\n\n            var success = await reader.ReadAsync();\n            Assert.True(success);\n\n            var strResult = reader.GetString(0);\n            Assert.Equal(str, strResult);\n\n            success = await reader.ReadAsync();\n            Assert.False(success);\n\n            var error = Assert.Throws<ClickHouseException>(() => reader.ConfigureDataReader(new ClickHouseColumnSettings(Encoding.UTF8)));\n            Assert.Equal(ClickHouseErrorCodes.DataReaderError, error.ErrorCode);\n        }\n\n        [Fact]\n        public async Task ReadNullableFixedStringAsArray()\n        {\n            await WithTemporaryTable(\"fixed_string_as_array\", \"id Int32, str Nullable(FixedString(32))\", Test);\n\n            async Task Test(ClickHouseConnection connection, string tableName)\n            {\n                var stringValues = new string?[] { null, \"фываasdf\", \"abcdef\", \"ghijkl\", \"null\", \"пролджэzcvgb\", \"ячсмить\" };\n                var byteValues = stringValues.Select(v => v == null ? null : Encoding.UTF8.GetBytes(v)).ToArray();\n\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id, str) VALUES\", CancellationToken.None))\n                {\n                    writer.ConfigureColumn(\"str\", new ClickHouseColumnSettings(Encoding.UTF8));\n                    await writer.WriteTableAsync(new object[] { Enumerable.Range(0, 10_000), stringValues }, stringValues.Length, CancellationToken.None);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT id, str FROM {tableName} ORDER BY id\");\n\n                int count = 0;\n                await using var reader = await cmd.ExecuteReaderAsync();\n                reader.ConfigureColumn(1, new ClickHouseColumnSettings(Encoding.UTF8));\n                char[] charBuffer = new char[stringValues.Select(v => v?.Length ?? 0).Max() + 7];\n                byte[] byteBuffer = new byte[32 + 3];\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    Assert.Equal(count, id);\n\n                    var expectedStr = stringValues[id];\n                    var valueAsCharArray = reader.GetFieldValue<char[]>(1, null);\n                    Assert.Equal(expectedStr, valueAsCharArray == null ? null : new string(valueAsCharArray));\n\n                    var valueAsByteArray = reader.GetFieldValue<byte[]>(1, null);\n                    byte[]? expectedByteArray = byteValues[id] == null ? null : new byte[32];\n                    byteValues[id]?.CopyTo((Memory<byte>)expectedByteArray);\n                    Assert.Equal(expectedByteArray, valueAsByteArray);\n\n                    if (expectedStr == null)\n                    {\n                        Assert.True(reader.IsDBNull(1));\n                    }\n                    else\n                    {\n                        var len = (int)reader.GetChars(1, 0, charBuffer, 0, charBuffer.Length);\n                        Assert.Equal(expectedStr.Length, len);\n                        Assert.Equal(expectedStr, new string(((ReadOnlySpan<char>)charBuffer).Slice(0, len)));\n\n                        len = (int)reader.GetBytes(1, 0, byteBuffer, 0, byteBuffer.Length);\n                        Assert.Equal(expectedByteArray!.Length, len);\n                        Assert.Equal(expectedByteArray, byteBuffer.Take(len));\n\n                        len = 0;\n                        while (len < charBuffer.Length - 7)\n                        {\n                            var size = Math.Min(3, charBuffer.Length - len - 7);\n                            var currentLen = (int)reader.GetChars(1, len, charBuffer, len + 7, size);\n                            len += currentLen;\n\n                            if (currentLen < size)\n                                break;\n                        }\n\n                        Assert.Equal(expectedStr.Length, len);\n                        Assert.Equal(expectedStr, new string(((ReadOnlySpan<char>)charBuffer).Slice(7, len)));\n\n                        len = 0;\n                        while (len < byteBuffer.Length - 3)\n                        {\n                            var size = Math.Min(3, byteBuffer.Length - len - 3);\n                            var currentLen = (int)reader.GetBytes(1, len, byteBuffer, len + 3, size);\n                            len += currentLen;\n\n                            if (currentLen < size)\n                                break;\n                        }\n\n                        Assert.Equal(expectedByteArray!.Length, len);\n                        Assert.Equal(expectedByteArray, byteBuffer.Skip(3).Take(len));\n                    }\n\n                    ++count;\n                }\n\n                Assert.Equal(stringValues.Length, count);\n            }\n        }\n\n        [Fact]\n        public async Task ReadStringWithEncoding()\n        {\n            const string str = \"АБВГДЕ\";\n            var encoding = Encoding.GetEncoding(\"windows-1251\");\n\n            await using var connection = await OpenConnectionAsync();\n            await using var cmd = connection.CreateCommand($\"SELECT convertCharset('{str}', 'UTF8', 'windows-1251') AS c\");\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            reader.ConfigureDataReader(new ClickHouseColumnSettings(encoding));\n\n            var success = await reader.ReadAsync();\n            Assert.True(success);\n\n            var error = Assert.Throws<ClickHouseException>(() => reader.ConfigureColumn(\"c\", new ClickHouseColumnSettings(Encoding.UTF8)));\n            Assert.Equal(ClickHouseErrorCodes.DataReaderError, error.ErrorCode);\n\n            var strResult = reader.GetString(0);\n            Assert.Equal(str, strResult);\n\n            success = await reader.ReadAsync();\n            Assert.False(success);\n        }\n\n        [Fact]\n        public async Task ReadStringWithEncodingScalar()\n        {\n            const string str = \"АБВГДЕ\";\n            var encoding = Encoding.GetEncoding(\"windows-1251\");\n\n            await using var connection = await OpenConnectionAsync();\n            await using var cmd = connection.CreateCommand($\"SELECT convertCharset('{str}', 'UTF8', 'windows-1251') AS c\");\n\n            var strResult = await cmd.ExecuteScalarAsync(new ClickHouseColumnSettings(encoding));\n            Assert.Equal(str, strResult);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadStringParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var strings = new[] {null, \"\", \"abcde\", \"fghi\", \"jklm\", \"ab\\tc\", \"'abc'\", \"new\\r\\nline\", \"\\\\\"};\n\n            var byteArray = Encoding.UTF8.GetBytes(strings[4]!);\n            var byteMemory = Encoding.UTF8.GetBytes(strings[5]!).AsMemory();\n            var byteRoMemory = (ReadOnlyMemory<byte>) Encoding.UTF8.GetBytes(strings[6]!).AsMemory();\n            var charMemory = strings[7]!.ToCharArray().AsMemory();\n            var charArray = strings[8]!.ToCharArray();\n            var values = new object?[] {strings[0], strings[1], strings[2], strings[3].AsMemory(), byteArray, byteMemory, byteRoMemory, charMemory, charArray};\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n            await using var cmd = connection.CreateCommand(\"SELECT {val}\");\n            var param = cmd.Parameters.AddWithValue(\"val\", \"some_value\", DbType.String);\n            for (var i = 0; i < strings.Length; i++)\n            {\n                param.Value = values[i];\n                var result = await cmd.ExecuteScalarAsync(CancellationToken.None);\n                if (values[i] == null)\n                    Assert.Equal(result, DBNull.Value);\n                else\n                    Assert.Equal(strings[i], Assert.IsType<string>(result));\n            }\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadStringArrayParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var arr = new[] { \"abc\", \"\\r\\n\\t'\\\\\", \"\\\\\", null, \"\", \"ЮНИКОД\", \"'quoted'\" };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n            await using var cmd = connection.CreateCommand(\"SELECT {val} v, toTypeName(v)\");\n            var param = cmd.Parameters.AddWithValue(\"val\", arr);\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            Assert.True(await reader.ReadAsync());\n\n            var typeName = reader.GetString(1);\n            Assert.Equal(\"Array(Nullable(String))\", typeName);\n\n            var value = reader.GetValue(0);\n            var valueArray = Assert.IsType<string?[]>(value);\n            Assert.Equal(arr.Length, valueArray.Length);\n            Assert.Equal(arr, valueArray);\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadMultidimensionalStringArrayParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var arr = new List<string?[,]>()\n            {\n                new[,] { {\"ab\", \"cd\"}, {\"ef\", null} },\n                new[,] { {null, \"foo\", \"\\\\\"}, {\"bar\\tbaz\", \"new\\r\\nline\", \"rock'n'roll\"} }\n            };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n            await using var cmd = connection.CreateCommand(\"SELECT {val} v, toTypeName(v)\");\n            var param = cmd.Parameters.AddWithValue(\"val\", arr);\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            Assert.True(await reader.ReadAsync());\n\n            var typeName = reader.GetString(1);\n            Assert.Equal(\"Array(Array(Array(Nullable(String))))\", typeName);\n\n            var value = reader.GetValue(0);\n            var valueArray = Assert.IsType<string?[][][]>(value);\n            Assert.Equal(arr.Count, valueArray.Length);\n            for (int i = 0; i < valueArray.Length; i++)\n            {\n                var expected = arr[i];\n                var actualArr2 = valueArray[i];\n\n                var expectedLengthI = expected.GetLength(0);\n                var expectedLengthJ = expected.GetLength(1);\n                Assert.Equal(expectedLengthI, actualArr2.Length);\n                for (int j = 0; j < expectedLengthI; j++)\n                {\n                    var actualArr = actualArr2[j];\n                    Assert.Equal(expectedLengthJ, actualArr.Length);\n\n                    for (int k = 0; k < expectedLengthJ; k++)\n                        Assert.Equal(expected[j, k], actualArr[k]);\n                }\n            }\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task ReadGuidScalar()\n        {\n            var guidValue = new Guid(\"74D47928-2423-4FE2-AD45-82E296BF6058\");\n\n            await using var connection = await OpenConnectionAsync();\n            await using var cmd = connection.CreateCommand($\"SELECT cast('{guidValue:D}' AS UUID)\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultGuid = Assert.IsType<Guid>(result);\n\n            Assert.Equal(guidValue, resultGuid);\n        }\n\n        [Fact]\n        public async Task ReadDecimal128Scalar()\n        {\n            var testData = new[] {decimal.Zero, decimal.One, decimal.MinusOne, decimal.MinValue / 100, decimal.MaxValue / 100, decimal.One / 100, decimal.MinusOne / 100};\n            \n            await using var connection = await OpenConnectionAsync();\n\n            foreach (var testValue in testData)\n            {\n                await using var cmd = connection.CreateCommand($\"SELECT cast('{testValue.ToString(CultureInfo.InvariantCulture)}' AS Decimal128(2))\");\n\n                var result = await cmd.ExecuteScalarAsync();\n                var resultDecimal = Assert.IsType<decimal>(result);\n\n                Assert.Equal(testValue, resultDecimal);\n            }\n\n            await using (var cmd2 = connection.CreateCommand(\"SELECT cast('-108.4815162342' AS Decimal128(35))\"))\n            {\n                var result2 = await cmd2.ExecuteScalarAsync<decimal>();\n                Assert.Equal(-108.4815162342m, result2);\n            }\n\n            await using (var cmd2 = connection.CreateCommand(\"SELECT cast('-999.9999999999999999999999999' AS Decimal128(35))\"))\n            {\n                var result2 = await cmd2.ExecuteScalarAsync<decimal>();\n                Assert.Equal(-999.9999999999999999999999999m, result2);\n            }\n        }\n\n        [Fact]\n        public async Task ReadDecimal64Scalar()\n        {\n            var testData = new[] { decimal.Zero, decimal.One, decimal.MinusOne, 999_999_999_999_999.999m, -999_999_999_999_999.999m, decimal.One / 1000, decimal.MinusOne / 1000 };\n\n            await using var connection = await OpenConnectionAsync();\n\n            foreach (var testValue in testData)\n            {\n                await using var cmd = connection.CreateCommand($\"SELECT cast('{testValue.ToString(CultureInfo.InvariantCulture)}' AS Decimal64(3))\");\n\n                var result = await cmd.ExecuteScalarAsync();\n                var resultDecimal = Assert.IsType<decimal>(result);\n\n                Assert.Equal(testValue, resultDecimal);\n            }\n        }\n\n        [Fact]\n        public async Task ReadDecimal32Scalar()\n        {\n            var testData = new[] { decimal.Zero, decimal.One, decimal.MinusOne, 9.9999999m, -9.9999999m, decimal.One / 100_000_000, decimal.MinusOne / 100_000_000 };\n\n            await using var connection = await OpenConnectionAsync();\n\n            foreach (var testValue in testData)\n            {\n                await using var cmd = connection.CreateCommand($\"SELECT cast('{testValue.ToString(CultureInfo.InvariantCulture)}' AS Decimal32(8))\");\n\n                var result = await cmd.ExecuteScalarAsync();\n                var resultDecimal = Assert.IsType<decimal>(result);\n\n                Assert.Equal(testValue, resultDecimal);\n            }\n        }\n\n        [Fact]\n        public async Task ReadDateTimeScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT cast('2015-04-21 14:59:44' AS DateTime)\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultDateTime = Assert.IsType<DateTimeOffset>(result);\n\n            Assert.Equal(new DateTime(2015, 4, 21, 14, 59, 44), resultDateTime.DateTime);\n        }\n\n        [Fact]\n        public async Task ReadDateTimeWithTimezoneScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            var tzName = TimeZoneHelper.GetTimeZoneId(TimeZoneInfo.Local);\n\n            await using var cmd = connection.CreateCommand($\"SELECT toDateTime('2015-04-21 14:59:44', '{tzName}')\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultDateTime = Assert.IsType<DateTimeOffset>(result);\n\n            Assert.Equal(new DateTime(2015, 4, 21, 14, 59, 44), resultDateTime);\n        }\n\n        [Theory]\n        [InlineData(\"2015-04-21 14:59:44.12345\", 3, 2015, 4, 21, 14, 59, 44, 123)]\n        [InlineData(\"1908-09-10 11:12:13.141516\", 6, 1908, 9, 10, 11, 12, 13, 141.516)]\n        [InlineData(\"2112-01-15 23:01:59.99999\", 1, 2112, 1, 15, 23, 1, 59, 900)]\n        [InlineData(\"1970-01-01\", 3, 1970, 1, 1, 0, 0, 0, 0)]\n        [InlineData(\"1931-03-05 07:09:23\", 0, 1931, 3, 5, 7, 9, 23, 0)]\n        [InlineData(\"1900-01-01\", 3, 1900, 1, 1, 0, 0, 0, 0)] // Min value\n        [InlineData(\"2299-12-31 23:59:59.999999\", 6, 2299, 12, 31, 23, 59, 59, 999.999)] // Max value\n        public async Task ReadDateTime64Scalar(string dateText, int scale, int year, int month, int day, int hour, int minute, int second, double milliseconds)\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            string queryStr = $\"SELECT cast('{dateText}' AS DateTime64({scale}, 'Etc/UTC'))\";\n            await using var cmd = connection.CreateCommand(queryStr);\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultDateTime = Assert.IsType<DateTimeOffset>(result);\n\n            Assert.Equal(new DateTime(year, month, day, hour, minute, second).Add(TimeSpan.FromMilliseconds(milliseconds)), resultDateTime.DateTime);\n        }\n\n        [Fact]\n        public async Task ReadDateTime64WithTimezoneScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            var tzName = TimeZoneHelper.GetTimeZoneId(TimeZoneInfo.Local);\n\n            await using var cmd = connection.CreateCommand($\"SELECT cast('2015-04-21 14:59:44.123456789' AS DateTime64(9,'{tzName}'))\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultDateTime = Assert.IsType<DateTimeOffset>(result);\n\n            Assert.Equal(new DateTime(2015, 4, 21, 14, 59, 44).Add(TimeSpan.FromMilliseconds(123.4567)), resultDateTime);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadDateTime64ParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var connection = await OpenConnectionAsync(parameterMode);\n            var timeZone = connection.GetServerTimeZone();\n            var unixEpochOffset = timeZone.GetUtcOffset(DateTime.UnixEpoch);\n            var minDate = new DateTime(1900, 1, 1);\n            var maxDate = new DateTime(2299, 12, 31, 23, 59, 59, 999);\n\n            var values = new[]\n            {\n                default,\n                DateTime.UnixEpoch.Add(TimeSpan.FromMilliseconds(1111.1111) + unixEpochOffset),\n                new DateTime(1966, 6, 6, 7, 8, 9).Add(TimeSpan.FromMilliseconds(987.654)),\n                new DateTime(1931, 3, 5, 7, 9, 23).Add(TimeSpan.FromMilliseconds(123.45)),\n                new DateTime(1984, 4, 21, 14, 59, 44).Add(TimeSpan.FromMilliseconds(123.4567)),\n                new DateTime(2032, 3, 18, 12, 0, 59).Add(TimeSpan.FromMilliseconds(987.6543)),\n                new DateTime(1970, 1, 1),\n                new DateTime(1970, 1, 1) + unixEpochOffset,\n                minDate + timeZone.GetUtcOffset(minDate),\n                maxDate + timeZone.GetUtcOffset(maxDate)\n            };\n\n            await using var cmd = connection.CreateCommand(\"SELECT {v}\");\n            var parameter = new ClickHouseParameter(\"v\") {ClickHouseDbType = ClickHouseDbType.DateTime64};\n            cmd.Parameters.Add(parameter);\n\n            for (int precision = 0; precision < 10; precision++)\n            {\n                parameter.Precision = (byte) precision;\n                var div = TimeSpan.TicksPerSecond / (long) Math.Pow(10, precision);\n                if (div == 0)\n                    div = -(long) Math.Pow(10, precision) / TimeSpan.TicksPerSecond;\n\n                foreach (var value in values)\n                {\n                    if (value.Year >= maxDate.Year && precision >= 9)\n                    {\n                        // The value is too large for writing\n                        continue;\n                    }\n\n                    parameter.Value = value;\n\n                    var result = await cmd.ExecuteScalarAsync();\n                    var resultDateTime = Assert.IsType<DateTimeOffset>(result);\n\n                    DateTime expectedValue;\n                    if (value == default)\n                        expectedValue = DateTime.UnixEpoch + unixEpochOffset;\n                    else if (div > 0)\n                        expectedValue = new DateTime(value.Ticks / div * div);\n                    else\n                        expectedValue = value;\n\n                    Assert.Equal(expectedValue, resultDateTime.DateTime);\n                }\n            }\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadDateTimeParameterWithTimezoneScalar(ClickHouseParameterMode parameterMode)\n        {\n            var valueShort = new DateTime(2014, 7, 5, 12, 13, 14);\n            var value = valueShort.Add(TimeSpan.FromMilliseconds(123.4567));\n\n            const string targetTzCode = \"Asia/Magadan\";\n            var targetTz = TimeZoneHelper.GetTimeZoneInfo(targetTzCode);\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n            await using var cmd = connection.CreateCommand($\"SELECT toTimeZone({{d}}, '{targetTzCode}')\");\n            var parameter = new ClickHouseParameter(\"d\") {Value = value, TimeZone = TimeZoneHelper.GetTimeZoneInfo(\"Pacific/Niue\"), Precision = 4};\n            cmd.Parameters.Add(parameter);\n            var deltaOffset = targetTz.GetUtcOffset(valueShort) - parameter.TimeZone.GetUtcOffset(valueShort);\n\n            object? resultObj;\n            DateTimeOffset result;\n            foreach (var parameterType in new ClickHouseDbType?[] {null, ClickHouseDbType.DateTime, ClickHouseDbType.DateTimeOffset})\n            {\n                if (parameterType != null)\n                    parameter.ClickHouseDbType = parameterType.Value;\n\n                resultObj = await cmd.ExecuteScalarAsync();\n                result = Assert.IsType<DateTimeOffset>(resultObj);\n                Assert.Equal(valueShort + deltaOffset, result.DateTime);\n                Assert.Equal(targetTz.GetUtcOffset(result), result.Offset);\n            }\n\n            parameter.ClickHouseDbType = ClickHouseDbType.DateTime2;\n            resultObj = await cmd.ExecuteScalarAsync();\n            result = Assert.IsType<DateTimeOffset>(resultObj);\n            Assert.Equal(value + deltaOffset, result.DateTime);\n            Assert.Equal(targetTz.GetUtcOffset(result), result.Offset);\n\n            parameter.ClickHouseDbType = ClickHouseDbType.DateTime64;\n            resultObj = await cmd.ExecuteScalarAsync();\n            result = Assert.IsType<DateTimeOffset>(resultObj);\n            Assert.Equal(valueShort + deltaOffset + TimeSpan.FromMilliseconds(123.4), result.DateTime);\n            Assert.Equal(targetTz.GetUtcOffset(result), result.Offset);\n\n            parameter.ResetDbType();\n            resultObj = await cmd.ExecuteScalarAsync();\n            result = Assert.IsType<DateTimeOffset>(resultObj);\n            deltaOffset = targetTz.GetUtcOffset(valueShort) - connection.GetServerTimeZone().GetUtcOffset(valueShort);\n            Assert.Equal(valueShort + deltaOffset, result.DateTime);\n        }\n\n        [Fact]\n        public async Task ReadFloatScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            var expectedValue = 1234567890.125f;\n            await using var cmd = connection.CreateCommand($\"SELECT CAST('{expectedValue:#.#}' AS Float32)\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultFloat = Assert.IsType<float>(result);\n\n            Assert.Equal(expectedValue, resultFloat);\n        }\n\n        [Fact]\n        public async Task ReadDoubleScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT -123456789109876.125\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultDouble = Assert.IsType<double>(result);\n\n            Assert.Equal(-123456789109876.125, resultDouble);\n        }\n\n        [Fact]\n        public async Task ReadNothingScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT NULL\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            Assert.IsType<DBNull>(result);\n        }\n\n        [Fact]\n        public async Task ReadEmptyArrayScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT []\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            var objResult = Assert.IsType<object[]>(result);\n            Assert.Empty(objResult);\n        }\n\n        [Fact]\n        public async Task ReadByteArrayScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT [4, 8, 15, 16, 23, 42]\");\n\n            var result = await cmd.ExecuteScalarAsync<byte[]>();\n\n            Assert.Equal(new byte[] {4, 8, 15, 16, 23, 42}, result);\n        }\n\n        [Fact]\n        public async Task ReadNullableByteArrayScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT [4, NULL, 8, NULL, 15, NULL, 16, NULL, 23, NULL, 42]\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultArr = Assert.IsType<byte?[]>(result);\n\n            Assert.Equal(new byte?[] { 4, null, 8, null, 15, null, 16, null, 23, null, 42 }, resultArr);\n        }\n\n        [Fact]\n        public async Task ReadArrayOfArraysOfArraysScalar()\n        {\n            const string query = @\"SELECT \n                                        [\n                                            [\n                                                [1],\n                                                [],\n                                                [2, NULL]\n                                            ],\n                                            [\n                                                [3]\n                                            ]\n                                        ]\";\n\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(query);\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultArr = Assert.IsType<byte?[][][]>(result);\n\n            Assert.NotNull(resultArr);\n            Assert.Equal(2, resultArr.Length);\n\n            Assert.NotNull(resultArr[0]);\n            Assert.Equal(3, resultArr[0].Length);\n\n            Assert.Equal(new byte?[] {1}, resultArr[0][0]);\n            Assert.Equal(new byte?[0], resultArr[0][1]);\n            Assert.Equal(new byte?[] {2, null}, resultArr[0][2]);\n\n            Assert.NotNull(resultArr[1]);\n            Assert.Equal(1, resultArr[1].Length);\n\n            Assert.Equal(new byte?[] {3}, resultArr[1][0]);\n        }\n\n        [Fact]\n        public async Task ReadNullableByteArrayAsUInt64ArrayScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT [4, NULL, 8, NULL, 15, NULL, 16, NULL, 23, NULL, 42]\");\n\n            var result = await cmd.ExecuteScalarAsync<ulong?[]>();\n\n            Assert.Equal(new ulong?[] { 4, null, 8, null, 15, null, 16, null, 23, null, 42 }, result);\n        }\n\n        [Fact]\n        public async Task ReadNullableStringArrayScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT ['All', NULL, 'your', NULL, 'base', NULL, 'are', NULL, 'belong', NULL, 'to', NULL, 'us!']\");\n\n            var result = await cmd.ExecuteScalarAsync<string?[]>();\n\n            Assert.Equal(new[] {\"All\", null, \"your\", null, \"base\", null, \"are\", null, \"belong\", null, \"to\", null, \"us!\"}, result);\n        }\n\n        [Fact]\n        public async Task ReadNullableNothingArrayScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT [NULL,NULL,NULL]\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            var resultArr = Assert.IsType<object?[]>(result);\n\n            Assert.Equal(new object[3], resultArr);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadArrayParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {p}\");\n            var expectedResult = new[] {4, 8, 15, 16, 23, 42};\n            var param = cmd.Parameters.AddWithValue(\"p\", expectedResult);\n\n            var result = await cmd.ExecuteScalarAsync();\n            var intResult = Assert.IsType<int[]>(result);\n\n            Assert.Equal(expectedResult, intResult);\n\n            param.IsArray = true;\n            param.DbType = DbType.Decimal;\n\n            result = await cmd.ExecuteScalarAsync();\n            var decResult = Assert.IsType<decimal[]>(result);\n\n            Assert.Equal(expectedResult.Select(v => (decimal) v), decResult);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadArrayOfArraysParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {p}\");\n            var expectedResult = new List<uint[]> {new uint[] {4, 8}, new uint[] {15, 16, 23}, new uint[] {42}};\n            var param = cmd.Parameters.AddWithValue(\"p\", expectedResult);\n\n            var result = await cmd.ExecuteScalarAsync();\n            var intResult = Assert.IsType<uint[][]>(result);\n\n            Assert.Equal(expectedResult.Count, intResult.Length);\n            for (int i = 0; i < expectedResult.Count; i++)\n                Assert.Equal(expectedResult[i], intResult[i]);\n\n            param.ArrayRank = 2;\n            param.DbType = DbType.UInt64;\n            param.IsNullable = true;\n\n            result = await cmd.ExecuteScalarAsync();\n            var decResult = Assert.IsType<ulong?[][]>(result);\n\n            Assert.Equal(expectedResult.Count, decResult.Length);\n            for (int i = 0; i < decResult.Length; i++)\n                Assert.Equal(expectedResult[i].Select(v => (ulong?) v), decResult[i]);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadMultidimensionalArrayParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {p}\");\n            var expectedResult = new List<int?[,]> {new[,] {{(int?) 4, 8}, {15, 16}, {23, 42}}, new[,] {{1}, {(int?) null}, {3}}, new[,] {{(int?) -4, -8, -15}, {-16, -23, -42}}};\n            var param = cmd.Parameters.AddWithValue(\"p\", expectedResult);\n\n            var result = await cmd.ExecuteScalarAsync();\n            var intResult = Assert.IsType<int?[][][]>(result);\n\n            Assert.Equal(expectedResult.Count, intResult.Length);\n            for (int i = 0; i < expectedResult.Count; i++)\n            {\n                var expectedLengthI = expectedResult[i].GetLength(0);\n                var expectedLengthJ = expectedResult[i].GetLength(1);\n                Assert.Equal(expectedLengthI, intResult[i].Length);\n                for (int j = 0; j < expectedLengthI; j++)\n                {\n                    Assert.Equal(expectedLengthJ, intResult[i][j].Length);\n                    for (int k = 0; k < expectedLengthJ; k++)\n                        Assert.Equal(expectedResult[i][j, k], intResult[i][j][k]);\n                }\n            }\n\n            param.ArrayRank = 3;\n            param.DbType = DbType.Decimal;\n\n            result = await cmd.ExecuteScalarAsync();\n            var decResult = Assert.IsType<decimal?[][][]>(result);\n\n            Assert.Equal(expectedResult.Count, decResult.Length);\n            for (int i = 0; i < expectedResult.Count; i++)\n            {\n                var expectedLengthI = expectedResult[i].GetLength(0);\n                var expectedLengthJ = expectedResult[i].GetLength(1);\n                Assert.Equal(expectedLengthI, decResult[i].Length);\n                for (int j = 0; j < expectedLengthI; j++)\n                {\n                    Assert.Equal(expectedLengthJ, decResult[i][j].Length);\n                    for (int k = 0; k < expectedLengthJ; k++)\n                        Assert.Equal(expectedResult[i][j, k], decResult[i][j][k]);\n                }\n            }\n        }\n\n        [Fact]\n        public async Task ReadEnumColumn()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(@\"SELECT CAST(T.col AS Enum(''=0, '\\\\e\\s\\c\\\\a\\p\\\\e'=1, '\\'val\\''=2, '\\r\\n\\t\\d\\\\\\r\\n'=3,'\\a\\b\\c\\d\\e\\f\\g\\h\\i\\j\\k\\l\\m\\n\\o\\p\\q\\r\\s\\t\\u\\v\\w\\x20\\y\\z'=4)) AS enumVal, toString(enumVal) AS strVal\n    FROM (SELECT 0 AS col UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) AS T\");\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n            var columnType = reader.GetFieldTypeInfo(0);\n            var typeNames = new Dictionary<int, string>();\n            for(int i=0; i<columnType.TypeArgumentsCount; i++)\n            {\n                var obj = columnType.GetTypeArgument(i);\n                var pair = Assert.IsType<KeyValuePair<string, sbyte>>(obj);\n                typeNames.Add(pair.Value, pair.Key);\n            }\n\n            int bitmap = 0;\n            while (await reader.ReadAsync())\n            {\n                var value = reader.GetFieldValue<int>(0);\n                var defaultValue = reader.GetValue(0);\n                var strValue = Assert.IsType<string>(defaultValue);\n                var expectedStrValue = reader.GetFieldValue<string>(1);\n                \n                Assert.Equal(expectedStrValue, strValue);\n\n                switch (value)\n                {\n                    case 0:\n                        Assert.Equal(string.Empty, strValue);\n                        break;\n                    case 1:\n                        Assert.Equal(@\"\\e\\s\\c\\a\\p\\e\", strValue);\n                        break;\n                    case 2:\n                        Assert.Equal(\"'val'\", strValue);\n                        break;\n                    case 3:\n                        Assert.Equal(\"\\r\\n\\t\\\\d\\\\\\r\\n\", strValue);\n                        break;\n                    case 4:\n                        Assert.Equal(\"\\a\\b\\\\c\\\\d\\u001b\\f\\\\g\\\\h\\\\i\\\\j\\\\k\\\\l\\\\m\\n\\\\o\\\\p\\\\q\\r\\\\s\\t\\\\u\\v\\\\w\\x20\\\\y\\\\z\", strValue);\n                        break;\n                    default:\n                        Assert.True(false, $\"Unexpected value: {value}.\");\n                        break;\n                }\n\n                Assert.Equal(strValue, typeNames[value]);\n\n                bitmap ^= 1 << value;\n            }\n\n            Assert.Equal(31, bitmap);\n        }\n\n        [Fact]\n        public async Task ReadEnumScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT CAST(42 AS Enum('' = 0, 'b' = -129, 'Hello, world! :)' = 42))\");\n\n            var result = await cmd.ExecuteScalarAsync();\n            Assert.Equal(\"Hello, world! :)\", result);\n        }\n\n        [Fact]\n        public async Task ReadClrEnumScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand(\"SELECT CAST(42 AS Enum('' = 0, 'b' = -129, 'Hello, world! :)' = 42))\");\n\n            var settings = new ClickHouseColumnSettings(new ClickHouseEnumConverter<TestEnum>());\n            var result = await cmd.ExecuteScalarAsync(settings);\n            Assert.Equal(TestEnum.Value1, result);\n        }\n\n        [Fact]\n        public async Task ReadInt32ArrayColumn()\n        {\n            int?[]?[] expected = new int?[10][];\n\n            var queryBuilder = new StringBuilder(\"SELECT T.* FROM (\").AppendLine();\n            for (int i = 0, k = 0; i < expected.Length; i++)\n            {\n                if (i > 0)\n                    queryBuilder.AppendLine().Append(\"UNION ALL \");\n\n                queryBuilder.Append(\"SELECT \").Append(i).Append(\" AS num, [\");\n                var expectedArray = expected[i] = new int?[i + 1];\n                for (int j = 0; j < expectedArray.Length; j++, k++)\n                {\n                    if (j > 0)\n                        queryBuilder.Append(\", \");\n\n                    if ((k % 3 == 0) == (k % 5 == 0))\n                    {\n                        queryBuilder.Append(\"CAST(\").Append(k).Append(\" AS Nullable(Int32))\");\n                        expectedArray[j] = k;\n                    }\n                    else\n                    {\n                        queryBuilder.Append(\"CAST(NULL AS Nullable(Int32))\");\n                    }\n                }\n\n                queryBuilder.Append(\"] AS arr\");\n            }\n\n            var queryString = queryBuilder.AppendLine(\") AS T\").Append(\"ORDER BY T.num DESC\").ToString();\n\n            await using var connection = await OpenConnectionAsync();\n\n            var cmd = connection.CreateCommand(queryString);\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            while (await reader.ReadAsync())\n            {\n                var num = reader.GetInt32(0);\n\n                var expectedArray = expected[num];\n                Assert.NotNull(expectedArray);\n\n                var value = reader.GetValue(1);\n                var array = Assert.IsType<int?[]>(value);\n\n                Assert.Equal(expectedArray, array);\n\n                expected[num] = null;\n            }\n\n            Assert.All(expected, Assert.Null);\n        }\n\n        [Fact]\n        public async Task SkipInt32ArrayColumn()\n        {\n            int?[]?[] expected = new int?[10][];\n\n            var queryBuilder = new StringBuilder(\"SELECT T.* FROM (\").AppendLine();\n            for (int i = 0, k = 0; i < expected.Length; i++)\n            {\n                if (i > 0)\n                    queryBuilder.AppendLine().Append(\"UNION ALL \");\n\n                queryBuilder.Append(\"SELECT \").Append(i).Append(\" AS num, [\");\n                var expectedArray = expected[i] = new int?[i + 1];\n                for (int j = 0; j < expectedArray.Length; j++, k++)\n                {\n                    if (j > 0)\n                        queryBuilder.Append(\", \");\n\n                    if ((k % 3 == 0) == (k % 5 == 0))\n                    {\n                        queryBuilder.Append(\"CAST(\").Append(k).Append(\" AS Nullable(Int32))\");\n                        expectedArray[j] = k;\n                    }\n                    else\n                    {\n                        queryBuilder.Append(\"CAST(NULL AS Nullable(Int32))\");\n                    }\n                }\n\n                queryBuilder.Append(\"] AS arr\");\n            }\n\n            var queryString = queryBuilder.AppendLine(\") AS T\").Append(\"ORDER BY T.num DESC\").ToString();\n\n            await using var connection = await OpenConnectionAsync();\n\n            var cmd = connection.CreateCommand(queryString);\n            await cmd.ExecuteNonQueryAsync();\n        }\n\n        [Fact]\n        public async Task ReadTuplesWithDifferentLength()\n        {\n            var tupleSb = new StringBuilder(\"tuple(\");\n            var querySb = new StringBuilder(\"SELECT\").AppendLine();\n\n            for (int i = 0; i < 15; i++)\n            {\n                if (i > 0)\n                {\n                    tupleSb.Append(\", \");\n                    querySb.AppendLine(\", \");\n                }\n\n                tupleSb.Append(\"CAST(\").Append(i + 1).Append(\" AS Int32)\");\n                querySb.Append(tupleSb, 0, tupleSb.Length).Append($\") AS t{i}\");\n            }\n\n            var queryString = querySb.ToString();\n\n            await using var connection = await OpenConnectionAsync();\n\n            var cmd = connection.CreateCommand(queryString);\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            var success = await reader.ReadAsync();\n            Assert.True(success);\n\n            var expected = new object[]\n            {\n                Tuple.Create(1),\n                Tuple.Create(1, 2),\n                Tuple.Create(1, 2, 3),\n                Tuple.Create(1, 2, 3, 4),\n                Tuple.Create(1, 2, 3, 4, 5),\n                Tuple.Create(1, 2, 3, 4, 5, 6),\n                Tuple.Create(1, 2, 3, 4, 5, 6, 7),\n                Tuple.Create(1, 2, 3, 4, 5, 6, 7, 8),\n                new Tuple<int, int, int, int, int, int, int, Tuple<int, int>>(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9)),\n                new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>>(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9, 10)),\n                new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int, int>>(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9, 10, 11)),\n                new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int, int, int>>(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9, 10, 11, 12)),\n                new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int, int, int, int>>(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9, 10, 11, 12, 13)),\n                new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int, int, int, int, int>>(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9, 10, 11, 12, 13, 14)),\n                new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int, int, int, int, int, Tuple<int>>>(1, 2, 3, 4, 5, 6, 7, Tuple.Create(8, 9, 10, 11, 12, 13, 14, 15))\n            };\n\n            for (int i = 0; i < 15; i++)\n            {\n                var columnType = reader.GetFieldType(i);\n                Assert.Equal(expected[i].GetType(), columnType);\n\n                var columnValue = reader.GetValue(i);\n\n                if (i == 12)\n                {\n                    var reinterpretedValue = reader.GetFieldValue<Tuple<int?, long, long?, int, int?, int, int?, Tuple<long, long?, int, int?, int, int?>>>(i);\n                    var expectedValue = new Tuple<int?, long, long?, int, int?, int, int?, Tuple<long, long?, int, int?, int, int?>>(\n                        1,\n                        2,\n                        3,\n                        4,\n                        5,\n                        6,\n                        7,\n                        Tuple.Create((long) 8, (long?) 9, 10, (int?) 11, 12, (int?) 13));\n\n                    Assert.Equal(expectedValue, reinterpretedValue);\n                }\n\n                Assert.Equal(expected[i].GetType(), columnValue.GetType());\n                Assert.Equal(columnValue, expected[i]);\n            }\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task ReadValueTuplesWithDifferentLength()\n        {\n            var tupleSb = new StringBuilder(\"tuple(\");\n            var querySb = new StringBuilder(\"SELECT\").AppendLine();\n\n            for (int i = 0; i < 15; i++)\n            {\n                if (i > 0)\n                {\n                    tupleSb.Append(\", \");\n                    querySb.AppendLine(\", \");\n                }\n\n                tupleSb.Append(\"CAST(\").Append(i + 1).Append(\" AS Int32)\");\n                querySb.Append(tupleSb, 0, tupleSb.Length).Append($\") AS t{i}\");\n            }\n\n            var queryString = querySb.ToString();\n\n            await using var connection = await OpenConnectionAsync();\n\n            var cmd = connection.CreateCommand(queryString);\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            var success = await reader.ReadAsync();\n            Assert.True(success);\n            Assert.Equal(15, reader.FieldCount);\n\n            for (int i = 0; i < reader.FieldCount; i++)\n            {\n                switch (i)\n                {\n                    case 0:\n                        AssertEqual(reader, i, new ValueTuple<int>(1));\n                        break;\n                    case 1:\n                        AssertEqual(reader, i, (1, (int?) 2));\n                        break;\n                    case 2:\n                        AssertEqual(reader, i, (1, 2, 3));\n                        break;\n                    case 3:\n                        AssertEqual(reader, i, (1, 2, 3, 4));\n                        break;\n                    case 4:\n                        AssertEqual(reader, i, (1, 2, 3, 4, 5));\n                        break;\n                    case 5:\n                        AssertEqual(reader, i, (1, 2, 3, 4, 5, 6));\n                        break;\n                    case 6:\n                        AssertEqual(reader, i, (1, 2, 3, 4, 5, 6, 7));\n                        break;\n                    case 7:\n                        AssertEqual(reader, i, (1, 2, 3, 4, 5, 6, 7, (long) 8));\n                        break;\n                    case 8:\n                        AssertEqual(reader, i, (1, 2, 3, 4, (int?) 5, 6, 7, (long?) 8, 9));\n                        break;\n                    case 9:\n                        AssertEqual(reader, i, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10));\n                        break;\n                    case 10:\n                        AssertEqual(reader, i, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));\n                        break;\n                    case 11:\n                        AssertEqual(reader, i, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));\n                        break;\n                    case 12:\n                        AssertEqual<(int?, long, long?, int, int?, int, int?, long, long?, int, int?, int, int?)>(reader, i, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13));\n                        break;\n                    case 13:\n                        AssertEqual(reader, i, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14));\n                        break;\n                    case 14:\n                        AssertEqual(reader, i, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, (long?) 15));\n                        break;\n                    default:\n                        Assert.True(i >= 0 && i < 15, \"Too many columns.\");\n                        break;\n                }\n            }\n\n            Assert.False(await reader.ReadAsync());\n\n            static void AssertEqual<T>(DbDataReader reader, int ordinal, T expectedValue)\n            {\n                var value = reader.GetFieldValue<T>(ordinal);\n                Assert.Equal(expectedValue, value);\n            }\n        }\n\n        [Fact]\n        public async Task ReadTupleColumn()\n        {\n            const string query = @\"SELECT T.tval\n    FROM (\n        SELECT tuple(cast(1 as Decimal(13, 4)), cast('one' as Nullable(String)), cast('1999-09-09 09:09:09' as Nullable(DateTime))) AS tval\n        UNION ALL SELECT tuple(2, 'two', cast('2019-12-11 16:55:54' as DateTime('Asia/Yekaterinburg')))\n        UNION ALL SELECT tuple(3, null, cast('2007-01-11 05:32:48' as DateTime))) T\n    ORDER BY T.tval.3\";\n\n            await using var connection = await OpenConnectionAsync();\n\n            var cmd = connection.CreateCommand(query);\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            int count = 0;\n            while (await reader.ReadAsync())\n            {\n                var value = reader.GetFieldValue<Tuple<decimal, string?, DateTime?>>(0);\n                DateTime dt;\n                switch (count)\n                {\n                    case 0:\n                        Assert.Equal(1, value.Item1);\n                        Assert.Equal(\"one\", value.Item2);\n                        dt = new DateTime(1999, 9, 9, 9, 9, 9);\n                        Assert.Equal(dt, value.Item3);\n                        break;\n\n                    case 1:\n                        Assert.Equal(3, value.Item1);\n                        Assert.Null(value.Item2);\n                        dt = new DateTime(2007, 1, 11, 5, 32, 48);\n                        Assert.Equal(dt, value.Item3);\n                        break;\n\n                    case 2:\n                        Assert.Equal(2, value.Item1);\n                        Assert.Equal(\"two\", value.Item2);\n                        var tz = TimeZoneHelper.GetTimeZoneInfo(\"Asia/Yekaterinburg\");\n                        dt = TimeZoneInfo.ConvertTime(new DateTime(2019, 12, 11, 16, 55, 54), tz, connection.GetServerTimeZone());\n                        Assert.Equal(dt, value.Item3);\n                        break;\n\n                    default:\n                        Assert.False(true, \"Too many rows.\");\n                        break;\n                }\n\n                ++count;\n            }\n\n            Assert.Equal(3, count);\n        }\n\n        [Fact]\n        public async Task ReadValueTupleColumn()\n        {\n            const string query = @\"SELECT T.tval\n    FROM (\n        SELECT tuple(cast(1 as Decimal(13, 4)), cast('one' as Nullable(String)), cast('1999-09-09 09:09:09' as Nullable(DateTime))) AS tval\n        UNION ALL SELECT tuple(2, 'two', cast('2019-12-11 16:55:54' as DateTime('Asia/Yekaterinburg')))\n        UNION ALL SELECT tuple(3, null, cast('2007-01-11 05:32:48' as DateTime))) T\n    ORDER BY T.tval.3\";\n\n            await using var connection = await OpenConnectionAsync();\n\n            var cmd = connection.CreateCommand(query);\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            int count = 0;\n            while (await reader.ReadAsync())\n            {\n                var value = reader.GetFieldValue<(decimal number, string? str, DateTime? date)>(0);\n                DateTime dt;\n                switch (count)\n                {\n                    case 0:\n                        Assert.Equal(1, value.number);\n                        Assert.Equal(\"one\", value.str);\n                        dt = new DateTime(1999, 9, 9, 9, 9, 9);\n                        Assert.Equal(dt, value.date);\n                        break;\n\n                    case 1:\n                        Assert.Equal(3, value.number);\n                        Assert.Null(value.str);\n                        dt = new DateTime(2007, 1, 11, 5, 32, 48);\n                        Assert.Equal(dt, value.date);\n                        break;\n\n                    case 2:\n                        Assert.Equal(2, value.number);\n                        Assert.Equal(\"two\", value.str);\n                        dt = new DateTime(2019, 12, 11, 16, 55, 54);\n                        dt = TimeZoneInfo.ConvertTime(dt, TimeZoneHelper.GetTimeZoneInfo(\"Asia/Yekaterinburg\"), connection.GetServerTimeZone());\n                        Assert.Equal(dt, value.date);\n                        break;\n\n                    default:\n                        Assert.False(true, \"Too many rows.\");\n                        break;\n                }\n\n                ++count;\n            }\n\n            Assert.Equal(3, count);\n        }\n\n        [Fact]\n        public async Task ReadNamedTupleScalar()\n        {\n            await using var connection = await OpenConnectionAsync();\n            var cmd = connection.CreateCommand(\"SELECT CAST(('hello', 1, -1) AS Tuple(name String, id UInt32, `e \\\\`e\\\\` e` Int32))\");\n            var result = await cmd.ExecuteScalarAsync<(string firstItem, uint secondItem, int thirdItem)>();\n            Assert.Equal((\"hello\", 1u, -1), result);\n        }\n\n        [Fact]\n        public async Task SkipTupleColumn()\n        {\n            const string query = @\"SELECT T.tval\n    FROM (\n        SELECT tuple(cast(1 as Decimal(13, 4)), cast('one' as Nullable(String)), cast('1999-09-09 09:09:09' as Nullable(DateTime))) AS tval\n        UNION ALL SELECT tuple(2, 'two', cast('2019-12-11 16:55:54' as DateTime('Asia/Yekaterinburg')))\n        UNION ALL SELECT tuple(3, null, cast('2007-01-11 05:32:48' as DateTime))) T\n    ORDER BY T.tval.3\";\n\n            await using var connection = await OpenConnectionAsync();\n\n            var cmd = connection.CreateCommand(query);\n            await cmd.ExecuteNonQueryAsync();\n        }\n\n        [Fact]\n        public async Task ReadIpV4Column()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS ip4_test\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand(\"CREATE TABLE ip4_test(val IPv4, strVal String) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"INSERT INTO ip4_test(val, strVal) VALUES ('116.253.40.133','116.253.40.133')('10.0.151.56','10.0.151.56')('192.0.121.234','192.0.121.234')\";\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"SELECT val, strVal FROM ip4_test\";\n                int count = 0;\n                await using (var reader = cmd.ExecuteReader())\n                {\n                    while (await reader.ReadAsync())\n                    {\n                        var ipAddr = reader.GetFieldValue<IPAddress>(0);\n                        var ipAddrStr = reader.GetFieldValue<string>(1);\n                        var expectedIpAddr = IPAddress.Parse(ipAddrStr);\n\n                        Assert.Equal(expectedIpAddr, ipAddr);\n                        ++count;\n                    }\n                }\n\n                Assert.Equal(3, count);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS ip4_test\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Fact]\n        public async Task ReadIpV6Column()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS ip6_test\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand(\"CREATE TABLE ip6_test(val IPv6, strVal String) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"INSERT INTO ip6_test(val, strVal) VALUES ('2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d','2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d')('2a02:aa08:e000:3100::2','2a02:aa08:e000:3100::2')('::ffff:192.0.121.234','::ffff:192.0.121.234')\";\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"SELECT val, strVal FROM ip6_test\";\n                int count = 0;\n                await using (var reader = cmd.ExecuteReader())\n                {\n                    while (await reader.ReadAsync())\n                    {\n                        var ipAddr = reader.GetFieldValue<IPAddress>(0);\n                        var ipAddrStr = reader.GetFieldValue<string>(1);\n                        var expectedIpAddr = IPAddress.Parse(ipAddrStr);\n\n                        Assert.Equal(expectedIpAddr, ipAddr);\n                        ++count;\n                    }\n                }\n\n                Assert.Equal(3, count);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS ip6_test\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Fact]\n        public async Task ReadLowCardinalityColumn()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS low_cardinality_test\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand(\"CREATE TABLE low_cardinality_test(id Int32, str LowCardinality(String)) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"SELECT * FROM low_cardinality_test\";\n                await using (var reader = cmd.ExecuteReader())\n                {\n                    Assert.False(await reader.ReadAsync());\n                }\n\n                cmd.CommandText = \"INSERT INTO low_cardinality_test(id, str) VALUES (1,'foo')(2,'bar')(4,'bar')(6,'bar')(3,'foo')(7,'foo')(8,'bar')(5,'foobar')\";\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"SELECT id, str FROM low_cardinality_test\";\n                int count = 0;\n                await using (var reader = cmd.ExecuteReader())\n                {\n                    while (await reader.ReadAsync())\n                    {\n                        var id = reader.GetInt32(0);\n                        var str = reader.GetString(1);\n\n                        var expected = id == 5 ? \"foobar\" : id % 2 == 1 ? \"foo\" : \"bar\";\n                        Assert.Equal(expected, str);\n                        ++count;\n                    }\n                }\n\n                Assert.Equal(8, count);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS low_cardinality_test\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Fact]\n        public async Task ReadNullableLowCardinalityColumn()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS low_cardinality_null_test\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand(\"CREATE TABLE low_cardinality_null_test(id Int32, str LowCardinality(Nullable(String))) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"SELECT * FROM low_cardinality_null_test\";\n                await using (var reader = cmd.ExecuteReader())\n                {\n                    Assert.False(await reader.ReadAsync());\n                }\n\n                cmd.CommandText = \"INSERT INTO low_cardinality_null_test(id, str) SELECT number, number%50 == 0 ? NULL : toString(number%200) FROM system.numbers LIMIT 30000\";\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"INSERT INTO low_cardinality_null_test(id, str) SELECT number, number%50 == 0 ? NULL : toString(number%200) FROM system.numbers WHERE number>=30000 LIMIT 30000\";\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"INSERT INTO low_cardinality_null_test(id, str) SELECT number, number%50 == 0 ? NULL : toString(number%400) FROM system.numbers WHERE number>=60000 LIMIT 30000\";\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"SELECT id, str FROM low_cardinality_null_test\";\n                int count = 0;\n                await using (var reader = cmd.ExecuteReader())\n                {\n                    while (await reader.ReadAsync())\n                    {\n                        var id = reader.GetInt32(0);\n                        var str = reader.GetString(1, null);\n\n                        if (id % 50 == 0)\n                            Assert.Null(str);\n                        else if (id < 60000)\n                            Assert.Equal((id % 200).ToString(), str);\n                        else\n                            Assert.Equal((id % 400).ToString(), str);\n\n                        ++count;\n                    }\n                }\n\n                Assert.Equal(90000, count);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS low_cardinality_null_test\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Fact]\n        public async Task ReadNullableStringLowCardinalityColumnAsArray()\n        {\n            await WithTemporaryTable(\"low_cardinality_as_array\", \"id Int32, str LowCardinality(Nullable(String))\", Test);\n\n            async Task Test(ClickHouseConnection connection, string tableName)\n            {\n                var stringValues = new string?[] { null, string.Empty, \"фываasdf\", \"abcdef\", \"ghijkl\", \"null\", \"пролджэzcvgb\", \"ячсмить\" };\n                var byteValues = stringValues.Select(v => v == null ? null : Encoding.UTF8.GetBytes(v)).ToArray();\n\n                const int rowCount = 150;\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id, str) VALUES\", CancellationToken.None))\n                {\n                    writer.ConfigureColumn(\"str\", new ClickHouseColumnSettings(Encoding.UTF8));\n                    await writer.WriteTableAsync(new object[] { Enumerable.Range(0, rowCount), Enumerable.Range(0, rowCount).Select(i => stringValues[i % stringValues.Length]) }, rowCount, CancellationToken.None);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT id, str FROM {tableName} ORDER BY id\");\n\n                int count = 0;\n                await using var reader = await cmd.ExecuteReaderAsync();\n                reader.ConfigureColumn(1, new ClickHouseColumnSettings(Encoding.UTF8));\n                char[] charBuffer = new char[stringValues.Select(v => v?.Length ?? 0).Max() + 7];\n                byte[] byteBuffer = new byte[byteValues.Select(v => v?.Length ?? 0).Max() + 3];\n                while(await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    Assert.Equal(count, id);\n\n                    var expectedStr = stringValues[id % stringValues.Length];\n                    var valueAsCharArray = reader.GetFieldValue<char[]>(1, null);\n                    Assert.Equal(expectedStr, valueAsCharArray == null ? null : new string(valueAsCharArray));\n\n                    var valueAsByteArray = reader.GetFieldValue<byte[]>(1, null);\n                    var expectedByteArray = byteValues[id % byteValues.Length];\n                    Assert.Equal(expectedByteArray, valueAsByteArray);\n\n                    if (expectedStr == null)\n                    {\n                        Assert.True(reader.IsDBNull(1));\n                    }\n                    else \n                    {\n                        var len = (int)reader.GetChars(1, 0, charBuffer, 0, charBuffer.Length);\n                        Assert.Equal(expectedStr.Length, len);\n                        Assert.Equal(expectedStr, new string(((ReadOnlySpan<char>)charBuffer).Slice(0, len)));\n\n                        len = (int)reader.GetBytes(1, 0, byteBuffer, 0, byteBuffer.Length);\n                        Assert.Equal(expectedByteArray!.Length, len);\n                        Assert.Equal(expectedByteArray, byteBuffer.Take(len));\n\n                        len = 0;\n                        while (len < charBuffer.Length - 7)\n                        {\n                            var size = Math.Min(3, charBuffer.Length - len - 7);\n                            var currentLen = (int)reader.GetChars(1, len, charBuffer, len + 7, size);\n                            len += currentLen;\n\n                            if (currentLen < size)\n                                break;\n                        }\n\n                        Assert.Equal(expectedStr.Length, len);\n                        Assert.Equal(expectedStr, new string(((ReadOnlySpan<char>)charBuffer).Slice(7, len)));\n\n                        len = 0;\n                        while (len < byteBuffer.Length - 3)\n                        {\n                            var size = Math.Min(3, byteBuffer.Length - len - 3);\n                            var currentLen = (int)reader.GetBytes(1, len, byteBuffer, len + 3, size);\n                            len += currentLen;\n\n                            if (currentLen < size)\n                                break;\n                        }\n\n                        Assert.Equal(expectedByteArray!.Length, len);\n                        Assert.Equal(expectedByteArray, byteBuffer.Skip(3).Take(len));\n                    }\n\n                    ++count;\n                }\n\n                Assert.Equal(rowCount, count);\n            }\n        }\n\n        [Fact]\n        public async Task ReadStringLowCardinalityColumnAsArray()\n        {\n            await WithTemporaryTable(\"low_cardinality_not_null_as_array\", \"id Int32, str LowCardinality(String)\", Test);\n\n            async Task Test(ClickHouseConnection connection, string tableName)\n            {\n                var stringValues = new string[] { string.Empty, \"фываasdf\", \"abcdef\", \"ghijkl\", \"null\", \"пролджэzcvgb\", \"ячсмить\" };\n                var byteValues = stringValues.Select(v => Encoding.UTF8.GetBytes(v)).ToArray();\n\n                const int rowCount = 150;\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id, str) VALUES\", CancellationToken.None))\n                {\n                    writer.ConfigureColumn(\"str\", new ClickHouseColumnSettings(Encoding.UTF8));\n                    await writer.WriteTableAsync(new object[] { Enumerable.Range(0, rowCount), Enumerable.Range(0, rowCount).Select(i => stringValues[i % stringValues.Length]) }, rowCount, CancellationToken.None);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT id, str FROM {tableName} ORDER BY id\");\n\n                int count = 0;\n                await using var reader = await cmd.ExecuteReaderAsync();\n                reader.ConfigureColumn(1, new ClickHouseColumnSettings(Encoding.UTF8));\n                char[] charBuffer = new char[stringValues.Select(v => v.Length).Max() + 7];\n                byte[] byteBuffer = new byte[byteValues.Select(v => v.Length).Max() + 3];\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    Assert.Equal(count, id);\n\n                    var expectedStr = stringValues[id % stringValues.Length];\n                    var valueAsCharArray = reader.GetFieldValue<char[]>(1);\n                    Assert.Equal(expectedStr, new string(valueAsCharArray));\n\n                    var valueAsByteArray = reader.GetFieldValue<byte[]>(1);\n                    var expectedByteArray = byteValues[id % byteValues.Length];\n                    Assert.Equal(expectedByteArray, valueAsByteArray);\n\n                    var len = (int)reader.GetChars(1, 0, charBuffer, 0, charBuffer.Length);\n                    Assert.Equal(expectedStr.Length, len);\n                    Assert.Equal(expectedStr, new string(((ReadOnlySpan<char>)charBuffer).Slice(0, len)));\n\n                    len = (int)reader.GetBytes(1, 0, byteBuffer, 0, byteBuffer.Length);\n                    Assert.Equal(expectedByteArray!.Length, len);\n                    Assert.Equal(expectedByteArray, byteBuffer.Take(len));\n\n                    len = 0;\n                    while (len < charBuffer.Length - 7)\n                    {\n                        var size = Math.Min(3, charBuffer.Length - len - 7);\n                        var currentLen = (int)reader.GetChars(1, len, charBuffer, len + 7, size);\n                        len += currentLen;\n\n                        if (currentLen < size)\n                            break;\n                    }\n\n                    Assert.Equal(expectedStr.Length, len);\n                    Assert.Equal(expectedStr, new string(((ReadOnlySpan<char>)charBuffer).Slice(7, len)));\n\n                    len = 0;\n                    while (len < byteBuffer.Length - 3)\n                    {\n                        var size = Math.Min(3, byteBuffer.Length - len - 3);\n                        var currentLen = (int)reader.GetBytes(1, len, byteBuffer, len + 3, size);\n                        len += currentLen;\n\n                        if (currentLen < size)\n                            break;\n                    }\n\n                    Assert.Equal(expectedByteArray!.Length, len);\n                    Assert.Equal(expectedByteArray, byteBuffer.Skip(3).Take(len));\n\n                    ++count;\n                }\n\n                Assert.Equal(rowCount, count);\n            }\n        }\n\n        [Fact]\n        public async Task SkipLowCardinalityColumn()\n        {\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS low_cardinality_skip_test\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd = connection.CreateCommand(\"CREATE TABLE low_cardinality_skip_test(id Int32, str LowCardinality(Nullable(String))) ENGINE=Memory\");\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"INSERT INTO low_cardinality_skip_test(id, str) SELECT number, number%50 == 0 ? NULL : toString(number%200) FROM system.numbers LIMIT 10000\";\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"INSERT INTO low_cardinality_skip_test(id, str) SELECT number, number%50 == 0 ? NULL : toString(number%200) FROM system.numbers WHERE number>=10000 LIMIT 10000\";\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = \"SELECT id, str FROM low_cardinality_skip_test\";\n                int count = 0;\n                await using (var reader = cmd.ExecuteReader())\n                {\n                    while (await reader.ReadAsync())\n                    {\n                        var id = reader.GetInt32(0);\n                        var str = reader.GetString(1, null);\n\n                        if (id % 50 == 0)\n                            Assert.Null(str);\n                        else\n                            Assert.Equal((id % 200).ToString(), str);\n\n                        if (++count == 100)\n                            break;\n                    }\n                }\n\n                Assert.Equal(100, count);\n\n                cmd.CommandText = \"SELECT count(*) FROM low_cardinality_skip_test\";\n                count = (int) await cmd.ExecuteScalarAsync<ulong>();\n                Assert.Equal(20000, count);\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                var cmd = connection.CreateCommand(\"DROP TABLE IF EXISTS low_cardinality_skip_test\");\n                await cmd.ExecuteNonQueryAsync();\n            }\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadFixedStringParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var values = new[] {string.Empty, \"0\", \"12345678\", \"abcdefg\", \"1234\", \"abcd\", \"абвг\"};\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}\");\n            var param = new ClickHouseParameter(\"param\") {DbType = DbType.StringFixedLength, Size = 8};\n            cmd.Parameters.Add(param);\n            \n            foreach (var testValue in values)\n            {\n                param.Value = testValue;\n\n                var value = await cmd.ExecuteScalarAsync<byte[]>();\n                var len = value.Length - value.Reverse().TakeWhile(b => b == 0).Count();\n                var strValue = Encoding.UTF8.GetString(value, 0, len);\n                Assert.Equal(testValue, strValue);\n            }\n\n            param.Value = \"123456789\";\n            var exception = await Assert.ThrowsAnyAsync<ClickHouseException>(() => cmd.ExecuteScalarAsync<byte[]>());\n            Assert.Equal(ClickHouseErrorCodes.InvalidQueryParameterConfiguration, exception.ErrorCode);\n\n            param.Value = \"абвг0\";\n            exception = await Assert.ThrowsAnyAsync<ClickHouseException>(() => cmd.ExecuteScalarAsync<byte[]>());\n            Assert.Equal(ClickHouseErrorCodes.InvalidQueryParameterConfiguration, exception.ErrorCode);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadRandomFixedStringParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            Memory<byte> parameterValue = new byte[11];\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}\");\n            var param = new ClickHouseParameter(\"param\") { DbType = DbType.StringFixedLength, Size = parameterValue.Length, Value = parameterValue };\n            cmd.Parameters.Add(param);\n\n            var random = new Random();\n            for (int i = 0; i < 512;)\n            {\n                if (i <= 256)\n                {\n                    for (int j = 0; j < parameterValue.Length; j++)\n                        parameterValue.Span[j] = (byte)(i++ % 256);\n                }\n                else\n                {\n                    for (int j = 0; j < parameterValue.Length; i++, j++)\n                        parameterValue.Span[j] = (byte)random.Next(257);\n                }\n\n                var value = await cmd.ExecuteScalarAsync<byte[]>();\n                Assert.Equal(parameterValue.ToArray(), value);\n            }\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadGuidParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var parameterValue = Guid.Parse(\"7FCFFE2D-E9A6-49E0-B8ED-9617603F5584\");\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}\");\n            var param = new ClickHouseParameter(\"param\") { DbType = DbType.Guid };\n            cmd.Parameters.Add(param);\n            param.Value = parameterValue;\n             \n            var result = await cmd.ExecuteScalarAsync<Guid>();\n            Assert.Equal(parameterValue, result);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadDecimalParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            // The default ClickHouse type for decimal is Decimal128(9)\n            const decimal minValueByDefault = 1m / 1_000_000_000;\n            const decimal binarySparseValue = 281479271677952m * 4294967296m;\n\n            var testData = new[]\n            {\n                decimal.Zero, decimal.One, decimal.MinusOne, decimal.MinValue, decimal.MaxValue, decimal.MinValue / 100, decimal.MaxValue / 100, decimal.One / 100, decimal.MinusOne / 100,\n                minValueByDefault, -minValueByDefault, minValueByDefault / 10, -minValueByDefault / 10, binarySparseValue, -binarySparseValue\n            };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}\");\n            var param = new ClickHouseParameter(\"param\") {DbType = DbType.Decimal};\n            cmd.Parameters.Add(param);\n            \n            foreach (var testValue in testData)\n            {\n                param.Value = testValue;\n\n                var result = await cmd.ExecuteScalarAsync();\n                var resultDecimal = Assert.IsType<decimal>(result);\n\n                if (Math.Abs(testValue) >= minValueByDefault)\n                    Assert.Equal(testValue, resultDecimal);\n                else\n                    Assert.Equal(0, resultDecimal);\n            }\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadCurrencyParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            const decimal maxCurrencyValue = 922_337_203_685_477.5807m, minCurrencyValue = -922_337_203_685_477.5808m, binarySparseValue = 7_205_759_833_289.5232m, currencyEpsilon = 0.0001m;\n\n            var testData = new[]\n            {\n                decimal.Zero, decimal.One, decimal.MinusOne, minCurrencyValue, maxCurrencyValue, decimal.One / 100, decimal.MinusOne / 100,\n                binarySparseValue, -binarySparseValue, currencyEpsilon, -currencyEpsilon, currencyEpsilon / 10, -currencyEpsilon / 10\n            };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}\");\n            var param = new ClickHouseParameter(\"param\") { DbType = DbType.Currency };\n            cmd.Parameters.Add(param);\n\n            param.Value = minCurrencyValue - currencyEpsilon;\n            var handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n\n            param.Value = maxCurrencyValue + currencyEpsilon;\n            handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n\n            foreach (var testValue in testData)\n            {\n                param.Value = testValue;\n\n                var result = await cmd.ExecuteScalarAsync();\n                var resultDecimal = Assert.IsType<decimal>(result);\n\n                if (Math.Abs(testValue) >= currencyEpsilon)\n                    Assert.Equal(testValue, resultDecimal);\n                else\n                    Assert.Equal(0, resultDecimal);\n            }\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadVarNumericParameter(ClickHouseParameterMode parameterMode)\n        {\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param} AS p, toString(p)\");\n            var param = new ClickHouseParameter(\"param\") {DbType = DbType.VarNumeric, Precision = 7, Scale = 3};\n            cmd.Parameters.Add(param);\n\n            {\n                const decimal epsilon = 0.001m, formalMax = 9_999.999m, actualMax = 2_147_483.647m, actualMin = -2_147_483.648m;\n                var values = new[] {decimal.Zero, decimal.One, decimal.MinusOne, epsilon, -epsilon, formalMax, -formalMax, actualMax, actualMin, epsilon / 10, -epsilon / 10};\n                foreach (var testValue in values)\n                {\n                    param.Value = testValue;\n\n                    await using var reader = await cmd.ExecuteReaderAsync();\n                    Assert.True(await reader.ReadAsync());\n\n                    var resultDecimal = reader.GetDecimal(0);\n                    var resultStr = reader.GetString(1);\n\n                    Assert.False(await reader.ReadAsync());\n\n                    if (resultStr.StartsWith(\"--\"))\n                    {\n                        Assert.True(testValue < -formalMax);\n                        resultStr = resultStr.Substring(1);\n                    }\n\n                    var parsedValue = decimal.Parse(resultStr, CultureInfo.InvariantCulture);\n                    if (Math.Abs(testValue) >= epsilon)\n                    {\n                        Assert.Equal(testValue, resultDecimal);\n                        Assert.Equal(testValue, parsedValue);\n                    }\n                    else\n                    {\n                        Assert.Equal(0, resultDecimal);\n                        Assert.Equal(0, parsedValue);\n                    }\n                }\n\n                param.Value = actualMax + epsilon;\n                var handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n                Assert.IsType<OverflowException>(handledException.InnerException);\n\n                param.Value = actualMin - epsilon;\n                handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n                Assert.IsType<OverflowException>(handledException.InnerException);\n            }\n\n            param.Precision = 18;\n            param.Scale = 6;\n            {\n                const decimal epsilon = 0.000_001m, formalMax = 999_999_999_999.999_999m, actualMax = 9_223_372_036_854.775807m, actualMin = -9_223_372_036_854.775808m;\n                var values = new[] { decimal.Zero, decimal.One, decimal.MinusOne, epsilon, -epsilon, formalMax, -formalMax, actualMax, actualMin, epsilon / 10, -epsilon / 10 };\n                foreach (var testValue in values)\n                {\n                    param.Value = testValue;\n\n                    await using var reader = await cmd.ExecuteReaderAsync();\n                    Assert.True(await reader.ReadAsync());\n\n                    var resultDecimal = reader.GetDecimal(0);\n                    var resultStr = reader.GetString(1);\n\n                    Assert.False(await reader.ReadAsync());\n\n                    if (resultStr.StartsWith(\"--\"))\n                    {\n                        Assert.True(testValue < -formalMax);\n                        resultStr = resultStr.Substring(1);\n                    }\n\n                    var parsedValue = decimal.Parse(resultStr, CultureInfo.InvariantCulture);\n                    if (Math.Abs(testValue) >= epsilon)\n                    {\n                        Assert.Equal(testValue, resultDecimal);\n                        Assert.Equal(testValue, parsedValue);\n                    }\n                    else\n                    {\n                        Assert.Equal(0, resultDecimal);\n                        Assert.Equal(0, parsedValue);\n                    }\n                }\n\n                param.Value = actualMax + epsilon;\n                var handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync()); \n                Assert.IsType<OverflowException>(handledException.InnerException);\n\n                param.Value = actualMin - epsilon;\n                handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n                Assert.IsType<OverflowException>(handledException.InnerException);\n            }\n\n            param.Precision = 35;\n            param.Scale = 30;\n            {\n                const decimal formalMax = 99_999.999_999_999_999_999_999_999_99m, actualMax = 170_141_183.460_469_231_731_687_303_71m, actualMin = -actualMax, epsilon= 0.000_000_000_000_000_000_01m;\n                var values = new[] {decimal.Zero, decimal.One, decimal.MinusOne, epsilon, -epsilon, formalMax, -formalMax, actualMax, actualMin};\n                foreach (var testValue in values)\n                {\n                    param.Value = testValue;\n\n                    await using var reader = await cmd.ExecuteReaderAsync();\n                    Assert.True(await reader.ReadAsync());\n\n                    var resultDecimal = reader.GetDecimal(0);\n                    var resultStr = reader.GetString(1);\n\n                    Assert.False(await reader.ReadAsync());\n\n                    if (resultStr.StartsWith(\"--\"))\n                    {\n                        Assert.True(testValue < -formalMax);\n                        resultStr = resultStr.Substring(1);\n                    }\n\n                    var parsedValue = decimal.Parse(resultStr, CultureInfo.InvariantCulture);\n                    Assert.Equal(testValue, resultDecimal);\n                    Assert.Equal(testValue, parsedValue);\n                }\n\n                param.Value = actualMax + epsilon;\n                var handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n                Assert.IsType<OverflowException>(handledException.InnerException);\n\n                param.Value = actualMin - epsilon;\n                handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n                Assert.IsType<OverflowException>(handledException.InnerException);\n            }\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ClickHouseDecimalTypeNames(ClickHouseParameterMode parameterMode)\n        {\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT toTypeName({param})\");\n            var param = new ClickHouseParameter(\"param\") {DbType = DbType.Decimal, Value = 0m};\n            cmd.Parameters.Add(param);\n\n            var typeName = await cmd.ExecuteScalarAsync<string>();\n            Assert.Matches(GetRegex(38, 9), typeName);\n\n            param.DbType = DbType.Currency;\n            typeName = await cmd.ExecuteScalarAsync<string>();\n            Assert.Matches(GetRegex(18, 4), typeName);\n\n            param.DbType = DbType.VarNumeric;\n            typeName = await cmd.ExecuteScalarAsync<string>();\n            Assert.Matches(GetRegex(38, 9), typeName);\n\n            param.Scale = 2;\n            param.Precision = 3;\n            typeName = await cmd.ExecuteScalarAsync<string>();\n            Assert.Matches(GetRegex(3, 2), typeName);\n\n            param.Scale = 14;\n            param.Precision = 14;\n            typeName = await cmd.ExecuteScalarAsync<string>();\n            Assert.Matches(GetRegex(14, 14), typeName);\n\n            param.Scale = 0;\n            param.Precision = 1;\n            typeName = await cmd.ExecuteScalarAsync<string>();\n            Assert.Matches(GetRegex(1, 0), typeName);\n\n            param.Scale = 32;\n            param.Precision = 33;\n            typeName = await cmd.ExecuteScalarAsync<string>();\n            Assert.Matches(GetRegex(33, 32), typeName);\n\n            static string GetRegex(int precision, int scale)\n            {\n                // ClickHouse server may wrap the parameter's type into Nullable\n                return string.Format(CultureInfo.InvariantCulture, @\"^(Nullable\\()?Decimal\\({0},\\s{1}\\)\\)?$\", precision, scale);\n            }\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadDateTimeParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var now = DateTime.Now;\n            now = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Kind);\n\n            var testData = new[] {now, default, new DateTime(1980, 12, 15, 3, 8, 58), new DateTime(2015, 1, 1, 18, 33, 55)};\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}\");\n            var param = new ClickHouseParameter(\"param\");\n            cmd.Parameters.Add(param);\n\n            foreach (var testValue in testData)\n            {\n                param.Value = testValue;\n\n                var result = await cmd.ExecuteScalarAsync<DateTime>();\n                Assert.Equal(testValue, result);\n            }\n\n            param.Value = DateTime.UnixEpoch.AddMonths(-1);\n            var handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n\n            param.Value = DateTime.UnixEpoch.AddSeconds(uint.MaxValue).AddMonths(1);\n            handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadDateTimeOffsetParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var now = DateTime.Now;\n            now = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Kind);\n\n            var testData = new[]\n            {\n                now, default, new DateTimeOffset(new DateTime(1980, 12, 15, 3, 8, 58), new TimeSpan(0, -5, 0, 0)), new DateTimeOffset(new DateTime(2015, 1, 1, 18, 33, 55), new TimeSpan(0, 3, 15, 0)),\n                new DateTimeOffset(DateTime.UnixEpoch.AddSeconds(1)).ToOffset(new TimeSpan(0, -11, 0, 0)),\n                new DateTimeOffset(DateTime.UnixEpoch.AddSeconds(uint.MaxValue)).ToOffset(new TimeSpan(0, 11, 0, 0))\n            };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}\");\n            var param = new ClickHouseParameter(\"param\");\n            cmd.Parameters.Add(param);\n\n            foreach (var testValue in testData)\n            {\n                param.Value = testValue;\n\n                var result = await cmd.ExecuteScalarAsync<DateTimeOffset>();\n                Assert.Equal(0, (result - testValue).Ticks);\n            }\n\n            param.Value = DateTimeOffset.UnixEpoch;\n            var unixEpochResult = await cmd.ExecuteScalarAsync<DateTimeOffset>();\n            Assert.Equal(default, unixEpochResult);\n\n            param.Value = new DateTimeOffset(DateTime.UnixEpoch.AddSeconds(-1)).ToOffset(new TimeSpan(0, -11, 0, 0));\n            var handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n            \n            param.Value = new DateTimeOffset(DateTime.UnixEpoch.AddSeconds((double) uint.MaxValue + 1)).ToOffset(new TimeSpan(0, 11, 0, 0));\n            handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadFloatParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var testData = new[]\n                {float.MinValue, float.MaxValue, float.Epsilon * 2, -float.Epsilon * 2, 1, -1, (float) Math.PI, (float) Math.Exp(1)};\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT CAST({param}/2 AS Float32)\");\n            var param = new ClickHouseParameter(\"param\") { DbType = DbType.Single };\n            cmd.Parameters.Add(param);\n\n            foreach (var testValue in testData)\n            {\n                param.Value = testValue;\n\n                var result = await cmd.ExecuteScalarAsync();\n                var resultFloat = Assert.IsType<float>(result);\n\n                Assert.Equal(testValue / 2, resultFloat);\n            }\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadDoubleParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var testData = new[]\n                {Math.Exp(1), double.MinValue, double.MaxValue, double.Epsilon * 2, -double.Epsilon * 2, 1, -1, Math.PI, };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}/2\");\n            var param = new ClickHouseParameter(\"param\") { DbType = DbType.Double };\n            cmd.Parameters.Add(param);\n\n            foreach (var testValue in testData)\n            {\n                param.Value = testValue;\n\n                var result = await cmd.ExecuteScalarAsync();\n                var resultDouble = Assert.IsType<double>(result);\n\n                Assert.Equal(testValue / 2, resultDouble);\n            }\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadNothingParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}\");\n            var param = new ClickHouseParameter(\"param\");\n            cmd.Parameters.Add(param);\n\n            var result = await cmd.ExecuteScalarAsync();\n            Assert.Equal(DBNull.Value, result);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadIpV4ParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}\");\n            var param = new ClickHouseParameter(\"param\") {Value = IPAddress.Parse(\"10.0.121.1\")};\n            Assert.Equal(ClickHouseDbType.IpV4, param.ClickHouseDbType);\n\n            cmd.Parameters.Add(param);\n            var result = await cmd.ExecuteScalarAsync();\n            Assert.Equal(param.Value, result);\n\n            param.Value = \"::ffff:192.0.2.1\";\n            param.ClickHouseDbType = ClickHouseDbType.IpV4;\n            result = await cmd.ExecuteScalarAsync<string>();\n            Assert.Equal(\"192.0.2.1\", result);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadIpV6ParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param}\");\n            var param = new ClickHouseParameter(\"param\") { Value = IPAddress.Parse(\"2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d\") };\n            Assert.Equal(ClickHouseDbType.IpV6, param.ClickHouseDbType);\n\n            cmd.Parameters.Add(param);\n            var result = await cmd.ExecuteScalarAsync();\n            Assert.Equal(param.Value, result);\n\n            param.Value = \"192.0.121.234\";\n            param.ClickHouseDbType = ClickHouseDbType.IpV6;\n            result = await cmd.ExecuteScalarAsync<string>();\n            Assert.Equal(\"::ffff:192.0.121.234\", result);\n\n            param.Value = null;\n            result = await cmd.ExecuteScalarAsync();\n            Assert.Equal(DBNull.Value, result);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadIntegerParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            object[] values =\n            {\n                sbyte.MinValue, sbyte.MaxValue, (sbyte) 0,\n                (short) sbyte.MinValue, (short) sbyte.MaxValue,\n                (int) sbyte.MinValue, (int) sbyte.MaxValue,\n                (long) sbyte.MinValue, (long) sbyte.MaxValue,\n                byte.MinValue, byte.MaxValue,\n                (ushort) byte.MaxValue,\n                (uint) byte.MaxValue,\n                (ulong) byte.MaxValue,\n                short.MinValue, short.MaxValue, (short) 0,\n                (int) short.MinValue, (int) short.MaxValue,\n                (long) short.MinValue, (long) short.MaxValue,\n                ushort.MinValue, ushort.MaxValue,\n                (uint) ushort.MaxValue,\n                (ulong) ushort.MaxValue,\n                int.MinValue, int.MaxValue, (int) 0,\n                (long) int.MinValue, (long) int.MaxValue,\n                uint.MinValue, uint.MaxValue,\n                (ulong) uint.MaxValue,\n                long.MinValue, long.MaxValue, (long) 0,\n                ulong.MinValue, ulong.MaxValue\n            };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {integerParam}\");\n            var param = new ClickHouseParameter(\"integerParam\");\n            cmd.Parameters.Add(param);\n            foreach (var value in values)\n            {\n                param.Value = value;\n                var result = await cmd.ExecuteScalarAsync();\n\n                Assert.IsType(value.GetType(), result);\n                Assert.Equal(value, result);\n            }\n        }\n\n        [Fact]\n        public async Task ReadValueWithOverridenType()\n        {\n            await WithTemporaryTable(\"col_settings_type\", \"id UInt16, ip Nullable(IPv4), enum Nullable(Enum16('min'=-512, 'avg'=0, 'max'=512)), num Nullable(Int32)\", RunTest);\n\n            static async Task RunTest(ClickHouseConnection connection, string tableName)\n            {\n                var cmd = connection.CreateCommand($\"INSERT INTO {tableName}(id, ip, enum, num) VALUES\" +\n                    \"(124, null, 'min', 1234)\" +\n                    \"(125, '10.0.0.1', 'avg', null)\" +\n                    \"(126, null, null, null)\" +\n                    \"(127, '127.0.0.1', 'max', -8990)\" +\n                    \"(128, '4.8.15.16', null, 12789)\");\n\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = $\"SELECT * FROM {tableName} ORDER BY id\";\n                await using var reader = await cmd.ExecuteReaderAsync();\n                var idIdx = reader.GetOrdinal(\"id\");\n                var numIdx = reader.GetOrdinal(\"num\");\n                var enumIdx = reader.GetOrdinal(\"enum\");\n                var ipIdx = reader.GetOrdinal(\"ip\");                \n\n                Assert.Equal(typeof(ushort), reader.GetFieldType(idIdx));\n                Assert.Equal(typeof(int), reader.GetFieldType(numIdx));\n                Assert.Equal(typeof(string), reader.GetFieldType(enumIdx));\n                Assert.Equal(typeof(IPAddress), reader.GetFieldType(ipIdx));\n\n                reader.ConfigureColumn(\"id\", new ClickHouseColumnSettings(typeof(int)));\n                reader.ConfigureColumn(\"num\", new ClickHouseColumnSettings(typeof(long)));\n                reader.ConfigureColumn(\"enum\", new ClickHouseColumnSettings(typeof(short?)));\n                reader.ConfigureColumn(\"ip\", new ClickHouseColumnSettings(typeof(string)));\n\n                Assert.Equal(typeof(int), reader.GetFieldType(idIdx));\n                Assert.Equal(typeof(long), reader.GetFieldType(numIdx));\n                Assert.Equal(typeof(short), reader.GetFieldType(enumIdx));\n                Assert.Equal(typeof(string), reader.GetFieldType(ipIdx));\n\n                var expectedData = new object[][]\n                {\n                    new object[] {124, DBNull.Value, (short)-512, 1234L},\n                    new object[] {125, \"10.0.0.1\", (short)0, DBNull.Value},\n                    new object[] {126, DBNull.Value, DBNull.Value, DBNull.Value},\n                    new object[] {127, \"127.0.0.1\", (short)512, -8990L},\n                    new object[] {128, \"4.8.15.16\", DBNull.Value, 12789L}\n                };\n\n                int count = 0;\n                while(await reader.ReadAsync())\n                {\n                    Assert.Equal(expectedData[count][0], reader.GetValue(idIdx));\n                    Assert.Equal(expectedData[count][1], reader.GetValue(ipIdx));\n                    Assert.Equal(expectedData[count][2], reader.GetValue(enumIdx));\n                    Assert.Equal(expectedData[count][3], reader.GetValue(numIdx));\n\n                    ++count;\n                }\n\n                Assert.Equal(expectedData.Length, count);                \n            }\n        }\n\n        [Fact]\n        public async Task ReadGuidColumn()\n        {\n            var guids = new List<Guid> { Guid.Parse(\"74D47928-2423-4FE2-AD45-82E296BF6058\"), Guid.Parse(\"2879D474-2324-E24F-AD45-82E296BF6058\"), Guid.Empty };\n            guids.AddRange(Enumerable.Range(1, 100 - guids.Count).Select(_ => Guid.NewGuid()));\n\n            await WithTemporaryTable(\"uuid\", \"id Int32, guid UUID, str String\", RunTest);\n\n            async Task RunTest(ClickHouseConnection connection, string tableName)\n            {\n                await using (var writer = await connection.CreateColumnWriterAsync($\"INSERT INTO {tableName}(id, guid, str) VALUES\", CancellationToken.None))\n                {\n                    await writer.WriteTableAsync(new object[] { Enumerable.Range(0, guids.Count), guids, guids.Select(v => v.ToString(\"D\")) }, guids.Count, CancellationToken.None);\n                }\n\n                var cmd = connection.CreateCommand($\"SELECT id, guid, CAST(guid AS String) strGuid, (guid = CAST(str AS UUID)) eq FROM {tableName} ORDER BY id\");\n\n                await using var reader = await cmd.ExecuteReaderAsync();\n\n                int count = 0;\n                while (await reader.ReadAsync())\n                {\n                    var id = reader.GetInt32(0);\n                    var guid = reader.GetGuid(1);\n                    var str = reader.GetString(2);\n                    var eq = reader.GetBoolean(3);\n\n                    Assert.Equal(count, id);\n                    Assert.Equal(guids[id], guid);\n                    Assert.True(Guid.TryParse(str, out var strGuid));\n                    Assert.Equal(guids[id], strGuid);\n                    Assert.True(eq);\n\n                    ++count;\n                }\n\n                Assert.Equal(guids.Count, count);\n            }\n        }\n\n        [Fact]\n        public async Task ReadMapScalar()\n        {\n            await using var cn = await OpenConnectionAsync();\n\n            var cmd = cn.CreateCommand(\"SELECT CAST(([1, 2, 3, 0], ['Ready', 'Steady', 'Go', null]), 'Map(UInt8, Nullable(String))') AS map\");\n            var result = await cmd.ExecuteScalarAsync();\n\n            Assert.Equal(new[] { new KeyValuePair<byte, string?>(1, \"Ready\"), new KeyValuePair<byte, string?>(2, \"Steady\"), new KeyValuePair<byte, string?>(3, \"Go\"), new KeyValuePair<byte, string?>(0, null) }, result);            \n        }\n\n        [Fact]\n        public async Task ReadMapColumn()\n        {\n            await WithTemporaryTable(\"map\", \"id Int32, map Map(String, Nullable(Int32))\", Test);\n\n            static async Task Test(ClickHouseConnection cn, string tableName)\n            {\n                var expectedDict = new Dictionary<string, int?>(StringComparer.OrdinalIgnoreCase)\n                {\n                    [\"Ready\"] = 1,\n                    [\"Steady\"] = 2,\n                    [\"Go\"] = 3,\n                    [\"Unknown\"] = null,\n                    [\"Ok\"] = 1,\n                    [\"NotOk\"] = -1,\n                    [\"Oh\"] = 199,\n                    [\"hh\"] = null,\n                    [\"hO\"] = 201,\n                    [\"null\"] = null,\n                };\n\n                var expectedLists = new string[][]\n                {\n                    new string[]{ \"Ready\", \"Steady\", \"Go\", \"Unknown\" },\n                    new string[]{ \"Ok\", \"NotOk\" },\n                    new string[]{ \"Oh\", \"hh\", \"hO\" },\n                    new string[0],\n                    new string[]{ \"null\" },\n                };\n\n                var cmd = cn.CreateCommand(@$\"INSERT INTO {tableName}(id, map)\nSELECT 1, CAST((['Ready', 'Steady', 'Go', 'Unknown'], [1, 2, 3, null]), 'Map(String, Nullable(Int32))') AS map\nUNION ALL SELECT 2, CAST((['Ok', 'NotOk'], [1, -1]), 'Map(String, Nullable(Int32))')\nUNION ALL SELECT 3, CAST((['Oh', 'hh', 'hO'], [199, null, 201]), 'Map(String, Nullable(Int32))')\nUNION ALL SELECT 4, CAST(([], []), 'Map(String, Nullable(Int32))')\nUNION ALL SELECT 5, CAST((['null'], [null]), 'Map(String, Nullable(Int32))')\");\n\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = $\"SELECT map FROM {tableName} ORDER BY id\";\n                await using var reader = await cmd.ExecuteReaderAsync();\n\n                var columnType = reader.GetFieldTypeInfo(0);\n                Assert.Equal(\"Map\", columnType.TypeName);\n                Assert.Equal(\"Map(String, Nullable(Int32))\", columnType.ComplexTypeName);\n                Assert.Equal(2, columnType.GenericArgumentsCount);\n                Assert.Equal(2, columnType.TypeArgumentsCount);\n\n                var keyArg = columnType.GetGenericArgument(0);\n                var typeArg1 = columnType.GetTypeArgument(0);\n                Assert.Same(keyArg, typeArg1);\n                Assert.Equal(\"String\", keyArg.ComplexTypeName);\n\n                var valueArg = columnType.GetGenericArgument(1);\n                var typeArg2 = columnType.GetTypeArgument(1);\n                Assert.Same(valueArg, typeArg2);\n                Assert.Equal(\"Nullable(Int32)\", valueArg.ComplexTypeName);\n\n                Assert.Equal(typeof(KeyValuePair<string, int?>[]), columnType.GetFieldType());\n\n                int count = 0;\n                while (await reader.ReadAsync())\n                {\n                    var value = reader.GetValue(0);\n                    var pairs = Assert.IsType<KeyValuePair<string, int?>[]>(value);\n                    var expectedKeys = expectedLists[count];\n\n                    Assert.Equal(expectedKeys.Length, pairs.Length);\n                    for (int i = 0; i < expectedKeys.Length; i++)\n                    {\n                        Assert.Equal(expectedKeys[i], pairs[i].Key);\n                        Assert.Equal(expectedDict[expectedKeys[i]], pairs[i].Value);\n                    }\n\n                    ++count;\n                }\n\n                Assert.Equal(expectedLists.Length, count);                \n            }            \n        }\n\n        [Fact]\n        public async Task ReadInt128Column()\n        {\n            var minValue = -(BigInteger.One << 127);\n            var maxValue = (BigInteger.One << 127) - BigInteger.One;\n\n            var maxStrLen = maxValue.ToString().Length;\n            var sb = new StringBuilder(maxStrLen + 1).Append('-');\n            for (int i = 1; i <= maxStrLen; i++)\n                sb.Append((char)('0' + (i % 10)));\n\n            var strValues = new[] { minValue.ToString(), sb.ToString(), \"-1\", \"0\", \"1\", sb.ToString(1, maxStrLen), maxValue.ToString() };\n\n            await using var cn = await OpenConnectionAsync();\n            var cmd = cn.CreateCommand(\"SELECT CAST(value AS Int128) AS v, v = bigIntValue AS testPassed FROM ptable ORDER BY id\");\n\n            var tableProvider = new ClickHouseTableProvider(\"ptable\", strValues.Length);\n            tableProvider.Columns.AddColumn(\"id\", Enumerable.Range(1, strValues.Length));\n            tableProvider.Columns.AddColumn(\"value\", strValues);\n            var bigIntColumn = tableProvider.Columns.AddColumn(\"bigIntValue\", strValues.Select(v => BigInteger.Parse(v)));\n            bigIntColumn.ClickHouseDbType = ClickHouseDbType.Int128;\n            cmd.TableProviders.Add(tableProvider);\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            var valueColumnType = reader.GetFieldTypeInfo(0);\n            Assert.Equal(\"Int128\", valueColumnType.ComplexTypeName);\n            Assert.Equal(ClickHouseDbType.Int128, valueColumnType.GetDbType());\n\n            int count = 0;\n            while (await reader.ReadAsync())\n            {\n                var value = reader.GetValue(0);\n                var expectedValue = BigInteger.Parse(strValues[count]);\n                Assert.Equal(expectedValue, value);\n\n                var testPassed = reader.GetBoolean(1);\n                Assert.True(testPassed);\n\n                ++count;\n            }\n\n            Assert.Equal(strValues.Length, count);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadInt128ParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var cn = await OpenConnectionAsync(parameterMode);\n\n            var cmd = cn.CreateCommand(\"SELECT {v}\");\n            var parameter = new ClickHouseParameter(\"v\") { ClickHouseDbType = ClickHouseDbType.Int128 };\n            cmd.Parameters.Add(parameter);\n\n            var pairs = new (object value, BigInteger expected)[] \n            {\n                (0, 0),\n                (-1, -1),\n                (1, 1),\n                (byte.MaxValue, byte.MaxValue),\n                (sbyte.MinValue, sbyte.MinValue),\n                (sbyte.MaxValue, sbyte.MaxValue),\n                (short.MinValue, short.MinValue),\n                (short.MaxValue, short.MaxValue),\n                (ushort.MaxValue, ushort.MaxValue),\n                (int.MinValue, int.MinValue),\n                (int.MaxValue, int.MaxValue),\n                (uint.MaxValue, uint.MaxValue),\n                (long.MinValue, long.MinValue),\n                (long.MaxValue, long.MaxValue),\n                (ulong.MaxValue, ulong.MaxValue),\n                (-(BigInteger.One << 127), -(BigInteger.One << 127)),\n                ((BigInteger.One << 127) - 1, (BigInteger.One << 127) - 1),\n            };\n\n            foreach(var pair in pairs)\n            {\n                parameter.Value = pair.value;\n                var result = await cmd.ExecuteScalarAsync();\n                var bigIntResult = Assert.IsType<BigInteger>(result);\n                Assert.Equal(pair.expected, bigIntResult);\n            }            \n        }\n\n        [Fact]\n        public async Task ReadUInt128Column()\n        {\n            var maxValue = (BigInteger.One << 128) - BigInteger.One;\n\n            var maxStrLen = maxValue.ToString().Length;\n            var sb = new StringBuilder(maxStrLen);\n            for (int i = 1; i <= maxStrLen; i++)\n                sb.Append((char)('0' + (i % 10)));\n\n            var strValues = new[] { \"0\", \"1\", sb.ToString(), maxValue.ToString() };\n\n            await using var cn = await OpenConnectionAsync();\n            var cmd = cn.CreateCommand(\"SELECT CAST(value AS UInt128) AS v, v = bigIntValue AS testPassed FROM ptable ORDER BY id\");\n\n            var tableProvider = new ClickHouseTableProvider(\"ptable\", strValues.Length);\n            tableProvider.Columns.AddColumn(\"id\", Enumerable.Range(1, strValues.Length));\n            tableProvider.Columns.AddColumn(\"value\", strValues);\n            var bigIntColumn = tableProvider.Columns.AddColumn(\"bigIntValue\", strValues.Select(v => BigInteger.Parse(v)));\n            bigIntColumn.ClickHouseDbType = ClickHouseDbType.UInt128;\n            cmd.TableProviders.Add(tableProvider);\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            var valueColumnType = reader.GetFieldTypeInfo(0);\n            Assert.Equal(\"UInt128\", valueColumnType.ComplexTypeName);\n            Assert.Equal(ClickHouseDbType.UInt128, valueColumnType.GetDbType());\n\n            int count = 0;\n            while (await reader.ReadAsync())\n            {\n                var value = reader.GetValue(0);\n                var expectedValue = BigInteger.Parse(strValues[count]);\n                Assert.Equal(expectedValue, value);\n\n                var testPassed = reader.GetBoolean(1);\n                Assert.True(testPassed);\n\n                ++count;\n            }\n\n            Assert.Equal(strValues.Length, count);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadUInt128ParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var cn = await OpenConnectionAsync(parameterMode);\n\n            var cmd = cn.CreateCommand(\"SELECT {v}\");\n            var parameter = new ClickHouseParameter(\"v\") { ClickHouseDbType = ClickHouseDbType.UInt128 };\n            cmd.Parameters.Add(parameter);\n\n            var pairs = new (object value, BigInteger expected)[]\n            {\n                (0u, 0u),\n                (1u, 1u),\n                (byte.MaxValue, byte.MaxValue),\n                (ushort.MaxValue, ushort.MaxValue),\n                (uint.MaxValue, uint.MaxValue),\n                (ulong.MaxValue, ulong.MaxValue),\n                ((BigInteger.One << 128) - 1, (BigInteger.One << 128) - 1),\n            };\n\n            foreach (var pair in pairs)\n            {\n                parameter.Value = pair.value;\n                var result = await cmd.ExecuteScalarAsync();\n                var bigIntResult = Assert.IsType<BigInteger>(result);\n                Assert.Equal(pair.expected, bigIntResult);\n            }\n        }\n\n        [Fact]\n        public async Task ReadInt256Column()\n        {\n            var minValue = -(BigInteger.One << 255);            \n            var maxValue = (BigInteger.One << 255) - BigInteger.One;\n\n            var maxStrLen = maxValue.ToString().Length;\n            var sb = new StringBuilder(maxStrLen + 1).Append('-');\n            for (int i = 1; i <= maxStrLen; i++)\n                sb.Append((char)('0' + (i % 10)));\n\n            var strValues = new[] { minValue.ToString(), sb.ToString(), \"-1\", \"0\", \"1\", sb.ToString(1, maxStrLen), maxValue.ToString() };\n\n            await using var cn = await OpenConnectionAsync();\n            var cmd = cn.CreateCommand(\"SELECT CAST(value AS Int256) AS v, v = bigIntValue AS testPassed FROM ptable ORDER BY id\");\n\n            var tableProvider = new ClickHouseTableProvider(\"ptable\", strValues.Length);\n            tableProvider.Columns.AddColumn(\"id\", Enumerable.Range(1, strValues.Length));\n            tableProvider.Columns.AddColumn(\"value\", strValues);\n            tableProvider.Columns.AddColumn(\"bigIntValue\", strValues.Select(v => BigInteger.Parse(v)));            \n            cmd.TableProviders.Add(tableProvider);\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            var valueColumnType = reader.GetFieldTypeInfo(0);\n            Assert.Equal(\"Int256\", valueColumnType.ComplexTypeName);\n            Assert.Equal(ClickHouseDbType.Int256, valueColumnType.GetDbType());\n\n            int count = 0;\n            while(await reader.ReadAsync())\n            {\n                var value = reader.GetValue(0);\n                var expectedValue = BigInteger.Parse(strValues[count]);\n                Assert.Equal(expectedValue, value);\n\n                var testPassed = reader.GetBoolean(1);\n                Assert.True(testPassed);\n\n                ++count;\n            }\n\n            Assert.Equal(strValues.Length, count);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadInt256ParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var cn = await OpenConnectionAsync(parameterMode);\n\n            var cmd = cn.CreateCommand(\"SELECT {v}\");\n            var parameter = new ClickHouseParameter(\"v\") { ClickHouseDbType = ClickHouseDbType.Int256 };\n            cmd.Parameters.Add(parameter);\n\n            var pairs = new (object value, BigInteger expected)[]\n            {\n                (0, 0),\n                (-1, -1),\n                (1, 1),\n                (byte.MaxValue, byte.MaxValue),\n                (sbyte.MinValue, sbyte.MinValue),\n                (sbyte.MaxValue, sbyte.MaxValue),\n                (short.MinValue, short.MinValue),\n                (short.MaxValue, short.MaxValue),\n                (ushort.MaxValue, ushort.MaxValue),\n                (int.MinValue, int.MinValue),\n                (int.MaxValue, int.MaxValue),\n                (uint.MaxValue, uint.MaxValue),\n                (long.MinValue, long.MinValue),\n                (long.MaxValue, long.MaxValue),\n                (ulong.MaxValue, ulong.MaxValue),\n                (-(BigInteger.One << 255), -(BigInteger.One << 255)),\n                ((BigInteger.One << 255) - 1, (BigInteger.One << 255) - 1),\n            };\n\n            foreach (var pair in pairs)\n            {\n                if (pair.value is BigInteger)\n                    parameter.ResetDbType();\n\n                parameter.Value = pair.value;\n                var result = await cmd.ExecuteScalarAsync();\n                var bigIntResult = Assert.IsType<BigInteger>(result);\n                Assert.Equal(pair.expected, bigIntResult);\n            }\n        }\n\n        [Fact]\n        public async Task ReadUInt256Column()\n        {\n            var maxValue = (BigInteger.One << 256) - BigInteger.One;\n\n            var maxStrLen = maxValue.ToString().Length;\n            var sb = new StringBuilder(maxStrLen);\n            sb.Append(\"11\");\n            for (int i = 3; i <= maxStrLen; i++)\n                sb.Append((char)('0' + (i % 10)));\n\n            var strValues = new[] { \"0\", \"1\", sb.ToString(), maxValue.ToString() };\n\n            await using var cn = await OpenConnectionAsync();\n            var cmd = cn.CreateCommand(\"SELECT CAST(value AS UInt256) AS v, v = bigIntValue AS testPassed FROM ptable ORDER BY id\");\n\n            var tableProvider = new ClickHouseTableProvider(\"ptable\", strValues.Length);\n            tableProvider.Columns.AddColumn(\"id\", Enumerable.Range(1, strValues.Length));\n            tableProvider.Columns.AddColumn(\"value\", strValues);\n            var bigIntColumn = tableProvider.Columns.AddColumn(\"bigIntValue\", strValues.Select(v => BigInteger.Parse(v)));\n            bigIntColumn.ClickHouseDbType = ClickHouseDbType.UInt256;\n            cmd.TableProviders.Add(tableProvider);\n\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            var valueColumnType = reader.GetFieldTypeInfo(0);\n            Assert.Equal(\"UInt256\", valueColumnType.ComplexTypeName);\n            Assert.Equal(ClickHouseDbType.UInt256, valueColumnType.GetDbType());            \n\n            int count = 0;\n            while (await reader.ReadAsync())\n            {\n                var value = reader.GetValue(0);\n                var expectedValue = BigInteger.Parse(strValues[count]);\n                Assert.Equal(expectedValue, value);\n\n                var testPassed = reader.GetBoolean(1);\n                Assert.True(testPassed);\n\n                ++count;\n            }\n\n            Assert.Equal(strValues.Length, count);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadUInt256ParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            await using var cn = await OpenConnectionAsync(parameterMode);\n\n            var cmd = cn.CreateCommand(\"SELECT {v}\");\n            var parameter = new ClickHouseParameter(\"v\") { ClickHouseDbType = ClickHouseDbType.UInt256 };\n            cmd.Parameters.Add(parameter);\n\n            var pairs = new (object value, BigInteger expected)[]\n            {\n                (0u, 0u),\n                (1u, 1u),\n                (byte.MaxValue, byte.MaxValue),\n                (ushort.MaxValue, ushort.MaxValue),\n                (uint.MaxValue, uint.MaxValue),\n                (ulong.MaxValue, ulong.MaxValue),\n                ((BigInteger.One << 256) - 1, (BigInteger.One << 256) - 1),\n            };\n\n            foreach (var pair in pairs)\n            {\n                parameter.Value = pair.value;\n                var result = await cmd.ExecuteScalarAsync();\n                var bigIntResult = Assert.IsType<BigInteger>(result);\n                Assert.Equal(pair.expected, bigIntResult);\n            }\n        }\n\n        [Fact]\n        public async Task ReadArrayLowCardinality()\n        {\n            await WithTemporaryTable(\"arrlc\", \"id Int32, arr Array(LowCardinality(String))\", Test);\n\n            static async Task Test(ClickHouseConnection cn, string tableName)\n            {\n                var cmd = cn.CreateCommand($\"SELECT * FROM {tableName}\");\n                await using (var reader = await cmd.ExecuteReaderAsync())\n                    Assert.False(await reader.ReadAsync());\n\n                cmd.CommandText = $\"INSERT INTO {tableName}(id, arr) VALUES (1, [])\";\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = $\"SELECT arr FROM {tableName}\";\n\n                await using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SchemaOnly))\n                {\n                    // Skipping all rows\n                    Assert.False(await reader.ReadAsync());\n                }\n\n                await using (var reader = await cmd.ExecuteReaderAsync())\n                {\n                    Assert.True(await reader.ReadAsync());\n                    Assert.Equal(Array.Empty<string>(), reader.GetValue(0));\n                    Assert.False(await reader.ReadAsync());\n                }\n\n                cmd.CommandText = $\"INSERT INTO {tableName}(id, arr) VALUES (2, ['abc', 'def'])(3, ['def', 'ghi'])(4, ['def'])(5, ['ghi', 'abc'])\";\n                await cmd.ExecuteNonQueryAsync();\n\n                var expectedValues = new Dictionary<int, string[]>\n                {\n                    [1] = Array.Empty<string>(),\n                    [2] = new[] { \"abc\", \"def\" },\n                    [3] = new[] { \"def\", \"ghi\" },\n                    [4] = new[] { \"def\" },\n                    [5] = new[] { \"ghi\", \"abc\" }\n                };\n\n                cmd.CommandText = $\"SELECT id, arr FROM {tableName}\";\n                await using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SchemaOnly))\n                {\n                    // Skipping all rows\n                    Assert.False(await reader.ReadAsync());\n                }\n\n                await using (var reader = await cmd.ExecuteReaderAsync())\n                {\n                    while (await reader.ReadAsync())\n                    {\n                        var id = reader.GetInt32(0);\n                        var value = reader.GetValue(1);\n                        var arr = Assert.IsType<string[]>(value);\n                        Assert.True(expectedValues.Remove(id, out var expectedArr));\n                        Assert.Equal(expectedArr, arr);\n                    }\n                }\n\n                Assert.Empty(expectedValues);\n            }\n        }\n\n        [Theory]\n        [InlineData(\"2021-11-09\", 2021, 11, 09)]\n        [InlineData(\"1970-1-1\", 0, 0, 0)] // Default value\n        [InlineData(\"1970-1-2\", 1970, 1, 2)]\n        [InlineData(\"2149-06-06\", 2149, 6, 6)]\n        public async Task ReadDateScalar(string str, int year, int month, int day)\n        {\n            DateTime expectedDateTime = default;\n#if NET6_0_OR_GREATER\n            DateOnly expectedDate = default;\n#endif\n            if (year != 0 || month != 0 || day != 0)\n            {\n                expectedDateTime = new DateTime(year, month, day);\n#if NET6_0_OR_GREATER\n                expectedDate = new DateOnly(year, month, day);\n#endif\n            }\n\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand($\"SELECT cast('{str}' AS Date)\");\n\n            var result = await cmd.ExecuteScalarAsync();\n\n            DateTime resultDateTime;\n#if NET6_0_OR_GREATER\n            var resultDateOnly = Assert.IsType<DateOnly>(result);\n            Assert.Equal(expectedDate, resultDateOnly);\n#else\n            resultDateTime = Assert.IsType<DateTime>(result);\n            Assert.Equal(expectedDateTime, resultDateTime);\n#endif\n\n            resultDateTime = await cmd.ExecuteScalarAsync<DateTime>();\n            Assert.Equal(expectedDateTime, resultDateTime);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadDateParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var now = DateTime.Now;\n            now = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Kind);\n\n            var testData = new[] { now, default, new DateTime(1980, 12, 15, 3, 8, 58), new DateTime(2015, 1, 1, 18, 33, 55) };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param} v, toString(v)\");\n            var param = new ClickHouseParameter(\"param\") { ClickHouseDbType = ClickHouseDbType.Date };\n            cmd.Parameters.Add(param);\n\n            foreach (var testValue in testData)\n            {\n                param.Value = testValue;\n\n                await using var reader = await cmd.ExecuteReaderAsync();\n                Assert.True(await reader.ReadAsync());\n\n                var result = reader.GetFieldValue<DateTime>(0);\n                Assert.Equal(testValue.Date, result);\n\n                if (testValue == default)\n                    continue;\n\n                var resultStr = reader.GetString(1);\n                Assert.Equal(testValue.ToString(\"yyyy-MM-dd\", CultureInfo.InvariantCulture), resultStr);\n            }\n\n            param.Value = DateTime.UnixEpoch.AddMonths(-1);\n            var handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n\n            param.Value = DateTime.UnixEpoch.AddDays(ushort.MaxValue).AddMonths(1);\n            handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n        }\n\n#if NET6_0_OR_GREATER\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadDateParameterScalarNet6(ClickHouseParameterMode parameterMode)\n        {\n            var nowDateTime = DateTime.Now;\n            var now = new DateOnly(nowDateTime.Year, nowDateTime.Month, nowDateTime.Day);\n\n            var testData = new[] { now, default, new DateOnly(1980, 12, 15), new DateOnly(2015, 1, 1) };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param} v, toString(v)\");\n            var param = new ClickHouseParameter(\"param\") { ClickHouseDbType = ClickHouseDbType.Date };\n            cmd.Parameters.Add(param);\n\n            foreach (var testValue in testData)\n            {\n                param.Value = testValue;\n\n                Assert.Equal(ClickHouseDbType.Date, param.ClickHouseDbType);\n\n                await using var reader = await cmd.ExecuteReaderAsync();\n                Assert.True(await reader.ReadAsync());\n\n                var result = reader.GetValue(0);\n                var resultDateOnly = Assert.IsType<DateOnly>(result);\n                Assert.Equal(testValue, resultDateOnly);\n\n                if (testValue == default)\n                    continue;\n\n                var resultStr = reader.GetString(1);\n                Assert.Equal(testValue.ToString(\"yyyy-MM-dd\", CultureInfo.InvariantCulture), resultStr);\n            }\n\n            param.Value = DateOnly.FromDateTime(DateTime.UnixEpoch.AddMonths(-1));\n            var handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n\n            param.Value = DateOnly.FromDateTime(DateTime.UnixEpoch.AddDays(ushort.MaxValue).AddMonths(1));\n            handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n        }\n#endif\n\n        [Theory]\n        [InlineData(\"2021-11-09\", 2021, 11, 09)]\n        [InlineData(\"1925-01-01\", 1925, 1, 1)]\n        [InlineData(\"1925-1-02\", 1925, 1, 2)]\n        [InlineData(\"1970-1-1\", 1970, 1, 1)]\n        [InlineData(\"2283-11-11\", 2283, 11, 11)]\n        [InlineData(\"1900-1-1\", 1900, 1, 1)] // Min value\n        [InlineData(\"2299-12-31\", 2299, 12, 31)] // Max value\n        public async Task ReadDate32Scalar(string str, int year, int month, int day)\n        {\n            DateTime expectedDateTime = default;\n#if NET6_0_OR_GREATER\n            DateOnly expectedDate = default;\n#endif\n            if (year != 0 || month != 0 || day != 0)\n            {\n                expectedDateTime = new DateTime(year, month, day);\n#if NET6_0_OR_GREATER\n                expectedDate = new DateOnly(year, month, day);\n#endif\n            }\n\n            await using var connection = await OpenConnectionAsync();\n\n            await using var cmd = connection.CreateCommand($\"SELECT cast('{str}' AS Date32)\");\n\n            var result = await cmd.ExecuteScalarAsync();\n\n            DateTime resultDateTime;\n#if NET6_0_OR_GREATER\n            var resultDateOnly = Assert.IsType<DateOnly>(result);\n            Assert.Equal(expectedDate, resultDateOnly);\n#else\n            resultDateTime = Assert.IsType<DateTime>(result);\n            Assert.Equal(expectedDateTime, resultDateTime);\n#endif\n\n            resultDateTime = await cmd.ExecuteScalarAsync<DateTime>();\n            Assert.Equal(expectedDateTime, resultDateTime);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadDate32ParameterScalar(ClickHouseParameterMode parameterMode)\n        {\n            var now = DateTime.Now;\n            var minValue = new DateTime(1900, 1, 1);\n            var maxValue = new DateTime(2299, 12, 31, 23, 59, 59, 999);\n            now = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Kind);\n\n            var testData = new[] { now, default, new DateTime(1919, 6, 28, 18, 19, 20), new DateTime(1980, 12, 15, 3, 8, 58), new DateTime(2015, 1, 1, 18, 33, 55), minValue.AddDays(1), maxValue, DateTime.UnixEpoch };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param} v, toString(v)\");\n            var param = new ClickHouseParameter(\"param\") { ClickHouseDbType = ClickHouseDbType.Date32 };\n            cmd.Parameters.Add(param);\n\n            foreach (var testValue in testData)\n            {\n                param.Value = testValue;\n\n                await using var reader = await cmd.ExecuteReaderAsync();\n                Assert.True(await reader.ReadAsync());\n\n                var result = reader.GetFieldValue<DateTime>(0);\n                var expected = testValue.Date;\n                if (expected == default)\n                    expected = minValue;\n\n                Assert.Equal(expected, result);\n\n                if (testValue == default)\n                    continue;\n\n                var resultStr = reader.GetString(1);\n                Assert.Equal(testValue.ToString(\"yyyy-MM-dd\", CultureInfo.InvariantCulture), resultStr);\n            }\n\n            param.Value = minValue.AddMonths(-1);\n            var handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n\n            param.Value = maxValue.AddMonths(1);\n            handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n        }\n\n#if NET6_0_OR_GREATER\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadDate32ParameterScalarNet6(ClickHouseParameterMode parameterMode)\n        {\n            var nowDateTime = DateTime.Now;\n            var now = new DateOnly(nowDateTime.Year, nowDateTime.Month, nowDateTime.Day);\n            var minValue = new DateOnly(1900, 1, 1);\n            var maxValue = new DateOnly(2299, 12, 31);\n            var testData = new[] { now, default, new DateOnly(1912, 6, 28), new DateOnly(1980, 12, 15), new DateOnly(2015, 1, 1), minValue.AddDays(1), maxValue, DateOnly.FromDateTime(DateTime.UnixEpoch) };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            await using var cmd = connection.CreateCommand(\"SELECT {param} AS v, toString(v)\");\n            var param = new ClickHouseParameter(\"param\") { ClickHouseDbType = ClickHouseDbType.Date32 };\n            cmd.Parameters.Add(param);\n\n            foreach (var testValue in testData)\n            {\n                param.Value = testValue;\n\n                await using var reader = await cmd.ExecuteReaderAsync();\n                Assert.True(await reader.ReadAsync());\n\n                var result = reader.GetValue(0);\n                var resultDateOnly = Assert.IsType<DateOnly>(result);\n                var expected = testValue;\n                if (expected == default)\n                    expected = minValue;\n\n                Assert.Equal(expected, resultDateOnly);\n\n                if (testValue == default)\n                    continue;\n\n                var resultStr = reader.GetString(1);\n                Assert.Equal(testValue.ToString(\"yyyy-MM-dd\", CultureInfo.InvariantCulture), resultStr);\n            }\n\n            param.Value = minValue.AddMonths(-1);\n            var handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n\n            param.Value = maxValue.AddMonths(1);\n            handledException = await Assert.ThrowsAsync<ClickHouseHandledException>(() => cmd.ExecuteScalarAsync());\n            Assert.IsType<OverflowException>(handledException.InnerException);\n        }\n#endif\n\n        [Fact]\n        public async Task ReadMultidimensionalArrayLowCardinality()\n        {\n            await WithTemporaryTable(\"arrlc\", \"id Int32, arr Array(Array(Array(LowCardinality(Nullable(String)))))\", Test);\n\n            static async Task Test(ClickHouseConnection cn, string tableName)\n            {\n                var cmd = cn.CreateCommand($\"SELECT * FROM {tableName}\");\n                await using (var reader = await cmd.ExecuteReaderAsync())\n                    Assert.False(await reader.ReadAsync());\n\n                cmd.CommandText = $\"INSERT INTO {tableName}(id, arr) VALUES (1, [[[]]])\";\n                await cmd.ExecuteNonQueryAsync();\n\n                cmd.CommandText = $\"SELECT arr FROM {tableName}\";\n                await using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SchemaOnly))\n                {\n                    // Skipping all rows\n                    Assert.False(await reader.ReadAsync());\n                }\n\n                await using (var reader = await cmd.ExecuteReaderAsync())\n                {\n                    Assert.True(await reader.ReadAsync());\n                    Assert.Equal(new[] { new[] { Array.Empty<string>() } }, reader.GetValue(0));\n                    Assert.False(await reader.ReadAsync());\n                }\n\n                cmd.CommandText = $\"INSERT INTO {tableName}(id, arr) VALUES \" +\n                    \"(2, [[['foo', 'bar'], [''], ['foo', 'bar', null, 'baz']]])\" +\n                    \"(3, [[['foo', 'bar'], ['bar']], []])\" +\n                    \"(4, [[['foo']], [[]], [['baz', null], ['bar', 'foo']]])\" +\n                    \"(5, [[[NULL]], [[], ['baz']]])\" +\n                    \"(6, [])\" +\n                    \"(7, [[]])\";\n\n                await cmd.ExecuteNonQueryAsync();\n\n                var expectedValues = new Dictionary<int, string?[][][]>\n                {\n                    [1] = new[] { new[] { Array.Empty<string>() } },\n                    [2] = new[] { new[] { new[] { \"foo\", \"bar\" }, new[] { string.Empty }, new[] { \"foo\", \"bar\", null, \"baz\" } } },\n                    [3] = new[] { new[] { new[] { \"foo\", \"bar\" }, new[] { \"bar\" } }, Array.Empty<string?[]>() },\n                    [4] = new[] { new[] { new[] { \"foo\" } }, new[] { Array.Empty<string>() }, new[] { new[] { \"baz\", null }, new[] { \"bar\", \"foo\" } } },\n                    [5] = new[] { new[] { new[] { default(string) } }, new[] { Array.Empty<string>(), new[] { \"baz\" } } },\n                    [6] = Array.Empty<string?[][]>(),\n                    [7] = new[] { Array.Empty<string?[]>() }\n                };\n\n                cmd.CommandText = $\"SELECT id, arr FROM {tableName}\";\n                await using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SchemaOnly))\n                {\n                    // Skipping all rows\n                    Assert.False(await reader.ReadAsync());\n                }\n\n                await using (var reader = await cmd.ExecuteReaderAsync())\n                {\n                    while(await reader.ReadAsync())\n                    {\n                        var id = reader.GetInt32(0);\n                        var value = reader.GetValue(1);\n                        var arr = Assert.IsType<string?[][][]>(value);\n                        Assert.True(expectedValues.Remove(id, out var expectedArr));\n                        Assert.Equal(expectedArr, arr);\n                    }\n                }\n\n                Assert.Empty(expectedValues);                \n            }\n        }\n\n        [Theory]\n        [InlineData(\"true::Bool\", true)]\n        [InlineData(\"false::Bool\", false)]\n        [InlineData(\"1::Bool\", true)]\n        [InlineData(\"0::Bool\", false)]\n        [InlineData(\"NULL::Nullable(Bool)\", null)]\n        public async Task ReadBoolScalar(string value, bool? expectedValue)\n        {\n            await using var connection = await OpenConnectionAsync();\n\n            var cmd = connection.CreateCommand();\n            cmd.CommandText = $\"SELECT {value}\";\n\n            var result = await cmd.ExecuteScalarAsync();\n            Assert.Equal((object?)expectedValue ?? DBNull.Value, result);\n        }\n\n        [Theory]\n        [MemberData(nameof(ParameterModes))]\n        public async Task ReadBoolParameter(ClickHouseParameterMode parameterMode)\n        {\n            var testData = new[] { false, true, (object?)null, DBNull.Value };\n\n            await using var connection = await OpenConnectionAsync(parameterMode);\n\n            var sb = new StringBuilder(\"SELECT \");\n            var cmd = connection.CreateCommand();\n            for (int i = 0; i < testData.Length * 2; i++)\n            {\n                var value = testData[i % testData.Length];\n                ClickHouseParameter p;\n                if (value is bool)\n                    p = cmd.Parameters.AddWithValue($\"p{i + 1}\", value);\n                else\n                    p = cmd.Parameters.AddWithValue($\"p{i + 1}\", value, ClickHouseDbType.Boolean);\n\n                if (i > 0)\n                    sb.Append(\", \");\n\n                if (i > testData.Length)\n                    p.ParameterMode = ClickHouseParameterMode.Interpolate;\n\n                sb.Append($\"{{p{i + 1}}} AS p{i + 1}\");\n            }\n\n            cmd.CommandText = sb.ToString();\n            await using var reader = await cmd.ExecuteReaderAsync();\n            Assert.Equal(testData.Length*2, reader.FieldCount);\n\n            Assert.True(await reader.ReadAsync());\n            for (int i = 0; i < testData.Length * 2; i++)\n            {\n                Assert.Equal(ClickHouseDbType.Boolean, reader.GetFieldTypeInfo(i).GetDbType());\n                var value = testData[i % testData.Length];\n\n                if (value is bool boolVal)\n                {\n                    var result = reader.GetValue(i);\n                    Assert.Equal(boolVal, result);\n                }\n                else\n                {\n                    Assert.True(reader.IsDBNull(i));\n                }\n            }\n\n            Assert.False(await reader.ReadAsync());\n        }\n\n        [Fact]\n        public async Task ReadVariant()\n        {\n            const string query = \"SELECT if(number = 1, NULL::Variant(Array(UInt64), UInt64), if(number % 2 != 0, number, range(number))) as variant FROM numbers(5)\";\n\n            await using var connection = await OpenConnectionAsync();\n            var cmd = connection.CreateCommand();\n\n            // TODO: remove when this feature will be non-experimental\n            cmd.CommandText = \"SET allow_experimental_variant_type = 1\";\n            await cmd.ExecuteNonQueryAsync();\n\n            cmd.CommandText = \"SET use_variant_as_common_type = 1\";\n            await cmd.ExecuteNonQueryAsync();\n\n            cmd.CommandText = query;\n            await using var reader = await cmd.ExecuteReaderAsync();\n\n            ulong count = 0;\n            while (await reader.ReadAsync())\n            {\n                var value = reader.GetValue(0);\n                if (count % 2 == 0)\n                {\n                    Assert.False(reader.IsDBNull(0));\n                    var arrValue = Assert.IsType<ulong[]>(value);\n                    Assert.Equal(count, (ulong)arrValue.Length);\n                    for (ulong i = 0; i < count; i++)\n                        Assert.Equal(i, arrValue[i]);\n                }\n                else if (count == 1)\n                {\n                    Assert.IsType<DBNull>(value);\n                    Assert.True(reader.IsDBNull(0));\n                }\n                else\n                {\n                    Assert.False(reader.IsDBNull(0));\n                    var longVal = Assert.IsType<ulong>(value);\n                    Assert.Equal(count, longVal);\n                }\n\n                ++count;\n            }\n        }\n\n        [Fact]\n        public async Task CreateInsertSelectAllKnownNullable()\n        {\n            const string ddl = @\"\nCREATE TABLE clickhouse_test_nullable (\n    int8     Nullable(Int8),\n    int16    Nullable(Int16),\n    int32    Nullable(Int32),\n    int64    Nullable(Int64),\n    uint8    Nullable(UInt8),\n    uint16   Nullable(UInt16),\n    uint32   Nullable(UInt32),\n    uint64   Nullable(UInt64),\n    float32  Nullable(Float32),\n    float64  Nullable(Float64),\n    string   Nullable(String),\n    fString  Nullable(FixedString(2)),\n    date     Nullable(Date),\n    datetime Nullable(DateTime),\n    enum8    Nullable(Enum8 ('a' = 127, 'b' = 2)),\n    enum16   Nullable(Enum16('c' = -32768, 'd' = 2, '' = 42))\n) Engine=Memory;\";\n\n            const string dml = @\"\nINSERT INTO clickhouse_test_nullable (\n    int8\n    ,int16\n    ,int32\n    ,int64\n    ,uint8\n    ,uint16\n    ,uint32\n    ,uint64\n    ,float32\n    ,float64\n    ,string\n    ,fString\n    ,date\n    ,datetime\n    ,enum8\n    ,enum16\n) SELECT\n    8\n    ,16\n    ,32\n    ,64\n    ,18\n    ,116\n    ,132\n    ,165\n    ,1.1\n    ,1.2\n    ,'RU'\n    ,'UA'\n    ,now()\n    ,now()\n    ,'a'\n    ,'c'\";\n\n            const string query = @\"\nSELECT\n    int8\n    ,int16\n    ,int32\n    ,int64\n    ,uint8\n    ,uint16\n    ,uint32\n    ,uint64\n    ,float32\n    ,float64\n    ,string\n    ,fString\n    ,date\n    ,datetime\n    ,enum8\n    ,enum16\nFROM clickhouse_test_nullable\";\n\n            try\n            {\n                await using var connection = await OpenConnectionAsync();\n                \n                await using var cmdDrop = connection.CreateCommand(\"DROP TABLE IF EXISTS clickhouse_test_nullable \");\n                var ddlResult = await cmdDrop.ExecuteNonQueryAsync();\n                Assert.Equal(0, ddlResult);\n\n                await using var cmd = connection.CreateCommand(ddl);\n                var result = await cmd.ExecuteNonQueryAsync();\n                Assert.Equal(0, result);\n\n                await using var dmlcmd = connection.CreateCommand(dml);\n                result = await dmlcmd.ExecuteNonQueryAsync();\n                if (!connection.ServerVersion.StartsWith(\"21.11.\"))\n                {\n                    // The server of the version 21.11 doesn't send profile events.\n                    Assert.Equal(1, result);\n                }\n\n                await using var queryCmd = connection.CreateCommand(query);\n                var r = await queryCmd.ExecuteReaderAsync();\n                while (r.Read())\n                {\n                    Assert.Equal(typeof(sbyte), r.GetFieldType(0));\n                    Assert.Equal((sbyte) 8, r.GetFieldValue<sbyte>(0)); //int8\n                    Assert.Equal((sbyte) 8, r.GetValue(0));\n\n                    Assert.Equal(typeof(short), r.GetFieldType(1));\n                    Assert.Equal((short) 16, r.GetInt16(1));\n                    Assert.Equal((short) 16, r.GetValue(1));\n\n                    Assert.Equal(typeof(int), r.GetFieldType(2));\n                    Assert.Equal((int) 32, r.GetInt32(2));\n                    Assert.Equal((int) 32, r.GetValue(2));\n\n                    Assert.Equal(typeof(long), r.GetFieldType(3));\n                    Assert.Equal((long) 64, r.GetInt64(3));\n                    Assert.Equal((long) 64, r.GetValue(3));\n\n                    Assert.Equal(typeof(byte), r.GetFieldType(4));\n                    Assert.Equal((byte) 18, r.GetFieldValue<byte>(4)); //uint8\n                    Assert.Equal((byte) 18, r.GetValue(4));\n\n                    Assert.Equal(typeof(ushort), r.GetFieldType(5));\n                    Assert.Equal((ushort) 116, r.GetFieldValue<ushort>(5));\n                    Assert.Equal((ushort) 116, r.GetValue(5));\n\n                    Assert.Equal(typeof(uint), r.GetFieldType(6));\n                    Assert.Equal((uint) 132, r.GetFieldValue<uint>(6));\n                    Assert.Equal((uint) 132, r.GetValue(6));\n\n                    Assert.Equal(typeof(ulong), r.GetFieldType(7));\n                    Assert.Equal((UInt64) 165, r.GetFieldValue<UInt64>(7));\n                    Assert.Equal((UInt64) 165, r.GetValue(7));\n\n                    Assert.Equal(typeof(float), r.GetFieldType(8));\n                    Assert.Equal((float) 1.1, r.GetFloat(8));\n                    Assert.Equal((float) 1.1, r.GetValue(8));\n\n                    Assert.Equal(typeof(double), r.GetFieldType(9));\n                    Assert.Equal((double) 1.2, r.GetDouble(9));\n                    Assert.Equal((double) 1.2, r.GetValue(9));\n\n                    Assert.Equal(typeof(string), r.GetFieldType(10));\n                    Assert.Equal(\"RU\", r.GetString(10));\n                    Assert.Equal(\"RU\", r.GetValue(10));\n\n                    Assert.Equal(typeof(byte[]), r.GetFieldType(11));\n                    var fixedStringBytes = r.GetFieldValue<byte[]>(11);\n                    var fixedStringBytesAsValue = r.GetValue(11) as byte[];\n                    Assert.Equal(fixedStringBytes, fixedStringBytesAsValue);\n                    Assert.NotNull(fixedStringBytes as byte[]);\n                    Assert.Equal(\"UA\", Encoding.Default.GetString(fixedStringBytes as byte[]));\n\n                    Assert.Equal(typeof(string), r.GetFieldType(14));\n                    Assert.Equal(\"a\", r.GetValue(14));\n                    Assert.Equal(127, r.GetFieldValue<sbyte>(14));\n\n                    Assert.Equal(typeof(string), r.GetFieldType(15));\n                    Assert.Equal(\"c\", r.GetValue(15));\n                    Assert.Equal(-32768, r.GetInt16(15));\n                }\n            }\n            finally\n            {\n                await using var connection = await OpenConnectionAsync();\n                await using var cmdDrop = connection.CreateCommand(\"DROP TABLE IF EXISTS clickhouse_test_nullable \");\n                await cmdDrop.ExecuteNonQueryAsync();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Octonica.ClickHouseClient.Tests/xunit.runner.json",
    "content": "{\n  \"parallelizeAssembly\": false,\n  \"parallelizeTestCollections\": false\n}"
  },
  {
    "path": "src/Octonica.ClickHouseClient.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.29319.158\nMinimumVisualStudioVersion = 15.0.26124.0\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Octonica.ClickHouseClient\", \"Octonica.ClickHouseClient\\Octonica.ClickHouseClient.csproj\", \"{7D934752-DF65-47D4-B183-9D1D6A8E4BDA}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Octonica.ClickHouseClient.Tests\", \"Octonica.ClickHouseClient.Tests\\Octonica.ClickHouseClient.Tests.csproj\", \"{AEE192D3-405E-4804-9883-10A4F9FC902E}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Octonica.ClickHouseClient.Benchmarks\", \"Octonica.ClickHouseClient.Benchmarks\\Octonica.ClickHouseClient.Benchmarks.csproj\", \"{71FA7684-9E05-4F20-A45C-8509CCA4E2D5}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{7D934752-DF65-47D4-B183-9D1D6A8E4BDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{7D934752-DF65-47D4-B183-9D1D6A8E4BDA}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{7D934752-DF65-47D4-B183-9D1D6A8E4BDA}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{7D934752-DF65-47D4-B183-9D1D6A8E4BDA}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{AEE192D3-405E-4804-9883-10A4F9FC902E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{AEE192D3-405E-4804-9883-10A4F9FC902E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{AEE192D3-405E-4804-9883-10A4F9FC902E}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{AEE192D3-405E-4804-9883-10A4F9FC902E}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{71FA7684-9E05-4F20-A45C-8509CCA4E2D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{71FA7684-9E05-4F20-A45C-8509CCA4E2D5}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{71FA7684-9E05-4F20-A45C-8509CCA4E2D5}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{71FA7684-9E05-4F20-A45C-8509CCA4E2D5}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {D02DF3C7-18AB-4D69-8819-6E4A21DC620C}\n\tEndGlobalSection\nEndGlobal\n"
  }
]