Skip to content

Commit 3648014

Browse files
authored
RCORE-2131: Don't throw immediately when convertion of string to number fails (#7715)
The parser will throw later anyway if the comparison cannot be done.
1 parent de18fc3 commit 3648014

File tree

3 files changed

+26
-42
lines changed

3 files changed

+26
-42
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
### Fixed
88
* A non-streaming progress notifier would not immediately call its callback after registration. Instead you would have to wait for a download message to be received to get your first update - if you were already caught up when you registered the notifier you could end up waiting a long time for the server to deliver a download that would call/expire your notifier ([#7627](https://github.com/realm/realm-core/issues/7627), since v14.6.0).
9+
* Comparing a numeric property with an argument list containing a string would throw. ([#7714](https://github.com/realm/realm-core/issues/7714), since v14.7.0)
910

1011
### Breaking changes
1112
* None.

src/realm/parser/driver.cpp

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -127,53 +127,26 @@ inline bool try_parse_specials(std::string str, T& ret)
127127
}
128128

129129
template <typename T>
130-
inline const char* get_type_name()
131-
{
132-
return "unknown";
133-
}
134-
template <>
135-
inline const char* get_type_name<int64_t>()
136-
{
137-
return "number";
138-
}
139-
template <>
140-
inline const char* get_type_name<float>()
141-
{
142-
return "floating point number";
143-
}
144-
template <>
145-
inline const char* get_type_name<double>()
146-
{
147-
return "floating point number";
148-
}
149-
150-
template <>
151-
inline const char* get_type_name<Decimal128>()
152-
{
153-
return "decimal number";
154-
}
155-
156-
template <typename T>
157-
inline T string_to(const std::string& s)
130+
inline std::optional<T> string_to(const std::string& s)
158131
{
159132
std::istringstream iss(s);
160133
iss.imbue(std::locale::classic());
161134
T value;
162135
iss >> value;
163136
if (iss.fail()) {
164137
if (!try_parse_specials(s, value)) {
165-
throw InvalidQueryArgError(util::format("Cannot convert '%1' to a %2", s, get_type_name<T>()));
138+
return {};
166139
}
167140
}
168141
return value;
169142
}
170143

171144
template <>
172-
inline Decimal128 string_to<Decimal128>(const std::string& s)
145+
inline std::optional<Decimal128> string_to<Decimal128>(const std::string& s)
173146
{
174147
Decimal128 value(s);
175148
if (value.is_nan()) {
176-
throw InvalidQueryArgError(util::format("Cannot convert '%1' to a %2", s, get_type_name<Decimal128>()));
149+
return {};
177150
}
178151
return value;
179152
}
@@ -1391,16 +1364,24 @@ std::unique_ptr<Subexpr> ConstantNode::visit(ParserDriver* drv, DataType hint)
13911364
StringData str = value.get_string();
13921365
switch (hint) {
13931366
case type_Int:
1394-
value = string_to<int64_t>(str);
1367+
if (auto val = string_to<int64_t>(str)) {
1368+
value = *val;
1369+
}
13951370
break;
13961371
case type_Float:
1397-
value = string_to<float>(str);
1372+
if (auto val = string_to<float>(str)) {
1373+
value = *val;
1374+
}
13981375
break;
13991376
case type_Double:
1400-
value = string_to<double>(str);
1377+
if (auto val = string_to<double>(str)) {
1378+
value = *val;
1379+
}
14011380
break;
14021381
case type_Decimal:
1403-
value = string_to<Decimal128>(str);
1382+
if (auto val = string_to<Decimal128>(str)) {
1383+
value = *val;
1384+
}
14041385
break;
14051386
default:
14061387
break;
@@ -1443,11 +1424,6 @@ std::unique_ptr<Subexpr> ConstantNode::visit(ParserDriver* drv, DataType hint)
14431424
}
14441425
else {
14451426
explain_value_message = util::format("argument %1 with value '%2'", explain_value_message, value);
1446-
if (!(m_target_table || Mixed::data_types_are_comparable(value.get_type(), hint) ||
1447-
Mixed::is_numeric(hint) || (value.is_type(type_String) && hint == type_TypeOfValue))) {
1448-
throw InvalidQueryArgError(
1449-
util::format("Cannot compare %1 to a %2", explain_value_message, get_data_type_name(hint)));
1450-
}
14511427
}
14521428
}
14531429
}
@@ -1485,6 +1461,13 @@ std::unique_ptr<Subexpr> ConstantNode::visit(ParserDriver* drv, DataType hint)
14851461

14861462
convert_if_needed(value);
14871463

1464+
if (type == Type::ARG && !(m_target_table || Mixed::data_types_are_comparable(value.get_type(), hint) ||
1465+
(value.is_type(type_TypedLink) && hint == type_Link) ||
1466+
(value.is_type(type_String) && hint == type_TypeOfValue))) {
1467+
throw InvalidQueryArgError(
1468+
util::format("Cannot compare %1 to a %2", explain_value_message, get_data_type_name(hint)));
1469+
}
1470+
14881471
switch (value.get_type()) {
14891472
case type_Int: {
14901473
ret = std::make_unique<Value<int64_t>>(value.get_int());

test/test_parser.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2266,7 +2266,7 @@ TEST(Parser_list_of_primitive_ints)
22662266
message,
22672267
"Unsupported comparison operator 'endswith' against type 'int', right side must be a string or binary type");
22682268
CHECK_THROW_ANY_GET_MESSAGE(verify_query(test_context, t, "integers == 'string'", 0), message);
2269-
CHECK_EQUAL(message, "Cannot convert 'string' to a number");
2269+
CHECK_EQUAL(message, "Unsupported comparison between type 'int' and type 'string'");
22702270
}
22712271

22722272
TEST_TYPES(Parser_list_of_primitive_strings, std::true_type, std::false_type)
@@ -3346,7 +3346,7 @@ TEST(Parser_BacklinkCount)
33463346
std::string message;
33473347
// backlink count requires comparison to a numeric type
33483348
CHECK_THROW_ANY_GET_MESSAGE(verify_query(test_context, items, "@links.@count == 'string'", -1), message);
3349-
CHECK_EQUAL(message, "Cannot convert 'string' to a number");
3349+
CHECK_EQUAL(message, "Unsupported comparison between type 'int' and type 'string'");
33503350
CHECK_THROW_ANY_GET_MESSAGE(verify_query(test_context, items, "@links.@count == 2018-04-09@14:21:0", -1),
33513351
message);
33523352
CHECK_EQUAL(message, "Unsupported comparison between type 'int' and type 'timestamp'");

0 commit comments

Comments
 (0)