-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alternative wire protocols support (RowBinaryWithNamesAndTypes) #255
Alternative wire protocols support (RowBinaryWithNamesAndTypes) #255
Conversation
Add default_format parameter
Use std::make_unique instead of bare "new"
Use std::make_unique instead of bare "new"
Refine existing cases
For the record, the average output of
|
d9d97a2
to
8a9b188
Compare
* switch-to-variant: Do not attempt to install already installed packages in brew No more Python 2 in brew, using Python (3) Fix description for CentOS 7 Roll back poco submodule Define BUILD_TYPE_* Enable perf tests only if BUILD_TYPE_Release is defined Bump submodules Update LICENSE Report iteration count, throughput, and latency in perf test measurements Rename CH_ODBC_ENABLE_SAFE_DISPATCH_ONLY to CH_ODBC_ALLOW_UNSAFE_DISPATCH Rename WORKAROUND_ENABLE_SAFE_DISPATCH_ONLY to WORKAROUND_ALLOW_UNSAFE_DISPATCH Rename WORKAROUND_ENABLE_SSL to WORKAROUND_DISABLE_SSL Add comments Fix getObjectHandleType() usage Remove general case implementations for getObjectHandleType() and getObjectTypeName() Fix int -> string attribute value extraction checks Fix fromString() # Conflicts: # .travis.yml # driver/connection.cpp # driver/test/performance_it.cpp # driver/utils/utils.h
…se them during deserialization
…{n}" is appended to each test command name) Modify testing to support arbitrary list of DSNs Add a DSN in travis testing config, that access server using RowBinaryWithNamesAndTypes and ANSI driver
Fix vs2017 compilation
afcf399
to
85c4163
Compare
} // namespace value_manip | ||
|
||
template <typename T> | ||
struct SimpleTypeWrapper { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any specific reason you need this wrapper? Why not just a type alias?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To define to_null()
'ing c-tor, and reuse code in general. Later, it is used as base for the most of DataSourceType<>
, which in their turn used for better control over types and their static dispatch/overloads.
driver/utils/type_info.h
Outdated
std::string sql_type_name; | ||
bool is_unsigned; | ||
SQLSMALLINT sql_type; | ||
int32_t column_size; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add comments explaining what is the difference between column_size
and octet_length
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are typical ODBC concepts with lengthy descriptions provided here: https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/column-size-decimal-digits-transfer-octet-length-and-display-size
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, please add this link as a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
#include "driver/platform/platform.h" | ||
#include "driver/result_set.h" | ||
|
||
class RowBinaryWithNamesAndTypesResultSet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add a comment explaining what is the solve purpose of this ResultSet
subclass and how it is different from any other ResultSet
?
dest.data = std::move(value); | ||
} | ||
|
||
void readValue(DataSourceType< DataSourceTypeId::Date > & dest, ColumnInfo & column_info); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO DataSourceType< DataSourceTypeId::Date>
is a bit verbose, would you consider adding a type-aliases DataSourceTypeDate
, etc ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not a big fan of aliases. You need to decompose those aliases in your mind each time you meet them, and store more "non-uniform" piece of data in your head, which spends your working memory resources.
I.e., while this may look like more raw data (in terms of characters):
DataSourceType< DataSourceTypeId::DateTime >
DataSourceType< DataSourceTypeId::Decimal >
DataSourceType< DataSourceTypeId::Int64 >
this is more data in terms of unique slots in your memory:
DataSourceTypeDateTime
DataSourceTypeDecimal
DataSourceTypeInt64
(but the latter is better than using some random names, obviously.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In other words, there is a hidden structure in this naming: DataSourceTypeDateTime
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whereas, that stricture is explicit in: DataSourceType< DataSourceTypeId::DateTime >
So you don't need to do extra work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, right now it looks "guts out": the 'hidden' struct here is a mere implementation detail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, it's not an underlying struct, it's a type id enum value. A very "facing out" thing.
void convert_via_proxy(const SourceType & src, DestinationType & dest); | ||
|
||
template <typename SourceType> | ||
struct from_value { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, from_value<T>::to_value<Y>::convert()
is a bit of over complication (both in calling code and implementation), could that be set of convertFromValue(const X & x, Y & y)
function overloads ?
Also, that would allow moving all that code into .cpp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During the evolution of this code I went through different configurations, and I actually saw what you mentioned too. It was just too non-uniform and hard to maintain, to have such different ways of calling the conversion code at that time. Things become more complicated when you try to do SFINAE or partial specializations. So I picked this representation, because it is actually the only maintainable one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be done in a more simple manner: https://godbolt.org/z/HZWv6M
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe... if it could be applied to the entire code, including partial specialization cases.
}; | ||
|
||
template <typename DestinationType> | ||
struct to_buffer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing applies to to_buffer<X>::from_value<Y>::convert
, maybe convertFromValueToBuffer(const Y & y, X & x)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ALSO, to_buffer
breaks pattern:
from_SOMETHING<X>::to_SOMETHING_ELSE<Y>::convert(x, y)
to opposite:
to_SOMETHING<X>::from_SOMETHING_ELSE<X>::convert(y, x)
and that complicates things even further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's severely affect maintainability, as mentioned earlier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does it brake the pattern?
to_value <OfType>
from_value <OfType>
to_buffer <OfType>
from_buffer <OfType>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, so to_smth
and from_smt
defined and used differently, according to the way the conversion are used, and that allows to define some default conversions that cover more cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By interrupting the flow, most of the code here converters from left type to right type, and to_buffer
is an exception. What I am trying to say: this is already complicated enough, no need to make it even harder to grasp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's all about compromises. Preserving to_*
-> from_*
structure may result in more actual code, that does pretty much the same thing.
}; | ||
|
||
template <typename SourceType> | ||
struct from_buffer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from_buffer<X>::to_value<Y>::convert()
=> convertFromBufferToValue(const X & x, Y & y)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's severely affect maintainability, as mentioned earlier.
@@ -126,6 +110,37 @@ struct UTF8CaseInsensitiveCompare { | |||
} | |||
}; | |||
|
|||
template<typename T> | |||
class ObjectPool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add comment describing that this pool is for, and to document that if behaves in LRU-fashion by dropping old object after reaching certain size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have few questions regarding this PR.
Also (if it is possible), please make sure that type_info.h
is detected as moved in git. That might help github to detect move also, and hence greatly simplify the review process.
Another issue type_info.h
is basically type info
+ data types
+ type traits
+ conversion functions
+ fillOutput functions
smashed together into a huge lump of code, too big to be digestible by anyone except you now (and I am afraid in two months time even you wouldn't be able to promptly reason about what is going on here). Please, split it into meaningful pieces.
The only way to achieve this that I know of, is to first move the file, and then make changes in a separate commit. Cannot be done retrospectively. |
Another thing: please point me to a location of tests for ResultSet parsing (both ODBCv2 and RowBinaryWithNames formats) |
struct from_value<std::string>::to_value<std::string> { | ||
using DestinationType = std::string; | ||
|
||
static inline void convert(const SourceType & src, DestinationType & dest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to add extra overload here? :
static inline void convert(SourceType & src, DestinationType & dest) {
dest = std::move(src);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This particular conversion is not used on a critical path. Also, managing storage in convert()
-type functions proved to be hell.
This is tested by virtually all integration tests, if the DSN is configured to used the corrsponding wire format. |
dest.value.day = tm.tm_mday; | ||
} | ||
|
||
void RowBinaryWithNamesAndTypesResultSet::readValue(DataSourceType<DataSourceTypeId::DateTime> & dest, ColumnInfo & column_info) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please validate that Date and DateTime values returned by the ODBC driver are equal to the values returned by ClickHouse client when local timezone differs from timezone of server.
dest = src; | ||
} | ||
else { | ||
convert_via_proxy<std::string>(src, dest); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This, and every other convert_via_proxy<std::string>
looks spooky and very sub-optimal, luckily these branches are never hit: traceon@cf58fff
And can be safely removed.
Clarify/fix temporaries handling Comments from @Enmk
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, please make sure to address #269 after merging
Yes, sure. Thank you |
Closes #242.
Prepares some parts for #235.
TEST_DSN_LIST
instead ofTEST_DSN
andTEST_DSN_W
)RowBinaryWithNamesAndTypes
formatNDEBUG
andBUILD_TYPE_*
macro settingdriver/utils/read_helpers.{h,cpp}
(functionality moved toODBCDriver2ResultSet
class)driver/utils/scope_guard.h
- not useddriver/type_info.cpp
todriver/utils/type_info.cpp
SQLGetData
/SQLFetch
/SQLFetchScroll
ODBCDriver2ResultSet
(old behavior), added newRowBinaryWithNamesAndTypesResultSet
TypeParser
to support extracting precision and scale info fromDecimalXYZ
typesDataSourceType<TypeID>
thin wrapper classes that store values, to be able to define overloads and dispatch easierstd::string
, nowstd::variant<all DataSourceType<TypeID>s...>
)driver/utils/utils.h
todriver/utils/type_info.h
(likefillOutputBuffer()
, etc.)