From 0d9455e8ee5ed764aee6a514d789ae1b5ed0af03 Mon Sep 17 00:00:00 2001 From: Shreyas Kalyan Date: Wed, 19 Oct 2022 18:45:29 -0400 Subject: SERVER-69671 FLE2 range float precision: update createCollection to enforce parameter constraints --- src/mongo/crypto/encryption_fields.idl | 8 ++++++ src/mongo/crypto/encryption_fields_validation.cpp | 35 ++++++++++++++++------- src/mongo/crypto/fle_crypto_test.cpp | 8 +++--- src/mongo/crypto/fle_field_schema.idl | 8 ++++++ src/mongo/crypto/fle_fields_util.cpp | 7 +++++ 5 files changed, 51 insertions(+), 15 deletions(-) (limited to 'src/mongo/crypto') diff --git a/src/mongo/crypto/encryption_fields.idl b/src/mongo/crypto/encryption_fields.idl index 567fb1b3237..5eb13c5d700 100644 --- a/src/mongo/crypto/encryption_fields.idl +++ b/src/mongo/crypto/encryption_fields.idl @@ -93,6 +93,14 @@ structs: optional: true stability: unstable validator: { gte: 1, lte: 4 } + precision: + description: >- + Defines the number of digits after the decimal point for floating point numbers. + Only applicable to floating point numbers and for queryType range. + type: safeInt + optional: true + stability: unstable + validator: { gte: 0 } EncryptedField: description: "Information about encrypted fields" diff --git a/src/mongo/crypto/encryption_fields_validation.cpp b/src/mongo/crypto/encryption_fields_validation.cpp index a0debee4a89..3c370136fb3 100644 --- a/src/mongo/crypto/encryption_fields_validation.cpp +++ b/src/mongo/crypto/encryption_fields_validation.cpp @@ -77,7 +77,7 @@ Value coerceValueToRangeIndexTypes(Value val, BSONType fieldType) { } -void validateRangeIndex(BSONType fieldType, const QueryTypeConfig& query) { +void validateRangeIndex(BSONType fieldType, QueryTypeConfig& query) { uassert(6775201, str::stream() << "Type '" << typeName(fieldType) << "' is not a supported range indexed type", @@ -90,18 +90,31 @@ void validateRangeIndex(BSONType fieldType, const QueryTypeConfig& query) { "The field 'sparsity' must be between 1 and 4", query.getSparsity().value() >= 1 && query.getSparsity().value() <= 4); + switch (fieldType) { case NumberDouble: - case NumberDecimal: - uassert(7006601, - "The field 'min' on floating point field is invalid for range index over " - "double/decimal fields.", - !query.getMin().has_value()); - uassert(7006602, - "The field 'max' on floating point field is invalid for range index over " - "double/decimal fields.", - !query.getMax().has_value()); - break; + case NumberDecimal: { + if (!((query.getMin().has_value() == query.getMax().has_value()) && + (query.getMin().has_value() == query.getPrecision().has_value()))) { + uasserted(6967100, + str::stream() << "Precision, min, and max must all be specified " + << "together for floating point fields"); + } + + if (!query.getMin().has_value()) { + if (fieldType == NumberDouble) { + query.setMin(mongo::Value(std::numeric_limits::min())); + query.setMax(mongo::Value(std::numeric_limits::max())); + } else { + query.setMin(mongo::Value(Decimal128::kLargestNegative)); + query.setMax(mongo::Value(Decimal128::kLargestPositive)); + } + } + } + // We want to perform the same validation after sanitizing floating + // point parameters, so we call FMT_FALLTHROUGH here. + + FMT_FALLTHROUGH; case NumberInt: case NumberLong: case Date: { diff --git a/src/mongo/crypto/fle_crypto_test.cpp b/src/mongo/crypto/fle_crypto_test.cpp index e99f4c854b9..c47f6e54e21 100644 --- a/src/mongo/crypto/fle_crypto_test.cpp +++ b/src/mongo/crypto/fle_crypto_test.cpp @@ -731,16 +731,16 @@ std::vector generatePlaceholder( upperDoc = BSON("ub" << 1234567890123456789LL); break; case BSONType::NumberDouble: - lowerDoc = BSON("lb" << std::numeric_limits::min()); - upperDoc = BSON("ub" << std::numeric_limits::max()); + lowerDoc = BSON("lb" << 0.0); + upperDoc = BSON("ub" << 1234567890123456789.0); break; case BSONType::Date: lowerDoc = BSON("lb" << Date_t::fromMillisSinceEpoch(0)); upperDoc = BSON("ub" << Date_t::fromMillisSinceEpoch(1234567890123456789LL)); break; case BSONType::NumberDecimal: - lowerDoc = BSON("lb" << Decimal128::kLargestNegative); - upperDoc = BSON("ub" << Decimal128::kLargestPositive); + lowerDoc = BSON("lb" << Decimal128(0)); + upperDoc = BSON("ub" << Decimal128(1234567890123456789LL)); break; default: LOGV2_WARNING(6775520, diff --git a/src/mongo/crypto/fle_field_schema.idl b/src/mongo/crypto/fle_field_schema.idl index 65b274dd3e7..918344b7e17 100644 --- a/src/mongo/crypto/fle_field_schema.idl +++ b/src/mongo/crypto/fle_field_schema.idl @@ -389,6 +389,10 @@ structs: ubIncluded: description: "Indicates if the upper bound should be included in the range." type: bool + precision: + description: "Determines the number of digits after the decimal point for floating point values" + type: safeInt + optional: true indexMin: description: "Minimum value for the encrypted index that this query is using." type: encrypted_numeric_element @@ -436,3 +440,7 @@ structs: type: encrypted_numeric_element optional: true cpp_name: maxBound + precision: + description: "Determines the number of digits after the decimal point for floating point values" + type: safeInt + optional: true diff --git a/src/mongo/crypto/fle_fields_util.cpp b/src/mongo/crypto/fle_fields_util.cpp index 8887645100f..02653266b8a 100644 --- a/src/mongo/crypto/fle_fields_util.cpp +++ b/src/mongo/crypto/fle_fields_util.cpp @@ -146,6 +146,13 @@ void validateIDLFLE2RangeFindSpec(const FLE2RangeFindSpec* placeholder) { auto min = edgesInfo.getIndexMin().getElement(); auto max = edgesInfo.getIndexMax().getElement(); uassert(6901304, "Range min and range max must be the same type.", min.type() == max.type()); + + if (edgesInfo.getPrecision().has_value()) { + uassert(6967102, + "Precision can only be set if type is floating point", + min.type() == BSONType::NumberDecimal || min.type() == BSONType::NumberDouble); + } + auto lb = edgesInfo.getLowerBound().getElement(); auto ub = edgesInfo.getUpperBound().getElement(); validateQueryBounds(min.type(), lb, ub); -- cgit v1.2.1