From 1f06eb8e696bb28265bc912dcd7b534fc0526a60 Mon Sep 17 00:00:00 2001 From: Artemkin Pavel Date: Wed, 17 May 2017 15:53:02 +0500 Subject: [PATCH] support nullable columns --- driver/CMakeLists.txt | 1 + driver/result_set.cpp | 32 +++++- driver/result_set.h | 1 + driver/type_parser.cpp | 129 +++++++++++++++++++++++++ driver/type_parser.h | 60 ++++++++++++ vs/driver32/driver32.vcxproj | 4 +- vs/driver32/driver32.vcxproj.filters | 8 +- vs/driver32w/driver32w.vcxproj | 2 + vs/driver32w/driver32w.vcxproj.filters | 6 ++ vs/driver64/driver64.vcxproj | 2 + vs/driver64/driver64.vcxproj.filters | 6 ++ vs/driver64w/driver64w.vcxproj | 2 + vs/driver64w/driver64w.vcxproj.filters | 6 ++ 13 files changed, 253 insertions(+), 6 deletions(-) create mode 100644 driver/type_parser.cpp create mode 100644 driver/type_parser.h diff --git a/driver/CMakeLists.txt b/driver/CMakeLists.txt index ed234f63a..53f13f53e 100644 --- a/driver/CMakeLists.txt +++ b/driver/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(clickhouse-odbc SHARED odbc.cpp result_set.cpp statement.cpp + type_parser.cpp ) set_target_properties(clickhouse-odbc diff --git a/driver/result_set.cpp b/driver/result_set.cpp index f690c8c16..25a16c605 100644 --- a/driver/result_set.cpp +++ b/driver/result_set.cpp @@ -1,6 +1,7 @@ #include "log.h" #include "result_set.h" #include "statement.h" +#include "type_parser.h" #include @@ -49,6 +50,22 @@ void Field::normalizeDate(T& date) const date.day = 1; } +static void assignTypeInfo(const TypeAst & ast, ColumnInfo * info) +{ + if (ast.meta == TypeAst::Terminal) + { + info->type_without_parameters = ast.name; + } + else if (ast.meta == TypeAst::Nullable) + { + info->is_nullable = true; + assignTypeInfo(ast.elements.front(), info); + } + else + { + throw std::runtime_error("compound types doesn't supported: " + info->type); + } +} void ResultSet::init(Statement * statement_) { @@ -70,10 +87,17 @@ void ResultSet::init(Statement * statement_) readString(columns_info[i].name, in()); readString(columns_info[i].type, in()); - columns_info[i].type_without_parameters = columns_info[i].type; - auto pos = columns_info[i].type_without_parameters.find('('); - if (std::string::npos != pos) - columns_info[i].type_without_parameters.resize(pos); + { + TypeAst ast; + if (TypeParser(columns_info[i].type).parse(&ast)) + { + assignTypeInfo(ast, &columns_info[i]); + } + else + { + throw std::runtime_error("can't pase name of type: " + columns_info[i].type); + } + } } readNextBlock(); diff --git a/driver/result_set.h b/driver/result_set.h index e697a14c1..1367940e3 100644 --- a/driver/result_set.h +++ b/driver/result_set.h @@ -55,6 +55,7 @@ struct ColumnInfo std::string type; std::string type_without_parameters; size_t display_size = 0; + bool is_nullable = false; }; diff --git a/driver/type_parser.cpp b/driver/type_parser.cpp new file mode 100644 index 000000000..4d2a589b7 --- /dev/null +++ b/driver/type_parser.cpp @@ -0,0 +1,129 @@ +#include "type_parser.h" + +#include + +template +static inline T fromString(const std::string& s) +{ + std::istringstream iss(s); + T result; + iss >> result; + return result; + +} +static TypeAst::Meta getTypeMeta(const std::string& name) { + if (name == "Array") { + return TypeAst::Array; + } + + if (name == "Null") { + return TypeAst::Null; + } + + if (name == "Nullable") { + return TypeAst::Nullable; + } + + if (name == "Tuple") { + return TypeAst::Tuple; + } + + return TypeAst::Terminal; +} + + +TypeParser::TypeParser(const std::string& name) + : cur_(name.data()) + , end_(name.data() + name.size()) + , type_(nullptr) +{ +} + +TypeParser::~TypeParser() = default; + +bool TypeParser::parse(TypeAst* type) { + type_ = type; + open_elements_.push(type_); + + do { + const Token& token = nextToken(); + + switch (token.type) { + case Token::Name: + type_->meta = getTypeMeta(token.value); + type_->name = token.value; + break; + case Token::Number: + type_->meta = TypeAst::Number; + type_->size = fromString(token.value); + break; + case Token::LPar: + type_->elements.emplace_back(TypeAst()); + open_elements_.push(type_); + type_ = &type_->elements.back(); + break; + case Token::RPar: + type_ = open_elements_.top(); + open_elements_.pop(); + break; + case Token::Comma: + type_ = open_elements_.top(); + open_elements_.pop(); + type_->elements.emplace_back(TypeAst()); + open_elements_.push(type_); + type_ = &type_->elements.back(); + break; + case Token::EOS: + return true; + case Token::Invalid: + return false; + } + } while (true); +} + +TypeParser::Token TypeParser::nextToken() { + for (; cur_ < end_; ++cur_) { + switch (*cur_) { + case ' ': + case '\n': + case '\t': + case '\0': + continue; + + case '(': + return Token{Token::LPar, std::string(cur_++, 1)}; + case ')': + return Token{Token::RPar, std::string(cur_++, 1)}; + case ',': + return Token{Token::Comma, std::string(cur_++, 1)}; + + default: { + const char* st = cur_; + + if (isalpha(*cur_)) { + for (; cur_ < end_; ++cur_) { + if (!isalpha(*cur_) && !isdigit(*cur_)) { + break; + } + } + + return Token{Token::Name, std::string(st, cur_)}; + } + + if (isdigit(*cur_)) { + for (; cur_ < end_; ++cur_) { + if (!isdigit(*cur_)) { + break; + } + } + + return Token{Token::Number, std::string(st, cur_)}; + } + + return Token{Token::Invalid, std::string()}; + } + } + } + + return Token{Token::EOS, std::string()}; +} diff --git a/driver/type_parser.h b/driver/type_parser.h new file mode 100644 index 000000000..10194fdfa --- /dev/null +++ b/driver/type_parser.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +struct TypeAst { + enum Meta { + Array, + Null, + Nullable, + Number, + Terminal, + Tuple, + }; + + /// Type's category. + Meta meta; + /// Type's name. + std::string name; + /// Size of type's instance. For fixed-width types only. + size_t size = 0; + /// Subelements of the type. + std::list elements; +}; + + +class TypeParser { + + struct Token { + enum Type { + Invalid = 0, + Name, + Number, + LPar, + RPar, + Comma, + EOS, + }; + + Type type; + std::string value; + }; + +public: + explicit TypeParser(const std::string& name); + ~TypeParser(); + + bool parse(TypeAst* type); + +private: + Token nextToken(); + +private: + const char* cur_; + const char* end_; + + TypeAst* type_; + std::stack open_elements_; +}; diff --git a/vs/driver32/driver32.vcxproj b/vs/driver32/driver32.vcxproj index 21ed224e0..33416d1e0 100644 --- a/vs/driver32/driver32.vcxproj +++ b/vs/driver32/driver32.vcxproj @@ -165,6 +165,7 @@ + @@ -178,6 +179,7 @@ + @@ -198,4 +200,4 @@ - + \ No newline at end of file diff --git a/vs/driver32/driver32.vcxproj.filters b/vs/driver32/driver32.vcxproj.filters index d4caba48f..18d19d4a3 100644 --- a/vs/driver32/driver32.vcxproj.filters +++ b/vs/driver32/driver32.vcxproj.filters @@ -51,6 +51,9 @@ Заголовочные файлы + + Заголовочные файлы + @@ -86,6 +89,9 @@ Файлы исходного кода + + Файлы исходного кода + @@ -97,4 +103,4 @@ Файлы исходного кода - + \ No newline at end of file diff --git a/vs/driver32w/driver32w.vcxproj b/vs/driver32w/driver32w.vcxproj index 62884d5b9..8127c17a7 100644 --- a/vs/driver32w/driver32w.vcxproj +++ b/vs/driver32w/driver32w.vcxproj @@ -157,6 +157,7 @@ + @@ -170,6 +171,7 @@ + diff --git a/vs/driver32w/driver32w.vcxproj.filters b/vs/driver32w/driver32w.vcxproj.filters index 26d794ff3..5dffb4c75 100644 --- a/vs/driver32w/driver32w.vcxproj.filters +++ b/vs/driver32w/driver32w.vcxproj.filters @@ -48,6 +48,9 @@ Заголовочные файлы + + Заголовочные файлы + @@ -83,6 +86,9 @@ Файлы исходного кода + + Файлы исходного кода + diff --git a/vs/driver64/driver64.vcxproj b/vs/driver64/driver64.vcxproj index da424d6ce..67e89cf56 100644 --- a/vs/driver64/driver64.vcxproj +++ b/vs/driver64/driver64.vcxproj @@ -160,6 +160,7 @@ + @@ -173,6 +174,7 @@ + diff --git a/vs/driver64/driver64.vcxproj.filters b/vs/driver64/driver64.vcxproj.filters index 03159cd7f..d44bedec4 100644 --- a/vs/driver64/driver64.vcxproj.filters +++ b/vs/driver64/driver64.vcxproj.filters @@ -51,6 +51,9 @@ Заголовочные файлы + + Заголовочные файлы + @@ -86,6 +89,9 @@ Файлы исходного кода + + Файлы исходного кода + diff --git a/vs/driver64w/driver64w.vcxproj b/vs/driver64w/driver64w.vcxproj index e78af9c30..8d6a6977f 100644 --- a/vs/driver64w/driver64w.vcxproj +++ b/vs/driver64w/driver64w.vcxproj @@ -159,6 +159,7 @@ + @@ -172,6 +173,7 @@ + diff --git a/vs/driver64w/driver64w.vcxproj.filters b/vs/driver64w/driver64w.vcxproj.filters index ccb3e93c8..fedfd5a90 100644 --- a/vs/driver64w/driver64w.vcxproj.filters +++ b/vs/driver64w/driver64w.vcxproj.filters @@ -51,6 +51,9 @@ Заголовочные файлы + + Заголовочные файлы + @@ -86,6 +89,9 @@ Файлы исходного кода + + Файлы исходного кода +