diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2022-06-15 21:20:11 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-06-16 02:21:54 +0000 |
commit | 33d893ff83c2873202628e6002a7e35b5b296db8 (patch) | |
tree | a14be19387a333d89b587c9a0c406d7ea9b71f8c /src/mongo/db/pipeline | |
parent | 86c763b7e96f5c8e990387e29dd63a6c702076b4 (diff) | |
download | mongo-33d893ff83c2873202628e6002a7e35b5b296db8.tar.gz |
SERVER-66724 Create FLE 2 Equality Match Expression
Diffstat (limited to 'src/mongo/db/pipeline')
-rw-r--r-- | src/mongo/db/pipeline/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/db/pipeline/abt/agg_expression_visitor.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 121 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.h | 33 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_parser.idl | 57 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_test.cpp | 237 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_visitor.h | 3 |
7 files changed, 458 insertions, 0 deletions
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript index ff2c639db8b..96c7d59a025 100644 --- a/src/mongo/db/pipeline/SConscript +++ b/src/mongo/db/pipeline/SConscript @@ -99,6 +99,7 @@ env.Library( 'expression_context.cpp', 'expression_function.cpp', 'expression_js_emit.cpp', + 'expression_parser.idl', 'expression_test_api_version.cpp', 'expression_trigonometric.cpp', 'javascript_execution.cpp', @@ -106,6 +107,7 @@ env.Library( 'variables.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/crypto/fle_crypto', '$BUILD_DIR/mongo/db/bson/dotted_path_support', '$BUILD_DIR/mongo/db/commands/test_commands_enabled', '$BUILD_DIR/mongo/db/exec/document_value/document_value', @@ -128,6 +130,7 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/mongohasher', '$BUILD_DIR/mongo/db/vector_clock', + '$BUILD_DIR/mongo/idl/idl_parser', ], ) diff --git a/src/mongo/db/pipeline/abt/agg_expression_visitor.cpp b/src/mongo/db/pipeline/abt/agg_expression_visitor.cpp index 20552e85599..05b9fff8932 100644 --- a/src/mongo/db/pipeline/abt/agg_expression_visitor.cpp +++ b/src/mongo/db/pipeline/abt/agg_expression_visitor.cpp @@ -322,6 +322,10 @@ public: unsupportedExpression(expr->getOpName()); } + void visit(const ExpressionInternalFLEEqual* expr) override final { + unsupportedExpression(expr->getOpName()); + } + void visit(const ExpressionMap* expr) override final { unsupportedExpression("$map"); } diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 12e1ddf2ec3..464d2ad6953 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -39,6 +39,9 @@ #include <utility> #include <vector> +#include "mongo/bson/bsonmisc.h" +#include "mongo/bson/bsontypes.h" +#include "mongo/crypto/fle_crypto.h" #include "mongo/db/bson/dotted_path_support.h" #include "mongo/db/commands/feature_compatibility_version_documentation.h" #include "mongo/db/exec/document_value/document.h" @@ -46,6 +49,7 @@ #include "mongo/db/hasher.h" #include "mongo/db/jsobj.h" #include "mongo/db/pipeline/expression_context.h" +#include "mongo/db/pipeline/expression_parser_gen.h" #include "mongo/db/pipeline/variable_validation.h" #include "mongo/db/query/datetime/date_time_support.h" #include "mongo/db/query/sort_pattern.h" @@ -3804,6 +3808,123 @@ const char* ExpressionLog10::getOpName() const { return "$log10"; } +/* ----------------------- ExpressionInternalFLEEqual ---------------------------- */ +constexpr auto kInternalFleEq = "$_internalFleEq"_sd; + +ExpressionInternalFLEEqual::ExpressionInternalFLEEqual(ExpressionContext* const expCtx, + boost::intrusive_ptr<Expression> field, + ConstDataRange serverToken, + int64_t contentionFactor, + ConstDataRange edcToken) + : Expression(expCtx, {std::move(field)}), + _serverToken(PrfBlockfromCDR(serverToken)), + _edcToken(PrfBlockfromCDR(edcToken)), + _contentionFactor(contentionFactor) { + expCtx->sbeCompatible = false; + + auto tokens = + EDCServerCollection::generateEDCTokens(ConstDataRange(_edcToken), _contentionFactor); + + for (auto& token : tokens) { + _cachedEDCTokens.insert(std::move(token.data)); + } +} + +void ExpressionInternalFLEEqual::_doAddDependencies(DepsTracker* deps) const { + for (auto&& operand : _children) { + operand->addDependencies(deps); + } +} + +REGISTER_EXPRESSION_WITH_MIN_VERSION(_internalFleEq, + ExpressionInternalFLEEqual::parse, + AllowedWithApiStrict::kAlways, + AllowedWithClientType::kAny, + multiversion::FeatureCompatibilityVersion::kVersion_6_0); + +intrusive_ptr<Expression> ExpressionInternalFLEEqual::parse(ExpressionContext* const expCtx, + BSONElement expr, + const VariablesParseState& vps) { + + IDLParserErrorContext ctx(kInternalFleEq); + auto fleEq = InternalFleEqStruct::parse(ctx, expr.Obj()); + + auto fieldExpr = Expression::parseOperand(expCtx, fleEq.getField().getElement(), vps); + + auto serverTokenPair = fromEncryptedConstDataRange(fleEq.getServerEncryptionToken()); + + uassert(6672405, + "Invalid server token", + serverTokenPair.first == EncryptedBinDataType::kFLE2TransientRaw && + serverTokenPair.second.length() == sizeof(PrfBlock)); + + auto edcTokenPair = fromEncryptedConstDataRange(fleEq.getEdcDerivedToken()); + + uassert(6672406, + "Invalid edc token", + edcTokenPair.first == EncryptedBinDataType::kFLE2TransientRaw && + edcTokenPair.second.length() == sizeof(PrfBlock)); + + + auto cf = fleEq.getMaxCounter(); + uassert(6672408, "Contention factor must be between 0 and 10000", cf >= 0 && cf < 10000); + + return new ExpressionInternalFLEEqual(expCtx, + std::move(fieldExpr), + serverTokenPair.second, + fleEq.getMaxCounter(), + edcTokenPair.second); +} + +Value toValue(const std::array<std::uint8_t, 32>& buf) { + auto vec = toEncryptedVector(EncryptedBinDataType::kFLE2TransientRaw, buf); + return Value(BSONBinData(vec.data(), vec.size(), BinDataType::Encrypt)); +} + +Value ExpressionInternalFLEEqual::serialize(bool explain) const { + return Value(Document{{kInternalFleEq, + Document{{"field", _children[0]->serialize(explain)}, + {"edc", toValue(_edcToken)}, + {"counter", Value(static_cast<long long>(_contentionFactor))}, + {"server", toValue(_serverToken)}}}}); +} + +Value ExpressionInternalFLEEqual::evaluate(const Document& root, Variables* variables) const { + // Inputs + // 1. Value for FLE2IndexedEqualityEncryptedValue field + + Value fieldValue = _children[0]->evaluate(root, variables); + + if (fieldValue.nullish()) { + return Value(BSONNULL); + } + + if (fieldValue.getType() != BinData) { + return Value(false); + } + + auto fieldValuePair = fromEncryptedBinData(fieldValue); + + uassert(6672407, + "Invalid encrypted indexed field", + fieldValuePair.first == EncryptedBinDataType::kFLE2EqualityIndexedValue); + + // Value matches if + // 1. Decrypt field is successful + // 2. EDC_u Token is in GenTokens(EDC Token, ContentionFactor) + // + auto swIndexed = + EDCServerCollection::decryptAndParse(ConstDataRange(_serverToken), fieldValuePair.second); + uassertStatusOK(swIndexed); + auto indexed = swIndexed.getValue(); + + return Value(_cachedEDCTokens.count(indexed.edc.data) == 1); +} + +const char* ExpressionInternalFLEEqual::getOpName() const { + return kInternalFleEq.rawData(); +} + /* ------------------------ ExpressionNary ----------------------------- */ /** diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h index ff53eaedf3e..4b5745bb2b6 100644 --- a/src/mongo/db/pipeline/expression.h +++ b/src/mongo/db/pipeline/expression.h @@ -29,6 +29,7 @@ #pragma once +#include "mongo/base/data_range.h" #include "mongo/platform/basic.h" #include <algorithm> @@ -2197,6 +2198,38 @@ public: } }; +class ExpressionInternalFLEEqual final : public Expression { +public: + ExpressionInternalFLEEqual(ExpressionContext* expCtx, + boost::intrusive_ptr<Expression> field, + ConstDataRange serverToken, + int64_t contentionFactor, + ConstDataRange edcToken); + Value serialize(bool explain) const final; + + Value evaluate(const Document& root, Variables* variables) const final; + const char* getOpName() const; + + static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx, + BSONElement expr, + const VariablesParseState& vps); + void _doAddDependencies(DepsTracker* deps) const final; + + void acceptVisitor(ExpressionMutableVisitor* visitor) final { + return visitor->visit(this); + } + + void acceptVisitor(ExpressionConstVisitor* visitor) const final { + return visitor->visit(this); + } + +private: + std::array<std::uint8_t, 32> _serverToken; + std::array<std::uint8_t, 32> _edcToken; + int64_t _contentionFactor; + stdx::unordered_set<std::array<std::uint8_t, 32>> _cachedEDCTokens; +}; + class ExpressionMap final : public Expression { public: ExpressionMap( diff --git a/src/mongo/db/pipeline/expression_parser.idl b/src/mongo/db/pipeline/expression_parser.idl new file mode 100644 index 00000000000..9f1cde70856 --- /dev/null +++ b/src/mongo/db/pipeline/expression_parser.idl @@ -0,0 +1,57 @@ +# Copyright (C) 2022-present MongoDB, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Server Side Public License, version 1, +# as published by MongoDB, Inc. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Server Side Public License for more details. +# +# You should have received a copy of the Server Side Public License +# along with this program. If not, see +# <http://www.mongodb.com/licensing/server-side-public-license>. +# +# As a special exception, the copyright holders give permission to link the +# code of portions of this program with the OpenSSL library under certain +# conditions as described in each individual source file and distribute +# linked combinations including the program with the OpenSSL library. You +# must comply with the Server Side Public License in all respects for +# all of the code used other than as permitted herein. If you modify file(s) +# with this exception, you may extend this exception to your version of the +# file(s), but you are not obligated to do so. If you do not wish to do so, +# delete this exception statement from your version. If you delete this +# exception statement from all source files in the program, then also delete +# it in the license file. + +global: + cpp_namespace: "mongo" + +imports: + - "mongo/idl/basic_types.idl" + +structs: + + InternalFleEqStruct: + description: "Struct for $_internalFleEq" + strict: true + fields: + field: + description: "Expression" + type: IDLAnyType + cpp_name: field + edc: + description: "EDCDerivedFromDataToken" + type: bindata_encrypt + cpp_name: edcDerivedToken + server: + description: "ServerDataEncryptionLevel1Token" + type: bindata_encrypt + cpp_name: serverEncryptionToken + counter: + description: "Queryable Encryption max counter" + type: long + cpp_name: maxCounter + + diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index a33de77322c..314062c3f03 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -175,6 +175,7 @@ void parseAndVerifyResults( ASSERT_VALUE_EQ(expr->evaluate({}, &expCtx.variables), expected); } + /* ------------------------- ExpressionArrayToObject -------------------------- */ TEST(ExpressionArrayToObjectTest, KVFormatSimple) { @@ -3930,4 +3931,240 @@ TEST(ExpressionAddTest, VerifyNoDoubleDoubleSummation) { ASSERT_VALUE_EQ(result, Value(straightSum)); ASSERT_VALUE_NE(result, Value(compensatedSum.getDouble())); } +TEST(ExpressionFLETest, BadInputs) { + + auto expCtx = ExpressionContextForTest(); + auto vps = expCtx.variablesParseState; + { + auto expr = fromjson("{$_internalFleEq: 12}"); + ASSERT_THROWS_CODE(ExpressionInternalFLEEqual::parse(&expCtx, expr.firstElement(), vps), + DBException, + 10065); + } +} + +// Test we return true if it matches +TEST(ExpressionFLETest, TestBinData) { + auto expCtx = ExpressionContextForTest(); + auto vps = expCtx.variablesParseState; + + { + auto expr = fromjson(R"({$_internalFleEq: { + field: { + "$binary": { + "base64": + "BxI0VngSNJh2EjQSNFZ4kBIQ0JE8aMUFkPk5sSTVqfdNNfjqUfQQ1Uoj0BBcthrWoe9wyU3cN6zmWaQBPJ97t0ZPbecnMsU736yXre6cBO4Zdt/wThtY+v5+7vFgNnWpgRP0e+vam6QPmLvbBrO0LdsvAPTGW4yqwnzCIXCoEg7QPGfbfAXKPDTNenBfRlawiblmTOhO/6ljKotWsMp22q/rpHrn9IEIeJmecwuuPIJ7EA+XYQ3hOKVccYf2ogoK73+8xD/Vul83Qvr84Q8afc4QUMVs8A==", + "subType": "6" + } + }, + server: { + "$binary": { + "base64": "COuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/Ps", + "subType": "6" + } + }, + counter: { + "$numberLong": "3" + }, + edc: { + "$binary": { + "base64": "CEWSmQID7SfwyAUI3ZkSFkATKryDQfnxXEOGad5d4Rsg", + "subType": "6" + } + } } })"); + auto exprFle = ExpressionInternalFLEEqual::parse(&expCtx, expr.firstElement(), vps); + + ASSERT_VALUE_EQ(exprFle->evaluate({}, &expCtx.variables), Value(true)); + } + + // Negative: Use wrong server token + { + auto expr = fromjson(R"({$_internalFleEq: { + field: { + "$binary": { + "base64": + "BxI0VngSNJh2EjQSNFZ4kBIQ0JE8aMUFkPk5sSTVqfdNNfjqUfQQ1Uoj0BBcthrWoe9wyU3cN6zmWaQBPJ97t0ZPbecnMsU736yXre6cBO4Zdt/wThtY+v5+7vFgNnWpgRP0e+vam6QPmLvbBrO0LdsvAPTGW4yqwnzCIXCoEg7QPGfbfAXKPDTNenBfRlawiblmTOhO/6ljKotWsMp22q/rpHrn9IEIeJmecwuuPIJ7EA+XYQ3hOKVccYf2ogoK73+8xD/Vul83Qvr84Q8afc4QUMVs8A==", + "subType": "6" + } + }, + server: { + "$binary": { + "base64": "COuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/Ps", + "subType": "6" + } + }, + counter: { + "$numberLong": "3" + }, + edc: { + "$binary": { + "base64": "CEWSMQID7SFWYAUI3ZKSFKATKRYDQFNXXEOGAD5D4RSG", + "subType": "6" + } + } } })"); + auto exprFle = ExpressionInternalFLEEqual::parse(&expCtx, expr.firstElement(), vps); + + ASSERT_VALUE_EQ(exprFle->evaluate({}, &expCtx.variables), Value(false)); + } + + // Negative: Use wrong edc token + { + auto expr = fromjson(R"({$_internalFleEq: { + field: { + "$binary": { + "base64": + "BxI0VngSNJh2EjQSNFZ4kBIQ0JE8aMUFkPk5sSTVqfdNNfjqUfQQ1Uoj0BBcthrWoe9wyU3cN6zmWaQBPJ97t0ZPbecnMsU736yXre6cBO4Zdt/wThtY+v5+7vFgNnWpgRP0e+vam6QPmLvbBrO0LdsvAPTGW4yqwnzCIXCoEg7QPGfbfAXKPDTNenBfRlawiblmTOhO/6ljKotWsMp22q/rpHrn9IEIeJmecwuuPIJ7EA+XYQ3hOKVccYf2ogoK73+8xD/Vul83Qvr84Q8afc4QUMVs8A==", + "subType": "6" + } + }, + server: { + "$binary": { + "base64": "COUAC/ERLYAKKX6B0VZ1R3QODOQFFJQJD+XLGIPU4/PS", + "subType": "6" + } + }, + counter: { + "$numberLong": "3" + }, + edc: { + "$binary": { + "base64": "CEWSmQID7SfwyAUI3ZkSFkATKryDQfnxXEOGad5d4Rsg", + "subType": "6" + } + } } })"); + auto exprFle = ExpressionInternalFLEEqual::parse(&expCtx, expr.firstElement(), vps); + + ASSERT_THROWS_CODE( + exprFle->evaluate({}, &expCtx.variables), DBException, ErrorCodes::Overflow); + } +} + +TEST(ExpressionFLETest, TestBinData_ContentionFactor) { + auto expCtx = ExpressionContextForTest(); + auto vps = expCtx.variablesParseState; + + // Use the wrong contention factor - 0 + { + auto expr = fromjson(R"({$_internalFleEq: { + field: { + "$binary": { + "base64": + "BxI0VngSNJh2EjQSNFZ4kBIQ5+Wa5+SZafJeRUDGdLNx+i2ADDkyV2qA90Xcve7FqltoDm1PllSSgUS4fYtw3XDjzoNZrFFg8LfG2wH0HYbLMswv681KJpmEw7+RXy4CcPVFgoRFt24N13p7jT+pqu2oQAHAoxYTy/TsiAyY4RnAMiXYGg3hWz4AO/WxHNSyq6B6kX5d7x/hrXvppsZDc2Pmhd+c5xmovlv5RPj7wnNld13kYcMluztjNswiCH05hM/kp2/P7kw30iVnbz0SZxn1FjjCug==", + "subType": "6" + } + }, + server: { + "$binary": { + "base64": "COuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/Ps", + "subType": "6" + } + }, + counter: { + "$numberLong": "0" + }, + edc: { + "$binary": { + "base64": "CEWSmQID7SfwyAUI3ZkSFkATKryDQfnxXEOGad5d4Rsg", + "subType": "6" + } + } } })"); + auto exprFle = ExpressionInternalFLEEqual::parse(&expCtx, expr.firstElement(), vps); + + ASSERT_VALUE_EQ(exprFle->evaluate({}, &expCtx.variables), Value(false)); + } + + // Use the right contention factor - 50 + { + auto expr = fromjson(R"({$_internalFleEq: { + field: { + "$binary": { + "base64": +"BxI0VngSNJh2EjQSNFZ4kBIQ5+Wa5+SZafJeRUDGdLNx+i2ADDkyV2qA90Xcve7FqltoDm1PllSSgUS4fYtw3XDjzoNZrFFg8LfG2wH0HYbLMswv681KJpmEw7+RXy4CcPVFgoRFt24N13p7jT+pqu2oQAHAoxYTy/TsiAyY4RnAMiXYGg3hWz4AO/WxHNSyq6B6kX5d7x/hrXvppsZDc2Pmhd+c5xmovlv5RPj7wnNld13kYcMluztjNswiCH05hM/kp2/P7kw30iVnbz0SZxn1FjjCug==", + "subType": "6" + } + }, + server: { + "$binary": { + "base64": "COuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/Ps", + "subType": "6" + } + }, + counter: { + "$numberLong": "50" + }, + edc: { + "$binary": { + "base64": "CEWSmQID7SfwyAUI3ZkSFkATKryDQfnxXEOGad5d4Rsg", + "subType": "6" + } + } } })"); + auto exprFle = ExpressionInternalFLEEqual::parse(&expCtx, expr.firstElement(), vps); + + ASSERT_VALUE_EQ(exprFle->evaluate({}, &expCtx.variables), Value(true)); + } +} + +TEST(ExpressionFLETest, TestBinData_RoundTrip) { + auto expCtx = ExpressionContextForTest(); + auto vps = expCtx.variablesParseState; + + auto expr = fromjson(R"({$_internalFleEq: { + field: { + "$binary": { + "base64": + "BxI0VngSNJh2EjQSNFZ4kBIQ0JE8aMUFkPk5sSTVqfdNNfjqUfQQ1Uoj0BBcthrWoe9wyU3cN6zmWaQBPJ97t0ZPbecnMsU736yXre6cBO4Zdt/wThtY+v5+7vFgNnWpgRP0e+vam6QPmLvbBrO0LdsvAPTGW4yqwnzCIXCoEg7QPGfbfAXKPDTNenBfRlawiblmTOhO/6ljKotWsMp22q/rpHrn9IEIeJmecwuuPIJ7EA+XYQ3hOKVccYf2ogoK73+8xD/Vul83Qvr84Q8afc4QUMVs8A==", + "subType": "6" + } + }, + server: { + "$binary": { + "base64": "COuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/Ps", + "subType": "6" + } + }, + counter: { + "$numberLong": "3" + }, + edc: { + "$binary": { + "base64": "CEWSmQID7SfwyAUI3ZkSFkATKryDQfnxXEOGad5d4Rsg", + "subType": "6" + } + } } })"); + auto exprFle = ExpressionInternalFLEEqual::parse(&expCtx, expr.firstElement(), vps); + + ASSERT_VALUE_EQ(exprFle->evaluate({}, &expCtx.variables), Value(true)); + + // Verify it round trips + auto value = exprFle->serialize(false); + + auto roundTripExpr = fromjson(R"({$_internalFleEq: { + field: { + "$const" : { "$binary": { + "base64": + "BxI0VngSNJh2EjQSNFZ4kBIQ0JE8aMUFkPk5sSTVqfdNNfjqUfQQ1Uoj0BBcthrWoe9wyU3cN6zmWaQBPJ97t0ZPbecnMsU736yXre6cBO4Zdt/wThtY+v5+7vFgNnWpgRP0e+vam6QPmLvbBrO0LdsvAPTGW4yqwnzCIXCoEg7QPGfbfAXKPDTNenBfRlawiblmTOhO/6ljKotWsMp22q/rpHrn9IEIeJmecwuuPIJ7EA+XYQ3hOKVccYf2ogoK73+8xD/Vul83Qvr84Q8afc4QUMVs8A==", + "subType": "6" + }} + }, + edc: { + "$binary": { + "base64": "CEWSmQID7SfwyAUI3ZkSFkATKryDQfnxXEOGad5d4Rsg", + "subType": "6" + } + }, + counter: { + "$numberLong": "3" + }, + server: { + "$binary": { + "base64": "COuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/Ps", + "subType": "6" + } + } + } })"); + + + ASSERT_BSONOBJ_EQ(value.getDocument().toBson(), roundTripExpr); +} + } // namespace ExpressionTests diff --git a/src/mongo/db/pipeline/expression_visitor.h b/src/mongo/db/pipeline/expression_visitor.h index 46ad3ee6295..6b7c4fc4cdd 100644 --- a/src/mongo/db/pipeline/expression_visitor.h +++ b/src/mongo/db/pipeline/expression_visitor.h @@ -153,6 +153,7 @@ class ExpressionHyperbolicSine; class ExpressionInternalFindSlice; class ExpressionInternalFindPositional; class ExpressionInternalFindElemMatch; +class ExpressionInternalFLEEqual; class ExpressionInternalJsEmit; class ExpressionFunction; class ExpressionDegreesToRadians; @@ -245,6 +246,7 @@ public: virtual void visit(expression_walker::MaybeConstPtr<IsConst, ExpressionLn>) = 0; virtual void visit(expression_walker::MaybeConstPtr<IsConst, ExpressionLog>) = 0; virtual void visit(expression_walker::MaybeConstPtr<IsConst, ExpressionLog10>) = 0; + virtual void visit(expression_walker::MaybeConstPtr<IsConst, ExpressionInternalFLEEqual>) = 0; virtual void visit(expression_walker::MaybeConstPtr<IsConst, ExpressionMap>) = 0; virtual void visit(expression_walker::MaybeConstPtr<IsConst, ExpressionMeta>) = 0; virtual void visit(expression_walker::MaybeConstPtr<IsConst, ExpressionMod>) = 0; @@ -424,6 +426,7 @@ struct SelectiveConstExpressionVisitorBase : public ExpressionConstVisitor { void visit(const ExpressionLn*) override {} void visit(const ExpressionLog*) override {} void visit(const ExpressionLog10*) override {} + void visit(const ExpressionInternalFLEEqual*) override {} void visit(const ExpressionMap*) override {} void visit(const ExpressionMeta*) override {} void visit(const ExpressionMod*) override {} |