diff --git a/docs/zh/quickstart/sdk/rest_api.md b/docs/zh/quickstart/sdk/rest_api.md index 0d9f3009dee..0a225e444f6 100644 --- a/docs/zh/quickstart/sdk/rest_api.md +++ b/docs/zh/quickstart/sdk/rest_api.md @@ -9,11 +9,13 @@ 与APIServer的交互中,请求体均为JSON格式,并支持一定的扩展格式。注意以下几点: +- 传入超过整型或浮点数最大值的数值,将会解析失败,比如,double类型传入`1e1000`。 - 非数值浮点数:在传入数据时,支持传入`NaN`、`Infinity`、`-Infinity`,与缩写`Inf`、`-Inf`(注意是unquoted的,并非字符串,也不支持其他变种写法)。在返回数据时,支持返回`NaN`、`Infinity`、`-Infinity`(不支持变种写法)。如果你需要将三者转换为null,可以配置 `write_nan_and_inf_null`。 - 可以传入整型数字到浮点数,比如,`1`可被读取为double。 +- float浮点数可能有精度损失,比如,`0.3`读取后将不会严格等于`0.3`,而是`0.30000000000000004`。我们不拒绝精度损失,请从业务层面考虑是否需要对此进行处理。传入超过float max但不超过double max的值,在读取后将成为`Inf`。 - `true/false`、`null`并不支持大写,只支持小写。 - timestamp类型暂不支持传入年月日字符串,只支持传入数值,比如`1635247427000`。 -- date类型请传入年月日字符串,中间不要包含任何空格。 +- date类型请传入**年月日字符串**,中间不要包含任何空格。 ## 数据插入 diff --git a/src/apiserver/api_server_impl.cc b/src/apiserver/api_server_impl.cc index d6e2bbb8204..98d7183b9cf 100644 --- a/src/apiserver/api_server_impl.cc +++ b/src/apiserver/api_server_impl.cc @@ -240,13 +240,14 @@ bool APIServerImpl::AppendJsonValue(const Value& v, hybridse::sdk::DataType type return row->AppendInt64(v.GetInt64()); } case hybridse::sdk::kTypeFloat: { - if (!v.IsNumber()) { // relax check, int can get as double + if (!v.IsNumber()) { // relax check, int can get as double and support set float NaN&Inf return false; } - return row->AppendFloat(boost::lexical_cast(v.GetDouble())); + // IEEE 754 arithmetic allows cast nan/inf to float + return row->AppendFloat(v.GetFloat()); } case hybridse::sdk::kTypeDouble: { - if (!v.IsNumber()) { + if (!v.IsLosslessDouble()) { return false; } return row->AppendDouble(v.GetDouble()); @@ -347,7 +348,7 @@ void APIServerImpl::RegisterPut() { // json2doc, then generate an insert sql Document document; - if (document.Parse(req_body.to_string().c_str()).HasParseError()) { + if (document.Parse(req_body.to_string().c_str()).HasParseError()) { DLOG(INFO) << "rapidjson doc parse [" << req_body.to_string().c_str() << "] failed, code " << document.GetParseError() << ", offset " << document.GetErrorOffset(); writer << resp.Set("Json parse failed, error code: " + std::to_string(document.GetParseError())); @@ -430,8 +431,9 @@ void APIServerImpl::ExecuteProcedure(bool has_common_col, const InterfaceProvide auto db = db_it->second; auto sp = sp_it->second; + // TODO(hw): JsonReader can't set SQLRequestRow simply(cuz common_cols), use raw rapidjson here Document document; - if (document.Parse(req_body.to_string().c_str()).HasParseError()) { + if (document.Parse(req_body.to_string().c_str()).HasParseError()) { writer << resp.Set("Request body json parse failed"); return; } diff --git a/src/apiserver/api_server_test.cc b/src/apiserver/api_server_test.cc index 52374f98513..f327ff89527 100644 --- a/src/apiserver/api_server_test.cc +++ b/src/apiserver/api_server_test.cc @@ -180,6 +180,31 @@ TEST_F(APIServerTest, jsonFormat) { reader >> d_res; ASSERT_TRUE(std::isinf(d_res)); } + { + // float nan inf + // IEEE 754 arithmetic allows cast nan/inf to float, so GetFloat is fine + JsonReader reader("[NaN, Infinity, -Infinity]"); + ASSERT_TRUE(reader); + float f_res = -1.0; + reader.StartArray(); + reader >> f_res; + ASSERT_TRUE(std::isnan(f_res)); + reader >> f_res; + ASSERT_TRUE(std::isinf(f_res)); + reader >> f_res; + ASSERT_TRUE(std::isinf(f_res)); + // raw way for put and procedure(common cols) + f_res = -1.0; + rapidjson::Document document; + document.Parse("[NaN, Infinity, -Infinity]"); + document.StartArray(); + f_res = document[0].GetFloat(); + ASSERT_TRUE(std::isnan(f_res)); + f_res = document[1].GetFloat(); + ASSERT_TRUE(std::isinf(f_res)); + f_res = document[2].GetFloat(); + ASSERT_TRUE(std::isinf(f_res)); + } { // illegal words JsonReader reader("nan"); ASSERT_FALSE(reader); @@ -196,11 +221,7 @@ TEST_F(APIServerTest, jsonFormat) { ASSERT_FALSE(reader); // get double failed ASSERT_FLOAT_EQ(d, -1.0); // won't change } - // StringBuffer buffer; - // rapidjson::Writer, rapidjson::UTF8<>, rapidjson::CrtAllocator, - // rapidjson::kWriteNanAndInfFlag> writerr(buffer); - // writerr.Double(std::numeric_limits::quiet_NaN()); - // LOG(INFO) << buffer.GetString(); + // test json writer JsonWriter writer; // about double nan, inf @@ -210,7 +231,7 @@ TEST_F(APIServerTest, jsonFormat) { writer << nan; writer << inf; double ninf = -inf; -writer << ninf; + writer << ninf; writer.EndArray(); ASSERT_STREQ("[NaN,Infinity,-Infinity]", writer.GetString()); }