imbajin commented on code in PR #3209:
URL: https://github.com/apache/hugegraph/pull/3209#discussion_r4081391256


##########
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/query/ConditionQuery.java:
##########
@@ -947,6 +947,11 @@ private static boolean numberEquals(Object number1, Object 
number2) {
             // Otherwise convert to BigDecimal to make two numbers comparable
             Number n1 = NumericUtil.convertToNumber(number1);
             Number n2 = NumericUtil.convertToNumber(number2);
+            if (n1 instanceof BigDecimal || n2 instanceof BigDecimal) {

Review Comment:
   ‼️ This exact-comparison change does not preserve DECIMAL conditions sent to 
HStore. ConditionQuery.bytes() serializes BigDecimal as an untyped JSON number, 
and QueryAdapter only writes a value class for Java primitive wrappers; the 
Store's ConditionQuery.fromBytes() therefore reads the filter value as Double. 
HStore's FilterIterator then compares against the BigDecimal property using 
this rounded value, so equality and range queries for high-precision DECIMAL 
values can miss or misclassify matches. Preserve BigDecimal type and precision 
in the Store query wire format and its struct-side filter path.



##########
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/type/define/DataType.java:
##########
@@ -143,6 +156,65 @@ public <V> Number valueToNumber(V value) {
         return number;
     }
 
+    /**
+     * Convert a value to BigDecimal: BigDecimal as is, any other Number and a
+     * decimal string through their exact decimal representation. Float and
+     * Double go through Number.toString(), i.e. the shortest string that
+     * round-trips the binary value, so a client that already holds a lossy
+     * double gets that double, exactly.
+     *
+     * @return the BigDecimal, or null if the value is not a Number or String
+     * @throws IllegalArgumentException if the string is not a decimal number
+     */
+    /*
+     * Bounds for a DECIMAL value: at most DECIMAL_MAX_PRECISION significant
+     * digits and an absolute scale of at most DECIMAL_MAX_SCALE. uint256
+     * with 18 fraction digits is 96 digits, so both fit with room to spare,
+     * while "1E+999999999" (a few bytes on disk, a billion characters from
+     * toPlainString() on every read) is rejected before it is stored.
+     */
+    public static final int DECIMAL_MAX_PRECISION = 128;
+    public static final int DECIMAL_MAX_SCALE = 128;
+
+    public <V> BigDecimal valueToDecimal(V value) {
+        if (!this.isDecimal()) {
+            return null;
+        }
+        BigDecimal decimal;
+        if (value instanceof BigDecimal) {
+            decimal = (BigDecimal) value;
+        } else if (value instanceof BigInteger) {
+            decimal = new BigDecimal((BigInteger) value);
+        } else if (value instanceof Byte || value instanceof Short ||
+                   value instanceof Integer || value instanceof Long) {
+            decimal = BigDecimal.valueOf(((Number) value).longValue());
+        } else if (!(value instanceof Number) && !(value instanceof String)) {
+            return null;
+        } else {
+            String text = value.toString().trim();
+            try {
+                decimal = new BigDecimal(text);
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException(String.format(
+                        "Can't read '%s' as decimal", value));
+            }
+        }
+        return checkDecimalBounds(decimal);
+    }
+
+    public static BigDecimal checkDecimalBounds(BigDecimal decimal) {
+        int scale = Math.abs(decimal.scale());

Review Comment:
   ⚠️ This scale check is bypassed when decimal.scale() is Integer.MIN_VALUE 
because Math.abs(Integer.MIN_VALUE) overflows and remains negative; a 
precision-1 BigDecimal then passes the ±128 bound. PropertyKey deliberately 
routes ready-made BigDecimal values through this check, and BytesBuffer 
persists the scale, so a later toPlainString response can trigger an enormous 
allocation. Compare scale directly with -DECIMAL_MAX_SCALE and 
DECIMAL_MAX_SCALE in both the server and struct copies.



##########
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/io/HugeGraphSONModule.java:
##########
@@ -956,4 +961,56 @@ public Blob deserialize(JsonParser jsonParser,
             return Blob.wrap(bytes);
         }
     }
+
+    private static class BigDecimalSerializer extends 
StdSerializer<BigDecimal> {
+
+        public BigDecimalSerializer() {
+            super(BigDecimal.class);
+        }
+
+        @Override
+        public void serialize(BigDecimal decimal, JsonGenerator jsonGenerator,
+                              SerializerProvider provider) throws IOException {
+            jsonGenerator.writeString(decimal.toPlainString());

Review Comment:
   ⚠️ The new GraphSON deserializer accepts BigDecimal values that bypass the 
property bounds, while this serializer expands every BigDecimal with 
toPlainString(). A Gremlin result such as new BigDecimal("1E+999999999") can 
therefore request about a billion output characters and exhaust a response 
worker's memory. Apply a safe bound to generic GraphSON BigDecimals or use an 
exact string representation that does not expand the exponent.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to