Merge pull request #26072 from vdimir/fix-offset-check-in-window-frame

Fix logical error with signed and unsigned offset in WindowFrame::checkValid
This commit is contained in:
alexey-milovidov 2021-07-09 03:06:15 +03:00 committed by GitHub
commit c98e131a81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 23 additions and 4 deletions

View File

@ -117,4 +117,16 @@ public:
} }
}; };
class FieldVisitorAccurateLessOrEqual : public StaticVisitor<bool>
{
public:
template <typename T, typename U>
bool operator()(const T & l, const U & r) const
{
auto less_cmp = FieldVisitorAccurateLess();
return !less_cmp(r, l);
}
};
} }

View File

@ -1,6 +1,7 @@
#include <Interpreters/WindowDescription.h> #include <Interpreters/WindowDescription.h>
#include <Core/Field.h> #include <Core/Field.h>
#include <Common/FieldVisitorsAccurateComparison.h>
#include <Common/FieldVisitorToString.h> #include <Common/FieldVisitorToString.h>
#include <IO/Operators.h> #include <IO/Operators.h>
#include <Parsers/ASTFunction.h> #include <Parsers/ASTFunction.h>
@ -99,7 +100,7 @@ void WindowFrame::checkValid() const
&& begin_offset.get<Int64>() < INT_MAX)) && begin_offset.get<Int64>() < INT_MAX))
{ {
throw Exception(ErrorCodes::BAD_ARGUMENTS, throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Frame start offset for '{}' frame must be a nonnegative 32-bit integer, '{}' of type '{}' given.", "Frame start offset for '{}' frame must be a nonnegative 32-bit integer, '{}' of type '{}' given",
toString(type), toString(type),
applyVisitor(FieldVisitorToString(), begin_offset), applyVisitor(FieldVisitorToString(), begin_offset),
Field::Types::toString(begin_offset.getType())); Field::Types::toString(begin_offset.getType()));
@ -112,7 +113,7 @@ void WindowFrame::checkValid() const
&& end_offset.get<Int64>() < INT_MAX)) && end_offset.get<Int64>() < INT_MAX))
{ {
throw Exception(ErrorCodes::BAD_ARGUMENTS, throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Frame end offset for '{}' frame must be a nonnegative 32-bit integer, '{}' of type '{}' given.", "Frame end offset for '{}' frame must be a nonnegative 32-bit integer, '{}' of type '{}' given",
toString(type), toString(type),
applyVisitor(FieldVisitorToString(), end_offset), applyVisitor(FieldVisitorToString(), end_offset),
Field::Types::toString(end_offset.getType())); Field::Types::toString(end_offset.getType()));
@ -160,7 +161,8 @@ void WindowFrame::checkValid() const
bool begin_less_equal_end; bool begin_less_equal_end;
if (begin_preceding && end_preceding) if (begin_preceding && end_preceding)
{ {
begin_less_equal_end = begin_offset >= end_offset; /// we can't compare Fields using operator<= if fields have different types
begin_less_equal_end = applyVisitor(FieldVisitorAccurateLessOrEqual(), end_offset, begin_offset);
} }
else if (begin_preceding && !end_preceding) else if (begin_preceding && !end_preceding)
{ {
@ -172,7 +174,7 @@ void WindowFrame::checkValid() const
} }
else /* if (!begin_preceding && !end_preceding) */ else /* if (!begin_preceding && !end_preceding) */
{ {
begin_less_equal_end = begin_offset <= end_offset; begin_less_equal_end = applyVisitor(FieldVisitorAccurateLessOrEqual(), begin_offset, end_offset);
} }
if (!begin_less_equal_end) if (!begin_less_equal_end)

View File

@ -13,3 +13,5 @@ select count() over (rows between 1 + 1 preceding and 1 + 1 following) from numb
5 5
4 4
3 3
-- signed and unsigned in offset do not cause logical error
select count() over (rows between 2 following and 1 + -1 following) FROM numbers(10); -- { serverError 36 }

View File

@ -4,3 +4,6 @@ set allow_experimental_window_functions = 1;
-- expressions in window frame -- expressions in window frame
select count() over (rows between 1 + 1 preceding and 1 + 1 following) from numbers(10); select count() over (rows between 1 + 1 preceding and 1 + 1 following) from numbers(10);
-- signed and unsigned in offset do not cause logical error
select count() over (rows between 2 following and 1 + -1 following) FROM numbers(10); -- { serverError 36 }