diff --git a/src/common/filter/Expressions.cpp b/src/common/filter/Expressions.cpp index 93856dcf346..82c0814a500 100644 --- a/src/common/filter/Expressions.cpp +++ b/src/common/filter/Expressions.cpp @@ -687,13 +687,56 @@ OptVariantType ArithmeticExpression::eval(Getters &getters) const { auto l = left.value(); auto r = right.value(); + static constexpr int64_t maxInt = std::numeric_limits::max(); + static constexpr int64_t minInt = std::numeric_limits::min(); + + auto isAddOverflow = [] (int64_t lv, int64_t rv) -> bool { + if (lv >= 0 && rv >= 0) { + return maxInt - lv < rv; + } else if (lv < 0 && rv < 0) { + return minInt - lv > rv; + } else { + return false; + } + }; + + auto isSubOverflow = [] (int64_t lv, int64_t rv) -> bool { + if (lv > 0 && rv < 0) { + return maxInt - lv < -rv; + } else if (lv < 0 && rv > 0) { + return minInt - lv > -rv; + } else { + return false; + } + }; + + auto isMulOverflow = [] (int64_t lv, int64_t rv) -> bool { + if (lv > 0 && rv > 0) { + return maxInt / lv < rv; + } else if (lv < 0 && rv < 0) { + return maxInt / lv > rv; + } else if (lv > 0 && rv < 0) { + return maxInt / lv < -rv; + } else if (lv < 0 && rv > 0) { + return maxInt / lv > -rv; + } else { + return false; + } + }; + switch (op_) { case ADD: if (isArithmetic(l) && isArithmetic(r)) { if (isDouble(l) || isDouble(r)) { return OptVariantType(asDouble(l) + asDouble(r)); } - return OptVariantType(asInt(l) + asInt(r)); + int64_t lValue = asInt(l); + int64_t rValue = asInt(r); + if (isAddOverflow(lValue, rValue)) { + return Status::Error(folly::stringPrintf("Out of range %ld + %ld", + lValue, rValue)); + } + return OptVariantType(lValue + rValue); } if (isString(l) && isString(r)) { @@ -705,7 +748,13 @@ OptVariantType ArithmeticExpression::eval(Getters &getters) const { if (isDouble(l) || isDouble(r)) { return OptVariantType(asDouble(l) - asDouble(r)); } - return OptVariantType(asInt(l) - asInt(r)); + int64_t lValue = asInt(l); + int64_t rValue = asInt(r); + if (isSubOverflow(lValue, rValue)) { + return Status::Error(folly::stringPrintf("Out of range %ld - %ld", + lValue, rValue)); + } + return OptVariantType(lValue - rValue); } break; case MUL: @@ -713,22 +762,44 @@ OptVariantType ArithmeticExpression::eval(Getters &getters) const { if (isDouble(l) || isDouble(r)) { return OptVariantType(asDouble(l) * asDouble(r)); } - return OptVariantType(asInt(l) * asInt(r)); + int64_t lValue = asInt(l); + int64_t rValue = asInt(r); + if (isMulOverflow(lValue, rValue)) { + return Status::Error("Out of range %ld * %ld", lValue, rValue); + } + return OptVariantType(lValue * rValue); } break; case DIV: if (isArithmetic(l) && isArithmetic(r)) { if (isDouble(l) || isDouble(r)) { + if (abs(asDouble(r)) < 1e-8) { + // When Null is supported, should be return NULL + return Status::Error("Division by zero"); + } return OptVariantType(asDouble(l) / asDouble(r)); } + + if (abs(asInt(r)) == 0) { + // When Null is supported, should be return NULL + return Status::Error("Division by zero"); + } return OptVariantType(asInt(l) / asInt(r)); } break; case MOD: if (isArithmetic(l) && isArithmetic(r)) { if (isDouble(l) || isDouble(r)) { + if (abs(asDouble(r)) < 1e-8) { + // When Null is supported, should be return NULL + return Status::Error("Division by zero"); + } return fmod(asDouble(l), asDouble(r)); } + if (abs(asInt(r)) == 0) { + // When Null is supported, should be return NULL + return Status::Error("Division by zero"); + } return OptVariantType(asInt(l) % asInt(r)); } break; diff --git a/src/graph/test/YieldTest.cpp b/src/graph/test/YieldTest.cpp index 80db4a1f0ae..201d1a5492f 100644 --- a/src/graph/test/YieldTest.cpp +++ b/src/graph/test/YieldTest.cpp @@ -501,6 +501,89 @@ TEST_F(YieldTest, Error) { } } +TEST_F(YieldTest, calculateOverflow) { + auto client = gEnv->getClient(); + ASSERT_NE(nullptr, client); + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD 9223372036854775807+1"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD -9223372036854775807-2"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD -9223372036854775807+-2"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD 1-(-9223372036854775807)"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD 9223372036854775807*2"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD -9223372036854775807*-2"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD 9223372036854775807*-2"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD -9223372036854775807*2"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD 1/0"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD 2%0"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_EXECUTION_ERROR, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD 9223372036854775807"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD -9223372036854775808"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); + } + { + cpp2::ExecutionResponse resp; + std::string query = "YIELD -9223372036854775809"; + auto code = client->execute(query, resp); + ASSERT_EQ(cpp2::ErrorCode::E_SYNTAX_ERROR, code); + } +} + TEST_F(YieldTest, AggCall) { { cpp2::ExecutionResponse resp; @@ -653,3 +736,4 @@ TEST_F(YieldTest, EmptyInput) { } } // namespace graph } // namespace nebula + diff --git a/src/parser/parser.yy b/src/parser/parser.yy index f5a0f665728..6de9f640df3 100644 --- a/src/parser/parser.yy +++ b/src/parser/parser.yy @@ -27,6 +27,9 @@ class GraphScanner; static int yylex(nebula::GraphParser::semantic_type* yylval, nebula::GraphParser::location_type *yylloc, nebula::GraphScanner& scanner); + + void ifOutOfRange(const int64_t input, + const nebula::GraphParser::location_type& loc); } %union { @@ -131,7 +134,7 @@ class GraphScanner; %type name_label unreserved_keyword agg_function %type expression logic_xor_expression logic_or_expression logic_and_expression %type relational_expression multiplicative_expression additive_expression arithmetic_xor_expression -%type unary_expression primary_expression equality_expression +%type unary_expression primary_expression equality_expression base_expression %type src_ref_expression %type dst_ref_expression %type input_ref_expression @@ -266,6 +269,10 @@ unreserved_keyword | KW_BIT_AND { $$ = new std::string("bit_and"); } | KW_BIT_OR { $$ = new std::string("bit_or"); } | KW_BIT_XOR { $$ = new std::string("bit_xor"); } + | KW_PATH { $$ = new std::string("path"); } + | KW_DATA { $$ = new std::string("data"); } + | KW_LEADER { $$ = new std::string("leader"); } + | KW_UUID { $$ = new std::string("uuid"); } ; agg_function @@ -283,9 +290,21 @@ agg_function primary_expression : INTEGER { + ifOutOfRange($1, @1); $$ = new PrimaryExpression($1); } - | DOUBLE { + | MINUS INTEGER { + $$ = new PrimaryExpression(-$2);; + } + | MINUS base_expression { + $$ = new UnaryExpression(UnaryExpression::NEGATE, $2); + } + | base_expression { + $$ = $1; + } + +base_expression + : DOUBLE { $$ = new PrimaryExpression($1); } | STRING { @@ -414,9 +433,6 @@ unary_expression | PLUS unary_expression { $$ = new UnaryExpression(UnaryExpression::PLUS, $2); } - | MINUS unary_expression { - $$ = new UnaryExpression(UnaryExpression::NEGATE, $2); - } | NOT unary_expression { $$ = new UnaryExpression(UnaryExpression::NOT, $2); } @@ -551,8 +567,14 @@ go_sentence step_clause : %empty { $$ = new StepClause(); } - | INTEGER KW_STEPS { $$ = new StepClause($1); } - | KW_UPTO INTEGER KW_STEPS { $$ = new StepClause($2, true); } + | INTEGER KW_STEPS { + ifOutOfRange($1, @1); + $$ = new StepClause($1); + } + | KW_UPTO INTEGER KW_STEPS { + ifOutOfRange($2, @2); + $$ = new StepClause($2, true); + } ; from_clause @@ -589,12 +611,14 @@ vid unary_integer : PLUS INTEGER { + ifOutOfRange($2, @2); $$ = $2; } | MINUS INTEGER { $$ = -$2; } | INTEGER { + ifOutOfRange($1, @1); $$ = $1; } ; @@ -868,7 +892,10 @@ find_path_sentence find_path_upto_clause : %empty { $$ = new StepClause(5, true); } - | KW_UPTO INTEGER KW_STEPS { $$ = new StepClause($2, true); } + | KW_UPTO INTEGER KW_STEPS { + ifOutOfRange($2, @2); + $$ = new StepClause($2, true); + } ; to_clause @@ -881,9 +908,20 @@ to_clause ; limit_sentence - : KW_LIMIT INTEGER { $$ = new LimitSentence(0, $2); } - | KW_LIMIT INTEGER COMMA INTEGER { $$ = new LimitSentence($2, $4); } - | KW_LIMIT INTEGER KW_OFFSET INTEGER { $$ = new LimitSentence($2, $4); } + : KW_LIMIT INTEGER { + ifOutOfRange($2, @2); + $$ = new LimitSentence(0, $2); + } + | KW_LIMIT INTEGER COMMA INTEGER { + ifOutOfRange($2, @2); + ifOutOfRange($4, @2); + $$ = new LimitSentence($2, $4); + } + | KW_LIMIT INTEGER KW_OFFSET INTEGER { + ifOutOfRange($2, @2); + ifOutOfRange($4, @4); + $$ = new LimitSentence($2, $4); + } ; group_by_sentence @@ -1632,9 +1670,11 @@ space_opt_list space_opt_item : KW_PARTITION_NUM ASSIGN INTEGER { + ifOutOfRange($3, @3); $$ = new SpaceOptItem(SpaceOptItem::PARTITION_NUM, $3); } | KW_REPLICA_FACTOR ASSIGN INTEGER { + ifOutOfRange($3, @3); $$ = new SpaceOptItem(SpaceOptItem::REPLICA_FACTOR, $3); } // TODO(YT) Create Spaces for different engines @@ -1822,6 +1862,7 @@ balance_sentence $$ = new BalanceSentence(BalanceSentence::SubType::kData); } | KW_BALANCE KW_DATA INTEGER { + ifOutOfRange($3, @3); $$ = new BalanceSentence($3); } | KW_BALANCE KW_DATA KW_STOP { @@ -1967,6 +2008,12 @@ void nebula::GraphParser::error(const nebula::GraphParser::location_type& loc, errmsg = os.str(); } +void ifOutOfRange(const int64_t input, + const nebula::GraphParser::location_type& loc) { + if ((uint64_t)input == 9223372036854775808ULL) { + throw nebula::GraphParser::syntax_error(loc, "out of range"); + } +} static int yylex(nebula::GraphParser::semantic_type* yylval, nebula::GraphParser::location_type *yylloc, diff --git a/src/parser/scanner.lex b/src/parser/scanner.lex index d17873c6e24..1c1eef3699b 100644 --- a/src/parser/scanner.lex +++ b/src/parser/scanner.lex @@ -380,7 +380,11 @@ IP_OCTET ([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]) {DEC}+ { try { folly::StringPiece text(yytext, yyleng); - yylval->intval = folly::to(text); + uint64_t val = folly::to(text); + if (val > 9223372036854775808ULL) { + yyterminate(); + } + yylval->intval = val; } catch (...) { yyterminate(); } diff --git a/src/parser/test/ParserTest.cpp b/src/parser/test/ParserTest.cpp index 49d0b48374d..3ffe42da254 100644 --- a/src/parser/test/ParserTest.cpp +++ b/src/parser/test/ParserTest.cpp @@ -1164,7 +1164,8 @@ TEST(Parser, UnreservedKeywords) { { GQLParser parser; std::string query = "CREATE TAG tag1(space string, spaces string, " - "email string, password string, roles string)"; + "email string, password string, roles string, uuid int, " + "path string, variables string, leader string, data string)"; auto result = parser.parse(query); ASSERT_TRUE(result.ok()) << result.status(); } diff --git a/src/parser/test/ScannerTest.cpp b/src/parser/test/ScannerTest.cpp index 602e3fc56bb..282e68c0fed 100644 --- a/src/parser/test/ScannerTest.cpp +++ b/src/parser/test/ScannerTest.cpp @@ -448,7 +448,7 @@ TEST(Scanner, Basic) { CHECK_SEMANTIC_VALUE("9223372036854775807", TokenType::INTEGER, 9223372036854775807L), CHECK_SEMANTIC_VALUE("001777777777777777777777", TokenType::INTEGER, 001777777777777777777777), - CHECK_LEXICAL_ERROR("9223372036854775808"), + CHECK_LEXICAL_ERROR("9223372036854775809"), CHECK_LEXICAL_ERROR("0xFFFFFFFFFFFFFFFFF"), CHECK_LEXICAL_ERROR("002777777777777777777777"), // TODO(dutor) It's too tedious to paste an overflowed double number here,