├── .gitattributes ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── bench ├── CMakeLists.txt └── bench.cpp ├── clickhouse ├── CMakeLists.txt ├── base │ ├── buffer.h │ ├── coded.cpp │ ├── coded.h │ ├── compressed.cpp │ ├── compressed.h │ ├── input.cpp │ ├── input.h │ ├── output.cpp │ ├── output.h │ ├── platform.cpp │ ├── platform.h │ ├── singleton.h │ ├── socket.cpp │ ├── socket.h │ ├── string_utils.h │ └── wire_format.h ├── block.cpp ├── block.h ├── client.cpp ├── client.h ├── columns │ ├── array.cpp │ ├── array.h │ ├── column.h │ ├── date.cpp │ ├── date.h │ ├── decimal.cpp │ ├── decimal.h │ ├── enum.cpp │ ├── enum.h │ ├── factory.cpp │ ├── factory.h │ ├── ip4.cpp │ ├── ip4.h │ ├── ip6.cpp │ ├── ip6.h │ ├── nothing.h │ ├── nullable.cpp │ ├── nullable.h │ ├── numeric.cpp │ ├── numeric.h │ ├── string.cpp │ ├── string.h │ ├── tuple.cpp │ ├── tuple.h │ ├── utils.h │ ├── uuid.cpp │ └── uuid.h ├── error_codes.h ├── exceptions.h ├── protocol.h ├── query.cpp ├── query.h └── types │ ├── type_parser.cpp │ ├── type_parser.h │ ├── types.cpp │ └── types.h ├── cmake ├── cpp17.cmake └── subdirs.cmake ├── contrib ├── absl │ ├── CMakeLists.txt │ ├── base │ │ ├── attributes.h │ │ ├── config.h │ │ ├── internal │ │ │ └── bits.h │ │ ├── macros.h │ │ ├── optimization.h │ │ ├── options.h │ │ ├── policy_checks.h │ │ └── port.h │ └── numeric │ │ ├── int128.cc │ │ ├── int128.h │ │ ├── int128_have_intrinsic.inc │ │ └── int128_no_intrinsic.inc ├── cityhash │ ├── CMakeLists.txt │ ├── COPYING │ ├── city.cc │ ├── city.h │ ├── citycrc.h │ └── config.h ├── gtest │ ├── CMakeLists.txt │ ├── gtest-all.cc │ └── gtest.h └── lz4 │ ├── CMakeLists.txt │ ├── LICENSE │ ├── lz4.c │ ├── lz4.h │ ├── lz4hc.c │ └── lz4hc.h ├── tests └── simple │ ├── CMakeLists.txt │ └── main.cpp └── ut ├── CMakeLists.txt ├── client_ut.cpp ├── columns_ut.cpp ├── main.cpp ├── socket_ut.cpp ├── stream_ut.cpp ├── tcp_server.cpp ├── tcp_server.h ├── type_parser_ut.cpp └── types_ut.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | # Temporary solution for VS 2015 13 | vs/ 14 | # Visual Studio Code 15 | .vscode/ 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | bld/ 25 | build/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # DNX 49 | project.lock.json 50 | project.fragment.lock.json 51 | artifacts/ 52 | 53 | *_i.c 54 | *_p.c 55 | *_i.h 56 | *.ilk 57 | *.meta 58 | *.obj 59 | *.pch 60 | *.pdb 61 | *.pgc 62 | *.pgd 63 | *.rsp 64 | *.sbr 65 | *.tlb 66 | *.tli 67 | *.tlh 68 | *.tmp 69 | *.tmp_proj 70 | *.log 71 | *.vspscc 72 | *.vssscc 73 | .builds 74 | *.pidb 75 | *.svclog 76 | *.scc 77 | 78 | # Chutzpah Test files 79 | _Chutzpah* 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opendb 86 | *.opensdf 87 | *.sdf 88 | *.cachefile 89 | *.VC.db 90 | *.VC.VC.opendb 91 | 92 | # Visual Studio profiler 93 | *.psess 94 | *.vsp 95 | *.vspx 96 | *.sap 97 | 98 | # TFS 2012 Local Workspace 99 | $tf/ 100 | 101 | # Guidance Automation Toolkit 102 | *.gpState 103 | 104 | # ReSharper is a .NET coding add-in 105 | _ReSharper*/ 106 | *.[Rr]e[Ss]harper 107 | *.DotSettings.user 108 | 109 | # JustCode is a .NET coding add-in 110 | .JustCode 111 | 112 | # TeamCity is a build add-in 113 | _TeamCity* 114 | 115 | # DotCover is a Code Coverage Tool 116 | *.dotCover 117 | 118 | # NCrunch 119 | _NCrunch_* 120 | .*crunch*.local.xml 121 | nCrunchTemp_* 122 | 123 | # MightyMoose 124 | *.mm.* 125 | AutoTest.Net/ 126 | 127 | # Web workbench (sass) 128 | .sass-cache/ 129 | 130 | # Installshield output folder 131 | [Ee]xpress/ 132 | 133 | # DocProject is a documentation generator add-in 134 | DocProject/buildhelp/ 135 | DocProject/Help/*.HxT 136 | DocProject/Help/*.HxC 137 | DocProject/Help/*.hhc 138 | DocProject/Help/*.hhk 139 | DocProject/Help/*.hhp 140 | DocProject/Help/Html2 141 | DocProject/Help/html 142 | 143 | # Click-Once directory 144 | publish/ 145 | 146 | # Publish Web Output 147 | *.[Pp]ublish.xml 148 | *.azurePubxml 149 | # TODO: Comment the next line if you want to checkin your web deploy settings 150 | # but database connection strings (with potential passwords) will be unencrypted 151 | #*.pubxml 152 | *.publishproj 153 | 154 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 155 | # checkin your Azure Web App publish settings, but sensitive information contained 156 | # in these scripts will be unencrypted 157 | PublishScripts/ 158 | 159 | # NuGet Packages 160 | *.nupkg 161 | # The packages folder can be ignored because of Package Restore 162 | **/packages/* 163 | # except build/, which is used as an MSBuild target. 164 | !**/packages/build/ 165 | # Uncomment if necessary however generally it will be regenerated when needed 166 | #!**/packages/repositories.config 167 | # NuGet v3's project.json files produces more ignoreable files 168 | *.nuget.props 169 | *.nuget.targets 170 | 171 | # Microsoft Azure Build Output 172 | csx/ 173 | *.build.csdef 174 | 175 | # Microsoft Azure Emulator 176 | ecf/ 177 | rcf/ 178 | 179 | # Windows Store app package directories and files 180 | AppPackages/ 181 | BundleArtifacts/ 182 | Package.StoreAssociation.xml 183 | _pkginfo.txt 184 | 185 | # Visual Studio cache files 186 | # files ending in .cache can be ignored 187 | *.[Cc]ache 188 | # but keep track of directories ending in .cache 189 | !*.[Cc]ache/ 190 | 191 | # Others 192 | ClientBin/ 193 | ~$* 194 | *~ 195 | *.dbmdl 196 | *.dbproj.schemaview 197 | *.jfm 198 | *.pfx 199 | *.publishsettings 200 | node_modules/ 201 | orleans.codegen.cs 202 | 203 | # Since there are multiple workflows, uncomment next line to ignore bower_components 204 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 205 | #bower_components/ 206 | 207 | # RIA/Silverlight projects 208 | Generated_Code/ 209 | 210 | # Backup & report files from converting an old project file 211 | # to a newer Visual Studio version. Backup files are not needed, 212 | # because we have git ;-) 213 | _UpgradeReport_Files/ 214 | Backup*/ 215 | UpgradeLog*.XML 216 | UpgradeLog*.htm 217 | 218 | # SQL Server files 219 | *.mdf 220 | *.ldf 221 | 222 | # Business Intelligence projects 223 | *.rdl.data 224 | *.bim.layout 225 | *.bim_*.settings 226 | 227 | # Microsoft Fakes 228 | FakesAssemblies/ 229 | 230 | # GhostDoc plugin setting file 231 | *.GhostDoc.xml 232 | 233 | # Node.js Tools for Visual Studio 234 | .ntvs_analysis.dat 235 | 236 | # Visual Studio 6 build log 237 | *.plg 238 | 239 | # Visual Studio 6 workspace options file 240 | *.opt 241 | 242 | # Visual Studio LightSwitch build output 243 | **/*.HTMLClient/GeneratedArtifacts 244 | **/*.DesktopClient/GeneratedArtifacts 245 | **/*.DesktopClient/ModelManifest.xml 246 | **/*.Server/GeneratedArtifacts 247 | **/*.Server/ModelManifest.xml 248 | _Pvt_Extensions 249 | 250 | # Paket dependency manager 251 | .paket/paket.exe 252 | paket-files/ 253 | 254 | # FAKE - F# Make 255 | .fake/ 256 | 257 | # JetBrains Rider 258 | .idea/ 259 | *.sln.iml 260 | 261 | # CodeRush 262 | .cr/ 263 | 264 | # Python Tools for Visual Studio (PTVS) 265 | __pycache__/ 266 | *.pyc 267 | 268 | # Buck 269 | /buck-out/ 270 | /.buckd/ 271 | /buckaroo/ 272 | .buckconfig.local 273 | BUCKAROO_DEPS 274 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Enable C++ support 2 | language: cpp 3 | 4 | matrix: 5 | include: 6 | - os: linux 7 | dist: bionic 8 | compiler: gcc 9 | addons: 10 | apt: 11 | sources: 12 | - ubuntu-toolchain-r-test 13 | packages: 14 | - g++-7 15 | env: 16 | - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" 17 | 18 | - os: linux 19 | dist: bionic 20 | compiler: clang 21 | addons: 22 | apt: 23 | packages: 24 | - g++-7 25 | - clang-7 26 | - libstdc++6 27 | env: 28 | - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" 29 | 30 | - os: osx 31 | osx_image: xcode9.4 32 | compiler: clang 33 | 34 | before_install: 35 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo 'deb http://repo.yandex.ru/clickhouse/deb/stable main/' | sudo tee /etc/apt/sources.list.d/clickhouse.list ; fi 36 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-key adv --keyserver keyserver.ubuntu.com --recv E0C56BD4 ; fi 37 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update -q && sudo apt-get install -q -y clickhouse-server-common ; fi 38 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo service clickhouse-server start ; fi 39 | 40 | # Build steps 41 | script: 42 | - eval "${MATRIX_EVAL}" 43 | - mkdir build 44 | - cd build 45 | - cmake .. -DBUILD_TESTS=ON && make 46 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./ut/clickhouse-cpp-ut ; fi 47 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./ut/clickhouse-cpp-ut --gtest_filter='-Client/*' ; fi 48 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2) 2 | 3 | INCLUDE (cmake/cpp17.cmake) 4 | INCLUDE (cmake/subdirs.cmake) 5 | 6 | OPTION(BUILD_BENCHMARK "Build benchmark" OFF) 7 | OPTION(BUILD_TESTS "Build tests" OFF) 8 | 9 | PROJECT (CLICKHOUSE-CLIENT) 10 | 11 | USE_CXX17() 12 | 13 | IF ("${CMAKE_BUILD_TYPE}" STREQUAL "") 14 | set(CMAKE_BUILD_TYPE "Debug") 15 | ENDIF() 16 | 17 | IF (UNIX) 18 | IF (APPLE) 19 | SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wall -Wextra -Werror") 20 | ELSE () 21 | SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -pthread -Wall -Wextra -Werror") 22 | ENDIF () 23 | SET (CMAKE_EXE_LINKER_FLAGS, "${CMAKE_EXE_LINKER_FLAGS} -lpthread") 24 | ENDIF () 25 | 26 | INCLUDE_DIRECTORIES(.) 27 | INCLUDE_DIRECTORIES(contrib) 28 | 29 | SUBDIRS ( 30 | clickhouse 31 | contrib/absl 32 | contrib/cityhash 33 | contrib/lz4 34 | ) 35 | 36 | IF (BUILD_BENCHMARK) 37 | SUBDIRS(bench) 38 | ENDIF (BUILD_BENCHMARK) 39 | 40 | IF (BUILD_TESTS) 41 | SUBDIRS( 42 | contrib/gtest 43 | tests/simple 44 | ut 45 | ) 46 | ENDIF (BUILD_TESTS) 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 - 2020 Pavel Artemkin 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ClickHouse C++ client 2 | ===== 3 | 4 | C++ client for [ClickHouse](https://clickhouse.tech/) 5 | 6 | **NOTE:** The library is no longer supported. Please use https://github.com/ClickHouse/clickhouse-cpp. 7 | 8 | ## Supported data types 9 | 10 | * Array(T) 11 | * Date 12 | * DateTime([timezone]), DateTime64(N, [timezone]) 13 | * Decimal32, Decimal64, Decimal128 14 | * Enum8, Enum16 15 | * FixedString(N) 16 | * Float32, Float64 17 | * IPv4, IPv6 18 | * Nullable(T) 19 | * String 20 | * Tuple 21 | * UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64 22 | 23 | ## C++ version 24 | 25 | Currently, minimal version of C++ standard is 17, for C++ 11 see [1.x release line](https://github.com/artpaul/clickhouse-cpp/releases). 26 | 27 | ## Building 28 | 29 | ```sh 30 | $ mkdir build . 31 | $ cd build 32 | $ cmake .. [-DBUILD_TESTS=ON] 33 | $ make 34 | ``` 35 | 36 | ## Example 37 | 38 | ```cpp 39 | #include 40 | 41 | using namespace clickhouse; 42 | 43 | /// Initialize client connection. 44 | Client client(ClientOptions().SetHost("localhost")); 45 | 46 | /// Create a table. 47 | client.Execute("CREATE TABLE IF NOT EXISTS test.numbers (id UInt64, name String) ENGINE = Memory"); 48 | 49 | /// Insert some values. 50 | { 51 | Block block; 52 | 53 | auto id = std::make_shared(); 54 | id->Append(1); 55 | id->Append(7); 56 | 57 | auto name = std::make_shared(); 58 | name->Append("one"); 59 | name->Append("seven"); 60 | 61 | block.AppendColumn("id" , id); 62 | block.AppendColumn("name", name); 63 | 64 | client.Insert("test.numbers", block); 65 | } 66 | 67 | /// Select values inserted in the previous step. 68 | client.Select("SELECT id, name FROM test.numbers", [] (const Block& block) 69 | { 70 | for (size_t i = 0; i < block.GetRowCount(); ++i) { 71 | std::cout << block[0]->As()->At(i) << " " 72 | << block[1]->As()->At(i) << "\n"; 73 | } 74 | } 75 | ); 76 | 77 | /// Delete table. 78 | client.Execute("DROP TABLE test.numbers"); 79 | ``` 80 | -------------------------------------------------------------------------------- /bench/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_EXECUTABLE (bench 2 | bench.cpp 3 | ) 4 | 5 | TARGET_LINK_LIBRARIES (bench 6 | clickhouse-cpp-lib 7 | benchmark 8 | ) 9 | -------------------------------------------------------------------------------- /bench/bench.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace clickhouse { 6 | 7 | Client g_client(ClientOptions() 8 | .SetHost("localhost") 9 | .SetPingBeforeQuery(false)); 10 | 11 | static void SelectNumber(benchmark::State& state) { 12 | while (state.KeepRunning()) { 13 | g_client.Select("SELECT number, number, number FROM system.numbers LIMIT 1000", 14 | [](const Block& block) { block.GetRowCount(); } 15 | ); 16 | } 17 | } 18 | BENCHMARK(SelectNumber); 19 | 20 | static void SelectNumberMoreColumns(benchmark::State& state) { 21 | // Mainly test performance on type name parsing. 22 | while (state.KeepRunning()) { 23 | g_client.Select("SELECT " 24 | "number, number, number, number, number, number, number, number, number, number " 25 | "FROM system.numbers LIMIT 100", 26 | [](const Block& block) { block.GetRowCount(); } 27 | ); 28 | } 29 | } 30 | BENCHMARK(SelectNumberMoreColumns); 31 | 32 | } 33 | 34 | BENCHMARK_MAIN(); 35 | -------------------------------------------------------------------------------- /clickhouse/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | SET ( clickhouse-cpp-lib-src 2 | base/coded.cpp 3 | base/compressed.cpp 4 | base/input.cpp 5 | base/output.cpp 6 | base/platform.cpp 7 | base/socket.cpp 8 | 9 | columns/array.cpp 10 | columns/date.cpp 11 | columns/decimal.cpp 12 | columns/enum.cpp 13 | columns/factory.cpp 14 | columns/ip4.cpp 15 | columns/ip6.cpp 16 | columns/nullable.cpp 17 | columns/numeric.cpp 18 | columns/string.cpp 19 | columns/tuple.cpp 20 | columns/uuid.cpp 21 | 22 | types/type_parser.cpp 23 | types/types.cpp 24 | 25 | block.cpp 26 | client.cpp 27 | query.cpp 28 | ) 29 | 30 | ADD_LIBRARY (clickhouse-cpp-lib SHARED ${clickhouse-cpp-lib-src}) 31 | 32 | SET_TARGET_PROPERTIES(clickhouse-cpp-lib 33 | PROPERTIES LINKER_LANGUAGE CXX) 34 | 35 | TARGET_LINK_LIBRARIES (clickhouse-cpp-lib 36 | absl-lib 37 | cityhash-lib 38 | lz4-lib 39 | ) 40 | 41 | ADD_LIBRARY (clickhouse-cpp-lib-static STATIC ${clickhouse-cpp-lib-src}) 42 | TARGET_LINK_LIBRARIES (clickhouse-cpp-lib-static 43 | absl-lib 44 | cityhash-lib 45 | lz4-lib 46 | ) 47 | -------------------------------------------------------------------------------- /clickhouse/base/buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace clickhouse { 7 | 8 | using Buffer = std::vector; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /clickhouse/base/coded.cpp: -------------------------------------------------------------------------------- 1 | #include "coded.h" 2 | 3 | #include 4 | 5 | namespace clickhouse { 6 | 7 | static const int MAX_VARINT_BYTES = 10; 8 | 9 | CodedInputStream::CodedInputStream(ZeroCopyInput* input) 10 | : input_(input) 11 | { 12 | } 13 | 14 | bool CodedInputStream::ReadRaw(void* buffer, size_t size) { 15 | uint8_t* p = static_cast(buffer); 16 | 17 | while (size > 0) { 18 | const void* ptr; 19 | size_t len = input_->Next(&ptr, size); 20 | 21 | memcpy(p, ptr, len); 22 | 23 | p += len; 24 | size -= len; 25 | } 26 | 27 | return true; 28 | } 29 | 30 | bool CodedInputStream::Skip(size_t count) { 31 | while (count > 0) { 32 | const void* ptr; 33 | size_t len = input_->Next(&ptr, count); 34 | 35 | if (len == 0) { 36 | return false; 37 | } 38 | 39 | count -= len; 40 | } 41 | 42 | return true; 43 | } 44 | 45 | bool CodedInputStream::ReadVarint64(uint64_t* value) { 46 | *value = 0; 47 | 48 | for (size_t i = 0; i < MAX_VARINT_BYTES; ++i) { 49 | uint8_t byte; 50 | 51 | if (!input_->ReadByte(&byte)) { 52 | return false; 53 | } else { 54 | *value |= uint64_t(byte & 0x7F) << (7 * i); 55 | 56 | if (!(byte & 0x80)) { 57 | return true; 58 | } 59 | } 60 | } 61 | 62 | // TODO skip invalid 63 | return false; 64 | } 65 | 66 | 67 | CodedOutputStream::CodedOutputStream(ZeroCopyOutput* output) 68 | : output_(output) 69 | { 70 | } 71 | 72 | void CodedOutputStream::Flush() { 73 | output_->Flush(); 74 | } 75 | 76 | void CodedOutputStream::WriteRaw(const void* buffer, int size) { 77 | output_->Write(buffer, size); 78 | } 79 | 80 | void CodedOutputStream::WriteVarint64(uint64_t value) { 81 | uint8_t bytes[MAX_VARINT_BYTES]; 82 | int size = 0; 83 | 84 | for (size_t i = 0; i < MAX_VARINT_BYTES; ++i) { 85 | uint8_t byte = value & 0x7F; 86 | if (value > 0x7F) 87 | byte |= 0x80; 88 | 89 | bytes[size++] = byte; 90 | 91 | value >>= 7; 92 | if (!value) { 93 | break; 94 | } 95 | } 96 | 97 | WriteRaw(bytes, size); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /clickhouse/base/coded.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "input.h" 4 | #include "output.h" 5 | 6 | #include 7 | 8 | namespace clickhouse { 9 | 10 | /** 11 | * Class which reads and decodes binary data which is composed of varint- 12 | * encoded integers and fixed-width pieces. 13 | */ 14 | class CodedInputStream { 15 | public: 16 | /// Create a CodedInputStream that reads from the given ZeroCopyInput. 17 | explicit CodedInputStream(ZeroCopyInput* input); 18 | 19 | // Read an unsigned integer with Varint encoding, truncating to 32 bits. 20 | // Reading a 32-bit value is equivalent to reading a 64-bit one and casting 21 | // it to uint32, but may be more efficient. 22 | bool ReadVarint32(uint32_t* value); 23 | 24 | // Read an unsigned integer with Varint encoding. 25 | bool ReadVarint64(uint64_t* value); 26 | 27 | // Read raw bytes, copying them into the given buffer. 28 | bool ReadRaw(void* buffer, size_t size); 29 | 30 | // Like ReadRaw, but reads into a string. 31 | // 32 | // Implementation Note: ReadString() grows the string gradually as it 33 | // reads in the data, rather than allocating the entire requested size 34 | // upfront. This prevents denial-of-service attacks in which a client 35 | // could claim that a string is going to be MAX_INT bytes long in order to 36 | // crash the server because it can't allocate this much space at once. 37 | bool ReadString(std::string* buffer, int size); 38 | 39 | // Skips a number of bytes. Returns false if an underlying read error 40 | // occurs. 41 | bool Skip(size_t count); 42 | 43 | private: 44 | ZeroCopyInput* input_; 45 | }; 46 | 47 | 48 | class CodedOutputStream { 49 | public: 50 | /// Create a CodedInputStream that writes to the given ZeroCopyOutput. 51 | explicit CodedOutputStream(ZeroCopyOutput* output); 52 | 53 | void Flush(); 54 | 55 | // Write raw bytes, copying them from the given buffer. 56 | void WriteRaw(const void* buffer, int size); 57 | 58 | /// Write an unsigned integer with Varint encoding. 59 | void WriteVarint64(const uint64_t value); 60 | 61 | private: 62 | ZeroCopyOutput* output_; 63 | }; 64 | 65 | } 66 | -------------------------------------------------------------------------------- /clickhouse/base/compressed.cpp: -------------------------------------------------------------------------------- 1 | #include "compressed.h" 2 | #include "wire_format.h" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #define DBMS_MAX_COMPRESSED_SIZE 0x40000000ULL // 1GB 10 | 11 | namespace clickhouse { 12 | 13 | CompressedInput::CompressedInput(CodedInputStream* input) 14 | : input_(input) 15 | { 16 | } 17 | 18 | size_t CompressedInput::DoNext(const void** ptr, size_t len) { 19 | if (mem_.Exhausted()) { 20 | if (!Decompress()) { 21 | return 0; 22 | } 23 | } 24 | 25 | return mem_.Next(ptr, len); 26 | } 27 | 28 | bool CompressedInput::Decompress() { 29 | uint128 hash; 30 | uint32_t compressed = 0; 31 | uint32_t original = 0; 32 | uint8_t method = 0; 33 | 34 | if (!WireFormat::ReadFixed(input_, &hash)) { 35 | return false; 36 | } 37 | if (!WireFormat::ReadFixed(input_, &method)) { 38 | return false; 39 | } 40 | 41 | if (method != 0x82) { 42 | throw std::runtime_error("unsupported compression method " + 43 | std::to_string(int(method))); 44 | } else { 45 | if (!WireFormat::ReadFixed(input_, &compressed)) { 46 | return false; 47 | } 48 | if (!WireFormat::ReadFixed(input_, &original)) { 49 | return false; 50 | } 51 | 52 | if (compressed > DBMS_MAX_COMPRESSED_SIZE) { 53 | throw std::runtime_error("compressed data too big"); 54 | } 55 | 56 | Buffer tmp(compressed); 57 | 58 | // Заполнить заголовок сжатых данных. 59 | { 60 | BufferOutput out(&tmp); 61 | out.Write(&method, sizeof(method)); 62 | out.Write(&compressed, sizeof(compressed)); 63 | out.Write(&original, sizeof(original)); 64 | } 65 | 66 | if (!WireFormat::ReadBytes(input_, tmp.data() + 9, compressed - 9)) { 67 | return false; 68 | } else { 69 | if (hash != CityHash128((const char*)tmp.data(), compressed)) { 70 | throw std::runtime_error("data was corrupted"); 71 | } 72 | } 73 | 74 | data_ = Buffer(original); 75 | 76 | if (LZ4_decompress_fast((const char*)tmp.data() + 9, (char*)data_.data(), original) < 0) { 77 | throw std::runtime_error("can't decompress data"); 78 | } else { 79 | mem_.Reset(data_.data(), original); 80 | } 81 | } 82 | 83 | return true; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /clickhouse/base/compressed.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "coded.h" 4 | 5 | namespace clickhouse { 6 | 7 | class CompressedInput : public ZeroCopyInput { 8 | public: 9 | CompressedInput(CodedInputStream* input); 10 | 11 | protected: 12 | size_t DoNext(const void** ptr, size_t len) override; 13 | 14 | bool Decompress(); 15 | 16 | private: 17 | CodedInputStream* const input_; 18 | 19 | Buffer data_; 20 | ArrayInput mem_; 21 | }; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /clickhouse/base/input.cpp: -------------------------------------------------------------------------------- 1 | #include "input.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace clickhouse { 7 | 8 | size_t ZeroCopyInput::DoRead(void* buf, size_t len) { 9 | const void* ptr; 10 | size_t result = DoNext(&ptr, len); 11 | 12 | if (result) { 13 | memcpy(buf, ptr, result); 14 | } 15 | 16 | return result; 17 | } 18 | 19 | ArrayInput::ArrayInput() noexcept 20 | : data_(nullptr) 21 | , len_(0) 22 | { 23 | } 24 | 25 | ArrayInput::ArrayInput(const void* buf, size_t len) noexcept 26 | : data_(static_cast(buf)) 27 | , len_(len) 28 | { 29 | } 30 | 31 | ArrayInput::~ArrayInput() = default; 32 | 33 | size_t ArrayInput::DoNext(const void** ptr, size_t len) { 34 | len = std::min(len_, len); 35 | 36 | *ptr = data_; 37 | len_ -= len; 38 | data_ += len; 39 | 40 | return len; 41 | } 42 | 43 | 44 | BufferedInput::BufferedInput(InputStream* slave, size_t buflen) 45 | : slave_(slave) 46 | , array_input_(nullptr, 0) 47 | , buffer_(buflen) 48 | { 49 | } 50 | 51 | BufferedInput::~BufferedInput() = default; 52 | 53 | void BufferedInput::Reset() { 54 | array_input_.Reset(nullptr, 0); 55 | } 56 | 57 | size_t BufferedInput::DoNext(const void** ptr, size_t len) { 58 | if (array_input_.Exhausted()) { 59 | array_input_.Reset( 60 | buffer_.data(), slave_->Read(buffer_.data(), buffer_.size()) 61 | ); 62 | } 63 | 64 | return array_input_.Next(ptr, len); 65 | } 66 | 67 | size_t BufferedInput::DoRead(void* buf, size_t len) { 68 | if (array_input_.Exhausted()) { 69 | if (len > buffer_.size() / 2) { 70 | return slave_->Read(buf, len); 71 | } 72 | 73 | array_input_.Reset( 74 | buffer_.data(), slave_->Read(buffer_.data(), buffer_.size()) 75 | ); 76 | } 77 | 78 | return array_input_.Read(buf, len); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /clickhouse/base/input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace clickhouse { 8 | 9 | class InputStream { 10 | public: 11 | virtual ~InputStream() noexcept (false) 12 | { } 13 | 14 | /// Reads one byte from the stream. 15 | inline bool ReadByte(uint8_t* byte) { 16 | return DoRead(byte, sizeof(uint8_t)) == sizeof(uint8_t); 17 | } 18 | 19 | /// Reads some data from the stream. 20 | inline size_t Read(void* buf, size_t len) { 21 | return DoRead(buf, len); 22 | } 23 | 24 | protected: 25 | virtual size_t DoRead(void* buf, size_t len) = 0; 26 | }; 27 | 28 | 29 | class ZeroCopyInput : public InputStream { 30 | public: 31 | inline size_t Next(const void** buf, size_t len) { 32 | return DoNext(buf, len); 33 | } 34 | 35 | protected: 36 | virtual size_t DoNext(const void** ptr, size_t len) = 0; 37 | 38 | size_t DoRead(void* buf, size_t len) override; 39 | }; 40 | 41 | 42 | /** 43 | * A ZeroCopyInput stream backed by an in-memory array of bytes. 44 | */ 45 | class ArrayInput : public ZeroCopyInput { 46 | public: 47 | ArrayInput() noexcept; 48 | ArrayInput(const void* buf, size_t len) noexcept; 49 | ~ArrayInput() override; 50 | 51 | /// Number of bytes available in the stream. 52 | inline size_t Avail() const noexcept { 53 | return len_; 54 | } 55 | 56 | /// Current read position in the memory block used by this stream. 57 | inline const uint8_t* Data() const noexcept { 58 | return data_; 59 | } 60 | 61 | /// Whether there is more data in the stream. 62 | inline bool Exhausted() const noexcept { 63 | return !Avail(); 64 | } 65 | 66 | inline void Reset(const void* buf, size_t len) noexcept { 67 | data_ = static_cast(buf); 68 | len_ = len; 69 | } 70 | 71 | private: 72 | size_t DoNext(const void** ptr, size_t len) override; 73 | 74 | private: 75 | const uint8_t* data_; 76 | size_t len_; 77 | }; 78 | 79 | 80 | class BufferedInput : public ZeroCopyInput { 81 | public: 82 | BufferedInput(InputStream* slave, size_t buflen = 8192); 83 | ~BufferedInput() override; 84 | 85 | void Reset(); 86 | 87 | protected: 88 | size_t DoRead(void* buf, size_t len) override; 89 | size_t DoNext(const void** ptr, size_t len) override; 90 | 91 | private: 92 | InputStream* const slave_; 93 | ArrayInput array_input_; 94 | std::vector buffer_; 95 | }; 96 | 97 | } 98 | -------------------------------------------------------------------------------- /clickhouse/base/output.cpp: -------------------------------------------------------------------------------- 1 | #include "output.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace clickhouse { 8 | 9 | void ZeroCopyOutput::DoWrite(const void* data, size_t len) { 10 | while (len > 0) { 11 | void* ptr; 12 | size_t result = DoNext(&ptr, len); 13 | 14 | if (result) { 15 | memcpy(ptr, data, result); 16 | len -= result; 17 | data = static_cast(data) + result; 18 | } else { 19 | break; 20 | } 21 | } 22 | } 23 | 24 | 25 | ArrayOutput::ArrayOutput(void* buf, size_t len) 26 | : buf_(static_cast(buf)) 27 | , end_(buf_ + len) 28 | { 29 | } 30 | 31 | ArrayOutput::~ArrayOutput() = default; 32 | 33 | size_t ArrayOutput::DoNext(void** data, size_t len) { 34 | len = std::min(len, Avail()); 35 | 36 | *data = buf_; 37 | buf_ += len; 38 | 39 | return len; 40 | } 41 | 42 | 43 | BufferOutput::BufferOutput(Buffer* buf) 44 | : buf_(buf) 45 | , pos_(0) 46 | { 47 | assert(buf_); 48 | } 49 | 50 | BufferOutput::~BufferOutput() 51 | { } 52 | 53 | size_t BufferOutput::DoNext(void** data, size_t len) { 54 | if (pos_ + len > buf_->size()) { 55 | buf_->resize(pos_ + len); 56 | } 57 | 58 | *data = buf_->data() + pos_; 59 | pos_ += len; 60 | 61 | return len; 62 | } 63 | 64 | 65 | BufferedOutput::BufferedOutput(OutputStream* slave, size_t buflen) 66 | : slave_(slave) 67 | , buffer_(buflen) 68 | , array_output_(buffer_.data(), buflen) 69 | { 70 | } 71 | 72 | BufferedOutput::~BufferedOutput() { 73 | Flush(); 74 | } 75 | 76 | void BufferedOutput::Reset() { 77 | array_output_.Reset(buffer_.data(), buffer_.size()); 78 | } 79 | 80 | void BufferedOutput::DoFlush() { 81 | if (array_output_.Data() != buffer_.data()) { 82 | slave_->Write(buffer_.data(), array_output_.Data() - buffer_.data()); 83 | slave_->Flush(); 84 | 85 | array_output_.Reset(buffer_.data(), buffer_.size()); 86 | } 87 | } 88 | 89 | size_t BufferedOutput::DoNext(void** data, size_t len) { 90 | if (array_output_.Avail() < len) { 91 | Flush(); 92 | } 93 | 94 | return array_output_.Next(data, len); 95 | 96 | } 97 | 98 | void BufferedOutput::DoWrite(const void* data, size_t len) { 99 | if (array_output_.Avail() < len) { 100 | Flush(); 101 | 102 | if (len > buffer_.size() / 2) { 103 | slave_->Write(data, len); 104 | return; 105 | } 106 | } 107 | 108 | array_output_.Write(data, len); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /clickhouse/base/output.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "buffer.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace clickhouse { 11 | 12 | class OutputStream { 13 | public: 14 | virtual ~OutputStream() 15 | { } 16 | 17 | inline void Flush() { 18 | DoFlush(); 19 | } 20 | 21 | inline void Write(const void* data, size_t len) { 22 | DoWrite(data, len); 23 | } 24 | 25 | protected: 26 | virtual void DoFlush() { } 27 | 28 | virtual void DoWrite(const void* data, size_t len) = 0; 29 | }; 30 | 31 | 32 | class ZeroCopyOutput : public OutputStream { 33 | public: 34 | inline size_t Next(void** data, size_t size) { 35 | return DoNext(data, size); 36 | } 37 | 38 | protected: 39 | // Obtains a buffer into which data can be written. Any data written 40 | // into this buffer will eventually (maybe instantly, maybe later on) 41 | // be written to the output. 42 | virtual size_t DoNext(void** data, size_t len) = 0; 43 | 44 | void DoWrite(const void* data, size_t len) override; 45 | }; 46 | 47 | 48 | /** 49 | * A ZeroCopyOutput stream backed by an in-memory array of bytes. 50 | */ 51 | class ArrayOutput : public ZeroCopyOutput { 52 | public: 53 | ArrayOutput(void* buf, size_t len); 54 | ~ArrayOutput() override; 55 | 56 | /// Number of bytes available in the stream. 57 | inline size_t Avail() const noexcept { 58 | return end_ - buf_; 59 | } 60 | 61 | /// Current write position in the memory block used by this stream. 62 | inline const uint8_t* Data() const noexcept { 63 | return buf_; 64 | } 65 | 66 | /// Whether there is more space in the stream. 67 | inline bool Exhausted() const noexcept { 68 | return !Avail(); 69 | } 70 | 71 | /// Initializes this stream with a new memory block. 72 | inline void Reset(void* buf, size_t len) noexcept { 73 | buf_ = static_cast(buf); 74 | end_ = buf_ + len; 75 | } 76 | 77 | protected: 78 | size_t DoNext(void** data, size_t len) override; 79 | 80 | private: 81 | uint8_t* buf_; 82 | uint8_t* end_; 83 | }; 84 | 85 | 86 | /** 87 | * A ZeroCopyOutput stream backed by an vector of bytes. 88 | */ 89 | class BufferOutput : public ZeroCopyOutput { 90 | public: 91 | BufferOutput(Buffer* buf); 92 | ~BufferOutput(); 93 | 94 | protected: 95 | size_t DoNext(void** data, size_t len) override; 96 | 97 | private: 98 | Buffer* buf_; 99 | size_t pos_; 100 | }; 101 | 102 | 103 | class BufferedOutput : public ZeroCopyOutput { 104 | public: 105 | BufferedOutput(OutputStream* slave, size_t buflen = 8192); 106 | ~BufferedOutput() override; 107 | 108 | void Reset(); 109 | 110 | protected: 111 | void DoFlush() override; 112 | size_t DoNext(void** data, size_t len) override; 113 | void DoWrite(const void* data, size_t len) override; 114 | 115 | private: 116 | OutputStream* const slave_; 117 | Buffer buffer_; 118 | ArrayOutput array_output_; 119 | }; 120 | 121 | template 122 | void WriteUnaligned(void* buf, const T& value) { 123 | memcpy(buf, &value, sizeof(value)); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /clickhouse/base/platform.cpp: -------------------------------------------------------------------------------- 1 | #include "platform.h" 2 | -------------------------------------------------------------------------------- /clickhouse/base/platform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__linux__) 4 | # define _linux_ 5 | #elif defined(_WIN64) 6 | # define _win64_ 7 | # define _win32_ 8 | #elif defined(__WIN32__) || defined(_WIN32) 9 | # define _win32_ 10 | #elif defined(__APPLE__) 11 | # define _darwin_ 12 | #endif 13 | 14 | #if defined(_win32_) || defined(_win64_) 15 | # define _win_ 16 | #endif 17 | 18 | #if defined(_linux_) || defined (_darwin_) 19 | # define _unix_ 20 | #endif 21 | 22 | #if defined(_MSC_VER) 23 | # undef NOMINMAX 24 | # define NOMINMAX 25 | # include 26 | # define ssize_t SSIZE_T 27 | # define HAVE_SSIZE_T 1 28 | #endif 29 | -------------------------------------------------------------------------------- /clickhouse/base/singleton.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace clickhouse { 4 | 5 | template 6 | T* Singleton() { 7 | static T instance; 8 | return &instance; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /clickhouse/base/socket.cpp: -------------------------------------------------------------------------------- 1 | #include "socket.h" 2 | #include "singleton.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #if !defined(_win_) 11 | # include 12 | # include 13 | # include 14 | # include 15 | # include 16 | # include 17 | #endif 18 | 19 | namespace clickhouse { 20 | namespace { 21 | 22 | class LocalNames : public std::unordered_set { 23 | public: 24 | LocalNames() { 25 | emplace("localhost"); 26 | emplace("localhost.localdomain"); 27 | emplace("localhost6"); 28 | emplace("localhost6.localdomain6"); 29 | emplace("::1"); 30 | emplace("127.0.0.1"); 31 | } 32 | 33 | inline bool IsLocalName(const std::string& name) const noexcept { 34 | return find(name) != end(); 35 | } 36 | }; 37 | 38 | void SetNonBlock(SOCKET fd, bool value) { 39 | #if defined(_unix_) 40 | int flags; 41 | int ret; 42 | #if defined(O_NONBLOCK) 43 | if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 44 | flags = 0; 45 | if (value) { 46 | flags |= O_NONBLOCK; 47 | } else { 48 | flags &= ~O_NONBLOCK; 49 | } 50 | ret = fcntl(fd, F_SETFL, flags); 51 | #else 52 | flags = value; 53 | return ioctl(fd, FIOBIO, &flags); 54 | #endif 55 | if (ret == -1) { 56 | throw std::system_error( 57 | errno, std::system_category(), "fail to set nonblocking mode"); 58 | } 59 | #elif defined(_win_) 60 | unsigned long inbuf = value; 61 | unsigned long outbuf = 0; 62 | DWORD written = 0; 63 | 64 | if (!inbuf) { 65 | WSAEventSelect(fd, nullptr, 0); 66 | } 67 | 68 | if (WSAIoctl(fd, FIONBIO, &inbuf, sizeof(inbuf), &outbuf, sizeof(outbuf), &written, 0, 0) == SOCKET_ERROR) { 69 | throw std::system_error( 70 | errno, std::system_category(), "fail to set nonblocking mode"); 71 | } 72 | #endif 73 | } 74 | 75 | void SetTimeout(SOCKET fd, SocketTimeoutParams params) { 76 | #if defined(_unix_) 77 | 78 | if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, ¶ms.GetRecvTimeout(), sizeof(params.GetRecvTimeout())) < 0) { 79 | throw std::system_error(errno, std::system_category(), "failed to setsockopt(SO_RCVTIMEO)"); 80 | } 81 | if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, ¶ms.GetSendTimeout(), sizeof(params.GetSendTimeout())) < 0) { 82 | throw std::system_error(errno, std::system_category(), "failed to setsockopt(SO_SNDTIMEO)"); 83 | } 84 | #endif 85 | } 86 | 87 | } // namespace 88 | 89 | NetworkAddress::NetworkAddress(const std::string& host, const std::string& port) 90 | : info_(nullptr) 91 | { 92 | struct addrinfo hints; 93 | memset(&hints, 0, sizeof(hints)); 94 | 95 | hints.ai_family = PF_UNSPEC; 96 | hints.ai_socktype = SOCK_STREAM; 97 | 98 | if (!Singleton()->IsLocalName(host)) { 99 | // https://linux.die.net/man/3/getaddrinfo 100 | // If hints.ai_flags includes the AI_ADDRCONFIG flag, 101 | // then IPv4 addresses are returned in the list pointed to by res only 102 | // if the local system has at least one IPv4 address configured, 103 | // and IPv6 addresses are only returned if the local system 104 | // has at least one IPv6 address configured. 105 | // The loopback address is not considered for this case 106 | // as valid as a configured address. 107 | hints.ai_flags |= AI_ADDRCONFIG; 108 | } 109 | 110 | const int error = getaddrinfo(host.c_str(), port.c_str(), &hints, &info_); 111 | 112 | if (error) { 113 | throw std::system_error(errno, std::system_category()); 114 | } 115 | } 116 | 117 | NetworkAddress::~NetworkAddress() { 118 | if (info_) { 119 | freeaddrinfo(info_); 120 | } 121 | } 122 | 123 | const struct addrinfo* NetworkAddress::Info() const { 124 | return info_; 125 | } 126 | 127 | 128 | SocketTimeoutParams::SocketTimeoutParams( unsigned int con_recv_timeout_sec, unsigned int con_send_timeout_sec) 129 | : recv_timeout_{.tv_sec = con_recv_timeout_sec, .tv_usec = 0}, 130 | send_timeout_{.tv_sec = con_send_timeout_sec, .tv_usec = 0} 131 | { 132 | } 133 | 134 | const struct timeval& SocketTimeoutParams::GetRecvTimeout(){ 135 | return recv_timeout_; 136 | } 137 | 138 | const struct timeval& SocketTimeoutParams::GetSendTimeout(){ 139 | return send_timeout_; 140 | } 141 | 142 | 143 | SocketHolder::SocketHolder() 144 | : handle_(-1) 145 | { 146 | } 147 | 148 | SocketHolder::SocketHolder(SOCKET s) 149 | : handle_(s) 150 | { 151 | } 152 | 153 | SocketHolder::SocketHolder(SocketHolder&& other) noexcept 154 | : handle_(other.handle_) 155 | { 156 | other.handle_ = -1; 157 | } 158 | 159 | SocketHolder::~SocketHolder() { 160 | Close(); 161 | } 162 | 163 | void SocketHolder::Close() noexcept { 164 | if (handle_ != -1) { 165 | #if defined(_win_) 166 | closesocket(handle_); 167 | #else 168 | close(handle_); 169 | #endif 170 | handle_ = -1; 171 | } 172 | } 173 | 174 | bool SocketHolder::Closed() const noexcept { 175 | return handle_ == -1; 176 | } 177 | 178 | void SocketHolder::SetTcpKeepAlive(int idle, int intvl, int cnt) noexcept { 179 | int val = 1; 180 | 181 | #if defined(_unix_) 182 | setsockopt(handle_, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); 183 | # if defined(_linux_) 184 | setsockopt(handle_, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)); 185 | # elif defined(_darwin_) 186 | setsockopt(handle_, IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle)); 187 | # else 188 | # error "platform does not supported" 189 | # endif 190 | setsockopt(handle_, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)); 191 | setsockopt(handle_, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)); 192 | #else 193 | setsockopt(handle_, SOL_SOCKET, SO_KEEPALIVE, (const char*)&val, sizeof(val)); 194 | std::ignore = idle = intvl = cnt; 195 | #endif 196 | } 197 | 198 | SocketHolder& SocketHolder::operator = (SocketHolder&& other) noexcept { 199 | if (this != &other) { 200 | Close(); 201 | 202 | handle_ = other.handle_; 203 | other.handle_ = -1; 204 | } 205 | 206 | return *this; 207 | } 208 | 209 | SocketHolder::operator SOCKET () const noexcept { 210 | return handle_; 211 | } 212 | 213 | 214 | SocketInput::SocketInput(SOCKET s) 215 | : s_(s) 216 | { 217 | } 218 | 219 | SocketInput::~SocketInput() = default; 220 | 221 | size_t SocketInput::DoRead(void* buf, size_t len) { 222 | const ssize_t ret = ::recv(s_, (char*)buf, (int)len, 0); 223 | 224 | if (ret > 0) { 225 | return (size_t)ret; 226 | } 227 | 228 | if (ret == 0) { 229 | throw std::system_error( 230 | errno, std::system_category(), "closed" 231 | ); 232 | } 233 | 234 | throw std::system_error( 235 | errno, std::system_category(), "can't receive string data" 236 | ); 237 | } 238 | 239 | 240 | SocketOutput::SocketOutput(SOCKET s) 241 | : s_(s) 242 | { 243 | } 244 | 245 | SocketOutput::~SocketOutput() = default; 246 | 247 | void SocketOutput::DoWrite(const void* data, size_t len) { 248 | #if defined (_linux_) 249 | static const int flags = MSG_NOSIGNAL; 250 | #else 251 | static const int flags = 0; 252 | #endif 253 | 254 | if (::send(s_, (const char*)data, (int)len, flags) != (int)len) { 255 | throw std::system_error( 256 | errno, std::system_category(), "fail to send data" 257 | ); 258 | } 259 | } 260 | 261 | 262 | NetworkInitializer::NetworkInitializer() { 263 | struct NetworkInitializerImpl { 264 | NetworkInitializerImpl() { 265 | #if defined (_win_) 266 | WSADATA data; 267 | const int result = WSAStartup(MAKEWORD(2, 2), &data); 268 | if (result) { 269 | assert(false); 270 | exit(-1); 271 | } 272 | #elif defined(_unix_) 273 | signal(SIGPIPE, SIG_IGN); 274 | #endif 275 | } 276 | }; 277 | 278 | 279 | (void)Singleton(); 280 | } 281 | 282 | 283 | SOCKET SocketConnect(const NetworkAddress& addr, const std::optional& socket_timeout_params) { 284 | int last_err = 0; 285 | for (auto res = addr.Info(); res != nullptr; res = res->ai_next) { 286 | SOCKET s(socket(res->ai_family, res->ai_socktype, res->ai_protocol)); 287 | 288 | if (s == -1) { 289 | continue; 290 | } 291 | 292 | SetNonBlock(s, true); 293 | if(socket_timeout_params) { 294 | SetTimeout(s, *socket_timeout_params); 295 | } 296 | 297 | if (connect(s, res->ai_addr, (int)res->ai_addrlen) != 0) { 298 | int err = errno; 299 | if (err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK) { 300 | pollfd fd; 301 | fd.fd = s; 302 | fd.events = POLLOUT; 303 | fd.revents = 0; 304 | ssize_t rval = Poll(&fd, 1, 5000); 305 | 306 | if (rval == -1) { 307 | throw std::system_error(errno, std::system_category(), "fail to connect"); 308 | } 309 | if (rval > 0) { 310 | socklen_t len = sizeof(err); 311 | getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, &len); 312 | 313 | if (!err) { 314 | SetNonBlock(s, false); 315 | return s; 316 | } 317 | last_err = err; 318 | } 319 | } 320 | } else { 321 | SetNonBlock(s, false); 322 | return s; 323 | } 324 | } 325 | if (last_err > 0) { 326 | throw std::system_error(last_err, std::system_category(), "fail to connect"); 327 | } 328 | throw std::system_error( 329 | errno, std::system_category(), "fail to connect" 330 | ); 331 | } 332 | 333 | 334 | ssize_t Poll(struct pollfd* fds, int nfds, int timeout) noexcept { 335 | #if defined(_win_) 336 | int rval = WSAPoll(fds, nfds, timeout); 337 | #else 338 | return poll(fds, nfds, timeout); 339 | #endif 340 | return -1; 341 | } 342 | 343 | } 344 | -------------------------------------------------------------------------------- /clickhouse/base/socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "input.h" 4 | #include "output.h" 5 | #include "platform.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #if defined(_win_) 12 | # pragma comment(lib, "Ws2_32.lib") 13 | 14 | # include 15 | # include 16 | #else 17 | # include 18 | # include 19 | # include 20 | # include 21 | 22 | # if !defined(SOCKET) 23 | # define SOCKET int 24 | # endif 25 | #endif 26 | 27 | struct addrinfo; 28 | struct timeval; 29 | 30 | namespace clickhouse { 31 | 32 | /** 33 | * 34 | */ 35 | class NetworkAddress { 36 | public: 37 | explicit NetworkAddress(const std::string& host, 38 | const std::string& port = "0"); 39 | ~NetworkAddress(); 40 | 41 | const struct addrinfo* Info() const; 42 | 43 | private: 44 | struct addrinfo* info_; 45 | }; 46 | 47 | class SocketTimeoutParams { 48 | public: 49 | explicit SocketTimeoutParams(unsigned int con_recv_timeout_sec, unsigned int con_send_timeout_sec); 50 | 51 | const struct timeval& GetRecvTimeout(); 52 | const struct timeval& GetSendTimeout(); 53 | 54 | private: 55 | const struct timeval recv_timeout_; 56 | const struct timeval send_timeout_; 57 | }; 58 | 59 | 60 | class SocketHolder { 61 | public: 62 | SocketHolder(); 63 | SocketHolder(SOCKET s); 64 | SocketHolder(SocketHolder&& other) noexcept; 65 | 66 | ~SocketHolder(); 67 | 68 | void Close() noexcept; 69 | 70 | bool Closed() const noexcept; 71 | 72 | /// @params idle the time (in seconds) the connection needs to remain 73 | /// idle before TCP starts sending keepalive probes. 74 | /// @params intvl the time (in seconds) between individual keepalive probes. 75 | /// @params cnt the maximum number of keepalive probes TCP should send 76 | /// before dropping the connection. 77 | void SetTcpKeepAlive(int idle, int intvl, int cnt) noexcept; 78 | 79 | SocketHolder& operator = (SocketHolder&& other) noexcept; 80 | 81 | operator SOCKET () const noexcept; 82 | 83 | private: 84 | SocketHolder(const SocketHolder&) = delete; 85 | SocketHolder& operator = (const SocketHolder&) = delete; 86 | 87 | SOCKET handle_; 88 | }; 89 | 90 | 91 | /** 92 | * 93 | */ 94 | class SocketInput : public InputStream { 95 | public: 96 | explicit SocketInput(SOCKET s); 97 | ~SocketInput(); 98 | 99 | protected: 100 | size_t DoRead(void* buf, size_t len) override; 101 | 102 | private: 103 | SOCKET s_; 104 | }; 105 | 106 | class SocketOutput : public OutputStream { 107 | public: 108 | explicit SocketOutput(SOCKET s); 109 | ~SocketOutput(); 110 | 111 | protected: 112 | void DoWrite(const void* data, size_t len) override; 113 | 114 | private: 115 | SOCKET s_; 116 | }; 117 | 118 | static struct NetworkInitializer { 119 | NetworkInitializer(); 120 | } gNetworkInitializer; 121 | 122 | /// 123 | SOCKET SocketConnect(const NetworkAddress& addr, const std::optional& socket_timeout_params); 124 | 125 | ssize_t Poll(struct pollfd* fds, int nfds, int timeout) noexcept; 126 | 127 | } 128 | -------------------------------------------------------------------------------- /clickhouse/base/string_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace clickhouse { 8 | 9 | template 10 | inline T FromString(const std::string_view& s) { 11 | std::istringstream iss((std::string(s))); 12 | T result; 13 | iss >> result; 14 | return result; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /clickhouse/base/wire_format.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "coded.h" 4 | 5 | #include 6 | 7 | namespace clickhouse { 8 | 9 | class WireFormat { 10 | public: 11 | template 12 | static bool ReadFixed(CodedInputStream* input, T* value); 13 | 14 | static bool ReadString(CodedInputStream* input, std::string* value); 15 | 16 | static bool ReadBytes(CodedInputStream* input, void* buf, size_t len); 17 | 18 | static bool ReadUInt64(CodedInputStream* input, uint64_t* value); 19 | 20 | 21 | template 22 | static void WriteFixed(CodedOutputStream* output, const T& value); 23 | 24 | static void WriteBytes(CodedOutputStream* output, const void* buf, size_t len); 25 | 26 | static void WriteString(CodedOutputStream* output, const std::string& value); 27 | 28 | static void WriteUInt64(CodedOutputStream* output, const uint64_t value); 29 | }; 30 | 31 | template 32 | inline bool WireFormat::ReadFixed( 33 | CodedInputStream* input, 34 | T* value) 35 | { 36 | return input->ReadRaw(value, sizeof(T)); 37 | } 38 | 39 | inline bool WireFormat::ReadString( 40 | CodedInputStream* input, 41 | std::string* value) 42 | { 43 | uint64_t len; 44 | 45 | if (input->ReadVarint64(&len)) { 46 | if (len > 0x00FFFFFFULL) { 47 | return false; 48 | } 49 | value->resize((size_t)len); 50 | return input->ReadRaw(&(*value)[0], (size_t)len); 51 | } 52 | 53 | return false; 54 | } 55 | 56 | inline bool WireFormat::ReadBytes( 57 | CodedInputStream* input, void* buf, size_t len) 58 | { 59 | return input->ReadRaw(buf, len); 60 | } 61 | 62 | inline bool WireFormat::ReadUInt64( 63 | CodedInputStream* input, 64 | uint64_t* value) 65 | { 66 | return input->ReadVarint64(value); 67 | } 68 | 69 | 70 | template 71 | inline void WireFormat::WriteFixed( 72 | CodedOutputStream* output, 73 | const T& value) 74 | { 75 | output->WriteRaw(&value, sizeof(T)); 76 | } 77 | 78 | inline void WireFormat::WriteBytes( 79 | CodedOutputStream* output, 80 | const void* buf, 81 | size_t len) 82 | { 83 | output->WriteRaw(buf, len); 84 | } 85 | 86 | inline void WireFormat::WriteString( 87 | CodedOutputStream* output, 88 | const std::string& value) 89 | { 90 | output->WriteVarint64(value.size()); 91 | output->WriteRaw(value.data(), value.size()); 92 | } 93 | 94 | inline void WireFormat::WriteUInt64( 95 | CodedOutputStream* output, 96 | const uint64_t value) 97 | { 98 | output->WriteVarint64(value); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /clickhouse/block.cpp: -------------------------------------------------------------------------------- 1 | #include "block.h" 2 | 3 | #include 4 | 5 | namespace clickhouse { 6 | 7 | Block::Iterator::Iterator(const Block& block) 8 | : block_(block) 9 | , idx_(0) 10 | { 11 | } 12 | 13 | const std::string& Block::Iterator::Name() const { 14 | return block_.columns_[idx_].name; 15 | } 16 | 17 | TypeRef Block::Iterator::Type() const { 18 | return block_.columns_[idx_].column->Type(); 19 | } 20 | 21 | ColumnRef Block::Iterator::Column() const { 22 | return block_.columns_[idx_].column; 23 | } 24 | 25 | void Block::Iterator::Next() { 26 | ++idx_; 27 | } 28 | 29 | bool Block::Iterator::IsValid() const { 30 | return idx_ < block_.columns_.size(); 31 | } 32 | 33 | 34 | Block::Block() 35 | : rows_(0) 36 | { 37 | } 38 | 39 | Block::Block(size_t cols, size_t rows) 40 | : rows_(rows) 41 | { 42 | columns_.reserve(cols); 43 | } 44 | 45 | Block::~Block() = default; 46 | 47 | void Block::AppendColumn(const std::string& name, const ColumnRef& col) { 48 | if (columns_.empty()) { 49 | rows_ = col->Size(); 50 | } else if (col->Size() != rows_) { 51 | throw std::runtime_error("all columns in block must have same count of rows. Name: ["+name+"], rows: ["+std::to_string(rows_)+"], columns: [" + std::to_string(col->Size())+"]"); 52 | } 53 | 54 | columns_.push_back(ColumnItem{name, col}); 55 | } 56 | 57 | /// Count of columns in the block. 58 | size_t Block::GetColumnCount() const { 59 | return columns_.size(); 60 | } 61 | 62 | const BlockInfo& Block::Info() const { 63 | return info_; 64 | } 65 | 66 | /// Count of rows in the block. 67 | size_t Block::GetRowCount() const { 68 | return rows_; 69 | } 70 | 71 | ColumnRef Block::operator [] (size_t idx) const { 72 | if (idx < columns_.size()) { 73 | return columns_[idx].column; 74 | } 75 | 76 | throw std::out_of_range("column index is out of range. Index: ["+std::to_string(idx)+"], columns: [" + std::to_string(columns_.size())+"]"); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /clickhouse/block.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "columns/column.h" 4 | 5 | namespace clickhouse { 6 | 7 | struct BlockInfo { 8 | uint8_t is_overflows = 0; 9 | int32_t bucket_num = -1; 10 | }; 11 | 12 | class Block { 13 | public: 14 | /// Allow to iterate over block's columns. 15 | class Iterator { 16 | public: 17 | Iterator(const Block& block); 18 | 19 | /// Name of column. 20 | const std::string& Name() const; 21 | 22 | /// Type of column. 23 | TypeRef Type() const; 24 | 25 | /// Reference to column object. 26 | ColumnRef Column() const; 27 | 28 | /// Move to next column. 29 | void Next(); 30 | 31 | /// Is the iterator still valid. 32 | bool IsValid() const; 33 | 34 | private: 35 | Iterator() = delete; 36 | 37 | const Block& block_; 38 | size_t idx_; 39 | }; 40 | 41 | public: 42 | Block(); 43 | Block(size_t cols, size_t rows); 44 | ~Block(); 45 | 46 | /// Append named column to the block. 47 | void AppendColumn(const std::string& name, const ColumnRef& col); 48 | 49 | /// Count of columns in the block. 50 | size_t GetColumnCount() const; 51 | 52 | const BlockInfo& Info() const; 53 | 54 | /// Count of rows in the block. 55 | size_t GetRowCount() const; 56 | 57 | const std::string& GetColumnName(size_t idx) const { 58 | return columns_.at(idx).name; 59 | } 60 | 61 | /// Reference to column by index in the block. 62 | ColumnRef operator [] (size_t idx) const; 63 | 64 | private: 65 | struct ColumnItem { 66 | std::string name; 67 | ColumnRef column; 68 | }; 69 | 70 | BlockInfo info_; 71 | std::vector columns_; 72 | /// Count of rows in the block. 73 | size_t rows_; 74 | }; 75 | 76 | } 77 | -------------------------------------------------------------------------------- /clickhouse/client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "query.h" 4 | #include "exceptions.h" 5 | 6 | #include "columns/array.h" 7 | #include "columns/date.h" 8 | #include "columns/decimal.h" 9 | #include "columns/enum.h" 10 | #include "columns/ip4.h" 11 | #include "columns/ip6.h" 12 | #include "columns/nullable.h" 13 | #include "columns/numeric.h" 14 | #include "columns/string.h" 15 | #include "columns/tuple.h" 16 | #include "columns/uuid.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace clickhouse { 24 | 25 | /// Methods of block compression. 26 | enum class CompressionMethod { 27 | None = -1, 28 | LZ4 = 1, 29 | }; 30 | 31 | struct ClientOptions { 32 | #define DECLARE_FIELD(name, type, setter, default) \ 33 | type name = default; \ 34 | inline ClientOptions& setter(const type& value) { \ 35 | name = value; \ 36 | return *this; \ 37 | } 38 | 39 | /// Hostname of the server. 40 | DECLARE_FIELD(host, std::string, SetHost, std::string()); 41 | /// Service port. 42 | DECLARE_FIELD(port, unsigned int, SetPort, 9000); 43 | 44 | /// Default database. 45 | DECLARE_FIELD(default_database, std::string, SetDefaultDatabase, "default"); 46 | /// User name. 47 | DECLARE_FIELD(user, std::string, SetUser, "default"); 48 | /// Access password. 49 | DECLARE_FIELD(password, std::string, SetPassword, std::string()); 50 | 51 | /// By default all exceptions received during query execution will be 52 | /// passed to OnException handler. Set rethrow_exceptions to true to 53 | /// enable throwing exceptions with standard c++ exception mechanism. 54 | DECLARE_FIELD(rethrow_exceptions, bool, SetRethrowException, true); 55 | 56 | /// Ping server every time before execute any query. 57 | DECLARE_FIELD(ping_before_query, bool, SetPingBeforeQuery, false); 58 | /// Count of retry to send request to server. 59 | DECLARE_FIELD(send_retries, unsigned int, SetSendRetries, 1); 60 | /// Amount of time to wait before next retry. 61 | DECLARE_FIELD(retry_timeout, std::chrono::seconds, SetRetryTimeout, std::chrono::seconds(5)); 62 | 63 | /// Compression method. 64 | DECLARE_FIELD(compression_method, CompressionMethod, SetCompressionMethod, CompressionMethod::None); 65 | 66 | /// TCP Keep alive options 67 | DECLARE_FIELD(tcp_keepalive, bool, TcpKeepAlive, false); 68 | DECLARE_FIELD(tcp_keepalive_idle, std::chrono::seconds, SetTcpKeepAliveIdle, std::chrono::seconds(60)); 69 | DECLARE_FIELD(tcp_keepalive_intvl, std::chrono::seconds, SetTcpKeepAliveInterval, std::chrono::seconds(5)); 70 | DECLARE_FIELD(tcp_keepalive_cnt, unsigned int, SetTcpKeepAliveCount, 3); 71 | 72 | /// Connection socket timeout 73 | DECLARE_FIELD(connection_timeout, bool, ConnectionTimeout, false); 74 | DECLARE_FIELD(connection_recv_timeout, std::chrono::seconds, SetConnectionRecvTimeout, std::chrono::seconds(60)); 75 | DECLARE_FIELD(connection_send_timeout, std::chrono::seconds, SetConnectionSendTimeout, std::chrono::seconds(60)); 76 | 77 | #undef DECLARE_FIELD 78 | }; 79 | 80 | std::ostream& operator<<(std::ostream& os, const ClientOptions& options); 81 | 82 | /** 83 | * 84 | */ 85 | class Client { 86 | public: 87 | Client(const ClientOptions& opts); 88 | ~Client(); 89 | 90 | /// Intends for execute arbitrary queries. 91 | void Execute(const Query& query); 92 | 93 | /// Intends for execute select queries. Data will be returned with 94 | /// one or more call of \p cb. 95 | void Select(const std::string& query, SelectCallback cb); 96 | 97 | /// Executes a select query which can be canceled by returning false from 98 | /// the data handler function \p cb. 99 | void SelectCancelable(const std::string& query, SelectCancelableCallback cb); 100 | 101 | /// Alias for Execute. 102 | void Select(const Query& query); 103 | 104 | /// Intends for insert block of data into a table \p table_name. 105 | void Insert(const std::string& table_name, const Block& block); 106 | 107 | /// Ping server for aliveness. 108 | void Ping(); 109 | 110 | /// Reset connection with initial params. 111 | void ResetConnection(); 112 | 113 | private: 114 | ClientOptions options_; 115 | 116 | class Impl; 117 | std::unique_ptr impl_; 118 | }; 119 | 120 | } 121 | -------------------------------------------------------------------------------- /clickhouse/columns/array.cpp: -------------------------------------------------------------------------------- 1 | #include "array.h" 2 | #include 3 | 4 | namespace clickhouse { 5 | 6 | ColumnArray::ColumnArray(ColumnRef data) 7 | : Column(Type::CreateArray(data->Type())) 8 | , data_(data) 9 | , offsets_(std::make_shared()) 10 | { 11 | } 12 | 13 | void ColumnArray::AppendAsColumn(ColumnRef array) { 14 | if (!data_->Type()->IsEqual(array->Type())) { 15 | throw std::runtime_error( 16 | "can't append column of type " + array->Type()->GetName() + " " 17 | "to column type " + data_->Type()->GetName()); 18 | } 19 | 20 | if (offsets_->Size() == 0) { 21 | offsets_->Append(array->Size()); 22 | } else { 23 | offsets_->Append((*offsets_)[offsets_->Size() - 1] + array->Size()); 24 | } 25 | 26 | data_->Append(array); 27 | } 28 | 29 | ColumnRef ColumnArray::GetAsColumn(size_t n) const { 30 | return data_->Slice(GetOffset(n), GetSize(n)); 31 | } 32 | 33 | ColumnRef ColumnArray::Slice(size_t begin, size_t size) { 34 | auto result = std::make_shared(GetAsColumn(begin)); 35 | result->OffsetsIncrease(1); 36 | 37 | for (size_t i = 1; i < size; i++) 38 | { 39 | result->Append(std::make_shared(GetAsColumn(begin + i))); 40 | } 41 | 42 | return result; 43 | } 44 | 45 | void ColumnArray::Append(ColumnRef column) { 46 | if (auto col = column->As()) { 47 | if (!col->data_->Type()->IsEqual(data_->Type())) { 48 | return; 49 | } 50 | 51 | for (size_t i = 0; i < col->Size(); ++i) { 52 | AppendAsColumn(col->GetAsColumn(i)); 53 | } 54 | } 55 | } 56 | 57 | bool ColumnArray::Load(CodedInputStream* input, size_t rows) { 58 | if (!rows) { 59 | return true; 60 | } 61 | if (!offsets_->Load(input, rows)) { 62 | return false; 63 | } 64 | if (!data_->Load(input, (*offsets_)[rows - 1])) { 65 | return false; 66 | } 67 | return true; 68 | } 69 | 70 | void ColumnArray::Save(CodedOutputStream* output) { 71 | offsets_->Save(output); 72 | data_->Save(output); 73 | } 74 | 75 | void ColumnArray::Clear() { 76 | offsets_->Clear(); 77 | data_->Clear(); 78 | } 79 | 80 | size_t ColumnArray::Size() const { 81 | return offsets_->Size(); 82 | } 83 | 84 | void ColumnArray::OffsetsIncrease(size_t n) { 85 | offsets_->Append(n); 86 | } 87 | 88 | size_t ColumnArray::GetOffset(size_t n) const { 89 | return (n == 0) ? 0 : (*offsets_)[n - 1]; 90 | } 91 | 92 | size_t ColumnArray::GetSize(size_t n) const { 93 | return (n == 0) ? (*offsets_)[n] : ((*offsets_)[n] - (*offsets_)[n - 1]); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /clickhouse/columns/array.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "numeric.h" 4 | 5 | namespace clickhouse { 6 | 7 | /** 8 | * Represents column of Array(T). 9 | */ 10 | class ColumnArray : public Column { 11 | public: 12 | ColumnArray(ColumnRef data); 13 | 14 | /// Converts input column to array and appends 15 | /// as one row to the current column. 16 | void AppendAsColumn(ColumnRef array); 17 | 18 | /// Convets array at pos n to column. 19 | /// Type of element of result column same as type of array element. 20 | ColumnRef GetAsColumn(size_t n) const; 21 | 22 | public: 23 | /// Appends content of given column to the end of current one. 24 | void Append(ColumnRef column) override; 25 | 26 | /// Loads column data from input stream. 27 | bool Load(CodedInputStream* input, size_t rows) override; 28 | 29 | /// Saves column data to output stream. 30 | void Save(CodedOutputStream* output) override; 31 | 32 | /// Clear column data . 33 | void Clear() override; 34 | 35 | /// Returns count of rows in the column. 36 | size_t Size() const override; 37 | 38 | /// Makes slice of the current column. 39 | ColumnRef Slice(size_t, size_t) override; 40 | 41 | void OffsetsIncrease(size_t); 42 | 43 | private: 44 | size_t GetOffset(size_t n) const; 45 | 46 | size_t GetSize(size_t n) const; 47 | 48 | private: 49 | ColumnRef data_; 50 | std::shared_ptr offsets_; 51 | }; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /clickhouse/columns/column.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../base/input.h" 4 | #include "../base/coded.h" 5 | #include "../types/types.h" 6 | 7 | namespace clickhouse { 8 | 9 | using ColumnRef = std::shared_ptr; 10 | 11 | /** 12 | * An abstract base of all columns classes. 13 | */ 14 | class Column : public std::enable_shared_from_this 15 | { 16 | public: 17 | explicit inline Column(TypeRef type) 18 | : type_(type) 19 | { 20 | } 21 | 22 | virtual ~Column() 23 | { } 24 | 25 | /// Downcast pointer to the specific column's subtype. 26 | template 27 | inline std::shared_ptr As() { 28 | return std::dynamic_pointer_cast(shared_from_this()); 29 | } 30 | 31 | /// Downcast pointer to the specific column's subtype. 32 | template 33 | inline std::shared_ptr As() const { 34 | return std::dynamic_pointer_cast(shared_from_this()); 35 | } 36 | 37 | /// Get type object of the column. 38 | inline TypeRef Type() const { return type_; } 39 | 40 | /// Appends content of given column to the end of current one. 41 | virtual void Append(ColumnRef column) = 0; 42 | 43 | /// Loads column data from input stream. 44 | virtual bool Load(CodedInputStream* input, size_t rows) = 0; 45 | 46 | /// Saves column data to output stream. 47 | virtual void Save(CodedOutputStream* output) = 0; 48 | 49 | /// Clear column data . 50 | virtual void Clear() = 0; 51 | 52 | /// Returns count of rows in the column. 53 | virtual size_t Size() const = 0; 54 | 55 | /// Makes slice of the current column. 56 | virtual ColumnRef Slice(size_t begin, size_t len) = 0; 57 | 58 | protected: 59 | TypeRef type_; 60 | }; 61 | 62 | } 63 | -------------------------------------------------------------------------------- /clickhouse/columns/date.cpp: -------------------------------------------------------------------------------- 1 | #include "date.h" 2 | 3 | namespace clickhouse { 4 | 5 | ColumnDate::ColumnDate() 6 | : Column(Type::CreateDate()) 7 | , data_(std::make_shared()) 8 | { 9 | } 10 | 11 | void ColumnDate::Append(const std::time_t& value) { 12 | data_->Append(static_cast(value / 86400)); 13 | } 14 | 15 | void ColumnDate::Clear() { 16 | data_->Clear(); 17 | } 18 | 19 | std::time_t ColumnDate::At(size_t n) const { 20 | return static_cast(data_->At(n)) * 86400; 21 | } 22 | 23 | void ColumnDate::Append(ColumnRef column) { 24 | if (auto col = column->As()) { 25 | data_->Append(col->data_); 26 | } 27 | } 28 | 29 | bool ColumnDate::Load(CodedInputStream* input, size_t rows) { 30 | return data_->Load(input, rows); 31 | } 32 | 33 | void ColumnDate::Save(CodedOutputStream* output) { 34 | data_->Save(output); 35 | } 36 | 37 | size_t ColumnDate::Size() const { 38 | return data_->Size(); 39 | } 40 | 41 | ColumnRef ColumnDate::Slice(size_t begin, size_t len) { 42 | auto col = data_->Slice(begin, len)->As(); 43 | auto result = std::make_shared(); 44 | 45 | result->data_->Append(col); 46 | 47 | return result; 48 | } 49 | 50 | 51 | ColumnDateTime::ColumnDateTime() 52 | : Column(Type::CreateDateTime()) 53 | , data_(std::make_shared()) 54 | { 55 | } 56 | 57 | ColumnDateTime::ColumnDateTime(std::string timezone) 58 | : Column(Type::CreateDateTime(std::move(timezone))) 59 | , data_(std::make_shared()) 60 | { 61 | } 62 | 63 | void ColumnDateTime::Append(const std::time_t& value) { 64 | data_->Append(static_cast(value)); 65 | } 66 | 67 | std::time_t ColumnDateTime::At(size_t n) const { 68 | return data_->At(n); 69 | } 70 | 71 | std::string ColumnDateTime::Timezone() const { 72 | return DateTimeType(type_).Timezone(); 73 | } 74 | 75 | void ColumnDateTime::Append(ColumnRef column) { 76 | if (auto col = column->As()) { 77 | data_->Append(col->data_); 78 | } 79 | } 80 | 81 | bool ColumnDateTime::Load(CodedInputStream* input, size_t rows) { 82 | return data_->Load(input, rows); 83 | } 84 | 85 | void ColumnDateTime::Save(CodedOutputStream* output) { 86 | data_->Save(output); 87 | } 88 | 89 | size_t ColumnDateTime::Size() const { 90 | return data_->Size(); 91 | } 92 | 93 | void ColumnDateTime::Clear() { 94 | data_->Clear(); 95 | } 96 | 97 | ColumnRef ColumnDateTime::Slice(size_t begin, size_t len) { 98 | auto col = data_->Slice(begin, len)->As(); 99 | auto result = std::make_shared(); 100 | 101 | result->data_->Append(col); 102 | 103 | return result; 104 | } 105 | 106 | 107 | ColumnDateTime64::ColumnDateTime64(size_t precision) 108 | : Column(Type::CreateDateTime64(precision)) 109 | , data_(std::make_shared()) 110 | { 111 | } 112 | 113 | ColumnDateTime64::ColumnDateTime64(size_t precision, std::string timezone) 114 | : Column(Type::CreateDateTime64(precision, std::move(timezone))) 115 | , data_(std::make_shared()) 116 | { 117 | } 118 | 119 | ColumnDateTime64::ColumnDateTime64(TypeRef type, std::shared_ptr data) 120 | : Column(type) 121 | , data_(std::move(data)) 122 | { 123 | } 124 | 125 | void ColumnDateTime64::Append(const uint64_t& value) { 126 | data_->Append(value); 127 | } 128 | 129 | uint64_t ColumnDateTime64::At(size_t n) const { 130 | return data_->At(n); 131 | } 132 | 133 | std::string ColumnDateTime64::Timezone() const { 134 | return DateTimeType(type_).Timezone(); 135 | } 136 | 137 | void ColumnDateTime64::Append(ColumnRef column) { 138 | if (auto col = column->As()) { 139 | data_->Append(col->data_); 140 | } 141 | } 142 | 143 | bool ColumnDateTime64::Load(CodedInputStream* input, size_t rows) { 144 | return data_->Load(input, rows); 145 | } 146 | 147 | void ColumnDateTime64::Save(CodedOutputStream* output) { 148 | data_->Save(output); 149 | } 150 | 151 | size_t ColumnDateTime64::Size() const { 152 | return data_->Size(); 153 | } 154 | 155 | void ColumnDateTime64::Clear() { 156 | data_->Clear(); 157 | } 158 | 159 | ColumnRef ColumnDateTime64::Slice(size_t begin, size_t len) { 160 | auto col = data_->Slice(begin, len)->As(); 161 | std::shared_ptr result(new ColumnDateTime64(type_, std::move(col))); 162 | return result; 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /clickhouse/columns/date.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "numeric.h" 4 | 5 | #include 6 | 7 | namespace clickhouse { 8 | 9 | /** */ 10 | class ColumnDate : public Column { 11 | public: 12 | ColumnDate(); 13 | 14 | /// Appends one element to the end of column. 15 | void Append(const std::time_t& value); 16 | 17 | /// Returns element at given row number. 18 | std::time_t At(size_t n) const; 19 | 20 | public: 21 | /// Appends content of given column to the end of current one. 22 | void Append(ColumnRef column) override; 23 | 24 | /// Loads column data from input stream. 25 | bool Load(CodedInputStream* input, size_t rows) override; 26 | 27 | /// Saves column data to output stream. 28 | void Save(CodedOutputStream* output) override; 29 | 30 | /// Clear column data . 31 | void Clear() override; 32 | 33 | /// Returns count of rows in the column. 34 | size_t Size() const override; 35 | 36 | /// Makes slice of the current column. 37 | ColumnRef Slice(size_t begin, size_t len) override; 38 | 39 | private: 40 | std::shared_ptr data_; 41 | }; 42 | 43 | /** */ 44 | class ColumnDateTime : public Column { 45 | public: 46 | ColumnDateTime(); 47 | explicit ColumnDateTime(std::string timezone); 48 | 49 | /// Appends one element to the end of column. 50 | void Append(const std::time_t& value); 51 | 52 | /// Returns element at given row number. 53 | std::time_t At(size_t n) const; 54 | 55 | /// Timezone associated with a data column. 56 | std::string Timezone() const; 57 | 58 | public: 59 | /// Appends content of given column to the end of current one. 60 | void Append(ColumnRef column) override; 61 | 62 | /// Loads column data from input stream. 63 | bool Load(CodedInputStream* input, size_t rows) override; 64 | 65 | /// Clear column data . 66 | void Clear() override; 67 | 68 | /// Saves column data to output stream. 69 | void Save(CodedOutputStream* output) override; 70 | 71 | /// Returns count of rows in the column. 72 | size_t Size() const override; 73 | 74 | /// Makes slice of the current column. 75 | ColumnRef Slice(size_t begin, size_t len) override; 76 | 77 | private: 78 | std::shared_ptr data_; 79 | }; 80 | 81 | /** */ 82 | class ColumnDateTime64 : public Column { 83 | public: 84 | explicit ColumnDateTime64(size_t precision); 85 | ColumnDateTime64(size_t precision, std::string timezone); 86 | 87 | /// Appends one element to the end of column. 88 | void Append(const uint64_t& value); 89 | 90 | /// Returns element at given row number. 91 | uint64_t At(size_t n) const; 92 | 93 | /// Timezone associated with a data column. 94 | std::string Timezone() const; 95 | 96 | public: 97 | /// Appends content of given column to the end of current one. 98 | void Append(ColumnRef column) override; 99 | 100 | /// Loads column data from input stream. 101 | bool Load(CodedInputStream* input, size_t rows) override; 102 | 103 | /// Clear column data . 104 | void Clear() override; 105 | 106 | /// Saves column data to output stream. 107 | void Save(CodedOutputStream* output) override; 108 | 109 | /// Returns count of rows in the column. 110 | size_t Size() const override; 111 | 112 | /// Makes slice of the current column. 113 | ColumnRef Slice(size_t begin, size_t len) override; 114 | 115 | private: 116 | std::shared_ptr data_; 117 | 118 | explicit ColumnDateTime64(TypeRef type, std::shared_ptr data); // for `Slice(…)` 119 | }; 120 | 121 | } 122 | -------------------------------------------------------------------------------- /clickhouse/columns/decimal.cpp: -------------------------------------------------------------------------------- 1 | #include "decimal.h" 2 | 3 | namespace clickhouse { 4 | 5 | ColumnDecimal::ColumnDecimal(size_t precision, size_t scale) 6 | : Column(Type::CreateDecimal(precision, scale)) 7 | { 8 | if (precision <= 9) { 9 | data_ = std::make_shared(); 10 | } else if (precision <= 18) { 11 | data_ = std::make_shared(); 12 | } else { 13 | data_ = std::make_shared(); 14 | } 15 | } 16 | 17 | ColumnDecimal::ColumnDecimal(TypeRef type) 18 | : Column(type) 19 | { 20 | } 21 | 22 | void ColumnDecimal::Append(const Int128& value) { 23 | if (data_->Type()->GetCode() == Type::Int32) { 24 | data_->As()->Append(static_cast(value)); 25 | } else if (data_->Type()->GetCode() == Type::Int64) { 26 | data_->As()->Append(static_cast(value)); 27 | } else { 28 | data_->As()->Append(static_cast(value)); 29 | } 30 | } 31 | 32 | void ColumnDecimal::Append(const std::string& value) { 33 | Int128 int_value = 0; 34 | auto c = value.begin(); 35 | bool sign = true; 36 | 37 | while (c != value.end()) { 38 | if (*c == '-') { 39 | sign = false; 40 | if (c != value.begin()) { 41 | break; 42 | } 43 | } else if (*c == '.') { 44 | // TODO: compare distance with `scale` 45 | } else if (*c >= '0' && *c <= '9') { 46 | int_value = int_value * 10 + (*c - '0'); 47 | } else { 48 | // TODO: throw exception on unexpected symbol 49 | } 50 | ++c; 51 | } 52 | 53 | if (c != value.end()) { 54 | // TODO: throw exception about symbols after 'minus' 55 | } 56 | 57 | Append(sign ? int_value : -int_value); 58 | } 59 | 60 | Int128 ColumnDecimal::At(size_t i) const { 61 | if (data_->Type()->GetCode() == Type::Int32) { 62 | return static_cast(data_->As()->At(i)); 63 | } else if (data_->Type()->GetCode() == Type::Int64) { 64 | return static_cast(data_->As()->At(i)); 65 | } else { 66 | return data_->As()->At(i); 67 | } 68 | } 69 | 70 | void ColumnDecimal::Append(ColumnRef column) { 71 | if (auto col = column->As()) { 72 | data_->Append(col->data_); 73 | } 74 | } 75 | 76 | bool ColumnDecimal::Load(CodedInputStream* input, size_t rows) { 77 | return data_->Load(input, rows); 78 | } 79 | 80 | void ColumnDecimal::Save(CodedOutputStream* output) { 81 | data_->Save(output); 82 | } 83 | 84 | void ColumnDecimal::Clear() { 85 | data_->Clear(); 86 | } 87 | 88 | size_t ColumnDecimal::Size() const { 89 | return data_->Size(); 90 | } 91 | 92 | ColumnRef ColumnDecimal::Slice(size_t begin, size_t len) { 93 | std::shared_ptr slice(new ColumnDecimal(type_)); 94 | slice->data_ = data_->Slice(begin, len); 95 | return slice; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /clickhouse/columns/decimal.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "column.h" 5 | #include "numeric.h" 6 | 7 | namespace clickhouse { 8 | 9 | /** 10 | * Represents a column of decimal type. 11 | */ 12 | class ColumnDecimal : public Column { 13 | public: 14 | ColumnDecimal(size_t precision, size_t scale); 15 | 16 | void Append(const Int128& value); 17 | void Append(const std::string& value); 18 | 19 | Int128 At(size_t i) const; 20 | 21 | public: 22 | void Append(ColumnRef column) override; 23 | bool Load(CodedInputStream* input, size_t rows) override; 24 | void Save(CodedOutputStream* output) override; 25 | void Clear() override; 26 | size_t Size() const override; 27 | ColumnRef Slice(size_t begin, size_t len) override; 28 | 29 | private: 30 | /// Depending on a precision it can be one of: 31 | /// - ColumnInt32 32 | /// - ColumnInt64 33 | /// - ColumnInt128 34 | ColumnRef data_; 35 | 36 | explicit ColumnDecimal(TypeRef type); // for `Slice(…)` 37 | }; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /clickhouse/columns/enum.cpp: -------------------------------------------------------------------------------- 1 | #include "enum.h" 2 | #include "utils.h" 3 | 4 | namespace clickhouse { 5 | 6 | template 7 | ColumnEnum::ColumnEnum(TypeRef type) 8 | : Column(type) 9 | { 10 | } 11 | 12 | template 13 | ColumnEnum::ColumnEnum(TypeRef type, const std::vector& data) 14 | : Column(type) 15 | , data_(data) 16 | { 17 | } 18 | 19 | template 20 | void ColumnEnum::Append(const T& value, bool checkValue) { 21 | if (checkValue) { 22 | // TODO type_->HasEnumValue(value), "Enum type doesn't have value " + std::to_string(value); 23 | } 24 | data_.push_back(value); 25 | } 26 | 27 | template 28 | void ColumnEnum::Append(const std::string& name) { 29 | data_.push_back(EnumType(type_).GetEnumValue(name)); 30 | } 31 | 32 | template 33 | void ColumnEnum::Clear() { 34 | data_.clear(); 35 | } 36 | 37 | template 38 | const T& ColumnEnum::At(size_t n) const { 39 | return data_.at(n); 40 | } 41 | 42 | template 43 | const std::string ColumnEnum::NameAt(size_t n) const { 44 | return EnumType(type_).GetEnumName(data_.at(n)); 45 | } 46 | 47 | template 48 | const T& ColumnEnum::operator[] (size_t n) const { 49 | return data_[n]; 50 | } 51 | 52 | template 53 | void ColumnEnum::SetAt(size_t n, const T& value, bool checkValue) { 54 | if (checkValue) { 55 | // TODO: type_->HasEnumValue(value), "Enum type doesn't have value " + std::to_string(value); 56 | } 57 | data_.at(n) = value; 58 | } 59 | 60 | template 61 | void ColumnEnum::SetNameAt(size_t n, const std::string& name) { 62 | data_.at(n) = EnumType(type_).GetEnumValue(name); 63 | } 64 | 65 | template 66 | void ColumnEnum::Append(ColumnRef column) { 67 | if (auto col = column->As>()) { 68 | data_.insert(data_.end(), col->data_.begin(), col->data_.end()); 69 | } 70 | } 71 | 72 | template 73 | bool ColumnEnum::Load(CodedInputStream* input, size_t rows) { 74 | data_.resize(rows); 75 | return input->ReadRaw(data_.data(), data_.size() * sizeof(T)); 76 | } 77 | 78 | template 79 | void ColumnEnum::Save(CodedOutputStream* output) { 80 | output->WriteRaw(data_.data(), data_.size() * sizeof(T)); 81 | } 82 | 83 | template 84 | size_t ColumnEnum::Size() const { 85 | return data_.size(); 86 | } 87 | 88 | template 89 | ColumnRef ColumnEnum::Slice(size_t begin, size_t len) { 90 | return std::make_shared>(type_, SliceVector(data_, begin, len)); 91 | } 92 | 93 | template class ColumnEnum; 94 | template class ColumnEnum; 95 | 96 | } 97 | -------------------------------------------------------------------------------- /clickhouse/columns/enum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "column.h" 4 | 5 | namespace clickhouse { 6 | 7 | 8 | template 9 | class ColumnEnum : public Column { 10 | public: 11 | ColumnEnum(TypeRef type); 12 | ColumnEnum(TypeRef type, const std::vector& data); 13 | 14 | /// Appends one element to the end of column. 15 | void Append(const T& value, bool checkValue = false); 16 | void Append(const std::string& name); 17 | 18 | /// Returns element at given row number. 19 | const T& At(size_t n) const; 20 | const std::string NameAt(size_t n) const; 21 | 22 | /// Returns element at given row number. 23 | const T& operator[] (size_t n) const; 24 | 25 | /// Set element at given row number. 26 | void SetAt(size_t n, const T& value, bool checkValue = false); 27 | void SetNameAt(size_t n, const std::string& name); 28 | 29 | public: 30 | /// Appends content of given column to the end of current one. 31 | void Append(ColumnRef column) override; 32 | 33 | /// Loads column data from input stream. 34 | bool Load(CodedInputStream* input, size_t rows) override; 35 | 36 | /// Saves column data to output stream. 37 | void Save(CodedOutputStream* output) override; 38 | 39 | /// Clear column data . 40 | void Clear() override; 41 | 42 | /// Returns count of rows in the column. 43 | size_t Size() const override; 44 | 45 | /// Makes slice of the current column. 46 | ColumnRef Slice(size_t begin, size_t len) override; 47 | 48 | private: 49 | std::vector data_; 50 | }; 51 | 52 | using ColumnEnum8 = ColumnEnum; 53 | using ColumnEnum16 = ColumnEnum; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /clickhouse/columns/factory.cpp: -------------------------------------------------------------------------------- 1 | #include "factory.h" 2 | 3 | #include "array.h" 4 | #include "date.h" 5 | #include "decimal.h" 6 | #include "enum.h" 7 | #include "ip4.h" 8 | #include "ip6.h" 9 | #include "nothing.h" 10 | #include "nullable.h" 11 | #include "numeric.h" 12 | #include "string.h" 13 | #include "tuple.h" 14 | #include "uuid.h" 15 | 16 | #include "../types/type_parser.h" 17 | 18 | namespace clickhouse { 19 | namespace { 20 | 21 | static ColumnRef CreateTerminalColumn(const TypeAst& ast) { 22 | switch (ast.code) { 23 | case Type::Void: 24 | return std::make_shared(); 25 | 26 | case Type::UInt8: 27 | return std::make_shared(); 28 | case Type::UInt16: 29 | return std::make_shared(); 30 | case Type::UInt32: 31 | return std::make_shared(); 32 | case Type::UInt64: 33 | return std::make_shared(); 34 | 35 | case Type::Int8: 36 | return std::make_shared(); 37 | case Type::Int16: 38 | return std::make_shared(); 39 | case Type::Int32: 40 | return std::make_shared(); 41 | case Type::Int64: 42 | return std::make_shared(); 43 | 44 | case Type::Float32: 45 | return std::make_shared(); 46 | case Type::Float64: 47 | return std::make_shared(); 48 | 49 | case Type::Decimal: 50 | return std::make_shared(ast.elements.front().value, ast.elements.back().value); 51 | case Type::Decimal32: 52 | return std::make_shared(9, ast.elements.front().value); 53 | case Type::Decimal64: 54 | return std::make_shared(18, ast.elements.front().value); 55 | case Type::Decimal128: 56 | return std::make_shared(38, ast.elements.front().value); 57 | 58 | case Type::String: 59 | return std::make_shared(); 60 | case Type::FixedString: 61 | return std::make_shared(ast.elements.front().value); 62 | 63 | case Type::DateTime: 64 | if (ast.elements.empty()) { 65 | return std::make_shared(); 66 | } else { 67 | return std::make_shared(ast.elements[0].value_string); 68 | } 69 | case Type::DateTime64: 70 | if (ast.elements.empty()) { 71 | return nullptr; 72 | } 73 | if (ast.elements.size() == 1) { 74 | return std::make_shared(ast.elements[0].value); 75 | } else { 76 | return std::make_shared(ast.elements[0].value, ast.elements[1].value_string); 77 | } 78 | case Type::Date: 79 | return std::make_shared(); 80 | 81 | case Type::IPv4: 82 | return std::make_shared(); 83 | case Type::IPv6: 84 | return std::make_shared(); 85 | 86 | case Type::UUID: 87 | return std::make_shared(); 88 | 89 | default: 90 | return nullptr; 91 | } 92 | } 93 | 94 | static ColumnRef CreateColumnFromAst(const TypeAst& ast) { 95 | switch (ast.meta) { 96 | case TypeAst::Array: { 97 | return std::make_shared( 98 | CreateColumnFromAst(ast.elements.front()) 99 | ); 100 | } 101 | 102 | case TypeAst::Nullable: { 103 | return std::make_shared( 104 | CreateColumnFromAst(ast.elements.front()), 105 | std::make_shared() 106 | ); 107 | } 108 | 109 | case TypeAst::Terminal: { 110 | return CreateTerminalColumn(ast); 111 | } 112 | 113 | case TypeAst::Tuple: { 114 | std::vector columns; 115 | 116 | columns.reserve(ast.elements.size()); 117 | for (const auto& elem : ast.elements) { 118 | if (auto col = CreateColumnFromAst(elem)) { 119 | columns.push_back(col); 120 | } else { 121 | return nullptr; 122 | } 123 | } 124 | 125 | return std::make_shared(columns); 126 | } 127 | 128 | case TypeAst::Enum: { 129 | std::vector enum_items; 130 | 131 | enum_items.reserve(ast.elements.size() / 2); 132 | for (size_t i = 0; i < ast.elements.size(); i += 2) { 133 | enum_items.push_back( 134 | Type::EnumItem{ast.elements[i].value_string, 135 | (int16_t)ast.elements[i + 1].value}); 136 | } 137 | 138 | if (ast.code == Type::Enum8) { 139 | return std::make_shared( 140 | Type::CreateEnum8(enum_items) 141 | ); 142 | } else if (ast.code == Type::Enum16) { 143 | return std::make_shared( 144 | Type::CreateEnum16(enum_items) 145 | ); 146 | } 147 | break; 148 | } 149 | 150 | case TypeAst::Assign: 151 | case TypeAst::Null: 152 | case TypeAst::Number: 153 | case TypeAst::String: 154 | break; 155 | } 156 | 157 | return nullptr; 158 | } 159 | 160 | } // namespace 161 | 162 | 163 | ColumnRef CreateColumnByType(const std::string& type_name) { 164 | auto ast = ParseTypeName(type_name); 165 | if (ast != nullptr) { 166 | return CreateColumnFromAst(*ast); 167 | } 168 | 169 | return nullptr; 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /clickhouse/columns/factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "column.h" 4 | 5 | namespace clickhouse { 6 | 7 | ColumnRef CreateColumnByType(const std::string& type_name); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /clickhouse/columns/ip4.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "ip4.h" 3 | 4 | #include 5 | 6 | #if defined(_win_) 7 | using in_addr_t = unsigned long; 8 | #endif 9 | 10 | namespace clickhouse { 11 | 12 | ColumnIPv4::ColumnIPv4() 13 | : Column(Type::CreateIPv4()) 14 | , data_(std::make_shared()) 15 | { 16 | } 17 | 18 | ColumnIPv4::ColumnIPv4(ColumnRef data) 19 | : Column(Type::CreateIPv4()) 20 | , data_(data->As()) 21 | { 22 | if (data_->Size() != 0) { 23 | throw std::runtime_error("number of entries must be even (32-bit numbers for each IPv4)"); 24 | } 25 | } 26 | 27 | void ColumnIPv4::Append(const std::string& str) { 28 | in_addr_t addr = inet_addr(str.c_str()); 29 | if (addr == INADDR_NONE) { 30 | throw std::runtime_error("invalid IPv4 format, ip: " + str); 31 | } 32 | data_->Append(htonl(addr)); 33 | } 34 | 35 | void ColumnIPv4::Append(uint32_t ip) { 36 | data_->Append(htonl(ip)); 37 | } 38 | 39 | void ColumnIPv4::Clear() { 40 | data_->Clear(); 41 | } 42 | 43 | in_addr ColumnIPv4::At(size_t n) const { 44 | struct in_addr addr; 45 | addr.s_addr = ntohl(data_->At(n)); 46 | return addr; 47 | } 48 | 49 | in_addr ColumnIPv4::operator [] (size_t n) const { 50 | struct in_addr addr; 51 | addr.s_addr = ntohl(data_->operator[](n)); 52 | return addr; 53 | } 54 | 55 | std::string ColumnIPv4::AsString(size_t n) const { 56 | return inet_ntoa(this->At(n)); 57 | } 58 | 59 | void ColumnIPv4::Append(ColumnRef column) { 60 | if (auto col = column->As()) { 61 | data_->Append(col->data_); 62 | } 63 | } 64 | 65 | bool ColumnIPv4::Load(CodedInputStream* input, size_t rows) { 66 | return data_->Load(input, rows); 67 | } 68 | 69 | void ColumnIPv4::Save(CodedOutputStream* output) { 70 | data_->Save(output); 71 | } 72 | 73 | size_t ColumnIPv4::Size() const { 74 | return data_->Size(); 75 | } 76 | 77 | ColumnRef ColumnIPv4::Slice(size_t begin, size_t len) { 78 | return std::make_shared(data_->Slice(begin, len)); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /clickhouse/columns/ip4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "numeric.h" 4 | #include "../base/socket.h" 5 | 6 | namespace clickhouse { 7 | 8 | class ColumnIPv4 : public Column { 9 | public: 10 | ColumnIPv4(); 11 | explicit ColumnIPv4(ColumnRef data); 12 | 13 | /// Appends one element to the column. 14 | void Append(const std::string& ip); 15 | 16 | /// @params ip numeric value with host byte order. 17 | void Append(uint32_t ip); 18 | 19 | /// Returns element at given row number. 20 | in_addr At(size_t n) const; 21 | 22 | /// Returns element at given row number. 23 | in_addr operator [] (size_t n) const; 24 | 25 | std::string AsString(size_t n) const; 26 | 27 | public: 28 | /// Appends content of given column to the end of current one. 29 | void Append(ColumnRef column) override; 30 | 31 | /// Loads column data from input stream. 32 | bool Load(CodedInputStream* input, size_t rows) override; 33 | 34 | /// Saves column data to output stream. 35 | void Save(CodedOutputStream* output) override; 36 | 37 | /// Clear column data . 38 | void Clear() override; 39 | 40 | /// Returns count of rows in the column. 41 | size_t Size() const override; 42 | 43 | /// Makes slice of the current column. 44 | ColumnRef Slice(size_t begin, size_t len) override; 45 | 46 | private: 47 | std::shared_ptr data_; 48 | }; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /clickhouse/columns/ip6.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "ip6.h" 3 | 4 | #include 5 | 6 | namespace clickhouse { 7 | 8 | static_assert(sizeof(struct in6_addr) == 16, "sizeof in6_addr should be 16 bytes"); 9 | 10 | ColumnIPv6::ColumnIPv6() 11 | : Column(Type::CreateIPv6()) 12 | , data_(std::make_shared(16)) 13 | { 14 | } 15 | 16 | ColumnIPv6::ColumnIPv6(ColumnRef data) 17 | : Column(Type::CreateIPv6()) 18 | , data_(data->As()) 19 | { 20 | if (data_->Size() != 0) { 21 | throw std::runtime_error("number of entries must be even (two 64-bit numbers for each IPv6)"); 22 | } 23 | } 24 | 25 | void ColumnIPv6::Append(const std::string& ip) { 26 | unsigned char buf[16]; 27 | if (inet_pton(AF_INET6, ip.c_str(), buf) != 1) { 28 | throw std::runtime_error("invalid IPv6 format, ip: " + ip); 29 | } 30 | data_->Append(std::string((const char*)buf, 16)); 31 | } 32 | 33 | void ColumnIPv6::Append(const in6_addr* addr) { 34 | data_->Append(std::string((const char*)addr->s6_addr, 16)); 35 | } 36 | 37 | void ColumnIPv6::Clear() { 38 | data_->Clear(); 39 | } 40 | 41 | std::string ColumnIPv6::AsString (size_t n) const{ 42 | const auto& addr = data_->At(n); 43 | char buf[INET6_ADDRSTRLEN]; 44 | const char* ip_str = inet_ntop(AF_INET6, addr.data(), buf, INET6_ADDRSTRLEN); 45 | if (ip_str == nullptr) { 46 | throw std::runtime_error("invalid IPv6 format: " + addr); 47 | } 48 | return ip_str; 49 | } 50 | 51 | in6_addr ColumnIPv6::At(size_t n) const { 52 | return *reinterpret_cast(data_->At(n).data()); 53 | } 54 | 55 | in6_addr ColumnIPv6::operator [] (size_t n) const { 56 | return *reinterpret_cast(data_->At(n).data()); 57 | } 58 | 59 | void ColumnIPv6::Append(ColumnRef column) { 60 | if (auto col = column->As()) { 61 | data_->Append(col->data_); 62 | } 63 | } 64 | 65 | bool ColumnIPv6::Load(CodedInputStream* input, size_t rows) { 66 | return data_->Load(input, rows); 67 | } 68 | 69 | void ColumnIPv6::Save(CodedOutputStream* output) { 70 | data_->Save(output); 71 | } 72 | 73 | size_t ColumnIPv6::Size() const { 74 | return data_->Size(); 75 | } 76 | 77 | ColumnRef ColumnIPv6::Slice(size_t begin, size_t len) { 78 | return std::make_shared(data_->Slice(begin, len)); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /clickhouse/columns/ip6.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "string.h" 4 | #include "../base/socket.h" 5 | 6 | namespace clickhouse { 7 | 8 | class ColumnIPv6 : public Column{ 9 | public: 10 | ColumnIPv6(); 11 | explicit ColumnIPv6(ColumnRef data); 12 | 13 | /// Appends one element to the column. 14 | void Append(const std::string& str); 15 | 16 | void Append(const in6_addr* addr); 17 | 18 | /// Returns element at given row number. 19 | in6_addr At(size_t n) const; 20 | 21 | /// Returns element at given row number. 22 | in6_addr operator [] (size_t n) const; 23 | 24 | std::string AsString(size_t n) const; 25 | 26 | public: 27 | /// Appends content of given column to the end of current one. 28 | void Append(ColumnRef column) override; 29 | 30 | /// Loads column data from input stream. 31 | bool Load(CodedInputStream* input, size_t rows) override; 32 | 33 | /// Saves column data to output stream. 34 | void Save(CodedOutputStream* output) override; 35 | 36 | /// Clear column data . 37 | void Clear() override; 38 | 39 | /// Returns count of rows in the column. 40 | size_t Size() const override; 41 | 42 | /// Makes slice of the current column. 43 | ColumnRef Slice(size_t begin, size_t len) override; 44 | 45 | private: 46 | std::shared_ptr data_; 47 | }; 48 | 49 | } 50 | -------------------------------------------------------------------------------- /clickhouse/columns/nothing.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "column.h" 5 | 6 | #include 7 | 8 | namespace clickhouse { 9 | 10 | /** 11 | * Represents dummy column of NULLs. 12 | */ 13 | class ColumnNothing : public Column { 14 | public: 15 | ColumnNothing() 16 | : Column(Type::CreateNothing()) 17 | , size_(0) 18 | { 19 | } 20 | 21 | explicit ColumnNothing(size_t n) 22 | : Column(Type::CreateNothing()) 23 | , size_(n) 24 | { 25 | } 26 | 27 | /// Appends one element to the column. 28 | void Append(std::unique_ptr) { ++size_; } 29 | 30 | /// Returns element at given row number. 31 | std::nullptr_t At(size_t) const { return nullptr; }; 32 | 33 | /// Returns element at given row number. 34 | std::nullptr_t operator [] (size_t) const { return nullptr; }; 35 | 36 | /// Makes slice of the current column. 37 | ColumnRef Slice(size_t, size_t len) override { 38 | return std::make_shared(len); 39 | } 40 | 41 | public: 42 | /// Appends content of given column to the end of current one. 43 | void Append(ColumnRef column) override { 44 | if (auto col = column->As()) { 45 | size_ += col->Size(); 46 | } 47 | } 48 | 49 | /// Loads column data from input stream. 50 | bool Load(CodedInputStream* input, size_t rows) override { 51 | input->Skip(rows); 52 | size_ += rows; 53 | return true; 54 | } 55 | 56 | /// Saves column data to output stream. 57 | void Save(CodedOutputStream*) override { 58 | throw std::runtime_error("method Save is not supported for Nothing column"); 59 | } 60 | 61 | /// Clear column data . 62 | void Clear() override { size_ = 0; } 63 | 64 | /// Returns count of rows in the column. 65 | size_t Size() const override { return size_; } 66 | 67 | private: 68 | size_t size_; 69 | }; 70 | 71 | } 72 | -------------------------------------------------------------------------------- /clickhouse/columns/nullable.cpp: -------------------------------------------------------------------------------- 1 | #include "nullable.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace clickhouse { 7 | 8 | ColumnNullable::ColumnNullable(ColumnRef nested, ColumnRef nulls) 9 | : Column(Type::CreateNullable(nested->Type())) 10 | , nested_(nested) 11 | , nulls_(nulls->As()) 12 | { 13 | if (nested_->Size() != nulls->Size()) { 14 | throw std::runtime_error("count of elements in nested and nulls should be the same"); 15 | } 16 | } 17 | 18 | bool ColumnNullable::IsNull(size_t n) const { 19 | return nulls_->At(n) != 0; 20 | } 21 | 22 | ColumnRef ColumnNullable::Nested() const { 23 | return nested_; 24 | } 25 | 26 | ColumnRef ColumnNullable::Nulls() const { 27 | return nulls_; 28 | } 29 | 30 | void ColumnNullable::Append(ColumnRef column) { 31 | if (auto col = column->As()) { 32 | if (!col->nested_->Type()->IsEqual(nested_->Type())) { 33 | return; 34 | } 35 | 36 | nested_->Append(col->nested_); 37 | nulls_->Append(col->nulls_); 38 | } 39 | } 40 | 41 | void ColumnNullable::Clear() { 42 | nested_->Clear(); 43 | nulls_->Clear(); 44 | } 45 | 46 | bool ColumnNullable::Load(CodedInputStream* input, size_t rows) { 47 | if (!nulls_->Load(input, rows)) { 48 | return false; 49 | } 50 | if (!nested_->Load(input, rows)) { 51 | return false; 52 | } 53 | return true; 54 | } 55 | 56 | void ColumnNullable::Save(CodedOutputStream* output) { 57 | nulls_->Save(output); 58 | nested_->Save(output); 59 | } 60 | 61 | size_t ColumnNullable::Size() const { 62 | assert(nested_->Size() == nulls_->Size()); 63 | return nulls_->Size(); 64 | } 65 | 66 | ColumnRef ColumnNullable::Slice(size_t begin, size_t len) { 67 | return std::make_shared(nested_->Slice(begin, len), nulls_->Slice(begin, len)); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /clickhouse/columns/nullable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "column.h" 4 | #include "numeric.h" 5 | 6 | namespace clickhouse { 7 | 8 | /** 9 | * Represents column of Nullable(T). 10 | */ 11 | class ColumnNullable : public Column { 12 | public: 13 | ColumnNullable(ColumnRef nested, ColumnRef nulls); 14 | 15 | /// Returns null flag at given row number. 16 | bool IsNull(size_t n) const; 17 | 18 | /// Returns nested column. 19 | ColumnRef Nested() const; 20 | 21 | /// Returns nulls column. 22 | ColumnRef Nulls() const; 23 | 24 | public: 25 | /// Appends content of given column to the end of current one. 26 | void Append(ColumnRef column) override; 27 | 28 | /// Loads column data from input stream. 29 | bool Load(CodedInputStream* input, size_t rows) override; 30 | 31 | /// Saves column data to output stream. 32 | void Save(CodedOutputStream* output) override; 33 | 34 | /// Clear column data . 35 | void Clear() override; 36 | 37 | /// Returns count of rows in the column. 38 | size_t Size() const override; 39 | 40 | /// Makes slice of the current column. 41 | ColumnRef Slice(size_t begin, size_t len) override; 42 | 43 | private: 44 | ColumnRef nested_; 45 | std::shared_ptr nulls_; 46 | }; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /clickhouse/columns/numeric.cpp: -------------------------------------------------------------------------------- 1 | #include "numeric.h" 2 | #include "utils.h" 3 | 4 | namespace clickhouse { 5 | 6 | template 7 | ColumnVector::ColumnVector() 8 | : Column(Type::CreateSimple()) 9 | { 10 | } 11 | 12 | template 13 | ColumnVector::ColumnVector(const std::vector& data) 14 | : Column(Type::CreateSimple()) 15 | , data_(data) 16 | { 17 | } 18 | 19 | template 20 | void ColumnVector::Append(const T& value) { 21 | data_.push_back(value); 22 | } 23 | 24 | template 25 | void ColumnVector::Clear() { 26 | data_.clear(); 27 | } 28 | 29 | template 30 | const T& ColumnVector::At(size_t n) const { 31 | return data_.at(n); 32 | } 33 | 34 | template 35 | const T& ColumnVector::operator [] (size_t n) const { 36 | return data_[n]; 37 | } 38 | 39 | template 40 | void ColumnVector::Append(ColumnRef column) { 41 | if (auto col = column->As>()) { 42 | data_.insert(data_.end(), col->data_.begin(), col->data_.end()); 43 | } 44 | } 45 | 46 | template 47 | bool ColumnVector::Load(CodedInputStream* input, size_t rows) { 48 | data_.resize(rows); 49 | 50 | return input->ReadRaw(data_.data(), data_.size() * sizeof(T)); 51 | } 52 | 53 | template 54 | void ColumnVector::Save(CodedOutputStream* output) { 55 | output->WriteRaw(data_.data(), data_.size() * sizeof(T)); 56 | } 57 | 58 | template 59 | size_t ColumnVector::Size() const { 60 | return data_.size(); 61 | } 62 | 63 | template 64 | ColumnRef ColumnVector::Slice(size_t begin, size_t len) { 65 | return std::make_shared>(SliceVector(data_, begin, len)); 66 | } 67 | 68 | template class ColumnVector; 69 | template class ColumnVector; 70 | template class ColumnVector; 71 | template class ColumnVector; 72 | 73 | template class ColumnVector; 74 | template class ColumnVector; 75 | template class ColumnVector; 76 | template class ColumnVector; 77 | template class ColumnVector; 78 | 79 | template class ColumnVector; 80 | template class ColumnVector; 81 | 82 | } 83 | 84 | -------------------------------------------------------------------------------- /clickhouse/columns/numeric.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "column.h" 4 | #include "absl/numeric/int128.h" 5 | 6 | namespace clickhouse { 7 | 8 | /** 9 | * Represents various numeric columns. 10 | */ 11 | template 12 | class ColumnVector : public Column { 13 | public: 14 | using DataType = T; 15 | 16 | ColumnVector(); 17 | 18 | explicit ColumnVector(const std::vector& data); 19 | 20 | /// Appends one element to the end of column. 21 | void Append(const T& value); 22 | 23 | /// Returns element at given row number. 24 | const T& At(size_t n) const; 25 | 26 | /// Returns element at given row number. 27 | const T& operator [] (size_t n) const; 28 | 29 | public: 30 | /// Appends content of given column to the end of current one. 31 | void Append(ColumnRef column) override; 32 | 33 | /// Loads column data from input stream. 34 | bool Load(CodedInputStream* input, size_t rows) override; 35 | 36 | /// Saves column data to output stream. 37 | void Save(CodedOutputStream* output) override; 38 | 39 | /// Clear column data . 40 | void Clear() override; 41 | 42 | /// Returns count of rows in the column. 43 | size_t Size() const override; 44 | 45 | /// Makes slice of the current column. 46 | ColumnRef Slice(size_t begin, size_t len) override; 47 | 48 | private: 49 | std::vector data_; 50 | }; 51 | 52 | using Int128 = absl::int128; 53 | 54 | using ColumnUInt8 = ColumnVector; 55 | using ColumnUInt16 = ColumnVector; 56 | using ColumnUInt32 = ColumnVector; 57 | using ColumnUInt64 = ColumnVector; 58 | 59 | using ColumnInt8 = ColumnVector; 60 | using ColumnInt16 = ColumnVector; 61 | using ColumnInt32 = ColumnVector; 62 | using ColumnInt64 = ColumnVector; 63 | using ColumnInt128 = ColumnVector; 64 | 65 | using ColumnFloat32 = ColumnVector; 66 | using ColumnFloat64 = ColumnVector; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /clickhouse/columns/string.cpp: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include "utils.h" 3 | 4 | #include "../base/wire_format.h" 5 | 6 | namespace clickhouse { 7 | 8 | ColumnFixedString::ColumnFixedString(size_t n) 9 | : Column(Type::CreateString(n)) 10 | , string_size_(n) 11 | { 12 | } 13 | 14 | void ColumnFixedString::Append(const std::string& str) { 15 | data_.push_back(str); 16 | data_.back().resize(string_size_); 17 | } 18 | 19 | void ColumnFixedString::Clear() { 20 | data_.clear(); 21 | } 22 | 23 | const std::string& ColumnFixedString::At(size_t n) const { 24 | return data_.at(n); 25 | } 26 | 27 | const std::string& ColumnFixedString::operator [] (size_t n) const { 28 | return data_[n]; 29 | } 30 | 31 | void ColumnFixedString::Append(ColumnRef column) { 32 | if (auto col = column->As()) { 33 | if (string_size_ == col->string_size_) { 34 | data_.insert(data_.end(), col->data_.begin(), col->data_.end()); 35 | } 36 | } 37 | } 38 | 39 | bool ColumnFixedString::Load(CodedInputStream* input, size_t rows) { 40 | for (size_t i = 0; i < rows; ++i) { 41 | std::string s; 42 | s.resize(string_size_); 43 | 44 | if (!WireFormat::ReadBytes(input, &s[0], s.size())) { 45 | return false; 46 | } 47 | 48 | data_.push_back(std::move(s)); 49 | } 50 | 51 | return true; 52 | } 53 | 54 | void ColumnFixedString::Save(CodedOutputStream* output) { 55 | for (size_t i = 0; i < data_.size(); ++i) { 56 | WireFormat::WriteBytes(output, data_[i].data(), string_size_); 57 | } 58 | } 59 | 60 | size_t ColumnFixedString::Size() const { 61 | return data_.size(); 62 | } 63 | 64 | ColumnRef ColumnFixedString::Slice(size_t begin, size_t len) { 65 | auto result = std::make_shared(string_size_); 66 | 67 | if (begin < data_.size()) { 68 | result->data_ = SliceVector(data_, begin, len); 69 | } 70 | 71 | return result; 72 | } 73 | 74 | 75 | ColumnString::ColumnString() 76 | : Column(Type::CreateString()) 77 | { 78 | } 79 | 80 | ColumnString::ColumnString(const std::vector& data) 81 | : Column(Type::CreateString()) 82 | , data_(data) 83 | { 84 | } 85 | 86 | void ColumnString::Append(const std::string& str) { 87 | data_.push_back(str); 88 | } 89 | 90 | void ColumnString::Clear() { 91 | data_.clear(); 92 | } 93 | 94 | const std::string& ColumnString::At(size_t n) const { 95 | return data_.at(n); 96 | } 97 | 98 | const std::string& ColumnString::operator [] (size_t n) const { 99 | return data_[n]; 100 | } 101 | 102 | void ColumnString::Append(ColumnRef column) { 103 | if (auto col = column->As()) { 104 | data_.insert(data_.end(), col->data_.begin(), col->data_.end()); 105 | } 106 | } 107 | 108 | bool ColumnString::Load(CodedInputStream* input, size_t rows) { 109 | for (size_t i = 0; i < rows; ++i) { 110 | std::string s; 111 | 112 | if (!WireFormat::ReadString(input, &s)) { 113 | return false; 114 | } 115 | 116 | data_.push_back(std::move(s)); 117 | } 118 | 119 | return true; 120 | } 121 | 122 | void ColumnString::Save(CodedOutputStream* output) { 123 | for (auto si = data_.begin(); si != data_.end(); ++si) { 124 | WireFormat::WriteString(output, *si); 125 | } 126 | } 127 | 128 | size_t ColumnString::Size() const { 129 | return data_.size(); 130 | } 131 | 132 | ColumnRef ColumnString::Slice(size_t begin, size_t len) { 133 | return std::make_shared(SliceVector(data_, begin, len)); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /clickhouse/columns/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "column.h" 4 | 5 | namespace clickhouse { 6 | 7 | /** 8 | * Represents column of fixed-length strings. 9 | */ 10 | class ColumnFixedString : public Column { 11 | public: 12 | explicit ColumnFixedString(size_t n); 13 | 14 | /// Appends one element to the column. 15 | void Append(const std::string& str); 16 | 17 | /// Returns element at given row number. 18 | const std::string& At(size_t n) const; 19 | 20 | /// Returns element at given row number. 21 | const std::string& operator [] (size_t n) const; 22 | 23 | public: 24 | /// Appends content of given column to the end of current one. 25 | void Append(ColumnRef column) override; 26 | 27 | /// Loads column data from input stream. 28 | bool Load(CodedInputStream* input, size_t rows) override; 29 | 30 | /// Saves column data to output stream. 31 | void Save(CodedOutputStream* output) override; 32 | 33 | /// Clear column data . 34 | void Clear() override; 35 | 36 | /// Returns count of rows in the column. 37 | size_t Size() const override; 38 | 39 | /// Makes slice of the current column. 40 | ColumnRef Slice(size_t begin, size_t len) override; 41 | 42 | private: 43 | const size_t string_size_; 44 | std::vector data_; 45 | }; 46 | 47 | /** 48 | * Represents column of variable-length strings. 49 | */ 50 | class ColumnString : public Column { 51 | public: 52 | ColumnString(); 53 | explicit ColumnString(const std::vector& data); 54 | 55 | /// Appends one element to the column. 56 | void Append(const std::string& str); 57 | 58 | /// Returns element at given row number. 59 | const std::string& At(size_t n) const; 60 | 61 | /// Returns element at given row number. 62 | const std::string& operator [] (size_t n) const; 63 | 64 | public: 65 | /// Appends content of given column to the end of current one. 66 | void Append(ColumnRef column) override; 67 | 68 | /// Loads column data from input stream. 69 | bool Load(CodedInputStream* input, size_t rows) override; 70 | 71 | /// Saves column data to output stream. 72 | void Save(CodedOutputStream* output) override; 73 | 74 | /// Clear column data . 75 | void Clear() override; 76 | 77 | /// Returns count of rows in the column. 78 | size_t Size() const override; 79 | 80 | /// Makes slice of the current column. 81 | ColumnRef Slice(size_t begin, size_t len) override; 82 | 83 | private: 84 | std::vector data_; 85 | }; 86 | 87 | } 88 | -------------------------------------------------------------------------------- /clickhouse/columns/tuple.cpp: -------------------------------------------------------------------------------- 1 | #include "tuple.h" 2 | 3 | namespace clickhouse { 4 | 5 | static std::vector CollectTypes(const std::vector& columns) { 6 | std::vector types; 7 | for (const auto& col : columns) { 8 | types.push_back(col->Type()); 9 | } 10 | return types; 11 | } 12 | 13 | ColumnTuple::ColumnTuple(const std::vector& columns) 14 | : Column(Type::CreateTuple(CollectTypes(columns))) 15 | , columns_(columns) 16 | { 17 | } 18 | 19 | size_t ColumnTuple::TupleSize() const { 20 | return columns_.size(); 21 | } 22 | 23 | size_t ColumnTuple::Size() const { 24 | return columns_.empty() ? 0 : columns_[0]->Size(); 25 | } 26 | 27 | bool ColumnTuple::Load(CodedInputStream* input, size_t rows) { 28 | for (auto ci = columns_.begin(); ci != columns_.end(); ++ci) { 29 | if (!(*ci)->Load(input, rows)) { 30 | return false; 31 | } 32 | } 33 | 34 | return true; 35 | } 36 | 37 | void ColumnTuple::Save(CodedOutputStream* output) { 38 | for (auto ci = columns_.begin(); ci != columns_.end(); ++ci) { 39 | (*ci)->Save(output); 40 | } 41 | } 42 | 43 | void ColumnTuple::Clear() { 44 | columns_.clear(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /clickhouse/columns/tuple.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "column.h" 4 | 5 | #include 6 | 7 | namespace clickhouse { 8 | 9 | /** 10 | * Represents column of Tuple([T]). 11 | */ 12 | class ColumnTuple : public Column { 13 | public: 14 | ColumnTuple(const std::vector& columns); 15 | 16 | /// Returns count of columns in the tuple. 17 | size_t TupleSize() const; 18 | 19 | ColumnRef operator [] (size_t n) const { 20 | return columns_[n]; 21 | } 22 | 23 | public: 24 | /// Appends content of given column to the end of current one. 25 | void Append(ColumnRef) override { } 26 | 27 | /// Loads column data from input stream. 28 | bool Load(CodedInputStream* input, size_t rows) override; 29 | 30 | /// Saves column data to output stream. 31 | void Save(CodedOutputStream* output) override; 32 | 33 | /// Clear column data . 34 | void Clear() override; 35 | 36 | /// Returns count of rows in the column. 37 | size_t Size() const override; 38 | 39 | /// Makes slice of the current column. 40 | ColumnRef Slice(size_t, size_t) override { return ColumnRef(); } 41 | 42 | private: 43 | std::vector columns_; 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /clickhouse/columns/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace clickhouse { 7 | 8 | template 9 | std::vector SliceVector(const std::vector& vec, size_t begin, size_t len) { 10 | std::vector result; 11 | 12 | if (begin < vec.size()) { 13 | len = std::min(len, vec.size() - begin); 14 | result.assign(vec.begin() + begin, vec.begin() + (begin + len)); 15 | } 16 | 17 | return result; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /clickhouse/columns/uuid.cpp: -------------------------------------------------------------------------------- 1 | #include "uuid.h" 2 | #include "utils.h" 3 | 4 | #include 5 | 6 | namespace clickhouse { 7 | 8 | ColumnUUID::ColumnUUID() 9 | : Column(Type::CreateUUID()) 10 | , data_(std::make_shared()) 11 | { 12 | } 13 | 14 | ColumnUUID::ColumnUUID(ColumnRef data) 15 | : Column(Type::CreateUUID()) 16 | , data_(data->As()) 17 | { 18 | if (data_->Size() % 2 != 0) { 19 | throw std::runtime_error("number of entries must be even (two 64-bit numbers for each UUID)"); 20 | } 21 | } 22 | 23 | void ColumnUUID::Append(const UInt128& value) { 24 | data_->Append(value.first); 25 | data_->Append(value.second); 26 | } 27 | 28 | void ColumnUUID::Clear() { 29 | data_->Clear(); 30 | } 31 | 32 | const UInt128 ColumnUUID::At(size_t n) const { 33 | return UInt128(data_->At(n * 2), data_->At(n * 2 + 1)); 34 | } 35 | 36 | const UInt128 ColumnUUID::operator [] (size_t n) const { 37 | return UInt128((*data_)[n * 2], (*data_)[n * 2 + 1]); 38 | } 39 | 40 | void ColumnUUID::Append(ColumnRef column) { 41 | if (auto col = column->As()) { 42 | data_->Append(col->data_); 43 | } 44 | } 45 | 46 | bool ColumnUUID::Load(CodedInputStream* input, size_t rows) { 47 | return data_->Load(input, rows * 2); 48 | } 49 | 50 | void ColumnUUID::Save(CodedOutputStream* output) { 51 | data_->Save(output); 52 | } 53 | 54 | size_t ColumnUUID::Size() const { 55 | return data_->Size() / 2; 56 | } 57 | 58 | ColumnRef ColumnUUID::Slice(size_t begin, size_t len) { 59 | return std::make_shared(data_->Slice(begin * 2, len * 2)); 60 | } 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /clickhouse/columns/uuid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "column.h" 4 | #include "numeric.h" 5 | 6 | namespace clickhouse { 7 | 8 | using UInt128 = std::pair; 9 | 10 | /** 11 | * Represents a UUID column. 12 | */ 13 | class ColumnUUID : public Column { 14 | public: 15 | ColumnUUID(); 16 | 17 | explicit ColumnUUID(ColumnRef data); 18 | 19 | /// Appends one element to the end of column. 20 | void Append(const UInt128& value); 21 | 22 | /// Returns element at given row number. 23 | const UInt128 At(size_t n) const; 24 | 25 | /// Returns element at given row number. 26 | const UInt128 operator [] (size_t n) const; 27 | 28 | public: 29 | /// Appends content of given column to the end of current one. 30 | void Append(ColumnRef column) override; 31 | 32 | /// Loads column data from input stream. 33 | bool Load(CodedInputStream* input, size_t rows) override; 34 | 35 | /// Saves column data to output stream. 36 | void Save(CodedOutputStream* output) override; 37 | 38 | /// Clear column data . 39 | void Clear() override; 40 | 41 | /// Returns count of rows in the column. 42 | size_t Size() const override; 43 | 44 | /// Makes slice of the current column. 45 | ColumnRef Slice(size_t begin, size_t len) override; 46 | 47 | private: 48 | std::shared_ptr data_; 49 | }; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /clickhouse/error_codes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace clickhouse { 4 | 5 | enum ErrorCodes { 6 | CHECKSUM_DOESNT_MATCH = 40, 7 | CANNOT_PARSE_DATETIME = 41, 8 | UNKNOWN_FUNCTION = 46, 9 | UNKNOWN_IDENTIFIER = 47, 10 | TABLE_ALREADY_EXISTS = 57, 11 | UNKNOWN_TABLE = 60, 12 | SYNTAX_ERROR = 62, 13 | UNKNOWN_DATABASE = 81, 14 | DATABASE_ALREADY_EXISTS = 82, 15 | UNKNOWN_PACKET_FROM_CLIENT = 99, 16 | UNEXPECTED_PACKET_FROM_CLIENT = 101, 17 | RECEIVED_DATA_FOR_WRONG_QUERY_ID = 103, 18 | ENGINE_REQUIRED = 119, 19 | READONLY = 164, 20 | UNKNOWN_USER = 192, 21 | WRONG_PASSWORD = 193, 22 | REQUIRED_PASSWORD = 194, 23 | IP_ADDRESS_NOT_ALLOWED = 195, 24 | LIMIT_EXCEEDED = 290, 25 | UNKNOWN_DATABASE_ENGINE = 336, 26 | UNKNOWN_EXCEPTION = 1002, 27 | }; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /clickhouse/exceptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "query.h" 4 | 5 | #include 6 | 7 | namespace clickhouse { 8 | 9 | class ServerException : public std::runtime_error { 10 | public: 11 | ServerException(std::unique_ptr e) 12 | : runtime_error(std::string()) 13 | , exception_(std::move(e)) 14 | { 15 | } 16 | 17 | int GetCode() const { 18 | return exception_->code; 19 | } 20 | 21 | const Exception& GetException() const { 22 | return *exception_; 23 | } 24 | 25 | const char* what() const noexcept override { 26 | return exception_->display_text.c_str(); 27 | } 28 | 29 | private: 30 | std::unique_ptr exception_; 31 | }; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /clickhouse/protocol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace clickhouse { 4 | 5 | /// Packet types that server transmits. 6 | namespace ServerCodes { 7 | enum { 8 | /// Name, version, revision. 9 | Hello = 0, 10 | /// A block of data (compressed or not). 11 | Data = 1, 12 | /// An exception during query execution. 13 | Exception = 2, 14 | /// Query execution progress: rows read, bytes read. 15 | Progress = 3, 16 | /// Ping response. 17 | Pong = 4, 18 | /// All packets were transmitted. 19 | EndOfStream = 5, 20 | /// A packet with profiling info. 21 | ProfileInfo = 6, 22 | /// A block of data with totals (compressed or not). 23 | Totals = 7, 24 | /// A block of data with minimums and maximums (compressed or not). 25 | Extremes = 8, 26 | }; 27 | } 28 | 29 | /// Packet types that client transmits. 30 | namespace ClientCodes { 31 | enum { 32 | /// Name, version, revision, default DB. 33 | Hello = 0, 34 | /// Query id, query settings, stage up to which the query must be executed, 35 | /// whether the compression must be used, 36 | /// query text (without data for INSERTs). 37 | Query = 1, 38 | /// A block of data (compressed or not). 39 | Data = 2, 40 | /// Cancel the query execution. 41 | Cancel = 3, 42 | /// Check that the connection to the server is alive. 43 | Ping = 4, 44 | }; 45 | } 46 | 47 | /// Whether the compression must be used. 48 | namespace CompressionState { 49 | enum { 50 | Disable = 0, 51 | Enable = 1, 52 | }; 53 | } 54 | 55 | namespace Stages { 56 | enum { 57 | Complete = 2, 58 | }; 59 | } 60 | 61 | } // namespace clickhouse 62 | -------------------------------------------------------------------------------- /clickhouse/query.cpp: -------------------------------------------------------------------------------- 1 | #include "query.h" 2 | 3 | namespace clickhouse { 4 | 5 | Query::Query() 6 | { } 7 | 8 | Query::Query(const char* query) 9 | : query_(query) 10 | { 11 | } 12 | 13 | Query::Query(const std::string& query) 14 | : query_(query) 15 | { 16 | } 17 | 18 | Query::~Query() 19 | { } 20 | 21 | void Query::OnData(const Block& block) { 22 | if (select_cb_) { 23 | select_cb_(block); 24 | } 25 | } 26 | 27 | bool Query::OnDataCancelable(const Block& block) { 28 | if (select_cancelable_cb_) { 29 | return select_cancelable_cb_(block); 30 | } else { 31 | return true; 32 | } 33 | } 34 | 35 | void Query::OnExtremes(const Block& block) { 36 | if (extremes_cb_) { 37 | extremes_cb_(block); 38 | } 39 | } 40 | 41 | void Query::OnServerException(const Exception& e) { 42 | if (exception_cb_) { 43 | exception_cb_(e); 44 | } 45 | } 46 | 47 | void Query::OnProfile(const Profile& profile) { 48 | (void)profile; 49 | } 50 | 51 | void Query::OnProgress(const Progress& progress) { 52 | if (progress_cb_) { 53 | progress_cb_(progress); 54 | } 55 | } 56 | 57 | void Query::OnFinish() { 58 | } 59 | 60 | void Query::OnTotals(const Block& block) { 61 | if (totals_cb_) { 62 | totals_cb_(block); 63 | } 64 | } 65 | 66 | } // namespace clickhouse 67 | -------------------------------------------------------------------------------- /clickhouse/query.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "block.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace clickhouse { 11 | 12 | /** 13 | * Settings of individual query. 14 | */ 15 | struct QuerySettings { 16 | /// A maximum number of threads for query execution. Determined automatically by default. 17 | int max_threads = 0; 18 | /// Calculate minimum and maximum values of each column. 19 | bool extremes = false; 20 | /// Silently skip unavailable shards. 21 | bool skip_unavailable_shards = false; 22 | /// Write statistics about read rows, bytes, time elapsed, etc. 23 | bool output_format_write_statistics = true; 24 | /// Use client timezone for interpreting DateTime string values, instead of adopting server timezone. 25 | bool use_client_time_zone = false; 26 | 27 | // connect_timeout 28 | // max_block_size 29 | // distributed_group_by_no_merge = false 30 | // strict_insert_defaults = 0 31 | // network_compression_method = LZ4 32 | // priority = 0 33 | }; 34 | 35 | struct Exception { 36 | int code = 0; 37 | std::string name; 38 | std::string display_text; 39 | std::string stack_trace; 40 | /// Pointer to nested exception. 41 | std::unique_ptr nested; 42 | }; 43 | 44 | struct Profile { 45 | uint64_t rows = 0; 46 | uint64_t blocks = 0; 47 | uint64_t bytes = 0; 48 | uint64_t rows_before_limit = 0; 49 | bool applied_limit = false; 50 | bool calculated_rows_before_limit = false; 51 | }; 52 | 53 | struct Progress { 54 | uint64_t rows = 0; 55 | uint64_t bytes = 0; 56 | uint64_t total_rows = 0; 57 | }; 58 | 59 | class QueryEvents { 60 | public: 61 | virtual ~QueryEvents() = default; 62 | 63 | /// Some data has been received. 64 | virtual void OnData(const Block& block) = 0; 65 | virtual bool OnDataCancelable(const Block& block) = 0; 66 | 67 | /// A block with extremes values has been received. 68 | virtual void OnExtremes(const Block& block) = 0; 69 | 70 | virtual void OnServerException(const Exception& e) = 0; 71 | 72 | virtual void OnProfile(const Profile& profile) = 0; 73 | 74 | virtual void OnProgress(const Progress& progress) = 0; 75 | 76 | virtual void OnFinish() = 0; 77 | 78 | /// A block with totals values has been received. 79 | virtual void OnTotals(const Block& block) = 0; 80 | }; 81 | 82 | using DataCallback = std::function; 83 | using ExceptionCallback = std::function; 84 | using ProgressCallback = std::function; 85 | using SelectCallback = DataCallback; 86 | using SelectCancelableCallback = std::function; 87 | 88 | class Query : public QueryEvents { 89 | public: 90 | Query(); 91 | Query(const char* query); 92 | Query(const std::string& query); 93 | ~Query() override; 94 | 95 | /// 96 | inline std::string GetText() const { 97 | return query_; 98 | } 99 | 100 | /// Set handler for receiving result data. 101 | inline Query& OnData(DataCallback cb) { 102 | select_cb_ = cb; 103 | return *this; 104 | } 105 | 106 | inline Query& OnDataCancelable(SelectCancelableCallback cb) { 107 | select_cancelable_cb_ = cb; 108 | return *this; 109 | } 110 | 111 | /// Set handler for receiving server's exception. 112 | inline Query& OnException(ExceptionCallback cb) { 113 | exception_cb_ = cb; 114 | return *this; 115 | } 116 | 117 | /// Set handler for receiving extremes values. 118 | inline Query& OnExtremes(DataCallback cb) { 119 | extremes_cb_ = cb; 120 | return *this; 121 | } 122 | 123 | /// Set handler for receiving a progress of query exceution. 124 | inline Query& OnProgress(ProgressCallback cb) { 125 | progress_cb_ = cb; 126 | return *this; 127 | } 128 | 129 | /// Set handler for receiving totals values. 130 | inline Query& OnTotals(DataCallback cb) { 131 | totals_cb_ = cb; 132 | return *this; 133 | } 134 | 135 | private: 136 | void OnData(const Block& block) override; 137 | 138 | bool OnDataCancelable(const Block& block) override; 139 | 140 | void OnExtremes(const Block& block) override; 141 | 142 | void OnServerException(const Exception& e) override; 143 | 144 | void OnProfile(const Profile& profile) override; 145 | 146 | void OnProgress(const Progress& progress) override; 147 | 148 | void OnFinish() override; 149 | 150 | void OnTotals(const Block& block) override; 151 | 152 | private: 153 | std::string query_; 154 | ExceptionCallback exception_cb_; 155 | ProgressCallback progress_cb_; 156 | DataCallback select_cb_; 157 | SelectCancelableCallback select_cancelable_cb_; 158 | DataCallback totals_cb_; 159 | DataCallback extremes_cb_; 160 | }; 161 | 162 | } // namespace clickhouse 163 | -------------------------------------------------------------------------------- /clickhouse/types/type_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "type_parser.h" 2 | #include "../base/string_utils.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace clickhouse { 9 | 10 | static const std::unordered_map kTypeCode = { 11 | { "Int8", Type::Int8 }, 12 | { "Int16", Type::Int16 }, 13 | { "Int32", Type::Int32 }, 14 | { "Int64", Type::Int64 }, 15 | { "UInt8", Type::UInt8 }, 16 | { "UInt16", Type::UInt16 }, 17 | { "UInt32", Type::UInt32 }, 18 | { "UInt64", Type::UInt64 }, 19 | { "Float32", Type::Float32 }, 20 | { "Float64", Type::Float64 }, 21 | { "String", Type::String }, 22 | { "FixedString", Type::FixedString }, 23 | { "DateTime", Type::DateTime }, 24 | { "DateTime64", Type::DateTime64 }, 25 | { "Date", Type::Date }, 26 | { "Array", Type::Array }, 27 | { "Nullable", Type::Nullable }, 28 | { "Tuple", Type::Tuple }, 29 | { "Enum8", Type::Enum8 }, 30 | { "Enum16", Type::Enum16 }, 31 | { "UUID", Type::UUID }, 32 | { "IPv4", Type::IPv4 }, 33 | { "IPv6", Type::IPv6 }, 34 | { "Decimal", Type::Decimal }, 35 | { "Decimal32", Type::Decimal32 }, 36 | { "Decimal64", Type::Decimal64 }, 37 | { "Decimal128", Type::Decimal128 }, 38 | }; 39 | 40 | static Type::Code GetTypeCode(const std::string& name) { 41 | auto it = kTypeCode.find(name); 42 | if (it != kTypeCode.end()) { 43 | return it->second; 44 | } 45 | return Type::Void; 46 | } 47 | 48 | static TypeAst::Meta GetTypeMeta(const std::string_view name) { 49 | if (name == "Array") { 50 | return TypeAst::Array; 51 | } 52 | 53 | if (name == "Null") { 54 | return TypeAst::Null; 55 | } 56 | 57 | if (name == "Nullable") { 58 | return TypeAst::Nullable; 59 | } 60 | 61 | if (name == "Tuple") { 62 | return TypeAst::Tuple; 63 | } 64 | 65 | if (name == "Enum8" || name == "Enum16") { 66 | return TypeAst::Enum; 67 | } 68 | 69 | return TypeAst::Terminal; 70 | } 71 | 72 | 73 | TypeParser::TypeParser(const std::string_view name) 74 | : cur_(name.data()) 75 | , end_(name.data() + name.size()) 76 | , type_(nullptr) 77 | { 78 | } 79 | 80 | TypeParser::~TypeParser() = default; 81 | 82 | bool TypeParser::Parse(TypeAst* type) { 83 | type_ = type; 84 | open_elements_.push(type_); 85 | 86 | do { 87 | const Token& token = NextToken(); 88 | 89 | switch (token.type) { 90 | case Token::Name: 91 | type_->meta = GetTypeMeta(token.value); 92 | type_->name = std::string(token.value); 93 | type_->code = GetTypeCode(type_->name); 94 | break; 95 | case Token::Number: 96 | type_->meta = TypeAst::Number; 97 | type_->value = std::stol(std::string(token.value)); 98 | break; 99 | case Token::String: 100 | type_->meta = TypeAst::String; 101 | type_->value_string = std::string(token.value); 102 | break; 103 | case Token::LPar: 104 | type_->elements.emplace_back(TypeAst()); 105 | open_elements_.push(type_); 106 | type_ = &type_->elements.back(); 107 | break; 108 | case Token::RPar: 109 | type_ = open_elements_.top(); 110 | open_elements_.pop(); 111 | break; 112 | case Token::Assign: 113 | case Token::Comma: 114 | type_ = open_elements_.top(); 115 | open_elements_.pop(); 116 | type_->elements.emplace_back(TypeAst()); 117 | open_elements_.push(type_); 118 | type_ = &type_->elements.back(); 119 | break; 120 | case Token::EOS: 121 | // Ubalanced braces, brackets, etc is an error. 122 | if (open_elements_.size() != 1) { 123 | return false; 124 | } 125 | return true; 126 | case Token::Invalid: 127 | return false; 128 | } 129 | } while (true); 130 | } 131 | 132 | TypeParser::Token TypeParser::NextToken() { 133 | for (; cur_ < end_; ++cur_) { 134 | switch (*cur_) { 135 | case ' ': 136 | case '\n': 137 | case '\t': 138 | case '\0': 139 | continue; 140 | 141 | case '=': 142 | return Token{Token::Assign, std::string_view(cur_++, 1)}; 143 | case '(': 144 | return Token{Token::LPar, std::string_view(cur_++, 1)}; 145 | case ')': 146 | return Token{Token::RPar, std::string_view(cur_++, 1)}; 147 | case ',': 148 | return Token{Token::Comma, std::string_view(cur_++, 1)}; 149 | 150 | default: { 151 | const char* st = cur_; 152 | 153 | if (*cur_ == '\'') { 154 | for (st = ++cur_; cur_ < end_; ++cur_) { 155 | if (*cur_ == '\'') { 156 | return Token{Token::String, std::string_view(st, cur_++ - st)}; 157 | } 158 | } 159 | 160 | return Token{Token::Invalid, std::string_view()}; 161 | } 162 | 163 | if (isalpha(*cur_) || *cur_ == '_') { 164 | for (; cur_ < end_; ++cur_) { 165 | if (!isalpha(*cur_) && !isdigit(*cur_) && *cur_ != '_') { 166 | break; 167 | } 168 | } 169 | 170 | return Token{Token::Name, std::string_view(st, cur_ - st)}; 171 | } 172 | 173 | if (isdigit(*cur_) || *cur_ == '-') { 174 | for (++cur_; cur_ < end_; ++cur_) { 175 | if (!isdigit(*cur_)) { 176 | break; 177 | } 178 | } 179 | 180 | return Token{Token::Number, std::string_view(st, cur_ - st)}; 181 | } 182 | 183 | return Token{Token::Invalid, std::string_view()}; 184 | } 185 | } 186 | } 187 | 188 | return Token{Token::EOS, std::string_view()}; 189 | } 190 | 191 | 192 | const TypeAst* ParseTypeName(const std::string& type_name) { 193 | // Cache for type_name. 194 | // Usually we won't have too many type names in the cache, so do not try to 195 | // limit cache size. 196 | static std::map ast_cache; 197 | static std::mutex lock; 198 | 199 | std::lock_guard guard(lock); 200 | auto it = ast_cache.find(type_name); 201 | if (it != ast_cache.end()) { 202 | return &it->second; 203 | } 204 | 205 | auto& ast = ast_cache[type_name]; 206 | if (TypeParser(type_name).Parse(&ast)) { 207 | return * 208 | } 209 | ast_cache.erase(type_name); 210 | return nullptr; 211 | } 212 | 213 | } 214 | -------------------------------------------------------------------------------- /clickhouse/types/type_parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace clickhouse { 11 | 12 | struct TypeAst { 13 | enum Meta { 14 | Array, 15 | Assign, 16 | Null, 17 | Nullable, 18 | Number, 19 | String, 20 | Terminal, 21 | Tuple, 22 | Enum, 23 | }; 24 | 25 | /// Type's category. 26 | Meta meta; 27 | Type::Code code; 28 | /// Type's name. 29 | /// Need to cache TypeAst, so can't use StringView for name. 30 | std::string name; 31 | /// Value associated with the node, 32 | /// used for fixed-width types and enum values. 33 | int64_t value = 0; 34 | std::string value_string; 35 | /// Subelements of the type. 36 | /// Used to store enum's names and values as well. 37 | std::vector elements; 38 | }; 39 | 40 | 41 | class TypeParser { 42 | 43 | struct Token { 44 | enum Type { 45 | Invalid = 0, 46 | Assign, 47 | Name, 48 | Number, 49 | String, 50 | LPar, 51 | RPar, 52 | Comma, 53 | EOS, 54 | }; 55 | 56 | Type type; 57 | std::string_view value; 58 | }; 59 | 60 | public: 61 | explicit TypeParser(const std::string_view name); 62 | ~TypeParser(); 63 | 64 | bool Parse(TypeAst* type); 65 | 66 | private: 67 | Token NextToken(); 68 | 69 | private: 70 | const char* cur_; 71 | const char* end_; 72 | 73 | TypeAst* type_; 74 | std::stack open_elements_; 75 | }; 76 | 77 | 78 | const TypeAst* ParseTypeName(const std::string& type_name); 79 | 80 | } 81 | -------------------------------------------------------------------------------- /clickhouse/types/types.cpp: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | 3 | #include 4 | 5 | namespace clickhouse { 6 | 7 | Type::Type(const Code code) 8 | : code_(code) 9 | { 10 | if (code_ == Array) { 11 | array_ = new ArrayImpl; 12 | } else if (code_ == DateTime || code_ == DateTime64) { 13 | date_time_ = new DateTimeImpl; 14 | } else if (code_ == Tuple) { 15 | tuple_ = new TupleImpl; 16 | } else if (code_ == Nullable) { 17 | nullable_ = new NullableImpl; 18 | } else if (code_ == Enum8 || code_ == Enum16) { 19 | enum_ = new EnumImpl; 20 | } else if (code_== Decimal || code_== Decimal32 || code_ == Decimal64 || code_ == Decimal128) { 21 | decimal_ = new DecimalImpl; 22 | } 23 | } 24 | 25 | Type::~Type() { 26 | if (code_ == Array) { 27 | delete array_; 28 | } else if (code_ == DateTime || code_ == DateTime64) { 29 | delete date_time_; 30 | } else if (code_ == Tuple) { 31 | delete tuple_; 32 | } else if (code_ == Nullable) { 33 | delete nullable_; 34 | } else if (code_ == Enum8 || code_ == Enum16) { 35 | delete enum_; 36 | } else if (code_== Decimal || code_== Decimal32 || code_ == Decimal64 || code_ == Decimal128) { 37 | delete decimal_; 38 | } 39 | } 40 | 41 | Type::Code Type::GetCode() const { 42 | return code_; 43 | } 44 | 45 | TypeRef Type::GetItemType() const { 46 | if (code_ == Array) { 47 | return array_->item_type; 48 | } 49 | return TypeRef(); 50 | } 51 | 52 | TypeRef Type::GetNestedType() const { 53 | if (code_ == Nullable) { 54 | return nullable_->nested_type; 55 | } 56 | return TypeRef(); 57 | } 58 | 59 | std::vector Type::GetTupleType() const { 60 | if (code_ == Tuple) { 61 | return tuple_->item_types; 62 | } 63 | return std::vector(); 64 | } 65 | 66 | std::string Type::GetName() const { 67 | switch (code_) { 68 | case Void: 69 | return "Void"; 70 | case Int8: 71 | return "Int8"; 72 | case Int16: 73 | return "Int16"; 74 | case Int32: 75 | return "Int32"; 76 | case Int64: 77 | return "Int64"; 78 | case Int128: 79 | return "Int128"; 80 | case UInt8: 81 | return "UInt8"; 82 | case UInt16: 83 | return "UInt16"; 84 | case UInt32: 85 | return "UInt32"; 86 | case UInt64: 87 | return "UInt64"; 88 | case UUID: 89 | return "UUID"; 90 | case Float32: 91 | return "Float32"; 92 | case Float64: 93 | return "Float64"; 94 | case String: 95 | return "String"; 96 | case FixedString: 97 | return "FixedString(" + std::to_string(string_size_) + ")"; 98 | case IPv4: 99 | return "IPv4"; 100 | case IPv6: 101 | return "IPv6"; 102 | case DateTime: 103 | if (date_time_->timezone.empty()) { 104 | return "DateTime"; 105 | } else { 106 | return "DateTime('" + date_time_->timezone + "')"; 107 | } 108 | case DateTime64: 109 | if (date_time_->timezone.empty()) { 110 | return "DateTime64(" + std::to_string(date_time_->precision) + ")"; 111 | } else { 112 | return "DateTime64(" + std::to_string(date_time_->precision) + ", '" + 113 | date_time_->timezone + "')"; 114 | } 115 | case Date: 116 | return "Date"; 117 | case Array: 118 | return std::string("Array(") + array_->item_type->GetName() +")"; 119 | case Nullable: 120 | return std::string("Nullable(") + nullable_->nested_type->GetName() + ")"; 121 | case Tuple: { 122 | std::string result("Tuple("); 123 | for (size_t i = 0; i < tuple_->item_types.size(); ++i) { 124 | result += tuple_->item_types[i]->GetName(); 125 | 126 | if (i + 1 != tuple_->item_types.size()) { 127 | result += ", "; 128 | } 129 | } 130 | result += ")"; 131 | return result; 132 | } 133 | case Enum8: 134 | case Enum16: { 135 | std::string result; 136 | if (code_ == Enum8) { 137 | result = "Enum8("; 138 | } else { 139 | result = "Enum16("; 140 | } 141 | for (auto ei = enum_->value_to_name.begin(); ei != enum_->value_to_name.end(); ++ei) { 142 | if (ei != enum_->value_to_name.begin()) { 143 | result += ", "; 144 | } 145 | result += "'"; 146 | result += ei->second; 147 | result += "' = "; 148 | result += std::to_string(ei->first); 149 | } 150 | result += ")"; 151 | return result; 152 | } 153 | case Decimal: 154 | return "Decimal(" + std::to_string(decimal_->precision) + "," + std::to_string(decimal_->scale) + ")"; 155 | case Decimal32: 156 | return "Decimal32(" + std::to_string(decimal_->scale) + ")"; 157 | case Decimal64: 158 | return "Decimal64(" + std::to_string(decimal_->scale) + ")"; 159 | case Decimal128: 160 | return "Decimal128(" + std::to_string(decimal_->scale) + ")"; 161 | } 162 | 163 | return std::string(); 164 | } 165 | 166 | bool Type::IsEqual(const TypeRef& other) const { 167 | return this->GetName() == other->GetName(); 168 | } 169 | 170 | TypeRef Type::CreateArray(TypeRef item_type) { 171 | TypeRef type(new Type(Type::Array)); 172 | type->array_->item_type = item_type; 173 | return type; 174 | } 175 | 176 | TypeRef Type::CreateDate() { 177 | return TypeRef(new Type(Type::Date)); 178 | } 179 | 180 | TypeRef Type::CreateDateTime(std::string timezone) { 181 | TypeRef type(new Type(Type::DateTime)); 182 | type->date_time_->timezone = std::move(timezone); 183 | return type; 184 | } 185 | 186 | TypeRef Type::CreateDateTime64(size_t precision, std::string timezone) { 187 | TypeRef type(new Type(Type::DateTime64)); 188 | type->date_time_->precision = precision; 189 | type->date_time_->timezone = std::move(timezone); 190 | return type; 191 | } 192 | 193 | TypeRef Type::CreateDecimal(size_t precision, size_t scale) { 194 | TypeRef type(new Type(Type::Decimal)); 195 | type->decimal_->precision = precision; 196 | type->decimal_->scale = scale; 197 | return type; 198 | } 199 | 200 | TypeRef Type::CreateIPv4() { 201 | return TypeRef(new Type(Type::IPv4)); 202 | } 203 | 204 | TypeRef Type::CreateIPv6() { 205 | return TypeRef(new Type(Type::IPv6)); 206 | } 207 | 208 | TypeRef Type::CreateNothing() { 209 | return TypeRef(new Type(Type::Void)); 210 | } 211 | 212 | TypeRef Type::CreateNullable(TypeRef nested_type) { 213 | TypeRef type(new Type(Type::Nullable)); 214 | type->nullable_->nested_type = nested_type; 215 | return type; 216 | } 217 | 218 | TypeRef Type::CreateString() { 219 | return TypeRef(new Type(Type::String)); 220 | } 221 | 222 | TypeRef Type::CreateString(size_t n) { 223 | TypeRef type(new Type(Type::FixedString)); 224 | type->string_size_ = n; 225 | return type; 226 | } 227 | 228 | TypeRef Type::CreateTuple(const std::vector& item_types) { 229 | TypeRef type(new Type(Type::Tuple)); 230 | type->tuple_->item_types.assign(item_types.begin(), item_types.end()); 231 | return type; 232 | } 233 | 234 | TypeRef Type::CreateEnum8(const std::vector& enum_items) { 235 | TypeRef type(new Type(Type::Enum8)); 236 | for (const auto& item : enum_items) { 237 | type->enum_->value_to_name[item.value] = item.name; 238 | type->enum_->name_to_value[item.name] = item.value; 239 | } 240 | return type; 241 | } 242 | 243 | TypeRef Type::CreateEnum16(const std::vector& enum_items) { 244 | TypeRef type(new Type(Type::Enum16)); 245 | for (const auto& item : enum_items) { 246 | type->enum_->value_to_name[item.value] = item.name; 247 | type->enum_->name_to_value[item.name] = item.value; 248 | } 249 | return type; 250 | } 251 | 252 | TypeRef Type::CreateUUID() { 253 | return TypeRef(new Type(Type::UUID)); 254 | } 255 | 256 | 257 | EnumType::EnumType(const TypeRef& type) 258 | : type_(type) 259 | { 260 | assert(type_->GetCode() == Type::Enum8 || 261 | type_->GetCode() == Type::Enum16); 262 | } 263 | 264 | const std::string& EnumType::GetEnumName(int16_t value) const { 265 | return type_->enum_->value_to_name[value]; 266 | } 267 | 268 | int16_t EnumType::GetEnumValue(const std::string& name) const { 269 | return type_->enum_->name_to_value[name]; 270 | } 271 | 272 | bool EnumType::HasEnumName(const std::string& name) const { 273 | return type_->enum_->name_to_value.find(name) != type_->enum_->name_to_value.end(); 274 | } 275 | 276 | bool EnumType::HasEnumValue(int16_t value) const { 277 | return type_->enum_->value_to_name.find(value) != type_->enum_->value_to_name.end(); 278 | } 279 | 280 | EnumType::ValueToNameIterator EnumType::BeginValueToName() const { 281 | return type_->enum_->value_to_name.begin(); 282 | } 283 | 284 | EnumType::ValueToNameIterator EnumType::EndValueToName() const { 285 | return type_->enum_->value_to_name.end(); 286 | } 287 | 288 | DateTimeType::DateTimeType(const TypeRef& type) 289 | : type_(type) 290 | { 291 | assert(type->GetCode() == Type::DateTime || 292 | type->GetCode() == Type::DateTime64); 293 | } 294 | 295 | std::string DateTimeType::Timezone() const { 296 | return type_->date_time_->timezone; 297 | } 298 | 299 | } 300 | -------------------------------------------------------------------------------- /clickhouse/types/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "absl/numeric/int128.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace clickhouse { 11 | 12 | using TypeRef = std::shared_ptr; 13 | 14 | class Type { 15 | public: 16 | enum Code { 17 | Void = 0, 18 | Int8, 19 | Int16, 20 | Int32, 21 | Int64, 22 | UInt8, 23 | UInt16, 24 | UInt32, 25 | UInt64, 26 | Float32, 27 | Float64, 28 | String, 29 | FixedString, 30 | DateTime, 31 | DateTime64, 32 | Date, 33 | Array, 34 | Nullable, 35 | Tuple, 36 | Enum8, 37 | Enum16, 38 | UUID, 39 | IPv4, 40 | IPv6, 41 | Int128, 42 | Decimal, 43 | Decimal32, 44 | Decimal64, 45 | Decimal128, 46 | }; 47 | 48 | struct EnumItem { 49 | std::string name; 50 | int16_t value; 51 | }; 52 | 53 | /// Destructor 54 | ~Type(); 55 | 56 | /// Type's code. 57 | Code GetCode() const; 58 | 59 | /// Type of array's elements. 60 | TypeRef GetItemType() const; 61 | 62 | /// Type of nested nullable element. 63 | TypeRef GetNestedType() const; 64 | 65 | /// Type of nested Tuple element type. 66 | std::vector GetTupleType() const; 67 | 68 | /// String representation of the type. 69 | std::string GetName() const; 70 | 71 | /// Is given type same as current one. 72 | bool IsEqual(const TypeRef& other) const; 73 | 74 | public: 75 | static TypeRef CreateArray(TypeRef item_type); 76 | 77 | static TypeRef CreateDate(); 78 | 79 | static TypeRef CreateDateTime(std::string timezone = std::string()); 80 | 81 | static TypeRef CreateDateTime64(size_t precision, std::string timezone = std::string()); 82 | 83 | static TypeRef CreateDecimal(size_t precision, size_t scale); 84 | 85 | static TypeRef CreateIPv4(); 86 | 87 | static TypeRef CreateIPv6(); 88 | 89 | static TypeRef CreateNothing(); 90 | 91 | static TypeRef CreateNullable(TypeRef nested_type); 92 | 93 | template 94 | static TypeRef CreateSimple(); 95 | 96 | static TypeRef CreateString(); 97 | 98 | static TypeRef CreateString(size_t n); 99 | 100 | static TypeRef CreateTuple(const std::vector& item_types); 101 | 102 | static TypeRef CreateEnum8(const std::vector& enum_items); 103 | 104 | static TypeRef CreateEnum16(const std::vector& enum_items); 105 | 106 | static TypeRef CreateUUID(); 107 | 108 | private: 109 | Type(const Code code); 110 | 111 | struct ArrayImpl { 112 | TypeRef item_type; 113 | }; 114 | 115 | struct DateTimeImpl { 116 | size_t precision; 117 | std::string timezone; 118 | }; 119 | 120 | struct DecimalImpl { 121 | size_t precision; 122 | size_t scale; 123 | }; 124 | 125 | struct NullableImpl { 126 | TypeRef nested_type; 127 | }; 128 | 129 | struct TupleImpl { 130 | std::vector item_types; 131 | }; 132 | 133 | struct EnumImpl { 134 | using ValueToNameType = std::map; 135 | using NameToValueType = std::map; 136 | ValueToNameType value_to_name; 137 | NameToValueType name_to_value; 138 | }; 139 | 140 | friend class EnumType; 141 | friend class DateTimeType; 142 | 143 | 144 | const Code code_; 145 | union { 146 | ArrayImpl* array_; 147 | DateTimeImpl* date_time_; 148 | DecimalImpl* decimal_; 149 | NullableImpl* nullable_; 150 | TupleImpl* tuple_; 151 | EnumImpl* enum_; 152 | int string_size_; 153 | }; 154 | }; 155 | 156 | class EnumType { 157 | public: 158 | explicit EnumType(const TypeRef& type); 159 | 160 | std::string GetName() const { 161 | return type_->GetName(); 162 | } 163 | /// Methods to work with enum types. 164 | const std::string& GetEnumName(int16_t value) const; 165 | int16_t GetEnumValue(const std::string& name) const; 166 | bool HasEnumName(const std::string& name) const; 167 | bool HasEnumValue(int16_t value) const; 168 | 169 | /// Iterator for enum elements. 170 | using ValueToNameIterator = Type::EnumImpl::ValueToNameType::const_iterator; 171 | ValueToNameIterator BeginValueToName() const; 172 | ValueToNameIterator EndValueToName() const; 173 | 174 | private: 175 | TypeRef type_; 176 | }; 177 | 178 | class DateTimeType { 179 | public: 180 | explicit DateTimeType(const TypeRef& type); 181 | 182 | /// Timezone associated with a data column. 183 | std::string Timezone() const; 184 | 185 | private: 186 | TypeRef type_; 187 | }; 188 | 189 | template <> 190 | inline TypeRef Type::CreateSimple() { 191 | return TypeRef(new Type(Int8)); 192 | } 193 | 194 | template <> 195 | inline TypeRef Type::CreateSimple() { 196 | return TypeRef(new Type(Int16)); 197 | } 198 | 199 | template <> 200 | inline TypeRef Type::CreateSimple() { 201 | return TypeRef(new Type(Int32)); 202 | } 203 | 204 | template <> 205 | inline TypeRef Type::CreateSimple() { 206 | return TypeRef(new Type(Int64)); 207 | } 208 | 209 | template <> 210 | inline TypeRef Type::CreateSimple() { 211 | return TypeRef(new Type(Int128)); 212 | } 213 | 214 | template <> 215 | inline TypeRef Type::CreateSimple() { 216 | return TypeRef(new Type(UInt8)); 217 | } 218 | 219 | template <> 220 | inline TypeRef Type::CreateSimple() { 221 | return TypeRef(new Type(UInt16)); 222 | } 223 | 224 | template <> 225 | inline TypeRef Type::CreateSimple() { 226 | return TypeRef(new Type(UInt32)); 227 | } 228 | 229 | template <> 230 | inline TypeRef Type::CreateSimple() { 231 | return TypeRef(new Type(UInt64)); 232 | } 233 | 234 | template <> 235 | inline TypeRef Type::CreateSimple() { 236 | return TypeRef(new Type(Float32)); 237 | } 238 | 239 | template <> 240 | inline TypeRef Type::CreateSimple() { 241 | return TypeRef(new Type(Float64)); 242 | } 243 | 244 | } 245 | -------------------------------------------------------------------------------- /cmake/cpp17.cmake: -------------------------------------------------------------------------------- 1 | MACRO (USE_CXX17) 2 | IF (CMAKE_VERSION VERSION_LESS "3.1") 3 | SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") 4 | ELSE () 5 | SET (CMAKE_CXX_STANDARD 17) 6 | SET (CMAKE_CXX_STANDARD_REQUIRED ON) 7 | ENDIF () 8 | ENDMACRO (USE_CXX17) 9 | -------------------------------------------------------------------------------- /cmake/subdirs.cmake: -------------------------------------------------------------------------------- 1 | FUNCTION (SUBDIRS) 2 | FOREACH (dir ${ARGV}) 3 | ADD_SUBDIRECTORY (${dir}) 4 | ENDFOREACH(dir) 5 | ENDFUNCTION (SUBDIRS) -------------------------------------------------------------------------------- /contrib/absl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_LIBRARY (absl-lib STATIC 2 | numeric/int128.cc 3 | ) 4 | -------------------------------------------------------------------------------- /contrib/absl/base/internal/bits.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef ABSL_BASE_INTERNAL_BITS_H_ 16 | #define ABSL_BASE_INTERNAL_BITS_H_ 17 | 18 | // This file contains bitwise ops which are implementation details of various 19 | // absl libraries. 20 | 21 | #include 22 | 23 | #include "absl/base/config.h" 24 | 25 | // Clang on Windows has __builtin_clzll; otherwise we need to use the 26 | // windows intrinsic functions. 27 | #if defined(_MSC_VER) && !defined(__clang__) 28 | #include 29 | #if defined(_M_X64) 30 | #pragma intrinsic(_BitScanReverse64) 31 | #pragma intrinsic(_BitScanForward64) 32 | #endif 33 | #pragma intrinsic(_BitScanReverse) 34 | #pragma intrinsic(_BitScanForward) 35 | #endif 36 | 37 | #include "absl/base/attributes.h" 38 | 39 | #if defined(_MSC_VER) && !defined(__clang__) 40 | // We can achieve something similar to attribute((always_inline)) with MSVC by 41 | // using the __forceinline keyword, however this is not perfect. MSVC is 42 | // much less aggressive about inlining, and even with the __forceinline keyword. 43 | #define ABSL_BASE_INTERNAL_FORCEINLINE __forceinline 44 | #else 45 | // Use default attribute inline. 46 | #define ABSL_BASE_INTERNAL_FORCEINLINE inline ABSL_ATTRIBUTE_ALWAYS_INLINE 47 | #endif 48 | 49 | 50 | namespace absl { 51 | ABSL_NAMESPACE_BEGIN 52 | namespace base_internal { 53 | 54 | ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { 55 | int zeroes = 60; 56 | if (n >> 32) { 57 | zeroes -= 32; 58 | n >>= 32; 59 | } 60 | if (n >> 16) { 61 | zeroes -= 16; 62 | n >>= 16; 63 | } 64 | if (n >> 8) { 65 | zeroes -= 8; 66 | n >>= 8; 67 | } 68 | if (n >> 4) { 69 | zeroes -= 4; 70 | n >>= 4; 71 | } 72 | return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; 73 | } 74 | 75 | ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) { 76 | #if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64) 77 | // MSVC does not have __buitin_clzll. Use _BitScanReverse64. 78 | unsigned long result = 0; // NOLINT(runtime/int) 79 | if (_BitScanReverse64(&result, n)) { 80 | return 63 - result; 81 | } 82 | return 64; 83 | #elif defined(_MSC_VER) && !defined(__clang__) 84 | // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse 85 | unsigned long result = 0; // NOLINT(runtime/int) 86 | if ((n >> 32) && 87 | _BitScanReverse(&result, static_cast(n >> 32))) { 88 | return 31 - result; 89 | } 90 | if (_BitScanReverse(&result, static_cast(n))) { 91 | return 63 - result; 92 | } 93 | return 64; 94 | #elif defined(__GNUC__) || defined(__clang__) 95 | // Use __builtin_clzll, which uses the following instructions: 96 | // x86: bsr 97 | // ARM64: clz 98 | // PPC: cntlzd 99 | static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) 100 | "__builtin_clzll does not take 64-bit arg"); 101 | 102 | // Handle 0 as a special case because __builtin_clzll(0) is undefined. 103 | if (n == 0) { 104 | return 64; 105 | } 106 | return __builtin_clzll(n); 107 | #else 108 | return CountLeadingZeros64Slow(n); 109 | #endif 110 | } 111 | 112 | ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) { 113 | int zeroes = 28; 114 | if (n >> 16) { 115 | zeroes -= 16; 116 | n >>= 16; 117 | } 118 | if (n >> 8) { 119 | zeroes -= 8; 120 | n >>= 8; 121 | } 122 | if (n >> 4) { 123 | zeroes -= 4; 124 | n >>= 4; 125 | } 126 | return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; 127 | } 128 | 129 | ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) { 130 | #if defined(_MSC_VER) && !defined(__clang__) 131 | unsigned long result = 0; // NOLINT(runtime/int) 132 | if (_BitScanReverse(&result, n)) { 133 | return 31 - result; 134 | } 135 | return 32; 136 | #elif defined(__GNUC__) || defined(__clang__) 137 | // Use __builtin_clz, which uses the following instructions: 138 | // x86: bsr 139 | // ARM64: clz 140 | // PPC: cntlzd 141 | static_assert(sizeof(int) == sizeof(n), 142 | "__builtin_clz does not take 32-bit arg"); 143 | 144 | // Handle 0 as a special case because __builtin_clz(0) is undefined. 145 | if (n == 0) { 146 | return 32; 147 | } 148 | return __builtin_clz(n); 149 | #else 150 | return CountLeadingZeros32Slow(n); 151 | #endif 152 | } 153 | 154 | ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) { 155 | int c = 63; 156 | n &= ~n + 1; 157 | if (n & 0x00000000FFFFFFFF) c -= 32; 158 | if (n & 0x0000FFFF0000FFFF) c -= 16; 159 | if (n & 0x00FF00FF00FF00FF) c -= 8; 160 | if (n & 0x0F0F0F0F0F0F0F0F) c -= 4; 161 | if (n & 0x3333333333333333) c -= 2; 162 | if (n & 0x5555555555555555) c -= 1; 163 | return c; 164 | } 165 | 166 | ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) { 167 | #if defined(_MSC_VER) && !defined(__clang__) && defined(_M_X64) 168 | unsigned long result = 0; // NOLINT(runtime/int) 169 | _BitScanForward64(&result, n); 170 | return result; 171 | #elif defined(_MSC_VER) && !defined(__clang__) 172 | unsigned long result = 0; // NOLINT(runtime/int) 173 | if (static_cast(n) == 0) { 174 | _BitScanForward(&result, static_cast(n >> 32)); 175 | return result + 32; 176 | } 177 | _BitScanForward(&result, static_cast(n)); 178 | return result; 179 | #elif defined(__GNUC__) || defined(__clang__) 180 | static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int) 181 | "__builtin_ctzll does not take 64-bit arg"); 182 | return __builtin_ctzll(n); 183 | #else 184 | return CountTrailingZerosNonZero64Slow(n); 185 | #endif 186 | } 187 | 188 | ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) { 189 | int c = 31; 190 | n &= ~n + 1; 191 | if (n & 0x0000FFFF) c -= 16; 192 | if (n & 0x00FF00FF) c -= 8; 193 | if (n & 0x0F0F0F0F) c -= 4; 194 | if (n & 0x33333333) c -= 2; 195 | if (n & 0x55555555) c -= 1; 196 | return c; 197 | } 198 | 199 | ABSL_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) { 200 | #if defined(_MSC_VER) && !defined(__clang__) 201 | unsigned long result = 0; // NOLINT(runtime/int) 202 | _BitScanForward(&result, n); 203 | return result; 204 | #elif defined(__GNUC__) || defined(__clang__) 205 | static_assert(sizeof(int) == sizeof(n), 206 | "__builtin_ctz does not take 32-bit arg"); 207 | return __builtin_ctz(n); 208 | #else 209 | return CountTrailingZerosNonZero32Slow(n); 210 | #endif 211 | } 212 | 213 | #undef ABSL_BASE_INTERNAL_FORCEINLINE 214 | 215 | } // namespace base_internal 216 | ABSL_NAMESPACE_END 217 | } // namespace absl 218 | 219 | #endif // ABSL_BASE_INTERNAL_BITS_H_ 220 | -------------------------------------------------------------------------------- /contrib/absl/base/macros.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Abseil Authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // File: macros.h 18 | // ----------------------------------------------------------------------------- 19 | // 20 | // This header file defines the set of language macros used within Abseil code. 21 | // For the set of macros used to determine supported compilers and platforms, 22 | // see absl/base/config.h instead. 23 | // 24 | // This code is compiled directly on many platforms, including client 25 | // platforms like Windows, Mac, and embedded systems. Before making 26 | // any changes here, make sure that you're not breaking any platforms. 27 | 28 | #ifndef ABSL_BASE_MACROS_H_ 29 | #define ABSL_BASE_MACROS_H_ 30 | 31 | #include 32 | #include 33 | 34 | #include "absl/base/attributes.h" 35 | #include "absl/base/config.h" 36 | #include "absl/base/optimization.h" 37 | #include "absl/base/port.h" 38 | 39 | // ABSL_ARRAYSIZE() 40 | // 41 | // Returns the number of elements in an array as a compile-time constant, which 42 | // can be used in defining new arrays. If you use this macro on a pointer by 43 | // mistake, you will get a compile-time error. 44 | #define ABSL_ARRAYSIZE(array) \ 45 | (sizeof(::absl::macros_internal::ArraySizeHelper(array))) 46 | 47 | namespace absl { 48 | ABSL_NAMESPACE_BEGIN 49 | namespace macros_internal { 50 | // Note: this internal template function declaration is used by ABSL_ARRAYSIZE. 51 | // The function doesn't need a definition, as we only use its type. 52 | template 53 | auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N]; 54 | } // namespace macros_internal 55 | ABSL_NAMESPACE_END 56 | } // namespace absl 57 | 58 | // ABSL_BAD_CALL_IF() 59 | // 60 | // Used on a function overload to trap bad calls: any call that matches the 61 | // overload will cause a compile-time error. This macro uses a clang-specific 62 | // "enable_if" attribute, as described at 63 | // https://clang.llvm.org/docs/AttributeReference.html#enable-if 64 | // 65 | // Overloads which use this macro should be bracketed by 66 | // `#ifdef ABSL_BAD_CALL_IF`. 67 | // 68 | // Example: 69 | // 70 | // int isdigit(int c); 71 | // #ifdef ABSL_BAD_CALL_IF 72 | // int isdigit(int c) 73 | // ABSL_BAD_CALL_IF(c <= -1 || c > 255, 74 | // "'c' must have the value of an unsigned char or EOF"); 75 | // #endif // ABSL_BAD_CALL_IF 76 | #if ABSL_HAVE_ATTRIBUTE(enable_if) 77 | #define ABSL_BAD_CALL_IF(expr, msg) \ 78 | __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg))) 79 | #endif 80 | 81 | // ABSL_ASSERT() 82 | // 83 | // In C++11, `assert` can't be used portably within constexpr functions. 84 | // ABSL_ASSERT functions as a runtime assert but works in C++11 constexpr 85 | // functions. Example: 86 | // 87 | // constexpr double Divide(double a, double b) { 88 | // return ABSL_ASSERT(b != 0), a / b; 89 | // } 90 | // 91 | // This macro is inspired by 92 | // https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ 93 | #if defined(NDEBUG) 94 | #define ABSL_ASSERT(expr) \ 95 | (false ? static_cast(expr) : static_cast(0)) 96 | #else 97 | #define ABSL_ASSERT(expr) \ 98 | (ABSL_PREDICT_TRUE((expr)) ? static_cast(0) \ 99 | : [] { assert(false && #expr); }()) // NOLINT 100 | #endif 101 | 102 | // `ABSL_INTERNAL_HARDENING_ABORT()` controls how `ABSL_HARDENING_ASSERT()` 103 | // aborts the program in release mode (when NDEBUG is defined). The 104 | // implementation should abort the program as quickly as possible and ideally it 105 | // should not be possible to ignore the abort request. 106 | #if (ABSL_HAVE_BUILTIN(__builtin_trap) && \ 107 | ABSL_HAVE_BUILTIN(__builtin_unreachable)) || \ 108 | (defined(__GNUC__) && !defined(__clang__)) 109 | #define ABSL_INTERNAL_HARDENING_ABORT() \ 110 | do { \ 111 | __builtin_trap(); \ 112 | __builtin_unreachable(); \ 113 | } while (false) 114 | #else 115 | #define ABSL_INTERNAL_HARDENING_ABORT() abort() 116 | #endif 117 | 118 | // ABSL_HARDENING_ASSERT() 119 | // 120 | // `ABSL_HARDENING_ASSERT()` is like `ABSL_ASSERT()`, but used to implement 121 | // runtime assertions that should be enabled in hardened builds even when 122 | // `NDEBUG` is defined. 123 | // 124 | // When `NDEBUG` is not defined, `ABSL_HARDENING_ASSERT()` is identical to 125 | // `ABSL_ASSERT()`. 126 | // 127 | // See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on 128 | // hardened mode. 129 | #if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG) 130 | #define ABSL_HARDENING_ASSERT(expr) \ 131 | (ABSL_PREDICT_TRUE((expr)) ? static_cast(0) \ 132 | : [] { ABSL_INTERNAL_HARDENING_ABORT(); }()) 133 | #else 134 | #define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr) 135 | #endif 136 | 137 | #ifdef ABSL_HAVE_EXCEPTIONS 138 | #define ABSL_INTERNAL_TRY try 139 | #define ABSL_INTERNAL_CATCH_ANY catch (...) 140 | #define ABSL_INTERNAL_RETHROW do { throw; } while (false) 141 | #else // ABSL_HAVE_EXCEPTIONS 142 | #define ABSL_INTERNAL_TRY if (true) 143 | #define ABSL_INTERNAL_CATCH_ANY else if (false) 144 | #define ABSL_INTERNAL_RETHROW do {} while (false) 145 | #endif // ABSL_HAVE_EXCEPTIONS 146 | 147 | #endif // ABSL_BASE_MACROS_H_ 148 | -------------------------------------------------------------------------------- /contrib/absl/base/optimization.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2017 The Abseil Authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // ----------------------------------------------------------------------------- 17 | // File: optimization.h 18 | // ----------------------------------------------------------------------------- 19 | // 20 | // This header file defines portable macros for performance optimization. 21 | 22 | #ifndef ABSL_BASE_OPTIMIZATION_H_ 23 | #define ABSL_BASE_OPTIMIZATION_H_ 24 | 25 | #include "absl/base/config.h" 26 | 27 | // ABSL_BLOCK_TAIL_CALL_OPTIMIZATION 28 | // 29 | // Instructs the compiler to avoid optimizing tail-call recursion. Use of this 30 | // macro is useful when you wish to preserve the existing function order within 31 | // a stack trace for logging, debugging, or profiling purposes. 32 | // 33 | // Example: 34 | // 35 | // int f() { 36 | // int result = g(); 37 | // ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); 38 | // return result; 39 | // } 40 | #if defined(__pnacl__) 41 | #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; } 42 | #elif defined(__clang__) 43 | // Clang will not tail call given inline volatile assembly. 44 | #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("") 45 | #elif defined(__GNUC__) 46 | // GCC will not tail call given inline volatile assembly. 47 | #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("") 48 | #elif defined(_MSC_VER) 49 | #include 50 | // The __nop() intrinsic blocks the optimisation. 51 | #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() __nop() 52 | #else 53 | #define ABSL_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; } 54 | #endif 55 | 56 | // ABSL_CACHELINE_SIZE 57 | // 58 | // Explicitly defines the size of the L1 cache for purposes of alignment. 59 | // Setting the cacheline size allows you to specify that certain objects be 60 | // aligned on a cacheline boundary with `ABSL_CACHELINE_ALIGNED` declarations. 61 | // (See below.) 62 | // 63 | // NOTE: this macro should be replaced with the following C++17 features, when 64 | // those are generally available: 65 | // 66 | // * `std::hardware_constructive_interference_size` 67 | // * `std::hardware_destructive_interference_size` 68 | // 69 | // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html 70 | // for more information. 71 | #if defined(__GNUC__) 72 | // Cache line alignment 73 | #if defined(__i386__) || defined(__x86_64__) 74 | #define ABSL_CACHELINE_SIZE 64 75 | #elif defined(__powerpc64__) 76 | #define ABSL_CACHELINE_SIZE 128 77 | #elif defined(__aarch64__) 78 | // We would need to read special register ctr_el0 to find out L1 dcache size. 79 | // This value is a good estimate based on a real aarch64 machine. 80 | #define ABSL_CACHELINE_SIZE 64 81 | #elif defined(__arm__) 82 | // Cache line sizes for ARM: These values are not strictly correct since 83 | // cache line sizes depend on implementations, not architectures. There 84 | // are even implementations with cache line sizes configurable at boot 85 | // time. 86 | #if defined(__ARM_ARCH_5T__) 87 | #define ABSL_CACHELINE_SIZE 32 88 | #elif defined(__ARM_ARCH_7A__) 89 | #define ABSL_CACHELINE_SIZE 64 90 | #endif 91 | #endif 92 | 93 | #ifndef ABSL_CACHELINE_SIZE 94 | // A reasonable default guess. Note that overestimates tend to waste more 95 | // space, while underestimates tend to waste more time. 96 | #define ABSL_CACHELINE_SIZE 64 97 | #endif 98 | 99 | // ABSL_CACHELINE_ALIGNED 100 | // 101 | // Indicates that the declared object be cache aligned using 102 | // `ABSL_CACHELINE_SIZE` (see above). Cacheline aligning objects allows you to 103 | // load a set of related objects in the L1 cache for performance improvements. 104 | // Cacheline aligning objects properly allows constructive memory sharing and 105 | // prevents destructive (or "false") memory sharing. 106 | // 107 | // NOTE: this macro should be replaced with usage of `alignas()` using 108 | // `std::hardware_constructive_interference_size` and/or 109 | // `std::hardware_destructive_interference_size` when available within C++17. 110 | // 111 | // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0154r1.html 112 | // for more information. 113 | // 114 | // On some compilers, `ABSL_CACHELINE_ALIGNED` expands to an `__attribute__` 115 | // or `__declspec` attribute. For compilers where this is not known to work, 116 | // the macro expands to nothing. 117 | // 118 | // No further guarantees are made here. The result of applying the macro 119 | // to variables and types is always implementation-defined. 120 | // 121 | // WARNING: It is easy to use this attribute incorrectly, even to the point 122 | // of causing bugs that are difficult to diagnose, crash, etc. It does not 123 | // of itself guarantee that objects are aligned to a cache line. 124 | // 125 | // NOTE: Some compilers are picky about the locations of annotations such as 126 | // this attribute, so prefer to put it at the beginning of your declaration. 127 | // For example, 128 | // 129 | // ABSL_CACHELINE_ALIGNED static Foo* foo = ... 130 | // 131 | // class ABSL_CACHELINE_ALIGNED Bar { ... 132 | // 133 | // Recommendations: 134 | // 135 | // 1) Consult compiler documentation; this comment is not kept in sync as 136 | // toolchains evolve. 137 | // 2) Verify your use has the intended effect. This often requires inspecting 138 | // the generated machine code. 139 | // 3) Prefer applying this attribute to individual variables. Avoid 140 | // applying it to types. This tends to localize the effect. 141 | #define ABSL_CACHELINE_ALIGNED __attribute__((aligned(ABSL_CACHELINE_SIZE))) 142 | #elif defined(_MSC_VER) 143 | #define ABSL_CACHELINE_SIZE 64 144 | #define ABSL_CACHELINE_ALIGNED __declspec(align(ABSL_CACHELINE_SIZE)) 145 | #else 146 | #define ABSL_CACHELINE_SIZE 64 147 | #define ABSL_CACHELINE_ALIGNED 148 | #endif 149 | 150 | // ABSL_PREDICT_TRUE, ABSL_PREDICT_FALSE 151 | // 152 | // Enables the compiler to prioritize compilation using static analysis for 153 | // likely paths within a boolean branch. 154 | // 155 | // Example: 156 | // 157 | // if (ABSL_PREDICT_TRUE(expression)) { 158 | // return result; // Faster if more likely 159 | // } else { 160 | // return 0; 161 | // } 162 | // 163 | // Compilers can use the information that a certain branch is not likely to be 164 | // taken (for instance, a CHECK failure) to optimize for the common case in 165 | // the absence of better information (ie. compiling gcc with `-fprofile-arcs`). 166 | // 167 | // Recommendation: Modern CPUs dynamically predict branch execution paths, 168 | // typically with accuracy greater than 97%. As a result, annotating every 169 | // branch in a codebase is likely counterproductive; however, annotating 170 | // specific branches that are both hot and consistently mispredicted is likely 171 | // to yield performance improvements. 172 | #if ABSL_HAVE_BUILTIN(__builtin_expect) || \ 173 | (defined(__GNUC__) && !defined(__clang__)) 174 | #define ABSL_PREDICT_FALSE(x) (__builtin_expect(false || (x), false)) 175 | #define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true)) 176 | #else 177 | #define ABSL_PREDICT_FALSE(x) (x) 178 | #define ABSL_PREDICT_TRUE(x) (x) 179 | #endif 180 | 181 | // ABSL_INTERNAL_ASSUME(cond) 182 | // Informs the compiler than a condition is always true and that it can assume 183 | // it to be true for optimization purposes. The call has undefined behavior if 184 | // the condition is false. 185 | // In !NDEBUG mode, the condition is checked with an assert(). 186 | // NOTE: The expression must not have side effects, as it will only be evaluated 187 | // in some compilation modes and not others. 188 | // 189 | // Example: 190 | // 191 | // int x = ...; 192 | // ABSL_INTERNAL_ASSUME(x >= 0); 193 | // // The compiler can optimize the division to a simple right shift using the 194 | // // assumption specified above. 195 | // int y = x / 16; 196 | // 197 | #if !defined(NDEBUG) 198 | #define ABSL_INTERNAL_ASSUME(cond) assert(cond) 199 | #elif ABSL_HAVE_BUILTIN(__builtin_assume) 200 | #define ABSL_INTERNAL_ASSUME(cond) __builtin_assume(cond) 201 | #elif defined(__GNUC__) || ABSL_HAVE_BUILTIN(__builtin_unreachable) 202 | #define ABSL_INTERNAL_ASSUME(cond) \ 203 | do { \ 204 | if (!(cond)) __builtin_unreachable(); \ 205 | } while (0) 206 | #elif defined(_MSC_VER) 207 | #define ABSL_INTERNAL_ASSUME(cond) __assume(cond) 208 | #else 209 | #define ABSL_INTERNAL_ASSUME(cond) \ 210 | do { \ 211 | static_cast(false && (cond)); \ 212 | } while (0) 213 | #endif 214 | 215 | // ABSL_INTERNAL_UNIQUE_SMALL_NAME(cond) 216 | // This macro forces small unique name on a static file level symbols like 217 | // static local variables or static functions. This is intended to be used in 218 | // macro definitions to optimize the cost of generated code. Do NOT use it on 219 | // symbols exported from translation unit since it may casue a link time 220 | // conflict. 221 | // 222 | // Example: 223 | // 224 | // #define MY_MACRO(txt) 225 | // namespace { 226 | // char VeryVeryLongVarName[] ABSL_INTERNAL_UNIQUE_SMALL_NAME() = txt; 227 | // const char* VeryVeryLongFuncName() ABSL_INTERNAL_UNIQUE_SMALL_NAME(); 228 | // const char* VeryVeryLongFuncName() { return txt; } 229 | // } 230 | // 231 | 232 | #if defined(__GNUC__) 233 | #define ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) #x 234 | #define ABSL_INTERNAL_UNIQUE_SMALL_NAME1(x) ABSL_INTERNAL_UNIQUE_SMALL_NAME2(x) 235 | #define ABSL_INTERNAL_UNIQUE_SMALL_NAME() \ 236 | asm(ABSL_INTERNAL_UNIQUE_SMALL_NAME1(.absl.__COUNTER__)) 237 | #else 238 | #define ABSL_INTERNAL_UNIQUE_SMALL_NAME() 239 | #endif 240 | 241 | #endif // ABSL_BASE_OPTIMIZATION_H_ 242 | -------------------------------------------------------------------------------- /contrib/absl/base/policy_checks.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // ----------------------------------------------------------------------------- 16 | // File: policy_checks.h 17 | // ----------------------------------------------------------------------------- 18 | // 19 | // This header enforces a minimum set of policies at build time, such as the 20 | // supported compiler and library versions. Unsupported configurations are 21 | // reported with `#error`. This enforcement is best effort, so successfully 22 | // compiling this header does not guarantee a supported configuration. 23 | 24 | #ifndef ABSL_BASE_POLICY_CHECKS_H_ 25 | #define ABSL_BASE_POLICY_CHECKS_H_ 26 | 27 | // Included for the __GLIBC_PREREQ macro used below. 28 | #include 29 | 30 | // Included for the _STLPORT_VERSION macro used below. 31 | #if defined(__cplusplus) 32 | #include 33 | #endif 34 | 35 | // ----------------------------------------------------------------------------- 36 | // Operating System Check 37 | // ----------------------------------------------------------------------------- 38 | 39 | #if defined(__CYGWIN__) 40 | #error "Cygwin is not supported." 41 | #endif 42 | 43 | // ----------------------------------------------------------------------------- 44 | // Toolchain Check 45 | // ----------------------------------------------------------------------------- 46 | 47 | // We support MSVC++ 14.0 update 2 and later. 48 | // This minimum will go up. 49 | #if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__) 50 | #error "This package requires Visual Studio 2015 Update 2 or higher." 51 | #endif 52 | 53 | // We support gcc 4.7 and later. 54 | // This minimum will go up. 55 | #if defined(__GNUC__) && !defined(__clang__) 56 | #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) 57 | #error "This package requires gcc 4.7 or higher." 58 | #endif 59 | #endif 60 | 61 | // We support Apple Xcode clang 4.2.1 (version 421.11.65) and later. 62 | // This corresponds to Apple Xcode version 4.5. 63 | // This minimum will go up. 64 | #if defined(__apple_build_version__) && __apple_build_version__ < 4211165 65 | #error "This package requires __apple_build_version__ of 4211165 or higher." 66 | #endif 67 | 68 | // ----------------------------------------------------------------------------- 69 | // C++ Version Check 70 | // ----------------------------------------------------------------------------- 71 | 72 | // Enforce C++11 as the minimum. Note that Visual Studio has not 73 | // advanced __cplusplus despite being good enough for our purposes, so 74 | // so we exempt it from the check. 75 | #if defined(__cplusplus) && !defined(_MSC_VER) 76 | #if __cplusplus < 201103L 77 | #error "C++ versions less than C++11 are not supported." 78 | #endif 79 | #endif 80 | 81 | // ----------------------------------------------------------------------------- 82 | // Standard Library Check 83 | // ----------------------------------------------------------------------------- 84 | 85 | #if defined(_STLPORT_VERSION) 86 | #error "STLPort is not supported." 87 | #endif 88 | 89 | // ----------------------------------------------------------------------------- 90 | // `char` Size Check 91 | // ----------------------------------------------------------------------------- 92 | 93 | // Abseil currently assumes CHAR_BIT == 8. If you would like to use Abseil on a 94 | // platform where this is not the case, please provide us with the details about 95 | // your platform so we can consider relaxing this requirement. 96 | #if CHAR_BIT != 8 97 | #error "Abseil assumes CHAR_BIT == 8." 98 | #endif 99 | 100 | // ----------------------------------------------------------------------------- 101 | // `int` Size Check 102 | // ----------------------------------------------------------------------------- 103 | 104 | // Abseil currently assumes that an int is 4 bytes. If you would like to use 105 | // Abseil on a platform where this is not the case, please provide us with the 106 | // details about your platform so we can consider relaxing this requirement. 107 | #if INT_MAX < 2147483647 108 | #error "Abseil assumes that int is at least 4 bytes. " 109 | #endif 110 | 111 | #endif // ABSL_BASE_POLICY_CHECKS_H_ 112 | -------------------------------------------------------------------------------- /contrib/absl/base/port.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Abseil Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // This files is a forwarding header for other headers containing various 16 | // portability macros and functions. 17 | // This file is used for both C and C++! 18 | 19 | #ifndef ABSL_BASE_PORT_H_ 20 | #define ABSL_BASE_PORT_H_ 21 | 22 | #include "absl/base/attributes.h" 23 | #include "absl/base/config.h" 24 | #include "absl/base/optimization.h" 25 | 26 | #endif // ABSL_BASE_PORT_H_ 27 | -------------------------------------------------------------------------------- /contrib/cityhash/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_LIBRARY (cityhash-lib STATIC 2 | city.cc 3 | ) 4 | 5 | set_property(TARGET cityhash-lib PROPERTY POSITION_INDEPENDENT_CODE ON) 6 | -------------------------------------------------------------------------------- /contrib/cityhash/COPYING: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Google, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /contrib/cityhash/city.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Google, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | // 21 | // CityHash, by Geoff Pike and Jyrki Alakuijala 22 | // 23 | // This file provides a few functions for hashing strings. On x86-64 24 | // hardware in 2011, CityHash64() is faster than other high-quality 25 | // hash functions, such as Murmur. This is largely due to higher 26 | // instruction-level parallelism. CityHash64() and CityHash128() also perform 27 | // well on hash-quality tests. 28 | // 29 | // CityHash128() is optimized for relatively long strings and returns 30 | // a 128-bit hash. For strings more than about 2000 bytes it can be 31 | // faster than CityHash64(). 32 | // 33 | // Functions in the CityHash family are not suitable for cryptography. 34 | // 35 | // WARNING: This code has not been tested on big-endian platforms! 36 | // It is known to work well on little-endian platforms that have a small penalty 37 | // for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. 38 | // 39 | // By the way, for some hash functions, given strings a and b, the hash 40 | // of a+b is easily derived from the hashes of a and b. This property 41 | // doesn't hold for any hash functions in this file. 42 | 43 | #ifndef CITY_HASH_H_ 44 | #define CITY_HASH_H_ 45 | 46 | #include // for size_t. 47 | #include 48 | #include 49 | 50 | typedef uint8_t uint8; 51 | typedef uint32_t uint32; 52 | typedef uint64_t uint64; 53 | typedef std::pair uint128; 54 | 55 | inline uint64 Uint128Low64(const uint128& x) { return x.first; } 56 | inline uint64 Uint128High64(const uint128& x) { return x.second; } 57 | 58 | // Hash function for a byte array. 59 | uint64 CityHash64(const char *buf, size_t len); 60 | 61 | // Hash function for a byte array. For convenience, a 64-bit seed is also 62 | // hashed into the result. 63 | uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed); 64 | 65 | // Hash function for a byte array. For convenience, two seeds are also 66 | // hashed into the result. 67 | uint64 CityHash64WithSeeds(const char *buf, size_t len, 68 | uint64 seed0, uint64 seed1); 69 | 70 | // Hash function for a byte array. 71 | uint128 CityHash128(const char *s, size_t len); 72 | 73 | // Hash function for a byte array. For convenience, a 128-bit seed is also 74 | // hashed into the result. 75 | uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed); 76 | 77 | // Hash 128 input bits down to 64 bits of output. 78 | // This is intended to be a reasonably good hash function. 79 | inline uint64 Hash128to64(const uint128& x) { 80 | // Murmur-inspired hashing. 81 | const uint64 kMul = 0x9ddfea08eb382d69ULL; 82 | uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; 83 | a ^= (a >> 47); 84 | uint64 b = (Uint128High64(x) ^ a) * kMul; 85 | b ^= (b >> 47); 86 | b *= kMul; 87 | return b; 88 | } 89 | 90 | #endif // CITY_HASH_H_ 91 | -------------------------------------------------------------------------------- /contrib/cityhash/citycrc.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Google, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | // 21 | // CityHash, by Geoff Pike and Jyrki Alakuijala 22 | // 23 | // This file declares the subset of the CityHash functions that require 24 | // _mm_crc32_u64(). See the CityHash README for details. 25 | // 26 | // Functions in the CityHash family are not suitable for cryptography. 27 | 28 | #ifndef CITY_HASH_CRC_H_ 29 | #define CITY_HASH_CRC_H_ 30 | 31 | #include "city.h" 32 | 33 | // Hash function for a byte array. 34 | uint128 CityHashCrc128(const char *s, size_t len); 35 | 36 | // Hash function for a byte array. For convenience, a 128-bit seed is also 37 | // hashed into the result. 38 | uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed); 39 | 40 | // Hash function for a byte array. Sets result[0] ... result[3]. 41 | void CityHashCrc256(const char *s, size_t len, uint64 *result); 42 | 43 | #endif // CITY_HASH_CRC_H_ 44 | -------------------------------------------------------------------------------- /contrib/cityhash/config.h: -------------------------------------------------------------------------------- 1 | /* config.h. Generated from config.h.in by configure. */ 2 | /* config.h.in. Generated from configure.ac by autoheader. */ 3 | 4 | /* Define if building universal (internal helper macro) */ 5 | /* #undef AC_APPLE_UNIVERSAL_BUILD */ 6 | 7 | /* Define to 1 if the compiler supports __builtin_expect. */ 8 | #if WIN32 || WIN64 9 | # define HAVE_BUILTIN_EXPECT 0 10 | #else 11 | # define HAVE_BUILTIN_EXPECT 1 12 | #endif 13 | 14 | /* Define to 1 if you have the header file. */ 15 | #define HAVE_DLFCN_H 1 16 | 17 | /* Define to 1 if you have the header file. */ 18 | #define HAVE_INTTYPES_H 1 19 | 20 | /* Define to 1 if you have the header file. */ 21 | #define HAVE_MEMORY_H 1 22 | 23 | /* Define to 1 if you have the header file. */ 24 | #define HAVE_STDINT_H 1 25 | 26 | /* Define to 1 if you have the header file. */ 27 | #define HAVE_STDLIB_H 1 28 | 29 | /* Define to 1 if you have the header file. */ 30 | #define HAVE_STRINGS_H 1 31 | 32 | /* Define to 1 if you have the header file. */ 33 | #define HAVE_STRING_H 1 34 | 35 | /* Define to 1 if you have the header file. */ 36 | #define HAVE_SYS_STAT_H 1 37 | 38 | /* Define to 1 if you have the header file. */ 39 | #define HAVE_SYS_TYPES_H 1 40 | 41 | /* Define to 1 if you have the header file. */ 42 | #define HAVE_UNISTD_H 1 43 | 44 | /* Define to the sub-directory in which libtool stores uninstalled libraries. 45 | */ 46 | #define LT_OBJDIR ".libs/" 47 | 48 | /* Define to the address where bug reports for this package should be sent. */ 49 | #define PACKAGE_BUGREPORT "cityhash-discuss@googlegroups.com" 50 | 51 | /* Define to the full name of this package. */ 52 | #define PACKAGE_NAME "CityHash" 53 | 54 | /* Define to the full name and version of this package. */ 55 | #define PACKAGE_STRING "CityHash 1.0.2" 56 | 57 | /* Define to the one symbol short name of this package. */ 58 | #define PACKAGE_TARNAME "cityhash" 59 | 60 | /* Define to the home page for this package. */ 61 | #define PACKAGE_URL "" 62 | 63 | /* Define to the version of this package. */ 64 | #define PACKAGE_VERSION "1.0.2" 65 | 66 | /* Define to 1 if you have the ANSI C header files. */ 67 | #define STDC_HEADERS 1 68 | 69 | /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most 70 | significant byte first (like Motorola and SPARC, unlike Intel). */ 71 | #if defined AC_APPLE_UNIVERSAL_BUILD 72 | # if defined __BIG_ENDIAN__ 73 | # define WORDS_BIGENDIAN 1 74 | # endif 75 | #else 76 | # ifndef WORDS_BIGENDIAN 77 | /* # undef WORDS_BIGENDIAN */ 78 | # endif 79 | #endif 80 | 81 | /* Define for Solaris 2.5.1 so the uint32_t typedef from , 82 | , or is not used. If the typedef were allowed, the 83 | #define below would cause a syntax error. */ 84 | /* #undef _UINT32_T */ 85 | 86 | /* Define for Solaris 2.5.1 so the uint64_t typedef from , 87 | , or is not used. If the typedef were allowed, the 88 | #define below would cause a syntax error. */ 89 | /* #undef _UINT64_T */ 90 | 91 | /* Define for Solaris 2.5.1 so the uint8_t typedef from , 92 | , or is not used. If the typedef were allowed, the 93 | #define below would cause a syntax error. */ 94 | /* #undef _UINT8_T */ 95 | 96 | /* Define to `__inline__' or `__inline' if that's what the C compiler 97 | calls it, or to nothing if 'inline' is not supported under any name. */ 98 | #ifndef __cplusplus 99 | /* #undef inline */ 100 | #endif 101 | 102 | /* Define to `unsigned int' if does not define. */ 103 | /* #undef size_t */ 104 | 105 | /* Define to `int' if does not define. */ 106 | /* #undef ssize_t */ 107 | 108 | /* Define to the type of an unsigned integer type of width exactly 32 bits if 109 | such a type exists and the standard includes do not define it. */ 110 | /* #undef uint32_t */ 111 | 112 | /* Define to the type of an unsigned integer type of width exactly 64 bits if 113 | such a type exists and the standard includes do not define it. */ 114 | /* #undef uint64_t */ 115 | 116 | /* Define to the type of an unsigned integer type of width exactly 8 bits if 117 | such a type exists and the standard includes do not define it. */ 118 | /* #undef uint8_t */ 119 | -------------------------------------------------------------------------------- /contrib/gtest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_LIBRARY (gtest-lib STATIC 2 | gtest-all.cc 3 | ) 4 | -------------------------------------------------------------------------------- /contrib/lz4/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_LIBRARY (lz4-lib STATIC 2 | lz4.c 3 | lz4hc.c 4 | ) 5 | 6 | set_property(TARGET lz4-lib PROPERTY POSITION_INDEPENDENT_CODE ON) 7 | -------------------------------------------------------------------------------- /contrib/lz4/LICENSE: -------------------------------------------------------------------------------- 1 | LZ4 Library 2 | Copyright (c) 2011-2014, Yann Collet 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or 13 | other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /tests/simple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_EXECUTABLE (simple-test 2 | main.cpp 3 | ) 4 | 5 | TARGET_LINK_LIBRARIES (simple-test 6 | clickhouse-cpp-lib 7 | ) -------------------------------------------------------------------------------- /ut/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_EXECUTABLE (clickhouse-cpp-ut 2 | main.cpp 3 | 4 | client_ut.cpp 5 | columns_ut.cpp 6 | socket_ut.cpp 7 | stream_ut.cpp 8 | tcp_server.cpp 9 | type_parser_ut.cpp 10 | types_ut.cpp 11 | ) 12 | 13 | TARGET_LINK_LIBRARIES (clickhouse-cpp-ut 14 | clickhouse-cpp-lib 15 | gtest-lib 16 | ) 17 | -------------------------------------------------------------------------------- /ut/columns_ut.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using namespace clickhouse; 13 | 14 | static std::vector MakeNumbers() { 15 | return std::vector 16 | {1, 2, 3, 7, 11, 13, 17, 19, 23, 29, 31}; 17 | } 18 | 19 | static std::vector MakeBools() { 20 | return std::vector 21 | {1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0}; 22 | } 23 | 24 | static std::vector MakeFixedStrings() { 25 | return std::vector 26 | {"aaa", "bbb", "ccc", "ddd"}; 27 | } 28 | 29 | static std::vector MakeStrings() { 30 | return std::vector 31 | {"a", "ab", "abc", "abcd"}; 32 | } 33 | 34 | static std::vector MakeUUIDs() { 35 | return std::vector 36 | {0xbb6a8c699ab2414cllu, 0x86697b7fd27f0825llu, 37 | 0x84b9f24bc26b49c6llu, 0xa03b4ab723341951llu, 38 | 0x3507213c178649f9llu, 0x9faf035d662f60aellu}; 39 | } 40 | 41 | TEST(ColumnsCase, NumericInit) { 42 | auto col = std::make_shared(MakeNumbers()); 43 | 44 | ASSERT_EQ(col->Size(), 11u); 45 | ASSERT_EQ(col->At(3), 7u); 46 | ASSERT_EQ(col->At(10), 31u); 47 | 48 | auto sun = std::make_shared(MakeNumbers()); 49 | } 50 | 51 | TEST(ColumnsCase, NumericSlice) { 52 | auto col = std::make_shared(MakeNumbers()); 53 | auto sub = col->Slice(3, 3)->As(); 54 | 55 | ASSERT_EQ(sub->Size(), 3u); 56 | ASSERT_EQ(sub->At(0), 7u); 57 | ASSERT_EQ(sub->At(2), 13u); 58 | } 59 | 60 | 61 | TEST(ColumnsCase, FixedStringInit) { 62 | auto col = std::make_shared(3); 63 | for (const auto& s : MakeFixedStrings()) { 64 | col->Append(s); 65 | } 66 | 67 | ASSERT_EQ(col->Size(), 4u); 68 | ASSERT_EQ(col->At(1), "bbb"); 69 | ASSERT_EQ(col->At(3), "ddd"); 70 | } 71 | 72 | TEST(ColumnsCase, StringInit) { 73 | auto col = std::make_shared(MakeStrings()); 74 | 75 | ASSERT_EQ(col->Size(), 4u); 76 | ASSERT_EQ(col->At(1), "ab"); 77 | ASSERT_EQ(col->At(3), "abcd"); 78 | } 79 | 80 | 81 | TEST(ColumnsCase, ArrayAppend) { 82 | auto arr1 = std::make_shared(std::make_shared()); 83 | auto arr2 = std::make_shared(std::make_shared()); 84 | 85 | auto id = std::make_shared(); 86 | id->Append(1); 87 | arr1->AppendAsColumn(id); 88 | 89 | id->Append(3); 90 | arr2->AppendAsColumn(id); 91 | 92 | arr1->Append(arr2); 93 | 94 | auto col = arr1->GetAsColumn(1); 95 | 96 | ASSERT_EQ(arr1->Size(), 2u); 97 | //ASSERT_EQ(col->As()->At(0), 1u); 98 | //ASSERT_EQ(col->As()->At(1), 3u); 99 | } 100 | 101 | TEST(ColumnsCase, DateAppend) { 102 | auto col1 = std::make_shared(); 103 | auto col2 = std::make_shared(); 104 | auto now = std::time(nullptr); 105 | 106 | col1->Append(now); 107 | col2->Append(col1); 108 | 109 | ASSERT_EQ(col2->Size(), 1u); 110 | ASSERT_EQ(col2->At(0), (now / 86400) * 86400); 111 | } 112 | 113 | TEST(ColumnsCase, Date2038) { 114 | auto col1 = std::make_shared(); 115 | std::time_t largeDate(25882ul * 86400ul); 116 | col1->Append(largeDate); 117 | 118 | ASSERT_EQ(col1->Size(), 1u); 119 | ASSERT_EQ(static_cast(col1->At(0)), 25882ul * 86400ul); 120 | } 121 | 122 | TEST(ColumnsCase, DateTime) { 123 | ASSERT_NE(nullptr, CreateColumnByType("DateTime")); 124 | ASSERT_NE(nullptr, CreateColumnByType("DateTime('Europe/Moscow')")); 125 | 126 | ASSERT_EQ(CreateColumnByType("DateTime('UTC')")->As()->Timezone(), "UTC"); 127 | ASSERT_EQ(CreateColumnByType("DateTime64(3, 'UTC')")->As()->Timezone(), "UTC"); 128 | } 129 | 130 | TEST(ColumnsCase, EnumTest) { 131 | std::vector enum_items = {{"Hi", 1}, {"Hello", 2}}; 132 | 133 | auto col = std::make_shared(Type::CreateEnum8(enum_items)); 134 | ASSERT_TRUE(col->Type()->IsEqual(Type::CreateEnum8(enum_items))); 135 | 136 | col->Append(1); 137 | ASSERT_EQ(col->Size(), 1u); 138 | ASSERT_EQ(col->At(0), 1); 139 | ASSERT_EQ(col->NameAt(0), "Hi"); 140 | 141 | col->Append("Hello"); 142 | ASSERT_EQ(col->Size(), 2u); 143 | ASSERT_EQ(col->At(1), 2); 144 | ASSERT_EQ(col->NameAt(1), "Hello"); 145 | 146 | auto col16 = std::make_shared(Type::CreateEnum16(enum_items)); 147 | ASSERT_TRUE(col16->Type()->IsEqual(Type::CreateEnum16(enum_items))); 148 | 149 | ASSERT_TRUE(CreateColumnByType("Enum8('Hi' = 1, 'Hello' = 2)")->Type()->IsEqual(Type::CreateEnum8(enum_items))); 150 | } 151 | 152 | TEST(ColumnsCase, NullableSlice) { 153 | auto data = std::make_shared(MakeNumbers()); 154 | auto nulls = std::make_shared(MakeBools()); 155 | auto col = std::make_shared(data, nulls); 156 | auto sub = col->Slice(3, 4)->As(); 157 | auto subData = sub->Nested()->As(); 158 | 159 | ASSERT_EQ(sub->Size(), 4u); 160 | ASSERT_FALSE(sub->IsNull(0)); 161 | ASSERT_EQ(subData->At(0), 7u); 162 | ASSERT_TRUE(sub->IsNull(1)); 163 | ASSERT_FALSE(sub->IsNull(3)); 164 | ASSERT_EQ(subData->At(3), 17u); 165 | } 166 | 167 | TEST(ColumnsCase, UUIDInit) { 168 | auto col = std::make_shared(std::make_shared(MakeUUIDs())); 169 | 170 | ASSERT_EQ(col->Size(), 3u); 171 | ASSERT_EQ(col->At(0), UInt128(0xbb6a8c699ab2414cllu, 0x86697b7fd27f0825llu)); 172 | ASSERT_EQ(col->At(2), UInt128(0x3507213c178649f9llu, 0x9faf035d662f60aellu)); 173 | } 174 | 175 | TEST(ColumnsCase, UUIDSlice) { 176 | auto col = std::make_shared(std::make_shared(MakeUUIDs())); 177 | auto sub = col->Slice(1, 2)->As(); 178 | 179 | ASSERT_EQ(sub->Size(), 2u); 180 | ASSERT_EQ(sub->At(0), UInt128(0x84b9f24bc26b49c6llu, 0xa03b4ab723341951llu)); 181 | ASSERT_EQ(sub->At(1), UInt128(0x3507213c178649f9llu, 0x9faf035d662f60aellu)); 182 | } 183 | 184 | TEST(ColumnsCase, UnmatchedBrackets) { 185 | ASSERT_NE(nullptr, CreateColumnByType("FixedString(10)")); 186 | // When type string has unmatched brackets, CreateColumnByType must return nullptr. 187 | ASSERT_EQ(nullptr, CreateColumnByType("FixedString(10")); 188 | ASSERT_EQ(nullptr, CreateColumnByType("Nullable(FixedString(10000")); 189 | ASSERT_EQ(nullptr, CreateColumnByType("Nullable(FixedString(10000)")); 190 | } 191 | -------------------------------------------------------------------------------- /ut/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char* argv[]) { 4 | ::testing::InitGoogleTest(&argc, argv); 5 | return RUN_ALL_TESTS(); 6 | } 7 | -------------------------------------------------------------------------------- /ut/socket_ut.cpp: -------------------------------------------------------------------------------- 1 | #include "tcp_server.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace clickhouse; 12 | 13 | TEST(Socketcase, connecterror) { 14 | int port = 9978; 15 | NetworkAddress addr("localhost", std::to_string(port)); 16 | LocalTcpServer server(port); 17 | server.start(); 18 | std::this_thread::sleep_for(std::chrono::seconds(1)); 19 | try { 20 | SocketConnect(addr); 21 | } catch (const std::system_error& e) { 22 | FAIL(); 23 | } 24 | std::this_thread::sleep_for(std::chrono::seconds(1)); 25 | server.stop(); 26 | try { 27 | SocketConnect(addr); 28 | FAIL(); 29 | } catch (const std::system_error& e) { 30 | ASSERT_NE(EINPROGRESS,e.code().value()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ut/stream_ut.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace clickhouse; 5 | 6 | TEST(CodedStreamCase, Varint64) { 7 | Buffer buf; 8 | 9 | { 10 | BufferOutput output(&buf); 11 | CodedOutputStream coded(&output); 12 | coded.WriteVarint64(18446744071965638648ULL); 13 | } 14 | 15 | 16 | { 17 | ArrayInput input(buf.data(), buf.size()); 18 | CodedInputStream coded(&input); 19 | uint64_t value; 20 | ASSERT_TRUE(coded.ReadVarint64(&value)); 21 | ASSERT_EQ(value, 18446744071965638648ULL); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ut/tcp_server.cpp: -------------------------------------------------------------------------------- 1 | #include "tcp_server.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace clickhouse { 13 | 14 | LocalTcpServer::LocalTcpServer(int port) 15 | : port_(port) 16 | , serverSd_(-1) 17 | {} 18 | 19 | LocalTcpServer::~LocalTcpServer() { 20 | stop(); 21 | } 22 | 23 | void LocalTcpServer::start() { 24 | //setup a socket 25 | sockaddr_in servAddr; 26 | bzero((char*)&servAddr, sizeof(servAddr)); 27 | servAddr.sin_family = AF_INET; 28 | servAddr.sin_addr.s_addr = htonl(INADDR_ANY); 29 | servAddr.sin_port = htons(port_); 30 | serverSd_ = socket(AF_INET, SOCK_STREAM, 0); 31 | if (serverSd_ < 0) { 32 | std::cerr << "Error establishing server socket" << std::endl; 33 | throw std::runtime_error("Error establishing server socket"); 34 | } 35 | int enable = 1; 36 | if (setsockopt(serverSd_, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { 37 | std::cerr << "setsockopt(SO_REUSEADDR) failed" << std::endl; 38 | } 39 | //bind the socket to its local address 40 | int bindStatus = bind(serverSd_, (struct sockaddr*) &servAddr, sizeof(servAddr)); 41 | if (bindStatus < 0) { 42 | std::cerr << "Error binding socket to local address" << std::endl; 43 | throw std::runtime_error("Error binding socket to local address"); 44 | } 45 | listen(serverSd_, 3); 46 | } 47 | 48 | void LocalTcpServer::stop() { 49 | if(serverSd_ > 0) { 50 | shutdown(serverSd_, SHUT_RDWR); 51 | close(serverSd_); 52 | serverSd_ = -1; 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /ut/tcp_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace clickhouse { 4 | 5 | class LocalTcpServer { 6 | public: 7 | LocalTcpServer(int port); 8 | ~LocalTcpServer(); 9 | 10 | void start(); 11 | void stop(); 12 | 13 | private: 14 | void startImpl(); 15 | 16 | private: 17 | int port_; 18 | int serverSd_; 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /ut/type_parser_ut.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace clickhouse; 5 | 6 | TEST(TypeParserCase, ParseTerminals) { 7 | TypeAst ast; 8 | TypeParser("UInt8").Parse(&ast); 9 | 10 | ASSERT_EQ(ast.meta, TypeAst::Terminal); 11 | ASSERT_EQ(ast.name, "UInt8"); 12 | ASSERT_EQ(ast.code, Type::UInt8); 13 | } 14 | 15 | TEST(TypeParserCase, ParseFixedString) { 16 | TypeAst ast; 17 | TypeParser("FixedString(24)").Parse(&ast); 18 | 19 | ASSERT_EQ(ast.meta, TypeAst::Terminal); 20 | ASSERT_EQ(ast.name, "FixedString"); 21 | ASSERT_EQ(ast.code, Type::FixedString); 22 | ASSERT_EQ(ast.elements.front().value, 24U); 23 | } 24 | 25 | TEST(TypeParserCase, ParseArray) { 26 | TypeAst ast; 27 | TypeParser("Array(Int32)").Parse(&ast); 28 | 29 | ASSERT_EQ(ast.meta, TypeAst::Array); 30 | ASSERT_EQ(ast.name, "Array"); 31 | ASSERT_EQ(ast.code, Type::Array); 32 | ASSERT_EQ(ast.elements.front().meta, TypeAst::Terminal); 33 | ASSERT_EQ(ast.elements.front().name, "Int32"); 34 | } 35 | 36 | TEST(TypeParserCase, ParseNullable) { 37 | TypeAst ast; 38 | TypeParser("Nullable(Date)").Parse(&ast); 39 | 40 | ASSERT_EQ(ast.meta, TypeAst::Nullable); 41 | ASSERT_EQ(ast.name, "Nullable"); 42 | ASSERT_EQ(ast.code, Type::Nullable); 43 | ASSERT_EQ(ast.elements.front().meta, TypeAst::Terminal); 44 | ASSERT_EQ(ast.elements.front().name, "Date"); 45 | } 46 | 47 | TEST(TypeParserCase, ParseEnum) { 48 | TypeAst ast; 49 | TypeParser( 50 | "Enum8('COLOR_red_10_T' = -12, 'COLOR_green_20_T'=-25, 'COLOR_blue_30_T'= 53, 'COLOR_black_30_T' = 107") 51 | .Parse(&ast); 52 | ASSERT_EQ(ast.meta, TypeAst::Enum); 53 | ASSERT_EQ(ast.name, "Enum8"); 54 | ASSERT_EQ(ast.code, Type::Enum8); 55 | ASSERT_EQ(ast.elements.size(), 8u); 56 | 57 | std::vector names = {"COLOR_red_10_T", "COLOR_green_20_T", "COLOR_blue_30_T", "COLOR_black_30_T"}; 58 | std::vector values = {-12, -25, 53, 107}; 59 | 60 | auto element = ast.elements.begin(); 61 | for (size_t i = 0; i < 4; ++i) { 62 | ASSERT_EQ(element->code, Type::Void); 63 | ASSERT_EQ(element->meta, TypeAst::String); 64 | ASSERT_EQ(element->value_string, names[i]); 65 | ++element; 66 | ASSERT_EQ(element->code, Type::Void); 67 | ASSERT_EQ(element->meta, TypeAst::Number); 68 | ASSERT_EQ(element->value, values[i]); 69 | ++element; 70 | } 71 | } 72 | 73 | TEST(TypeParserCase, ParseTuple) { 74 | TypeAst ast; 75 | TypeParser( 76 | "Tuple(UInt8, String)") 77 | .Parse(&ast); 78 | ASSERT_EQ(ast.meta, TypeAst::Tuple); 79 | ASSERT_EQ(ast.name, "Tuple"); 80 | ASSERT_EQ(ast.code, Type::Tuple); 81 | ASSERT_EQ(ast.elements.size(), 2u); 82 | 83 | std::vector names = {"UInt8", "String"}; 84 | 85 | auto element = ast.elements.begin(); 86 | for (size_t i = 0; i < 2; ++i) { 87 | ASSERT_EQ(element->name, names[i]); 88 | ++element; 89 | } 90 | } 91 | 92 | TEST(TypeParserCase, ParseDecimal) { 93 | TypeAst ast; 94 | TypeParser("Decimal(12, 5)").Parse(&ast); 95 | ASSERT_EQ(ast.meta, TypeAst::Terminal); 96 | ASSERT_EQ(ast.name, "Decimal"); 97 | ASSERT_EQ(ast.code, Type::Decimal); 98 | ASSERT_EQ(ast.elements.size(), 2u); 99 | ASSERT_EQ(ast.elements[0].value, 12); 100 | ASSERT_EQ(ast.elements[1].value, 5); 101 | } 102 | 103 | TEST(TypeParserCase, ParseDecimal32) { 104 | TypeAst ast; 105 | TypeParser("Decimal32(7)").Parse(&ast); 106 | ASSERT_EQ(ast.meta, TypeAst::Terminal); 107 | ASSERT_EQ(ast.name, "Decimal32"); 108 | ASSERT_EQ(ast.code, Type::Decimal32); 109 | ASSERT_EQ(ast.elements.size(), 1u); 110 | ASSERT_EQ(ast.elements[0].value, 7); 111 | } 112 | 113 | TEST(TypeParserCase, ParseDecimal64) { 114 | TypeAst ast; 115 | TypeParser("Decimal64(1)").Parse(&ast); 116 | ASSERT_EQ(ast.meta, TypeAst::Terminal); 117 | ASSERT_EQ(ast.name, "Decimal64"); 118 | ASSERT_EQ(ast.code, Type::Decimal64); 119 | ASSERT_EQ(ast.elements.size(), 1u); 120 | ASSERT_EQ(ast.elements[0].value, 1); 121 | } 122 | 123 | TEST(TypeParserCase, ParseDecimal128) { 124 | TypeAst ast; 125 | TypeParser("Decimal128(3)").Parse(&ast); 126 | ASSERT_EQ(ast.meta, TypeAst::Terminal); 127 | ASSERT_EQ(ast.name, "Decimal128"); 128 | ASSERT_EQ(ast.code, Type::Decimal128); 129 | ASSERT_EQ(ast.elements.size(), 1u); 130 | ASSERT_EQ(ast.elements[0].value, 3); 131 | } 132 | 133 | TEST(TypeParserCase, ParseDateTime) { 134 | TypeAst ast; 135 | TypeParser("DateTime('UTC')").Parse(&ast); 136 | ASSERT_EQ(ast.meta, TypeAst::Terminal); 137 | ASSERT_EQ(ast.name, "DateTime"); 138 | ASSERT_EQ(ast.code, Type::DateTime); 139 | ASSERT_EQ(ast.elements.size(), 1u); 140 | ASSERT_EQ(ast.elements[0].value_string, "UTC"); 141 | } 142 | 143 | TEST(TypeParserCase, ParseDateTime64) { 144 | TypeAst ast; 145 | TypeParser("DateTime64(3, 'UTC')").Parse(&ast); 146 | ASSERT_EQ(ast.meta, TypeAst::Terminal); 147 | ASSERT_EQ(ast.name, "DateTime64"); 148 | ASSERT_EQ(ast.code, Type::DateTime64); 149 | ASSERT_EQ(ast.elements.size(), 2u); 150 | ASSERT_EQ(ast.elements[0].name, ""); 151 | ASSERT_EQ(ast.elements[0].value, 3); 152 | ASSERT_EQ(ast.elements[1].value_string, "UTC"); 153 | ASSERT_EQ(ast.elements[1].value, 0); 154 | } 155 | -------------------------------------------------------------------------------- /ut/types_ut.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace clickhouse; 5 | 6 | TEST(TypesCase, TypeName) { 7 | ASSERT_EQ( 8 | Type::CreateDate()->GetName(), 9 | "Date" 10 | ); 11 | 12 | ASSERT_EQ( 13 | Type::CreateArray(Type::CreateSimple())->GetName(), 14 | "Array(Int32)" 15 | ); 16 | 17 | ASSERT_EQ( 18 | Type::CreateNullable(Type::CreateSimple())->GetName(), 19 | "Nullable(Int32)" 20 | ); 21 | 22 | ASSERT_EQ( 23 | Type::CreateArray(Type::CreateSimple())->GetItemType()->GetCode(), 24 | Type::Int32 25 | ); 26 | 27 | ASSERT_EQ( 28 | Type::CreateTuple({ 29 | Type::CreateSimple(), 30 | Type::CreateString()})->GetName(), 31 | "Tuple(Int32, String)" 32 | ); 33 | 34 | ASSERT_EQ( 35 | Type::CreateEnum8({{"One", 1}})->GetName(), 36 | "Enum8('One' = 1)" 37 | ); 38 | ASSERT_EQ( 39 | Type::CreateEnum8({})->GetName(), 40 | "Enum8()" 41 | ); 42 | } 43 | 44 | TEST(TypesCase, NullableType) { 45 | TypeRef nested = Type::CreateSimple(); 46 | ASSERT_EQ( 47 | Type::CreateNullable(nested)->GetNestedType(), 48 | nested 49 | ); 50 | } 51 | 52 | TEST(TypesCase, EnumTypes) { 53 | EnumType enum8(Type::CreateEnum8({{"One", 1}, {"Two", 2}})); 54 | ASSERT_EQ(enum8.GetName(), "Enum8('One' = 1, 'Two' = 2)"); 55 | ASSERT_TRUE(enum8.HasEnumValue(1)); 56 | ASSERT_TRUE(enum8.HasEnumName("Two")); 57 | ASSERT_FALSE(enum8.HasEnumValue(10)); 58 | ASSERT_FALSE(enum8.HasEnumName("Ten")); 59 | ASSERT_EQ(enum8.GetEnumName(2), "Two"); 60 | ASSERT_EQ(enum8.GetEnumValue("Two"), 2); 61 | 62 | EnumType enum16(Type::CreateEnum16({{"Green", 1}, {"Red", 2}, {"Yellow", 3}})); 63 | ASSERT_EQ(enum16.GetName(), "Enum16('Green' = 1, 'Red' = 2, 'Yellow' = 3)"); 64 | ASSERT_TRUE(enum16.HasEnumValue(3)); 65 | ASSERT_TRUE(enum16.HasEnumName("Green")); 66 | ASSERT_FALSE(enum16.HasEnumValue(10)); 67 | ASSERT_FALSE(enum16.HasEnumName("Black")); 68 | ASSERT_EQ(enum16.GetEnumName(2), "Red"); 69 | ASSERT_EQ(enum16.GetEnumValue("Green"), 1); 70 | 71 | ASSERT_EQ(std::distance(enum16.BeginValueToName(), enum16.EndValueToName()), 3); 72 | ASSERT_EQ((*enum16.BeginValueToName()).first, 1); 73 | ASSERT_EQ((*enum16.BeginValueToName()).second, "Green"); 74 | ASSERT_EQ((*(++enum16.BeginValueToName())).first, 2); 75 | ASSERT_EQ((*(++enum16.BeginValueToName())).second, "Red"); 76 | } 77 | --------------------------------------------------------------------------------