diff options
56 files changed, 578 insertions, 319 deletions
diff --git a/jstests/core/api_version_new_50_language_features.js b/jstests/core/api_version_new_50_language_features.js new file mode 100644 index 00000000000..a5dd6341eab --- /dev/null +++ b/jstests/core/api_version_new_50_language_features.js @@ -0,0 +1,80 @@ +/** + * Tests that language features introduced in version 4.9 or 5.0 are not included in API Version 1 + * yet. This test should be updated or removed in a future release when we have more confidence that + * the behavior and syntax is stable. + * + * @tags: [ + * requires_fcv_50, + * uses_api_parameters, + * ] + */ + +(function() { +"use strict"; + +const collName = "api_version_new_50_language_features"; +const coll = db[collName]; +coll.drop(); +assert.commandWorked(coll.insert({a: 1, date: new ISODate()})); + +const unstablePipelines = [ + [{ + $setWindowFields: { + sortBy: {_id: 1}, + output: {runningCount: {$sum: 1, window: {documents: ["unbounded", "current"]}}} + } + }], + [{$set: {x: {$dateTrunc: {date: "$date", unit: "second", binSize: 5}}}}], + [{$set: {x: {$dateAdd: {startDate: "$date", unit: "day", amount: 1}}}}], + [{$set: {x: {$dateSubtract: {startDate: "$date", unit: "day", amount: 1}}}}], + [{$set: {x: {$getField: {input: "$$ROOT", field: "x"}}}}], + [{$set: {x: {$setField: {input: "$$ROOT", field: "x", value: "foo"}}}}], + [{$set: {x: {$tsSecond: new Timestamp(0, 0)}}}], + [{$set: {x: {$tsIncrement: new Timestamp(0, 0)}}}], +]; + +function assertAggregateFailsWithAPIStrict(pipeline) { + assert.commandFailedWithCode(db.runCommand({ + aggregate: collName, + pipeline: pipeline, + cursor: {}, + apiStrict: true, + apiVersion: "1" + }), + ErrorCodes.APIStrictError, + pipeline); +} + +for (let pipeline of unstablePipelines) { + // Assert error thrown when running a pipeline with stages not in API Version 1. + assertAggregateFailsWithAPIStrict(pipeline); + + // Assert error thrown when creating a view on a pipeline with stages not in API Version 1. + assert.commandFailedWithCode(db.runCommand({ + create: 'new_50_feature_view', + viewOn: collName, + pipeline: pipeline, + apiStrict: true, + apiVersion: "1" + }), + ErrorCodes.APIStrictError, + pipeline); + + // Assert error is not thrown when running without apiStrict=true. + assert.commandWorked(db.runCommand({ + aggregate: coll.getName(), + pipeline: pipeline, + apiVersion: "1", + cursor: {}, + })); +} + +// Creating a collection with the unstable validator is not allowed with apiStrict:true. +assert.commandFailedWithCode(db.runCommand({ + create: 'new_50_features_validator', + validator: {$expr: {$eq: [{$getField: {input: "$$ROOT", field: "dotted.path"}}, 2]}}, + apiVersion: "1", + apiStrict: true +}), + ErrorCodes.APIStrictError); +})(); diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 81d69af7443..910c52a521a 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -595,7 +595,7 @@ env.Library( '$BUILD_DIR/mongo/db/auth/authprivilege', '$BUILD_DIR/mongo/db/catalog/collection_options_idl', '$BUILD_DIR/mongo/db/commands/create_command', - '$BUILD_DIR/mongo/db/query/explain_options', + '$BUILD_DIR/mongo/db/query/common_query_enums_and_helpers', '$BUILD_DIR/mongo/idl/idl_parser', '$BUILD_DIR/mongo/rpc/command_status', '$BUILD_DIR/mongo/rpc/rewrite_state_change_errors', @@ -2391,7 +2391,7 @@ if wiredtiger: '$BUILD_DIR/mongo/db/catalog/import_collection_oplog_entry', '$BUILD_DIR/mongo/db/catalog/index_build_entry_idl', '$BUILD_DIR/mongo/db/mongohasher', - '$BUILD_DIR/mongo/db/query/explain_options', + '$BUILD_DIR/mongo/db/query/common_query_enums_and_helpers', '$BUILD_DIR/mongo/db/query/query_test_service_context', '$BUILD_DIR/mongo/db/storage/wiredtiger/storage_wiredtiger', '$BUILD_DIR/mongo/executor/async_timer_mock', diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript index dff5f659997..473c54aa8e5 100644 --- a/src/mongo/db/pipeline/SConscript +++ b/src/mongo/db/pipeline/SConscript @@ -49,7 +49,7 @@ env.Library( '$BUILD_DIR/mongo/db/exec/document_value/document_value', '$BUILD_DIR/mongo/db/namespace_string', '$BUILD_DIR/mongo/db/query/command_request_response', - '$BUILD_DIR/mongo/db/query/explain_options', + '$BUILD_DIR/mongo/db/query/common_query_enums_and_helpers', '$BUILD_DIR/mongo/db/query/query_request', '$BUILD_DIR/mongo/db/repl/read_concern_args', '$BUILD_DIR/mongo/db/storage/storage_options', @@ -198,6 +198,7 @@ env.Library( 'lite_parsed_pipeline.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/query/common_query_enums_and_helpers', '$BUILD_DIR/mongo/db/stats/counters', 'aggregation_request_helper', ] diff --git a/src/mongo/db/pipeline/accumulator_avg.cpp b/src/mongo/db/pipeline/accumulator_avg.cpp index 201ee31c342..b141b091074 100644 --- a/src/mongo/db/pipeline/accumulator_avg.cpp +++ b/src/mongo/db/pipeline/accumulator_avg.cpp @@ -45,7 +45,7 @@ namespace mongo { using boost::intrusive_ptr; REGISTER_ACCUMULATOR(avg, genericParseSingleExpressionAccumulator<AccumulatorAvg>); -REGISTER_EXPRESSION(avg, ExpressionFromAccumulator<AccumulatorAvg>::parse); +REGISTER_STABLE_EXPRESSION(avg, ExpressionFromAccumulator<AccumulatorAvg>::parse); REGISTER_REMOVABLE_WINDOW_FUNCTION(avg, AccumulatorAvg, WindowFunctionAvg); const char* AccumulatorAvg::getOpName() const { diff --git a/src/mongo/db/pipeline/accumulator_merge_objects.cpp b/src/mongo/db/pipeline/accumulator_merge_objects.cpp index 8804a8a0f05..72943b74a70 100644 --- a/src/mongo/db/pipeline/accumulator_merge_objects.cpp +++ b/src/mongo/db/pipeline/accumulator_merge_objects.cpp @@ -43,7 +43,7 @@ using boost::intrusive_ptr; REGISTER_ACCUMULATOR(mergeObjects, genericParseSingleExpressionAccumulator<AccumulatorMergeObjects>); -REGISTER_EXPRESSION(mergeObjects, ExpressionFromAccumulator<AccumulatorMergeObjects>::parse); +REGISTER_STABLE_EXPRESSION(mergeObjects, ExpressionFromAccumulator<AccumulatorMergeObjects>::parse); const char* AccumulatorMergeObjects::getOpName() const { return "$mergeObjects"; diff --git a/src/mongo/db/pipeline/accumulator_min_max.cpp b/src/mongo/db/pipeline/accumulator_min_max.cpp index d0d3758487d..b6d7c3db74d 100644 --- a/src/mongo/db/pipeline/accumulator_min_max.cpp +++ b/src/mongo/db/pipeline/accumulator_min_max.cpp @@ -43,8 +43,8 @@ using boost::intrusive_ptr; REGISTER_ACCUMULATOR(max, genericParseSingleExpressionAccumulator<AccumulatorMax>); REGISTER_ACCUMULATOR(min, genericParseSingleExpressionAccumulator<AccumulatorMin>); -REGISTER_EXPRESSION(max, ExpressionFromAccumulator<AccumulatorMax>::parse); -REGISTER_EXPRESSION(min, ExpressionFromAccumulator<AccumulatorMin>::parse); +REGISTER_STABLE_EXPRESSION(max, ExpressionFromAccumulator<AccumulatorMax>::parse); +REGISTER_STABLE_EXPRESSION(min, ExpressionFromAccumulator<AccumulatorMin>::parse); REGISTER_REMOVABLE_WINDOW_FUNCTION(max, AccumulatorMax, WindowFunctionMax); REGISTER_REMOVABLE_WINDOW_FUNCTION(min, AccumulatorMin, WindowFunctionMin); diff --git a/src/mongo/db/pipeline/accumulator_std_dev.cpp b/src/mongo/db/pipeline/accumulator_std_dev.cpp index e2208d92ff0..dd64ffb8750 100644 --- a/src/mongo/db/pipeline/accumulator_std_dev.cpp +++ b/src/mongo/db/pipeline/accumulator_std_dev.cpp @@ -44,8 +44,8 @@ using boost::intrusive_ptr; REGISTER_ACCUMULATOR(stdDevPop, genericParseSingleExpressionAccumulator<AccumulatorStdDevPop>); REGISTER_ACCUMULATOR(stdDevSamp, genericParseSingleExpressionAccumulator<AccumulatorStdDevSamp>); -REGISTER_EXPRESSION(stdDevPop, ExpressionFromAccumulator<AccumulatorStdDevPop>::parse); -REGISTER_EXPRESSION(stdDevSamp, ExpressionFromAccumulator<AccumulatorStdDevSamp>::parse); +REGISTER_STABLE_EXPRESSION(stdDevPop, ExpressionFromAccumulator<AccumulatorStdDevPop>::parse); +REGISTER_STABLE_EXPRESSION(stdDevSamp, ExpressionFromAccumulator<AccumulatorStdDevSamp>::parse); REGISTER_REMOVABLE_WINDOW_FUNCTION(stdDevPop, AccumulatorStdDevPop, WindowFunctionStdDevPop); REGISTER_REMOVABLE_WINDOW_FUNCTION(stdDevSamp, AccumulatorStdDevSamp, WindowFunctionStdDevSamp); diff --git a/src/mongo/db/pipeline/accumulator_sum.cpp b/src/mongo/db/pipeline/accumulator_sum.cpp index c0033f281cc..587a1b86e88 100644 --- a/src/mongo/db/pipeline/accumulator_sum.cpp +++ b/src/mongo/db/pipeline/accumulator_sum.cpp @@ -47,7 +47,7 @@ namespace mongo { using boost::intrusive_ptr; REGISTER_ACCUMULATOR(sum, genericParseSingleExpressionAccumulator<AccumulatorSum>); -REGISTER_EXPRESSION(sum, ExpressionFromAccumulator<AccumulatorSum>::parse); +REGISTER_STABLE_EXPRESSION(sum, ExpressionFromAccumulator<AccumulatorSum>::parse); REGISTER_REMOVABLE_WINDOW_FUNCTION(sum, AccumulatorSum, WindowFunctionSum); REGISTER_ACCUMULATOR_WITH_MIN_VERSION( count, parseCountAccumulator, ServerGlobalParams::FeatureCompatibility::Version::kVersion50); diff --git a/src/mongo/db/pipeline/document_source.h b/src/mongo/db/pipeline/document_source.h index 49cc5cddcfd..f81aa3ff78a 100644 --- a/src/mongo/db/pipeline/document_source.h +++ b/src/mongo/db/pipeline/document_source.h @@ -56,6 +56,7 @@ #include "mongo/db/pipeline/lite_parsed_document_source.h" #include "mongo/db/pipeline/pipeline.h" #include "mongo/db/pipeline/stage_constraints.h" +#include "mongo/db/query/allowed_contexts.h" #include "mongo/db/query/explain_options.h" #include "mongo/util/intrusive_counter.h" @@ -86,13 +87,13 @@ class Document; * LiteParsedDocumentSourceDefault::parse, * DocumentSourceFoo::createFromBson); */ -#define REGISTER_DOCUMENT_SOURCE(key, liteParser, fullParser, allowedWithApiStrict) \ - REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(key, \ - liteParser, \ - fullParser, \ - allowedWithApiStrict, \ - LiteParsedDocumentSource::AllowedWithClientType::kAny, \ - boost::none, \ +#define REGISTER_DOCUMENT_SOURCE(key, liteParser, fullParser, allowedWithApiStrict) \ + REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(key, \ + liteParser, \ + fullParser, \ + allowedWithApiStrict, \ + AllowedWithClientType::kAny, \ + boost::none, \ true) /** @@ -100,28 +101,27 @@ class Document; * We store minVersion in the parserMap, so that changing FCV at runtime correctly enables/disables * the parser. */ -#define REGISTER_DOCUMENT_SOURCE_WITH_MIN_VERSION( \ - key, liteParser, fullParser, allowedWithApiStrict, minVersion) \ - REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(key, \ - liteParser, \ - fullParser, \ - allowedWithApiStrict, \ - LiteParsedDocumentSource::AllowedWithClientType::kAny, \ - minVersion, \ +#define REGISTER_DOCUMENT_SOURCE_WITH_MIN_VERSION( \ + key, liteParser, fullParser, allowedWithApiStrict, minVersion) \ + REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(key, \ + liteParser, \ + fullParser, \ + allowedWithApiStrict, \ + AllowedWithClientType::kAny, \ + minVersion, \ true) /** * Registers a DocumentSource which cannot be exposed to the users. */ #define REGISTER_INTERNAL_DOCUMENT_SOURCE(key, liteParser, fullParser, condition) \ - REGISTER_DOCUMENT_SOURCE_CONDITIONALLY( \ - key, \ - liteParser, \ - fullParser, \ - LiteParsedDocumentSource::AllowedWithApiStrict::kInternal, \ - LiteParsedDocumentSource::AllowedWithClientType::kInternal, \ - boost::none, \ - condition) + REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(key, \ + liteParser, \ + fullParser, \ + AllowedWithApiStrict::kInternal, \ + AllowedWithClientType::kInternal, \ + boost::none, \ + condition) /** * Like REGISTER_DOCUMENT_SOURCE_WITH_MIN_VERSION, except you can also specify a condition, @@ -152,15 +152,14 @@ class Document; /** * Like REGISTER_DOCUMENT_SOURCE, except the parser is only enabled when test-commands are enabled. */ -#define REGISTER_TEST_DOCUMENT_SOURCE(key, liteParser, fullParser) \ - REGISTER_DOCUMENT_SOURCE_CONDITIONALLY( \ - key, \ - liteParser, \ - fullParser, \ - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1, \ - LiteParsedDocumentSource::AllowedWithClientType::kAny, \ - boost::none, \ - ::mongo::getTestCommandsEnabled()) +#define REGISTER_TEST_DOCUMENT_SOURCE(key, liteParser, fullParser) \ + REGISTER_DOCUMENT_SOURCE_CONDITIONALLY(key, \ + liteParser, \ + fullParser, \ + AllowedWithApiStrict::kNeverInVersion1, \ + AllowedWithClientType::kAny, \ + boost::none, \ + ::mongo::getTestCommandsEnabled()) class DocumentSource : public RefCountable { public: diff --git a/src/mongo/db/pipeline/document_source_add_fields.cpp b/src/mongo/db/pipeline/document_source_add_fields.cpp index ec245402c2a..448715a96ea 100644 --- a/src/mongo/db/pipeline/document_source_add_fields.cpp +++ b/src/mongo/db/pipeline/document_source_add_fields.cpp @@ -43,11 +43,11 @@ using boost::intrusive_ptr; REGISTER_DOCUMENT_SOURCE(addFields, LiteParsedDocumentSourceDefault::parse, DocumentSourceAddFields::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); REGISTER_DOCUMENT_SOURCE(set, LiteParsedDocumentSourceDefault::parse, DocumentSourceAddFields::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); intrusive_ptr<DocumentSource> DocumentSourceAddFields::create( BSONObj addFieldsSpec, diff --git a/src/mongo/db/pipeline/document_source_bucket.cpp b/src/mongo/db/pipeline/document_source_bucket.cpp index 5fe098ae418..4de8daee491 100644 --- a/src/mongo/db/pipeline/document_source_bucket.cpp +++ b/src/mongo/db/pipeline/document_source_bucket.cpp @@ -43,7 +43,7 @@ using std::vector; REGISTER_DOCUMENT_SOURCE(bucket, LiteParsedDocumentSourceDefault::parse, DocumentSourceBucket::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); namespace { intrusive_ptr<ExpressionConstant> getExpressionConstant(ExpressionContext* const expCtx, diff --git a/src/mongo/db/pipeline/document_source_bucket_auto.cpp b/src/mongo/db/pipeline/document_source_bucket_auto.cpp index 61630565963..413d98c157a 100644 --- a/src/mongo/db/pipeline/document_source_bucket_auto.cpp +++ b/src/mongo/db/pipeline/document_source_bucket_auto.cpp @@ -45,7 +45,7 @@ using std::vector; REGISTER_DOCUMENT_SOURCE(bucketAuto, LiteParsedDocumentSourceDefault::parse, DocumentSourceBucketAuto::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); namespace { diff --git a/src/mongo/db/pipeline/document_source_change_stream.cpp b/src/mongo/db/pipeline/document_source_change_stream.cpp index c4de7837030..61f38343e83 100644 --- a/src/mongo/db/pipeline/document_source_change_stream.cpp +++ b/src/mongo/db/pipeline/document_source_change_stream.cpp @@ -75,7 +75,7 @@ using std::vector; REGISTER_DOCUMENT_SOURCE(changeStream, DocumentSourceChangeStream::LiteParsed::parse, DocumentSourceChangeStream::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); constexpr StringData DocumentSourceChangeStream::kDocumentKeyField; constexpr StringData DocumentSourceChangeStream::kFullDocumentBeforeChangeField; diff --git a/src/mongo/db/pipeline/document_source_coll_stats.cpp b/src/mongo/db/pipeline/document_source_coll_stats.cpp index e7c9681d880..6dc42ee7659 100644 --- a/src/mongo/db/pipeline/document_source_coll_stats.cpp +++ b/src/mongo/db/pipeline/document_source_coll_stats.cpp @@ -44,7 +44,7 @@ namespace mongo { REGISTER_DOCUMENT_SOURCE(collStats, DocumentSourceCollStats::LiteParsed::parse, DocumentSourceCollStats::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kSometimes); + AllowedWithApiStrict::kSometimes); void DocumentSourceCollStats::LiteParsed::assertPermittedInAPIVersion( const APIParameters& apiParameters) const { diff --git a/src/mongo/db/pipeline/document_source_count.cpp b/src/mongo/db/pipeline/document_source_count.cpp index 988bf414da3..bef22eef178 100644 --- a/src/mongo/db/pipeline/document_source_count.cpp +++ b/src/mongo/db/pipeline/document_source_count.cpp @@ -46,7 +46,7 @@ using std::string; REGISTER_DOCUMENT_SOURCE(count, LiteParsedDocumentSourceDefault::parse, DocumentSourceCount::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); list<intrusive_ptr<DocumentSource>> DocumentSourceCount::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) { diff --git a/src/mongo/db/pipeline/document_source_current_op.cpp b/src/mongo/db/pipeline/document_source_current_op.cpp index da1dda80226..7b453935dee 100644 --- a/src/mongo/db/pipeline/document_source_current_op.cpp +++ b/src/mongo/db/pipeline/document_source_current_op.cpp @@ -55,7 +55,7 @@ using boost::intrusive_ptr; REGISTER_DOCUMENT_SOURCE(currentOp, DocumentSourceCurrentOp::LiteParsed::parse, DocumentSourceCurrentOp::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1); + AllowedWithApiStrict::kNeverInVersion1); constexpr StringData DocumentSourceCurrentOp::kStageName; diff --git a/src/mongo/db/pipeline/document_source_facet.cpp b/src/mongo/db/pipeline/document_source_facet.cpp index 2cee70a250c..4d8da6de141 100644 --- a/src/mongo/db/pipeline/document_source_facet.cpp +++ b/src/mongo/db/pipeline/document_source_facet.cpp @@ -124,7 +124,7 @@ std::unique_ptr<DocumentSourceFacet::LiteParsed> DocumentSourceFacet::LiteParsed REGISTER_DOCUMENT_SOURCE(facet, DocumentSourceFacet::LiteParsed::parse, DocumentSourceFacet::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); intrusive_ptr<DocumentSourceFacet> DocumentSourceFacet::create( std::vector<FacetPipeline> facetPipelines, diff --git a/src/mongo/db/pipeline/document_source_geo_near.cpp b/src/mongo/db/pipeline/document_source_geo_near.cpp index a5b49788551..e10b9e59b50 100644 --- a/src/mongo/db/pipeline/document_source_geo_near.cpp +++ b/src/mongo/db/pipeline/document_source_geo_near.cpp @@ -47,7 +47,7 @@ constexpr StringData DocumentSourceGeoNear::kKeyFieldName; REGISTER_DOCUMENT_SOURCE(geoNear, LiteParsedDocumentSourceDefault::parse, DocumentSourceGeoNear::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); Value DocumentSourceGeoNear::serialize(boost::optional<ExplainOptions::Verbosity> explain) const { MutableDocument result; diff --git a/src/mongo/db/pipeline/document_source_graph_lookup.cpp b/src/mongo/db/pipeline/document_source_graph_lookup.cpp index eca4b8b8211..c6550aef5f6 100644 --- a/src/mongo/db/pipeline/document_source_graph_lookup.cpp +++ b/src/mongo/db/pipeline/document_source_graph_lookup.cpp @@ -112,7 +112,7 @@ std::unique_ptr<DocumentSourceGraphLookUp::LiteParsed> DocumentSourceGraphLookUp REGISTER_DOCUMENT_SOURCE(graphLookup, DocumentSourceGraphLookUp::LiteParsed::parse, DocumentSourceGraphLookUp::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); const char* DocumentSourceGraphLookUp::getSourceName() const { return kStageName.rawData(); diff --git a/src/mongo/db/pipeline/document_source_group.cpp b/src/mongo/db/pipeline/document_source_group.cpp index 1cb4cd7d303..f4583a8be00 100644 --- a/src/mongo/db/pipeline/document_source_group.cpp +++ b/src/mongo/db/pipeline/document_source_group.cpp @@ -127,7 +127,7 @@ constexpr StringData DocumentSourceGroup::kStageName; REGISTER_DOCUMENT_SOURCE(group, LiteParsedDocumentSourceDefault::parse, DocumentSourceGroup::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); const char* DocumentSourceGroup::getSourceName() const { return kStageName.rawData(); diff --git a/src/mongo/db/pipeline/document_source_index_stats.cpp b/src/mongo/db/pipeline/document_source_index_stats.cpp index ad6d600e74d..e87b614e59a 100644 --- a/src/mongo/db/pipeline/document_source_index_stats.cpp +++ b/src/mongo/db/pipeline/document_source_index_stats.cpp @@ -42,7 +42,7 @@ using boost::intrusive_ptr; REGISTER_DOCUMENT_SOURCE(indexStats, DocumentSourceIndexStats::LiteParsed::parse, DocumentSourceIndexStats::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1); + AllowedWithApiStrict::kNeverInVersion1); const char* DocumentSourceIndexStats::getSourceName() const { return kStageName.rawData(); diff --git a/src/mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.cpp b/src/mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.cpp index 3e523696eb8..5e08827daef 100644 --- a/src/mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.cpp +++ b/src/mongo/db/pipeline/document_source_internal_convert_bucket_index_stats.cpp @@ -105,7 +105,7 @@ BSONObj makeTimeseriesIndexStats(const TimeseriesConversionOptions& bucketSpec, REGISTER_DOCUMENT_SOURCE(_internalConvertBucketIndexStats, LiteParsedDocumentSourceDefault::parse, DocumentSourceInternalConvertBucketIndexStats::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kInternal); + AllowedWithApiStrict::kInternal); DocumentSourceInternalConvertBucketIndexStats::DocumentSourceInternalConvertBucketIndexStats( const boost::intrusive_ptr<ExpressionContext>& expCtx, diff --git a/src/mongo/db/pipeline/document_source_internal_inhibit_optimization.cpp b/src/mongo/db/pipeline/document_source_internal_inhibit_optimization.cpp index 10f10171f06..af9d4c297b1 100644 --- a/src/mongo/db/pipeline/document_source_internal_inhibit_optimization.cpp +++ b/src/mongo/db/pipeline/document_source_internal_inhibit_optimization.cpp @@ -36,7 +36,7 @@ namespace mongo { REGISTER_DOCUMENT_SOURCE(_internalInhibitOptimization, LiteParsedDocumentSourceDefault::parse, DocumentSourceInternalInhibitOptimization::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1); + AllowedWithApiStrict::kNeverInVersion1); constexpr StringData DocumentSourceInternalInhibitOptimization::kStageName; diff --git a/src/mongo/db/pipeline/document_source_internal_split_pipeline.cpp b/src/mongo/db/pipeline/document_source_internal_split_pipeline.cpp index c2721ff77d3..8f5f9927ada 100644 --- a/src/mongo/db/pipeline/document_source_internal_split_pipeline.cpp +++ b/src/mongo/db/pipeline/document_source_internal_split_pipeline.cpp @@ -36,7 +36,7 @@ namespace mongo { REGISTER_DOCUMENT_SOURCE(_internalSplitPipeline, LiteParsedDocumentSourceDefault::parse, DocumentSourceInternalSplitPipeline::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1); + AllowedWithApiStrict::kNeverInVersion1); constexpr StringData DocumentSourceInternalSplitPipeline::kStageName; diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp index 2fa8291fee2..8ef46b46734 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp @@ -66,7 +66,7 @@ namespace mongo { REGISTER_DOCUMENT_SOURCE(_internalUnpackBucket, LiteParsedDocumentSourceDefault::parse, DocumentSourceInternalUnpackBucket::createFromBsonInternal, - LiteParsedDocumentSource::AllowedWithApiStrict::kInternal); + AllowedWithApiStrict::kInternal); /* * $_unpackBucket is an alias of $_internalUnpackBucket. It only exposes the "timeField" and the @@ -76,7 +76,7 @@ REGISTER_DOCUMENT_SOURCE(_internalUnpackBucket, REGISTER_DOCUMENT_SOURCE(_unpackBucket, LiteParsedDocumentSourceDefault::parse, DocumentSourceInternalUnpackBucket::createFromBsonExternal, - LiteParsedDocumentSource::AllowedWithApiStrict::kInternal); + AllowedWithApiStrict::kInternal); namespace { /** diff --git a/src/mongo/db/pipeline/document_source_limit.cpp b/src/mongo/db/pipeline/document_source_limit.cpp index 1f8051fa1d0..227fe4dc212 100644 --- a/src/mongo/db/pipeline/document_source_limit.cpp +++ b/src/mongo/db/pipeline/document_source_limit.cpp @@ -50,7 +50,7 @@ DocumentSourceLimit::DocumentSourceLimit(const intrusive_ptr<ExpressionContext>& REGISTER_DOCUMENT_SOURCE(limit, LiteParsedDocumentSourceDefault::parse, DocumentSourceLimit::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); constexpr StringData DocumentSourceLimit::kStageName; diff --git a/src/mongo/db/pipeline/document_source_list_local_sessions.cpp b/src/mongo/db/pipeline/document_source_list_local_sessions.cpp index f9c329ff65e..d4e96fc135d 100644 --- a/src/mongo/db/pipeline/document_source_list_local_sessions.cpp +++ b/src/mongo/db/pipeline/document_source_list_local_sessions.cpp @@ -40,7 +40,7 @@ namespace mongo { REGISTER_DOCUMENT_SOURCE(listLocalSessions, DocumentSourceListLocalSessions::LiteParsed::parse, DocumentSourceListLocalSessions::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1); + AllowedWithApiStrict::kNeverInVersion1); DocumentSource::GetNextResult DocumentSourceListLocalSessions::doGetNext() { while (!_ids.empty()) { diff --git a/src/mongo/db/pipeline/document_source_list_sessions.cpp b/src/mongo/db/pipeline/document_source_list_sessions.cpp index 996856b3657..3c19beb4d5f 100644 --- a/src/mongo/db/pipeline/document_source_list_sessions.cpp +++ b/src/mongo/db/pipeline/document_source_list_sessions.cpp @@ -41,7 +41,7 @@ namespace mongo { REGISTER_DOCUMENT_SOURCE(listSessions, DocumentSourceListSessions::LiteParsed::parse, DocumentSourceListSessions::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1); + AllowedWithApiStrict::kNeverInVersion1); boost::intrusive_ptr<DocumentSource> DocumentSourceListSessions::createFromBson( BSONElement elem, const boost::intrusive_ptr<ExpressionContext>& pExpCtx) { diff --git a/src/mongo/db/pipeline/document_source_lookup.cpp b/src/mongo/db/pipeline/document_source_lookup.cpp index aade4c22aae..d34df7f095a 100644 --- a/src/mongo/db/pipeline/document_source_lookup.cpp +++ b/src/mongo/db/pipeline/document_source_lookup.cpp @@ -294,7 +294,7 @@ PrivilegeVector DocumentSourceLookUp::LiteParsed::requiredPrivileges( REGISTER_DOCUMENT_SOURCE(lookup, DocumentSourceLookUp::LiteParsed::parse, DocumentSourceLookUp::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kSometimes); + AllowedWithApiStrict::kSometimes); const char* DocumentSourceLookUp::getSourceName() const { return kStageName.rawData(); diff --git a/src/mongo/db/pipeline/document_source_match.cpp b/src/mongo/db/pipeline/document_source_match.cpp index 6686626438b..8ab9a5c0ece 100644 --- a/src/mongo/db/pipeline/document_source_match.cpp +++ b/src/mongo/db/pipeline/document_source_match.cpp @@ -58,7 +58,7 @@ using std::vector; REGISTER_DOCUMENT_SOURCE(match, LiteParsedDocumentSourceDefault::parse, DocumentSourceMatch::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); const char* DocumentSourceMatch::getSourceName() const { return kStageName.rawData(); diff --git a/src/mongo/db/pipeline/document_source_merge.cpp b/src/mongo/db/pipeline/document_source_merge.cpp index 36f652c66cd..7fc546d864d 100644 --- a/src/mongo/db/pipeline/document_source_merge.cpp +++ b/src/mongo/db/pipeline/document_source_merge.cpp @@ -50,7 +50,7 @@ MONGO_FAIL_POINT_DEFINE(hangWhileBuildingDocumentSourceMergeBatch); REGISTER_DOCUMENT_SOURCE(merge, DocumentSourceMerge::LiteParsed::parse, DocumentSourceMerge::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); namespace { using MergeStrategyDescriptor = DocumentSourceMerge::MergeStrategyDescriptor; diff --git a/src/mongo/db/pipeline/document_source_operation_metrics.cpp b/src/mongo/db/pipeline/document_source_operation_metrics.cpp index 548a7b3679b..a5e523465e4 100644 --- a/src/mongo/db/pipeline/document_source_operation_metrics.cpp +++ b/src/mongo/db/pipeline/document_source_operation_metrics.cpp @@ -44,7 +44,7 @@ using boost::intrusive_ptr; REGISTER_DOCUMENT_SOURCE(operationMetrics, DocumentSourceOperationMetrics::LiteParsed::parse, DocumentSourceOperationMetrics::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1); + AllowedWithApiStrict::kNeverInVersion1); const char* DocumentSourceOperationMetrics::getSourceName() const { return kStageName.rawData(); diff --git a/src/mongo/db/pipeline/document_source_out.cpp b/src/mongo/db/pipeline/document_source_out.cpp index 1fef7bb2bd2..5f97cbb0555 100644 --- a/src/mongo/db/pipeline/document_source_out.cpp +++ b/src/mongo/db/pipeline/document_source_out.cpp @@ -52,7 +52,7 @@ MONGO_FAIL_POINT_DEFINE(outWaitAfterTempCollectionCreation); REGISTER_DOCUMENT_SOURCE(out, DocumentSourceOut::LiteParsed::parse, DocumentSourceOut::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); DocumentSourceOut::~DocumentSourceOut() { DESTRUCTOR_GUARD( diff --git a/src/mongo/db/pipeline/document_source_plan_cache_stats.cpp b/src/mongo/db/pipeline/document_source_plan_cache_stats.cpp index bfbe60b4ec8..51f7bf3d650 100644 --- a/src/mongo/db/pipeline/document_source_plan_cache_stats.cpp +++ b/src/mongo/db/pipeline/document_source_plan_cache_stats.cpp @@ -36,7 +36,7 @@ namespace mongo { REGISTER_DOCUMENT_SOURCE(planCacheStats, DocumentSourcePlanCacheStats::LiteParsed::parse, DocumentSourcePlanCacheStats::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1); + AllowedWithApiStrict::kNeverInVersion1); boost::intrusive_ptr<DocumentSource> DocumentSourcePlanCacheStats::createFromBson( BSONElement spec, const boost::intrusive_ptr<ExpressionContext>& pExpCtx) { diff --git a/src/mongo/db/pipeline/document_source_project.cpp b/src/mongo/db/pipeline/document_source_project.cpp index 3630cae3440..72c19be3c05 100644 --- a/src/mongo/db/pipeline/document_source_project.cpp +++ b/src/mongo/db/pipeline/document_source_project.cpp @@ -46,12 +46,12 @@ using boost::intrusive_ptr; REGISTER_DOCUMENT_SOURCE(project, LiteParsedDocumentSourceDefault::parse, DocumentSourceProject::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); REGISTER_DOCUMENT_SOURCE(unset, LiteParsedDocumentSourceDefault::parse, DocumentSourceProject::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); namespace { BSONObj buildExclusionProjectionSpecification(const std::vector<BSONElement>& unsetSpec) { diff --git a/src/mongo/db/pipeline/document_source_redact.cpp b/src/mongo/db/pipeline/document_source_redact.cpp index eaefcdf672b..80dc406c620 100644 --- a/src/mongo/db/pipeline/document_source_redact.cpp +++ b/src/mongo/db/pipeline/document_source_redact.cpp @@ -52,7 +52,7 @@ DocumentSourceRedact::DocumentSourceRedact(const intrusive_ptr<ExpressionContext REGISTER_DOCUMENT_SOURCE(redact, LiteParsedDocumentSourceDefault::parse, DocumentSourceRedact::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); const char* DocumentSourceRedact::getSourceName() const { return kStageName.rawData(); diff --git a/src/mongo/db/pipeline/document_source_replace_root.cpp b/src/mongo/db/pipeline/document_source_replace_root.cpp index 084d7398fb9..137791a4ca7 100644 --- a/src/mongo/db/pipeline/document_source_replace_root.cpp +++ b/src/mongo/db/pipeline/document_source_replace_root.cpp @@ -77,11 +77,11 @@ Document ReplaceRootTransformation::applyTransformation(const Document& input) { REGISTER_DOCUMENT_SOURCE(replaceRoot, LiteParsedDocumentSourceDefault::parse, DocumentSourceReplaceRoot::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); REGISTER_DOCUMENT_SOURCE(replaceWith, LiteParsedDocumentSourceDefault::parse, DocumentSourceReplaceRoot::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); intrusive_ptr<DocumentSource> DocumentSourceReplaceRoot::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& expCtx) { diff --git a/src/mongo/db/pipeline/document_source_sample.cpp b/src/mongo/db/pipeline/document_source_sample.cpp index 57d5661c388..6f3f95d16f1 100644 --- a/src/mongo/db/pipeline/document_source_sample.cpp +++ b/src/mongo/db/pipeline/document_source_sample.cpp @@ -49,7 +49,7 @@ DocumentSourceSample::DocumentSourceSample(const intrusive_ptr<ExpressionContext REGISTER_DOCUMENT_SOURCE(sample, LiteParsedDocumentSourceDefault::parse, DocumentSourceSample::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); DocumentSource::GetNextResult DocumentSourceSample::doGetNext() { if (_size == 0) diff --git a/src/mongo/db/pipeline/document_source_set_window_fields.cpp b/src/mongo/db/pipeline/document_source_set_window_fields.cpp index 1e3f549edb7..ff728f14349 100644 --- a/src/mongo/db/pipeline/document_source_set_window_fields.cpp +++ b/src/mongo/db/pipeline/document_source_set_window_fields.cpp @@ -77,14 +77,14 @@ REGISTER_DOCUMENT_SOURCE_WITH_MIN_VERSION( setWindowFields, LiteParsedDocumentSourceDefault::parse, document_source_set_window_fields::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1, + AllowedWithApiStrict::kNeverInVersion1, ServerGlobalParams::FeatureCompatibility::Version::kVersion50); REGISTER_DOCUMENT_SOURCE_WITH_MIN_VERSION( _internalSetWindowFields, LiteParsedDocumentSourceDefault::parse, DocumentSourceInternalSetWindowFields::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kNeverInVersion1, + AllowedWithApiStrict::kNeverInVersion1, ServerGlobalParams::FeatureCompatibility::Version::kVersion50); list<intrusive_ptr<DocumentSource>> document_source_set_window_fields::createFromBson( diff --git a/src/mongo/db/pipeline/document_source_skip.cpp b/src/mongo/db/pipeline/document_source_skip.cpp index 7fa58be38ec..df7537fd1dd 100644 --- a/src/mongo/db/pipeline/document_source_skip.cpp +++ b/src/mongo/db/pipeline/document_source_skip.cpp @@ -51,7 +51,7 @@ DocumentSourceSkip::DocumentSourceSkip(const intrusive_ptr<ExpressionContext>& p REGISTER_DOCUMENT_SOURCE(skip, LiteParsedDocumentSourceDefault::parse, DocumentSourceSkip::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); constexpr StringData DocumentSourceSkip::kStageName; diff --git a/src/mongo/db/pipeline/document_source_sort.cpp b/src/mongo/db/pipeline/document_source_sort.cpp index 2b01f416e52..46bf7005883 100644 --- a/src/mongo/db/pipeline/document_source_sort.cpp +++ b/src/mongo/db/pipeline/document_source_sort.cpp @@ -74,7 +74,7 @@ DocumentSourceSort::DocumentSourceSort(const boost::intrusive_ptr<ExpressionCont REGISTER_DOCUMENT_SOURCE(sort, LiteParsedDocumentSourceDefault::parse, DocumentSourceSort::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); DocumentSource::GetNextResult DocumentSourceSort::doGetNext() { if (!_populated) { diff --git a/src/mongo/db/pipeline/document_source_sort_by_count.cpp b/src/mongo/db/pipeline/document_source_sort_by_count.cpp index d35ec06f33e..846a90e0fa9 100644 --- a/src/mongo/db/pipeline/document_source_sort_by_count.cpp +++ b/src/mongo/db/pipeline/document_source_sort_by_count.cpp @@ -45,7 +45,7 @@ using std::list; REGISTER_DOCUMENT_SOURCE(sortByCount, LiteParsedDocumentSourceDefault::parse, DocumentSourceSortByCount::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); list<intrusive_ptr<DocumentSource>> DocumentSourceSortByCount::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) { diff --git a/src/mongo/db/pipeline/document_source_union_with.cpp b/src/mongo/db/pipeline/document_source_union_with.cpp index 17570253cda..9027c9b7224 100644 --- a/src/mongo/db/pipeline/document_source_union_with.cpp +++ b/src/mongo/db/pipeline/document_source_union_with.cpp @@ -45,7 +45,7 @@ namespace mongo { REGISTER_DOCUMENT_SOURCE(unionWith, DocumentSourceUnionWith::LiteParsed::parse, DocumentSourceUnionWith::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); namespace { std::unique_ptr<Pipeline, PipelineDeleter> buildPipelineFromViewDefinition( diff --git a/src/mongo/db/pipeline/document_source_unwind.cpp b/src/mongo/db/pipeline/document_source_unwind.cpp index ae0bfecbb9d..d70e001a0be 100644 --- a/src/mongo/db/pipeline/document_source_unwind.cpp +++ b/src/mongo/db/pipeline/document_source_unwind.cpp @@ -171,7 +171,7 @@ DocumentSourceUnwind::DocumentSourceUnwind(const intrusive_ptr<ExpressionContext REGISTER_DOCUMENT_SOURCE(unwind, LiteParsedDocumentSourceDefault::parse, DocumentSourceUnwind::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kAlways); + AllowedWithApiStrict::kAlways); const char* DocumentSourceUnwind::getSourceName() const { return kStageName.rawData(); diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 5159241062d..89ed4b7f9ea 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -110,6 +110,8 @@ intrusive_ptr<Expression> Expression::parseObject(ExpressionContext* const expCt namespace { struct ParserRegistration { Parser parser; + AllowedWithApiStrict allowedWithApiStrict; + AllowedWithClientType allowedWithClientType; boost::optional<ServerGlobalParams::FeatureCompatibility::Version> requiredMinVersion; }; @@ -171,12 +173,15 @@ StringMap<ParserRegistration> parserMap; void Expression::registerExpression( string key, Parser parser, + AllowedWithApiStrict allowedWithApiStrict, + AllowedWithClientType allowedWithClientType, boost::optional<ServerGlobalParams::FeatureCompatibility::Version> requiredMinVersion) { auto op = parserMap.find(key); massert(17064, str::stream() << "Duplicate expression (" << key << ") registered.", op == parserMap.end()); - parserMap[key] = {parser, requiredMinVersion}; + parserMap[key] = + ParserRegistration{parser, allowedWithApiStrict, allowedWithClientType, requiredMinVersion}; // Add this expression to the global map of operator counters for expressions. operatorCountersExpressions.addExpressionCounter(key); } @@ -211,6 +216,14 @@ intrusive_ptr<Expression> Expression::parseExpression(ExpressionContext* const e !expCtx->maxFeatureCompatibilityVersion || !entry.requiredMinVersion || (*entry.requiredMinVersion <= *expCtx->maxFeatureCompatibilityVersion)); + if (expCtx->opCtx) { + // Only confirm API versioning if in a user operation, and not parsing a collection + // validator. Instead we perform checks for validators at time of access in subsequent + // insert/update. + assertLanguageFeatureIsAllowed( + expCtx->opCtx, opName, entry.allowedWithApiStrict, entry.allowedWithClientType); + } + // Increment the global counter for this expression. operatorCountersExpressions.incrementExpressionCounter(opName); return entry.parser(expCtx, obj.firstElement(), vps); @@ -254,19 +267,19 @@ bool Expression::isExpressionName(StringData name) { /* ------------------------- Register Date Expressions ----------------------------- */ -REGISTER_EXPRESSION(dayOfMonth, ExpressionDayOfMonth::parse); -REGISTER_EXPRESSION(dayOfWeek, ExpressionDayOfWeek::parse); -REGISTER_EXPRESSION(dayOfYear, ExpressionDayOfYear::parse); -REGISTER_EXPRESSION(hour, ExpressionHour::parse); -REGISTER_EXPRESSION(isoDayOfWeek, ExpressionIsoDayOfWeek::parse); -REGISTER_EXPRESSION(isoWeek, ExpressionIsoWeek::parse); -REGISTER_EXPRESSION(isoWeekYear, ExpressionIsoWeekYear::parse); -REGISTER_EXPRESSION(millisecond, ExpressionMillisecond::parse); -REGISTER_EXPRESSION(minute, ExpressionMinute::parse); -REGISTER_EXPRESSION(month, ExpressionMonth::parse); -REGISTER_EXPRESSION(second, ExpressionSecond::parse); -REGISTER_EXPRESSION(week, ExpressionWeek::parse); -REGISTER_EXPRESSION(year, ExpressionYear::parse); +REGISTER_STABLE_EXPRESSION(dayOfMonth, ExpressionDayOfMonth::parse); +REGISTER_STABLE_EXPRESSION(dayOfWeek, ExpressionDayOfWeek::parse); +REGISTER_STABLE_EXPRESSION(dayOfYear, ExpressionDayOfYear::parse); +REGISTER_STABLE_EXPRESSION(hour, ExpressionHour::parse); +REGISTER_STABLE_EXPRESSION(isoDayOfWeek, ExpressionIsoDayOfWeek::parse); +REGISTER_STABLE_EXPRESSION(isoWeek, ExpressionIsoWeek::parse); +REGISTER_STABLE_EXPRESSION(isoWeekYear, ExpressionIsoWeekYear::parse); +REGISTER_STABLE_EXPRESSION(millisecond, ExpressionMillisecond::parse); +REGISTER_STABLE_EXPRESSION(minute, ExpressionMinute::parse); +REGISTER_STABLE_EXPRESSION(month, ExpressionMonth::parse); +REGISTER_STABLE_EXPRESSION(second, ExpressionSecond::parse); +REGISTER_STABLE_EXPRESSION(week, ExpressionWeek::parse); +REGISTER_STABLE_EXPRESSION(year, ExpressionYear::parse); /* ----------------------- ExpressionAbs ---------------------------- */ @@ -286,7 +299,7 @@ Value ExpressionAbs::evaluateNumericArg(const Value& numericArg) const { } } -REGISTER_EXPRESSION(abs, ExpressionAbs::parse); +REGISTER_STABLE_EXPRESSION(abs, ExpressionAbs::parse); const char* ExpressionAbs::getOpName() const { return "$abs"; } @@ -400,7 +413,7 @@ Value ExpressionAdd::evaluate(const Document& root, Variables* variables) const } } -REGISTER_EXPRESSION(add, ExpressionAdd::parse); +REGISTER_STABLE_EXPRESSION(add, ExpressionAdd::parse); const char* ExpressionAdd::getOpName() const { return "$add"; } @@ -422,7 +435,7 @@ Value ExpressionAllElementsTrue::evaluate(const Document& root, Variables* varia return Value(true); } -REGISTER_EXPRESSION(allElementsTrue, ExpressionAllElementsTrue::parse); +REGISTER_STABLE_EXPRESSION(allElementsTrue, ExpressionAllElementsTrue::parse); const char* ExpressionAllElementsTrue::getOpName() const { return "$allElementsTrue"; } @@ -496,7 +509,7 @@ Value ExpressionAnd::evaluate(const Document& root, Variables* variables) const return Value(true); } -REGISTER_EXPRESSION(and, ExpressionAnd::parse); +REGISTER_STABLE_EXPRESSION(and, ExpressionAnd::parse); const char* ExpressionAnd::getOpName() const { return "$and"; } @@ -518,7 +531,7 @@ Value ExpressionAnyElementTrue::evaluate(const Document& root, Variables* variab return Value(false); } -REGISTER_EXPRESSION(anyElementTrue, ExpressionAnyElementTrue::parse); +REGISTER_STABLE_EXPRESSION(anyElementTrue, ExpressionAnyElementTrue::parse); const char* ExpressionAnyElementTrue::getOpName() const { return "$anyElementTrue"; } @@ -609,7 +622,7 @@ Value ExpressionArrayElemAt::evaluate(const Document& root, Variables* variables return arrayElemAt(this, array, indexArg); } -REGISTER_EXPRESSION(arrayElemAt, ExpressionArrayElemAt::parse); +REGISTER_STABLE_EXPRESSION(arrayElemAt, ExpressionArrayElemAt::parse); const char* ExpressionArrayElemAt::getOpName() const { return "$arrayElemAt"; } @@ -621,7 +634,7 @@ Value ExpressionFirst::evaluate(const Document& root, Variables* variables) cons return arrayElemAt(this, array, Value(0)); } -REGISTER_EXPRESSION(first, ExpressionFirst::parse); +REGISTER_STABLE_EXPRESSION(first, ExpressionFirst::parse); const char* ExpressionFirst::getOpName() const { return "$first"; @@ -634,7 +647,7 @@ Value ExpressionLast::evaluate(const Document& root, Variables* variables) const return arrayElemAt(this, array, Value(-1)); } -REGISTER_EXPRESSION(last, ExpressionLast::parse); +REGISTER_STABLE_EXPRESSION(last, ExpressionLast::parse); const char* ExpressionLast::getOpName() const { return "$last"; @@ -668,7 +681,7 @@ Value ExpressionObjectToArray::evaluate(const Document& root, Variables* variabl return Value(output); } -REGISTER_EXPRESSION(objectToArray, ExpressionObjectToArray::parse); +REGISTER_STABLE_EXPRESSION(objectToArray, ExpressionObjectToArray::parse); const char* ExpressionObjectToArray::getOpName() const { return "$objectToArray"; } @@ -779,14 +792,14 @@ Value ExpressionArrayToObject::evaluate(const Document& root, Variables* variabl return output.freezeToValue(); } -REGISTER_EXPRESSION(arrayToObject, ExpressionArrayToObject::parse); +REGISTER_STABLE_EXPRESSION(arrayToObject, ExpressionArrayToObject::parse); const char* ExpressionArrayToObject::getOpName() const { return "$arrayToObject"; } /* ------------------------- ExpressionBsonSize -------------------------- */ -REGISTER_EXPRESSION(bsonSize, ExpressionBsonSize::parse); +REGISTER_STABLE_EXPRESSION(bsonSize, ExpressionBsonSize::parse); Value ExpressionBsonSize::evaluate(const Document& root, Variables* variables) const { Value arg = _children[0]->evaluate(root, variables); @@ -818,7 +831,7 @@ Value ExpressionCeil::evaluateNumericArg(const Value& numericArg) const { } } -REGISTER_EXPRESSION(ceil, ExpressionCeil::parse); +REGISTER_STABLE_EXPRESSION(ceil, ExpressionCeil::parse); const char* ExpressionCeil::getOpName() const { return "$ceil"; } @@ -881,13 +894,13 @@ struct BoundOp { }; } // namespace -REGISTER_EXPRESSION(cmp, BoundOp{ExpressionCompare::CMP}); -REGISTER_EXPRESSION(eq, BoundOp{ExpressionCompare::EQ}); -REGISTER_EXPRESSION(gt, BoundOp{ExpressionCompare::GT}); -REGISTER_EXPRESSION(gte, BoundOp{ExpressionCompare::GTE}); -REGISTER_EXPRESSION(lt, BoundOp{ExpressionCompare::LT}); -REGISTER_EXPRESSION(lte, BoundOp{ExpressionCompare::LTE}); -REGISTER_EXPRESSION(ne, BoundOp{ExpressionCompare::NE}); +REGISTER_STABLE_EXPRESSION(cmp, BoundOp{ExpressionCompare::CMP}); +REGISTER_STABLE_EXPRESSION(eq, BoundOp{ExpressionCompare::EQ}); +REGISTER_STABLE_EXPRESSION(gt, BoundOp{ExpressionCompare::GT}); +REGISTER_STABLE_EXPRESSION(gte, BoundOp{ExpressionCompare::GTE}); +REGISTER_STABLE_EXPRESSION(lt, BoundOp{ExpressionCompare::LT}); +REGISTER_STABLE_EXPRESSION(lte, BoundOp{ExpressionCompare::LTE}); +REGISTER_STABLE_EXPRESSION(ne, BoundOp{ExpressionCompare::NE}); intrusive_ptr<Expression> ExpressionCompare::parse(ExpressionContext* const expCtx, BSONElement bsonExpr, @@ -978,7 +991,7 @@ Value ExpressionConcat::evaluate(const Document& root, Variables* variables) con return Value(result.str()); } -REGISTER_EXPRESSION(concat, ExpressionConcat::parse); +REGISTER_STABLE_EXPRESSION(concat, ExpressionConcat::parse); const char* ExpressionConcat::getOpName() const { return "$concat"; } @@ -1006,7 +1019,7 @@ Value ExpressionConcatArrays::evaluate(const Document& root, Variables* variable return Value(std::move(values)); } -REGISTER_EXPRESSION(concatArrays, ExpressionConcatArrays::parse); +REGISTER_STABLE_EXPRESSION(concatArrays, ExpressionConcatArrays::parse); const char* ExpressionConcatArrays::getOpName() const { return "$concatArrays"; } @@ -1051,7 +1064,7 @@ intrusive_ptr<Expression> ExpressionCond::parse(ExpressionContext* const expCtx, return ret; } -REGISTER_EXPRESSION(cond, ExpressionCond::parse); +REGISTER_STABLE_EXPRESSION(cond, ExpressionCond::parse); const char* ExpressionCond::getOpName() const { return "$cond"; } @@ -1092,8 +1105,8 @@ Value ExpressionConstant::serialize(bool explain) const { return serializeConstant(_value); } -REGISTER_EXPRESSION(const, ExpressionConstant::parse); -REGISTER_EXPRESSION(literal, ExpressionConstant::parse); // alias +REGISTER_STABLE_EXPRESSION(const, ExpressionConstant::parse); +REGISTER_STABLE_EXPRESSION(literal, ExpressionConstant::parse); // alias const char* ExpressionConstant::getOpName() const { return "$const"; } @@ -1131,7 +1144,7 @@ boost::optional<TimeZone> makeTimeZone(const TimeZoneDatabase* tzdb, } // namespace -REGISTER_EXPRESSION(dateFromParts, ExpressionDateFromParts::parse); +REGISTER_STABLE_EXPRESSION(dateFromParts, ExpressionDateFromParts::parse); intrusive_ptr<Expression> ExpressionDateFromParts::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vps) { @@ -1469,7 +1482,7 @@ void ExpressionDateFromParts::_doAddDependencies(DepsTracker* deps) const { /* ---------------------- ExpressionDateFromString --------------------- */ -REGISTER_EXPRESSION(dateFromString, ExpressionDateFromString::parse); +REGISTER_STABLE_EXPRESSION(dateFromString, ExpressionDateFromString::parse); intrusive_ptr<Expression> ExpressionDateFromString::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vps) { @@ -1652,7 +1665,7 @@ void ExpressionDateFromString::_doAddDependencies(DepsTracker* deps) const { /* ---------------------- ExpressionDateToParts ----------------------- */ -REGISTER_EXPRESSION(dateToParts, ExpressionDateToParts::parse); +REGISTER_STABLE_EXPRESSION(dateToParts, ExpressionDateToParts::parse); intrusive_ptr<Expression> ExpressionDateToParts::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vps) { @@ -1800,7 +1813,7 @@ void ExpressionDateToParts::_doAddDependencies(DepsTracker* deps) const { /* ---------------------- ExpressionDateToString ----------------------- */ -REGISTER_EXPRESSION(dateToString, ExpressionDateToString::parse); +REGISTER_STABLE_EXPRESSION(dateToString, ExpressionDateToString::parse); intrusive_ptr<Expression> ExpressionDateToString::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vps) { @@ -1949,6 +1962,8 @@ void ExpressionDateToString::_doAddDependencies(DepsTracker* deps) const { // TODO SERVER-53028: make the expression to be available for any FCV when 5.0 becomes last-lts. REGISTER_EXPRESSION_WITH_MIN_VERSION(dateDiff, ExpressionDateDiff::parse, + AllowedWithApiStrict::kNeverInVersion1, + AllowedWithClientType::kAny, ServerGlobalParams::FeatureCompatibility::Version::kVersion49); ExpressionDateDiff::ExpressionDateDiff(ExpressionContext* const expCtx, @@ -2129,7 +2144,7 @@ StatusWith<Value> ExpressionDivide::apply(Value lhs, Value rhs) { } } -REGISTER_EXPRESSION(divide, ExpressionDivide::parse); +REGISTER_STABLE_EXPRESSION(divide, ExpressionDivide::parse); const char* ExpressionDivide::getOpName() const { return "$divide"; } @@ -2144,7 +2159,7 @@ Value ExpressionExp::evaluateNumericArg(const Value& numericArg) const { return Value(exp(numericArg.coerceToDouble())); } -REGISTER_EXPRESSION(exp, ExpressionExp::parse); +REGISTER_STABLE_EXPRESSION(exp, ExpressionExp::parse); const char* ExpressionExp::getOpName() const { return "$exp"; } @@ -2465,7 +2480,7 @@ std::unique_ptr<Expression> ExpressionFieldPath::copyWithSubstitution( /* ------------------------- ExpressionFilter ----------------------------- */ -REGISTER_EXPRESSION(filter, ExpressionFilter::parse); +REGISTER_STABLE_EXPRESSION(filter, ExpressionFilter::parse); intrusive_ptr<Expression> ExpressionFilter::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vpsIn) { @@ -2584,14 +2599,14 @@ Value ExpressionFloor::evaluateNumericArg(const Value& numericArg) const { } } -REGISTER_EXPRESSION(floor, ExpressionFloor::parse); +REGISTER_STABLE_EXPRESSION(floor, ExpressionFloor::parse); const char* ExpressionFloor::getOpName() const { return "$floor"; } /* ------------------------- ExpressionLet ----------------------------- */ -REGISTER_EXPRESSION(let, ExpressionLet::parse); +REGISTER_STABLE_EXPRESSION(let, ExpressionLet::parse); intrusive_ptr<Expression> ExpressionLet::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vpsIn) { @@ -2705,7 +2720,7 @@ void ExpressionLet::_doAddDependencies(DepsTracker* deps) const { /* ------------------------- ExpressionMap ----------------------------- */ -REGISTER_EXPRESSION(map, ExpressionMap::parse); +REGISTER_STABLE_EXPRESSION(map, ExpressionMap::parse); intrusive_ptr<Expression> ExpressionMap::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vpsIn) { @@ -2845,7 +2860,7 @@ Expression::ComputedPaths ExpressionMap::getComputedPaths(const std::string& exp /* ------------------------- ExpressionMeta ----------------------------- */ -REGISTER_EXPRESSION(meta, ExpressionMeta::parse); +REGISTER_STABLE_EXPRESSION(meta, ExpressionMeta::parse); namespace { const std::string textScoreName = "textScore"; @@ -3018,7 +3033,7 @@ Value ExpressionMod::evaluate(const Document& root, Variables* variables) const } } -REGISTER_EXPRESSION(mod, ExpressionMod::parse); +REGISTER_STABLE_EXPRESSION(mod, ExpressionMod::parse); const char* ExpressionMod::getOpName() const { return "$mod"; } @@ -3115,7 +3130,7 @@ Value ExpressionMultiply::evaluate(const Document& root, Variables* variables) c return state.getValue(); } -REGISTER_EXPRESSION(multiply, ExpressionMultiply::parse); +REGISTER_STABLE_EXPRESSION(multiply, ExpressionMultiply::parse); const char* ExpressionMultiply::getOpName() const { return "$multiply"; } @@ -3138,7 +3153,7 @@ Value ExpressionIfNull::evaluate(const Document& root, Variables* variables) con return Value(); } -REGISTER_EXPRESSION(ifNull, ExpressionIfNull::parse); +REGISTER_STABLE_EXPRESSION(ifNull, ExpressionIfNull::parse); const char* ExpressionIfNull::getOpName() const { return "$ifNull"; } @@ -3161,7 +3176,7 @@ Value ExpressionIn::evaluate(const Document& root, Variables* variables) const { return Value(false); } -REGISTER_EXPRESSION(in, ExpressionIn::parse); +REGISTER_STABLE_EXPRESSION(in, ExpressionIn::parse); const char* ExpressionIn::getOpName() const { return "$in"; } @@ -3310,7 +3325,7 @@ intrusive_ptr<Expression> ExpressionIndexOfArray::optimize() { return this; } -REGISTER_EXPRESSION(indexOfArray, ExpressionIndexOfArray::parse); +REGISTER_STABLE_EXPRESSION(indexOfArray, ExpressionIndexOfArray::parse); const char* ExpressionIndexOfArray::getOpName() const { return "$indexOfArray"; } @@ -3375,7 +3390,7 @@ Value ExpressionIndexOfBytes::evaluate(const Document& root, Variables* variable return Value(static_cast<int>(position)); } -REGISTER_EXPRESSION(indexOfBytes, ExpressionIndexOfBytes::parse); +REGISTER_STABLE_EXPRESSION(indexOfBytes, ExpressionIndexOfBytes::parse); const char* ExpressionIndexOfBytes::getOpName() const { return "$indexOfBytes"; } @@ -3463,7 +3478,7 @@ Value ExpressionIndexOfCP::evaluate(const Document& root, Variables* variables) return Value(-1); } -REGISTER_EXPRESSION(indexOfCP, ExpressionIndexOfCP::parse); +REGISTER_STABLE_EXPRESSION(indexOfCP, ExpressionIndexOfCP::parse); const char* ExpressionIndexOfCP::getOpName() const { return "$indexOfCP"; } @@ -3484,7 +3499,7 @@ Value ExpressionLn::evaluateNumericArg(const Value& numericArg) const { return Value(std::log(argDouble)); } -REGISTER_EXPRESSION(ln, ExpressionLn::parse); +REGISTER_STABLE_EXPRESSION(ln, ExpressionLn::parse); const char* ExpressionLn::getOpName() const { return "$ln"; } @@ -3528,7 +3543,7 @@ Value ExpressionLog::evaluate(const Document& root, Variables* variables) const return Value(std::log(argDouble) / std::log(baseDouble)); } -REGISTER_EXPRESSION(log, ExpressionLog::parse); +REGISTER_STABLE_EXPRESSION(log, ExpressionLog::parse); const char* ExpressionLog::getOpName() const { return "$log"; } @@ -3550,7 +3565,7 @@ Value ExpressionLog10::evaluateNumericArg(const Value& numericArg) const { return Value(std::log10(argDouble)); } -REGISTER_EXPRESSION(log10, ExpressionLog10::parse); +REGISTER_STABLE_EXPRESSION(log10, ExpressionLog10::parse); const char* ExpressionLog10::getOpName() const { return "$log10"; } @@ -3687,7 +3702,7 @@ Value ExpressionNot::evaluate(const Document& root, Variables* variables) const return Value(!b); } -REGISTER_EXPRESSION(not, ExpressionNot::parse); +REGISTER_STABLE_EXPRESSION(not, ExpressionNot::parse); const char* ExpressionNot::getOpName() const { return "$not"; } @@ -3757,7 +3772,7 @@ intrusive_ptr<Expression> ExpressionOr::optimize() { return pE; } -REGISTER_EXPRESSION(or, ExpressionOr::parse); +REGISTER_STABLE_EXPRESSION(or, ExpressionOr::parse); const char* ExpressionOr::getOpName() const { return "$or"; } @@ -3979,7 +3994,7 @@ Value ExpressionPow::evaluate(const Document& root, Variables* variables) const return formatResult(computeWithRepeatedMultiplication(baseLong, expLong)); } -REGISTER_EXPRESSION(pow, ExpressionPow::parse); +REGISTER_STABLE_EXPRESSION(pow, ExpressionPow::parse); const char* ExpressionPow::getOpName() const { return "$pow"; } @@ -4041,14 +4056,14 @@ Value ExpressionRange::evaluate(const Document& root, Variables* variables) cons return Value(output); } -REGISTER_EXPRESSION(range, ExpressionRange::parse); +REGISTER_STABLE_EXPRESSION(range, ExpressionRange::parse); const char* ExpressionRange::getOpName() const { return "$range"; } /* ------------------------ ExpressionReduce ------------------------------ */ -REGISTER_EXPRESSION(reduce, ExpressionReduce::parse); +REGISTER_STABLE_EXPRESSION(reduce, ExpressionReduce::parse); intrusive_ptr<Expression> ExpressionReduce::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vps) { @@ -4224,7 +4239,7 @@ intrusive_ptr<Expression> ExpressionReplaceBase::optimize() { /* ------------------------ ExpressionReplaceOne ------------------------ */ -REGISTER_EXPRESSION(replaceOne, ExpressionReplaceOne::parse); +REGISTER_STABLE_EXPRESSION(replaceOne, ExpressionReplaceOne::parse); intrusive_ptr<Expression> ExpressionReplaceOne::parse(ExpressionContext* const expCtx, BSONElement expr, @@ -4255,7 +4270,7 @@ Value ExpressionReplaceOne::_doEval(StringData input, /* ------------------------ ExpressionReplaceAll ------------------------ */ -REGISTER_EXPRESSION(replaceAll, ExpressionReplaceAll::parse); +REGISTER_STABLE_EXPRESSION(replaceAll, ExpressionReplaceAll::parse); intrusive_ptr<Expression> ExpressionReplaceAll::parse(ExpressionContext* const expCtx, BSONElement expr, @@ -4321,7 +4336,7 @@ Value ExpressionReverseArray::evaluate(const Document& root, Variables* variable return Value(array); } -REGISTER_EXPRESSION(reverseArray, ExpressionReverseArray::parse); +REGISTER_STABLE_EXPRESSION(reverseArray, ExpressionReverseArray::parse); const char* ExpressionReverseArray::getOpName() const { return "$reverseArray"; } @@ -4368,7 +4383,7 @@ Value ExpressionSetDifference::evaluate(const Document& root, Variables* variabl return Value(std::move(returnVec)); } -REGISTER_EXPRESSION(setDifference, ExpressionSetDifference::parse); +REGISTER_STABLE_EXPRESSION(setDifference, ExpressionSetDifference::parse); const char* ExpressionSetDifference::getOpName() const { return "$setDifference"; } @@ -4410,7 +4425,7 @@ Value ExpressionSetEquals::evaluate(const Document& root, Variables* variables) return Value(true); } -REGISTER_EXPRESSION(setEquals, ExpressionSetEquals::parse); +REGISTER_STABLE_EXPRESSION(setEquals, ExpressionSetEquals::parse); const char* ExpressionSetEquals::getOpName() const { return "$setEquals"; } @@ -4454,7 +4469,7 @@ Value ExpressionSetIntersection::evaluate(const Document& root, Variables* varia return Value(vector<Value>(currentIntersection.begin(), currentIntersection.end())); } -REGISTER_EXPRESSION(setIntersection, ExpressionSetIntersection::parse); +REGISTER_STABLE_EXPRESSION(setIntersection, ExpressionSetIntersection::parse); const char* ExpressionSetIntersection::getOpName() const { return "$setIntersection"; } @@ -4546,7 +4561,7 @@ intrusive_ptr<Expression> ExpressionSetIsSubset::optimize() { return optimized; } -REGISTER_EXPRESSION(setIsSubset, ExpressionSetIsSubset::parse); +REGISTER_STABLE_EXPRESSION(setIsSubset, ExpressionSetIsSubset::parse); const char* ExpressionSetIsSubset::getOpName() const { return "$setIsSubset"; } @@ -4571,7 +4586,7 @@ Value ExpressionSetUnion::evaluate(const Document& root, Variables* variables) c return Value(vector<Value>(unionedSet.begin(), unionedSet.end())); } -REGISTER_EXPRESSION(setUnion, ExpressionSetUnion::parse); +REGISTER_STABLE_EXPRESSION(setUnion, ExpressionSetUnion::parse); const char* ExpressionSetUnion::getOpName() const { return "$setUnion"; } @@ -4583,7 +4598,7 @@ Value ExpressionIsArray::evaluate(const Document& root, Variables* variables) co return Value(argument.isArray()); } -REGISTER_EXPRESSION(isArray, ExpressionIsArray::parse); +REGISTER_STABLE_EXPRESSION(isArray, ExpressionIsArray::parse); const char* ExpressionIsArray::getOpName() const { return "$isArray"; } @@ -4669,7 +4684,7 @@ Value ExpressionSlice::evaluate(const Document& root, Variables* variables) cons return Value(vector<Value>(array.begin() + start, array.begin() + end)); } -REGISTER_EXPRESSION(slice, ExpressionSlice::parse); +REGISTER_STABLE_EXPRESSION(slice, ExpressionSlice::parse); const char* ExpressionSlice::getOpName() const { return "$slice"; } @@ -4686,7 +4701,7 @@ Value ExpressionSize::evaluate(const Document& root, Variables* variables) const return Value::createIntOrLong(array.getArray().size()); } -REGISTER_EXPRESSION(size, ExpressionSize::parse); +REGISTER_STABLE_EXPRESSION(size, ExpressionSize::parse); const char* ExpressionSize::getOpName() const { return "$size"; } @@ -4736,7 +4751,7 @@ Value ExpressionSplit::evaluate(const Document& root, Variables* variables) cons return Value(std::move(output)); } -REGISTER_EXPRESSION(split, ExpressionSplit::parse); +REGISTER_STABLE_EXPRESSION(split, ExpressionSplit::parse); const char* ExpressionSplit::getOpName() const { return "$split"; } @@ -4758,7 +4773,7 @@ Value ExpressionSqrt::evaluateNumericArg(const Value& numericArg) const { return Value(sqrt(argDouble)); } -REGISTER_EXPRESSION(sqrt, ExpressionSqrt::parse); +REGISTER_STABLE_EXPRESSION(sqrt, ExpressionSqrt::parse); const char* ExpressionSqrt::getOpName() const { return "$sqrt"; } @@ -4782,7 +4797,7 @@ Value ExpressionStrcasecmp::evaluate(const Document& root, Variables* variables) return Value(-1); } -REGISTER_EXPRESSION(strcasecmp, ExpressionStrcasecmp::parse); +REGISTER_STABLE_EXPRESSION(strcasecmp, ExpressionStrcasecmp::parse); const char* ExpressionStrcasecmp::getOpName() const { return "$strcasecmp"; } @@ -4841,8 +4856,8 @@ Value ExpressionSubstrBytes::evaluate(const Document& root, Variables* variables } // $substr is deprecated in favor of $substrBytes, but for now will just parse into a $substrBytes. -REGISTER_EXPRESSION(substrBytes, ExpressionSubstrBytes::parse); -REGISTER_EXPRESSION(substr, ExpressionSubstrBytes::parse); +REGISTER_STABLE_EXPRESSION(substrBytes, ExpressionSubstrBytes::parse); +REGISTER_STABLE_EXPRESSION(substr, ExpressionSubstrBytes::parse); const char* ExpressionSubstrBytes::getOpName() const { return "$substrBytes"; } @@ -4915,7 +4930,7 @@ Value ExpressionSubstrCP::evaluate(const Document& root, Variables* variables) c return Value(std::string(str, startIndexBytes, endIndexBytes - startIndexBytes)); } -REGISTER_EXPRESSION(substrCP, ExpressionSubstrCP::parse); +REGISTER_STABLE_EXPRESSION(substrCP, ExpressionSubstrCP::parse); const char* ExpressionSubstrCP::getOpName() const { return "$substrCP"; } @@ -4944,7 +4959,7 @@ Value ExpressionStrLenBytes::evaluate(const Document& root, Variables* variables return strLenBytes(str.getStringData()); } -REGISTER_EXPRESSION(strLenBytes, ExpressionStrLenBytes::parse); +REGISTER_STABLE_EXPRESSION(strLenBytes, ExpressionStrLenBytes::parse); const char* ExpressionStrLenBytes::getOpName() const { return "$strLenBytes"; } @@ -4970,7 +4985,7 @@ Value ExpressionBinarySize::evaluate(const Document& root, Variables* variables) return Value(binData.length); } -REGISTER_EXPRESSION(binarySize, ExpressionBinarySize::parse); +REGISTER_STABLE_EXPRESSION(binarySize, ExpressionBinarySize::parse); const char* ExpressionBinarySize::getOpName() const { return "$binarySize"; @@ -4996,7 +5011,7 @@ Value ExpressionStrLenCP::evaluate(const Document& root, Variables* variables) c return Value(static_cast<int>(strLen)); } -REGISTER_EXPRESSION(strLenCP, ExpressionStrLenCP::parse); +REGISTER_STABLE_EXPRESSION(strLenCP, ExpressionStrLenCP::parse); const char* ExpressionStrLenCP::getOpName() const { return "$strLenCP"; } @@ -5050,14 +5065,14 @@ StatusWith<Value> ExpressionSubtract::apply(Value lhs, Value rhs) { } } -REGISTER_EXPRESSION(subtract, ExpressionSubtract::parse); +REGISTER_STABLE_EXPRESSION(subtract, ExpressionSubtract::parse); const char* ExpressionSubtract::getOpName() const { return "$subtract"; } /* ------------------------- ExpressionSwitch ------------------------------ */ -REGISTER_EXPRESSION(switch, ExpressionSwitch::parse); +REGISTER_STABLE_EXPRESSION(switch, ExpressionSwitch::parse); Value ExpressionSwitch::evaluate(const Document& root, Variables* variables) const { for (auto&& branch : _branches) { @@ -5201,7 +5216,7 @@ Value ExpressionToLower::evaluate(const Document& root, Variables* variables) co return Value(str); } -REGISTER_EXPRESSION(toLower, ExpressionToLower::parse); +REGISTER_STABLE_EXPRESSION(toLower, ExpressionToLower::parse); const char* ExpressionToLower::getOpName() const { return "$toLower"; } @@ -5215,16 +5230,16 @@ Value ExpressionToUpper::evaluate(const Document& root, Variables* variables) co return Value(str); } -REGISTER_EXPRESSION(toUpper, ExpressionToUpper::parse); +REGISTER_STABLE_EXPRESSION(toUpper, ExpressionToUpper::parse); const char* ExpressionToUpper::getOpName() const { return "$toUpper"; } /* -------------------------- ExpressionTrim ------------------------------ */ -REGISTER_EXPRESSION(trim, ExpressionTrim::parse); -REGISTER_EXPRESSION(ltrim, ExpressionTrim::parse); -REGISTER_EXPRESSION(rtrim, ExpressionTrim::parse); +REGISTER_STABLE_EXPRESSION(trim, ExpressionTrim::parse); +REGISTER_STABLE_EXPRESSION(ltrim, ExpressionTrim::parse); +REGISTER_STABLE_EXPRESSION(rtrim, ExpressionTrim::parse); intrusive_ptr<Expression> ExpressionTrim::parse(ExpressionContext* const expCtx, BSONElement expr, @@ -5534,7 +5549,7 @@ Value ExpressionRound::evaluate(const Document& root, Variables* variables) cons root, _children, getOpName(), Decimal128::kRoundTiesToEven, &std::round, variables); } -REGISTER_EXPRESSION(round, ExpressionRound::parse); +REGISTER_STABLE_EXPRESSION(round, ExpressionRound::parse); const char* ExpressionRound::getOpName() const { return "$round"; } @@ -5550,7 +5565,7 @@ intrusive_ptr<Expression> ExpressionTrunc::parse(ExpressionContext* const expCtx return ExpressionRangedArity<ExpressionTrunc, 1, 2>::parse(expCtx, elem, vps); } -REGISTER_EXPRESSION(trunc, ExpressionTrunc::parse); +REGISTER_STABLE_EXPRESSION(trunc, ExpressionTrunc::parse); const char* ExpressionTrunc::getOpName() const { return "$trunc"; } @@ -5562,7 +5577,7 @@ Value ExpressionType::evaluate(const Document& root, Variables* variables) const return Value(StringData(typeName(val.getType()))); } -REGISTER_EXPRESSION(type, ExpressionType::parse); +REGISTER_STABLE_EXPRESSION(type, ExpressionType::parse); const char* ExpressionType::getOpName() const { return "$type"; } @@ -5574,7 +5589,7 @@ Value ExpressionIsNumber::evaluate(const Document& root, Variables* variables) c return Value(val.numeric()); } -REGISTER_EXPRESSION(isNumber, ExpressionIsNumber::parse); +REGISTER_STABLE_EXPRESSION(isNumber, ExpressionIsNumber::parse); const char* ExpressionIsNumber::getOpName() const { return "$isNumber"; @@ -5582,7 +5597,7 @@ const char* ExpressionIsNumber::getOpName() const { /* -------------------------- ExpressionZip ------------------------------ */ -REGISTER_EXPRESSION(zip, ExpressionZip::parse); +REGISTER_STABLE_EXPRESSION(zip, ExpressionZip::parse); intrusive_ptr<Expression> ExpressionZip::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vps) { @@ -6190,18 +6205,19 @@ Expression::Parser makeConversionAlias(const StringData shortcutName, BSONType t } // namespace -REGISTER_EXPRESSION(convert, ExpressionConvert::parse); +REGISTER_STABLE_EXPRESSION(convert, ExpressionConvert::parse); // Also register shortcut expressions like $toInt, $toString, etc. which can be used as a shortcut // for $convert without an 'onNull' or 'onError'. -REGISTER_EXPRESSION(toString, makeConversionAlias("$toString"_sd, BSONType::String)); -REGISTER_EXPRESSION(toObjectId, makeConversionAlias("$toObjectId"_sd, BSONType::jstOID)); -REGISTER_EXPRESSION(toDate, makeConversionAlias("$toDate"_sd, BSONType::Date)); -REGISTER_EXPRESSION(toDouble, makeConversionAlias("$toDouble"_sd, BSONType::NumberDouble)); -REGISTER_EXPRESSION(toInt, makeConversionAlias("$toInt"_sd, BSONType::NumberInt)); -REGISTER_EXPRESSION(toLong, makeConversionAlias("$toLong"_sd, BSONType::NumberLong)); -REGISTER_EXPRESSION(toDecimal, makeConversionAlias("$toDecimal"_sd, BSONType::NumberDecimal)); -REGISTER_EXPRESSION(toBool, makeConversionAlias("$toBool"_sd, BSONType::Bool)); +REGISTER_STABLE_EXPRESSION(toString, makeConversionAlias("$toString"_sd, BSONType::String)); +REGISTER_STABLE_EXPRESSION(toObjectId, makeConversionAlias("$toObjectId"_sd, BSONType::jstOID)); +REGISTER_STABLE_EXPRESSION(toDate, makeConversionAlias("$toDate"_sd, BSONType::Date)); +REGISTER_STABLE_EXPRESSION(toDouble, makeConversionAlias("$toDouble"_sd, BSONType::NumberDouble)); +REGISTER_STABLE_EXPRESSION(toInt, makeConversionAlias("$toInt"_sd, BSONType::NumberInt)); +REGISTER_STABLE_EXPRESSION(toLong, makeConversionAlias("$toLong"_sd, BSONType::NumberLong)); +REGISTER_STABLE_EXPRESSION(toDecimal, + makeConversionAlias("$toDecimal"_sd, BSONType::NumberDecimal)); +REGISTER_STABLE_EXPRESSION(toBool, makeConversionAlias("$toBool"_sd, BSONType::Bool)); boost::intrusive_ptr<Expression> ExpressionConvert::create(ExpressionContext* const expCtx, boost::intrusive_ptr<Expression> input, @@ -6724,7 +6740,7 @@ ExpressionRegex::getConstantPatternAndOptions() const { /* -------------------------- ExpressionRegexFind ------------------------------ */ -REGISTER_EXPRESSION(regexFind, ExpressionRegexFind::parse); +REGISTER_STABLE_EXPRESSION(regexFind, ExpressionRegexFind::parse); boost::intrusive_ptr<Expression> ExpressionRegexFind::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vpsIn) { @@ -6744,7 +6760,7 @@ Value ExpressionRegexFind::evaluate(const Document& root, Variables* variables) /* -------------------------- ExpressionRegexFindAll ------------------------------ */ -REGISTER_EXPRESSION(regexFindAll, ExpressionRegexFindAll::parse); +REGISTER_STABLE_EXPRESSION(regexFindAll, ExpressionRegexFindAll::parse); boost::intrusive_ptr<Expression> ExpressionRegexFindAll::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vpsIn) { @@ -6805,7 +6821,7 @@ Value ExpressionRegexFindAll::evaluate(const Document& root, Variables* variable /* -------------------------- ExpressionRegexMatch ------------------------------ */ -REGISTER_EXPRESSION(regexMatch, ExpressionRegexMatch::parse); +REGISTER_STABLE_EXPRESSION(regexMatch, ExpressionRegexMatch::parse); boost::intrusive_ptr<Expression> ExpressionRegexMatch::parse(ExpressionContext* const expCtx, BSONElement expr, const VariablesParseState& vpsIn) { @@ -6822,7 +6838,7 @@ Value ExpressionRegexMatch::evaluate(const Document& root, Variables* variables) } /* -------------------------- ExpressionRandom ------------------------------ */ -REGISTER_EXPRESSION(rand, ExpressionRandom::parse); +REGISTER_STABLE_EXPRESSION(rand, ExpressionRandom::parse); static thread_local PseudoRandom threadLocalRNG(SecureRandom().nextInt64()); @@ -6867,7 +6883,7 @@ Value ExpressionRandom::serialize(const bool explain) const { } /* ------------------------- ExpressionToHashedIndexKey -------------------------- */ -REGISTER_EXPRESSION(toHashedIndexKey, ExpressionToHashedIndexKey::parse); +REGISTER_STABLE_EXPRESSION(toHashedIndexKey, ExpressionToHashedIndexKey::parse); boost::intrusive_ptr<Expression> ExpressionToHashedIndexKey::parse(ExpressionContext* const expCtx, BSONElement expr, @@ -7008,6 +7024,8 @@ Value ExpressionDateArithmetics::evaluate(const Document& root, Variables* varia REGISTER_EXPRESSION_WITH_MIN_VERSION(dateAdd, ExpressionDateAdd::parse, + AllowedWithApiStrict::kNeverInVersion1, + AllowedWithClientType::kAny, ServerGlobalParams::FeatureCompatibility::Version::kVersion49); boost::intrusive_ptr<Expression> ExpressionDateAdd::parse(ExpressionContext* const expCtx, @@ -7033,6 +7051,8 @@ Value ExpressionDateAdd::evaluateDateArithmetics(Date_t date, REGISTER_EXPRESSION_WITH_MIN_VERSION(dateSubtract, ExpressionDateSubtract::parse, + AllowedWithApiStrict::kNeverInVersion1, + AllowedWithClientType::kAny, ServerGlobalParams::FeatureCompatibility::Version::kVersion49); boost::intrusive_ptr<Expression> ExpressionDateSubtract::parse(ExpressionContext* const expCtx, @@ -7061,6 +7081,8 @@ Value ExpressionDateSubtract::evaluateDateArithmetics(Date_t date, // TODO SERVER-53028: make the expression to be available for any FCV when 5.0 becomes last-lts. REGISTER_EXPRESSION_WITH_MIN_VERSION(dateTrunc, ExpressionDateTrunc::parse, + AllowedWithApiStrict::kNeverInVersion1, + AllowedWithClientType::kAny, ServerGlobalParams::FeatureCompatibility::Version::kVersion49); ExpressionDateTrunc::ExpressionDateTrunc(ExpressionContext* const expCtx, @@ -7485,7 +7507,11 @@ Value ExpressionTsSecond::evaluate(const Document& root, Variables* variables) c return Value(static_cast<long long>(operand.getTimestamp().getSecs())); } -REGISTER_EXPRESSION(tsSecond, ExpressionTsSecond::parse); +REGISTER_EXPRESSION_WITH_MIN_VERSION(tsSecond, + ExpressionTsSecond::parse, + AllowedWithApiStrict::kNeverInVersion1, + AllowedWithClientType::kAny, + ServerGlobalParams::FeatureCompatibility::Version::kVersion50); /* ------------------------- ExpressionTsIncrement ----------------------------- */ @@ -7504,7 +7530,11 @@ Value ExpressionTsIncrement::evaluate(const Document& root, Variables* variables return Value(static_cast<long long>(operand.getTimestamp().getInc())); } -REGISTER_EXPRESSION(tsIncrement, ExpressionTsIncrement::parse); +REGISTER_EXPRESSION_WITH_MIN_VERSION(tsIncrement, + ExpressionTsIncrement::parse, + AllowedWithApiStrict::kNeverInVersion1, + AllowedWithClientType::kAny, + ServerGlobalParams::FeatureCompatibility::Version::kVersion50); MONGO_INITIALIZER_GROUP(BeginExpressionRegistration, ("default"), ("EndExpressionRegistration")) MONGO_INITIALIZER_GROUP(EndExpressionRegistration, ("BeginExpressionRegistration"), ()) diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h index 2b4820929d2..df9e1802e8e 100644 --- a/src/mongo/db/pipeline/expression.h +++ b/src/mongo/db/pipeline/expression.h @@ -49,6 +49,7 @@ #include "mongo/db/pipeline/expression_visitor.h" #include "mongo/db/pipeline/field_path.h" #include "mongo/db/pipeline/variables.h" +#include "mongo/db/query/allowed_contexts.h" #include "mongo/db/query/datetime/date_time_support.h" #include "mongo/db/query/query_feature_flags_gen.h" #include "mongo/db/query/sort_pattern.h" @@ -67,16 +68,21 @@ class DocumentSource; * Registers a Parser so it can be called from parseExpression and friends. * * As an example, if your expression looks like {"$foo": [1,2,3]} you would add this line: - * REGISTER_EXPRESSION(foo, ExpressionFoo::parse); + * REGISTER_STABLE_EXPRESSION(foo, ExpressionFoo::parse); * - * An expression registered this way can be used in any featureCompatibilityVersion. + * An expression registered this way can be used in any featureCompatibilityVersion and will be + * considered part of the stable API. */ -#define REGISTER_EXPRESSION(key, parser) \ - MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \ - ("BeginExpressionRegistration"), \ - ("EndExpressionRegistration")) \ - (InitializerContext*) { \ - Expression::registerExpression("$" #key, (parser), boost::none); \ +#define REGISTER_STABLE_EXPRESSION(key, parser) \ + MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \ + ("BeginExpressionRegistration"), \ + ("EndExpressionRegistration")) \ + (InitializerContext*) { \ + Expression::registerExpression("$" #key, \ + (parser), \ + AllowedWithApiStrict::kAlways, \ + AllowedWithClientType::kAny, \ + boost::none); \ } /** @@ -87,16 +93,22 @@ class DocumentSource; * feature_flags::gFoo and version >= X, you would add this line: * REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION_WITH_MIN_VERSION( * foo, ExpressionFoo::parse, feature_flags::gFoo, X); + * + * Expressions registered in this way will not be included in the stable API. */ -#define REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION_WITH_MIN_VERSION( \ - key, parser, featureFlag, minVersion) \ - MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \ - ("BeginExpressionRegistration"), \ - ("EndExpressionRegistration")) \ - (InitializerContext*) { \ - if (featureFlag.isEnabledAndIgnoreFCV()) { \ - Expression::registerExpression("$" #key, (parser), (minVersion)); \ - } \ +#define REGISTER_FEATURE_FLAG_GUARDED_EXPRESSION_WITH_MIN_VERSION( \ + key, parser, featureFlag, minVersion) \ + MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \ + ("BeginExpressionRegistration"), \ + ("EndExpressionRegistration")) \ + (InitializerContext*) { \ + if (featureFlag.isEnabledAndIgnoreFCV()) { \ + Expression::registerExpression("$" #key, \ + (parser), \ + AllowedWithApiStrict::kNeverInVersion1, \ + AllowedWithClientType::kAny, \ + (minVersion)); \ + } \ } /** @@ -106,28 +118,43 @@ class DocumentSource; * * As an example, if your expression looks like {"$foo": [1,2,3]}, and can only be used in a feature * compatibility version >= X, you would add this line: - * REGISTER_EXPRESSION_WITH_MIN_VERSION(foo, ExpressionFoo::parse, X); + * REGISTER_EXPRESSION_WITH_MIN_VERSION( + * foo, + * ExpressionFoo::parse, + * AllowedWithApiStrict::kNeverInVersion1, + * AllowedWithClientType::kAny, + * X); + * + * Generally new language features should be excluded from the stable API for a stabilization period + * to allow for incorporating feedback or fixing accidental semantics bugs. + * + * If 'allowedWithApiStrict' is set to 'kSometimes', this expression is expected to register its own + * parser and enforce the 'sometimes' behavior during that invocation. No extra validation will be + * done here. */ -#define REGISTER_EXPRESSION_WITH_MIN_VERSION(key, parser, minVersion) \ - MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \ - ("BeginExpressionRegistration"), \ - ("EndExpressionRegistration")) \ - (InitializerContext*) { \ - Expression::registerExpression("$" #key, (parser), (minVersion)); \ +#define REGISTER_EXPRESSION_WITH_MIN_VERSION( \ + key, parser, allowedWithApiStrict, allowedClientType, minVersion) \ + MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \ + ("BeginExpressionRegistration"), \ + ("EndExpressionRegistration")) \ + (InitializerContext*) { \ + Expression::registerExpression( \ + "$" #key, (parser), (allowedWithApiStrict), (allowedClientType), (minVersion)); \ } /** * Registers a Parser only if test commands are enabled. Use this if your expression is only used * for testing purposes. */ -#define REGISTER_TEST_EXPRESSION(key, parser) \ - MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \ - ("BeginExpressionRegistration"), \ - ("EndExpressionRegistration")) \ - (InitializerContext*) { \ - if (getTestCommandsEnabled()) { \ - Expression::registerExpression("$" #key, (parser), boost::none); \ - } \ +#define REGISTER_TEST_EXPRESSION(key, allowedWithApiStrict, allowedClientType, parser) \ + MONGO_INITIALIZER_GENERAL(addToExpressionParserMap_##key, \ + ("BeginExpressionRegistration"), \ + ("EndExpressionRegistration")) \ + (InitializerContext*) { \ + if (getTestCommandsEnabled()) { \ + Expression::registerExpression( \ + "$" #key, (parser), (allowedWithApiStrict), (allowedClientType), boost::none); \ + } \ } class Expression : public RefCountable { @@ -295,6 +322,8 @@ public: static void registerExpression( std::string key, Parser parser, + AllowedWithApiStrict allowedWithApiStrict, + AllowedWithClientType allowedWithClientType, boost::optional<ServerGlobalParams::FeatureCompatibility::Version> requiredMinVersion); const auto& getChildren() const { @@ -452,7 +481,7 @@ public: /** * Used to make Accumulators available as Expressions, e.g., to make $sum available as an Expression - * use "REGISTER_EXPRESSION(sum, ExpressionAccumulator<AccumulatorSum>::parse);". + * use "REGISTER_STABLE_EXPRESSION(sum, ExpressionAccumulator<AccumulatorSum>::parse);". */ template <typename AccumulatorState> class ExpressionFromAccumulator diff --git a/src/mongo/db/pipeline/expression_function.cpp b/src/mongo/db/pipeline/expression_function.cpp index e28acaca61b..c53b0004b31 100644 --- a/src/mongo/db/pipeline/expression_function.cpp +++ b/src/mongo/db/pipeline/expression_function.cpp @@ -31,7 +31,7 @@ namespace mongo { -REGISTER_EXPRESSION(function, ExpressionFunction::parse); +REGISTER_STABLE_EXPRESSION(function, ExpressionFunction::parse); ExpressionFunction::ExpressionFunction(ExpressionContext* const expCtx, boost::intrusive_ptr<Expression> passedArgs, diff --git a/src/mongo/db/pipeline/expression_js_emit.cpp b/src/mongo/db/pipeline/expression_js_emit.cpp index 44242fefcf3..d19a9c53191 100644 --- a/src/mongo/db/pipeline/expression_js_emit.cpp +++ b/src/mongo/db/pipeline/expression_js_emit.cpp @@ -36,7 +36,7 @@ namespace mongo { -REGISTER_EXPRESSION(_internalJsEmit, ExpressionInternalJsEmit::parse); +REGISTER_STABLE_EXPRESSION(_internalJsEmit, ExpressionInternalJsEmit::parse); namespace { /** diff --git a/src/mongo/db/pipeline/expression_test_api_version.cpp b/src/mongo/db/pipeline/expression_test_api_version.cpp index 7c77895bfff..3201ceada5e 100644 --- a/src/mongo/db/pipeline/expression_test_api_version.cpp +++ b/src/mongo/db/pipeline/expression_test_api_version.cpp @@ -33,7 +33,10 @@ namespace mongo { -REGISTER_TEST_EXPRESSION(_testApiVersion, ExpressionTestApiVersion::parse); +REGISTER_TEST_EXPRESSION(_testApiVersion, + AllowedWithApiStrict::kSometimes, + AllowedWithClientType::kAny, + ExpressionTestApiVersion::parse); ExpressionTestApiVersion::ExpressionTestApiVersion(ExpressionContext* const expCtx, bool unstable, diff --git a/src/mongo/db/pipeline/expression_trigonometric.cpp b/src/mongo/db/pipeline/expression_trigonometric.cpp index a97bc000ef7..06b63e16bcb 100644 --- a/src/mongo/db/pipeline/expression_trigonometric.cpp +++ b/src/mongo/db/pipeline/expression_trigonometric.cpp @@ -39,29 +39,29 @@ namespace mongo { /** * Inclusive Bounds */ -REGISTER_EXPRESSION(acos, ExpressionArcCosine::parse); -REGISTER_EXPRESSION(asin, ExpressionArcSine::parse); -REGISTER_EXPRESSION(atanh, ExpressionHyperbolicArcTangent::parse); -REGISTER_EXPRESSION(acosh, ExpressionHyperbolicArcCosine::parse); +REGISTER_STABLE_EXPRESSION(acos, ExpressionArcCosine::parse); +REGISTER_STABLE_EXPRESSION(asin, ExpressionArcSine::parse); +REGISTER_STABLE_EXPRESSION(atanh, ExpressionHyperbolicArcTangent::parse); +REGISTER_STABLE_EXPRESSION(acosh, ExpressionHyperbolicArcCosine::parse); /** * Exclusive Bounds */ -REGISTER_EXPRESSION(cos, ExpressionCosine::parse); -REGISTER_EXPRESSION(sin, ExpressionSine::parse); -REGISTER_EXPRESSION(tan, ExpressionTangent::parse); +REGISTER_STABLE_EXPRESSION(cos, ExpressionCosine::parse); +REGISTER_STABLE_EXPRESSION(sin, ExpressionSine::parse); +REGISTER_STABLE_EXPRESSION(tan, ExpressionTangent::parse); /** * Unbounded */ -REGISTER_EXPRESSION(atan, ExpressionArcTangent::parse); -REGISTER_EXPRESSION(asinh, ExpressionHyperbolicArcSine::parse); -REGISTER_EXPRESSION(cosh, ExpressionHyperbolicCosine::parse); -REGISTER_EXPRESSION(sinh, ExpressionHyperbolicSine::parse); -REGISTER_EXPRESSION(tanh, ExpressionHyperbolicTangent::parse); +REGISTER_STABLE_EXPRESSION(atan, ExpressionArcTangent::parse); +REGISTER_STABLE_EXPRESSION(asinh, ExpressionHyperbolicArcSine::parse); +REGISTER_STABLE_EXPRESSION(cosh, ExpressionHyperbolicCosine::parse); +REGISTER_STABLE_EXPRESSION(sinh, ExpressionHyperbolicSine::parse); +REGISTER_STABLE_EXPRESSION(tanh, ExpressionHyperbolicTangent::parse); -REGISTER_EXPRESSION(atan2, ExpressionArcTangent2::parse); +REGISTER_STABLE_EXPRESSION(atan2, ExpressionArcTangent2::parse); -REGISTER_EXPRESSION(degreesToRadians, ExpressionDegreesToRadians::parse); -REGISTER_EXPRESSION(radiansToDegrees, ExpressionRadiansToDegrees::parse); +REGISTER_STABLE_EXPRESSION(degreesToRadians, ExpressionDegreesToRadians::parse); +REGISTER_STABLE_EXPRESSION(radiansToDegrees, ExpressionRadiansToDegrees::parse); } // namespace mongo diff --git a/src/mongo/db/pipeline/lite_parsed_document_source.h b/src/mongo/db/pipeline/lite_parsed_document_source.h index ceb33521bcc..3c2ab7d2772 100644 --- a/src/mongo/db/pipeline/lite_parsed_document_source.h +++ b/src/mongo/db/pipeline/lite_parsed_document_source.h @@ -38,6 +38,7 @@ #include "mongo/db/auth/privilege.h" #include "mongo/db/commands/server_status_metric.h" #include "mongo/db/namespace_string.h" +#include "mongo/db/query/allowed_contexts.h" #include "mongo/db/read_concern_support_result.h" #include "mongo/db/repl/read_concern_args.h" #include "mongo/stdx/unordered_set.h" @@ -54,32 +55,6 @@ class LiteParsedPipeline; */ class LiteParsedDocumentSource { public: - /** - * Flags to mark stages with different allowance constrains when API versioning is enabled. - */ - enum class AllowedWithApiStrict { - // The stage is always allowed in the pipeline regardless of API versions. - kAlways, - // This stage can be allowed in a stable API version, depending on the parameters. - kSometimes, - // The stage is allowed only for internal client when 'apiStrict' is set to true. - kInternal, - // The stage is never allowed in API version '1' when 'apiStrict' is set to true. - kNeverInVersion1 - }; - - /** - * Determines the type of client which is permitted to use a particular stage in its command - * request. Ensures that only internal clients are permitted to send or deserialize certain - * stages. - */ - enum class AllowedWithClientType { - // The stage can be specified in the command request of any client. - kAny, - // The stage can be specified in the command request of an internal client only. - kInternal, - }; - /* * This is the type of parser you should register using REGISTER_DOCUMENT_SOURCE. It need not * do any validation of options, only enough parsing to be able to implement the interface. diff --git a/src/mongo/db/pipeline/lite_parsed_pipeline.cpp b/src/mongo/db/pipeline/lite_parsed_pipeline.cpp index 437c7d17f5c..6c25f38573c 100644 --- a/src/mongo/db/pipeline/lite_parsed_pipeline.cpp +++ b/src/mongo/db/pipeline/lite_parsed_pipeline.cpp @@ -128,58 +128,27 @@ void LiteParsedPipeline::tickGlobalStageCounters() const { void LiteParsedPipeline::validate(const OperationContext* opCtx, bool performApiVersionChecks) const { - // An internal client could be one of the following : - // - Does not have any transport session - // - The transport session tag is internal - auto client = opCtx->getClient(); - const auto isInternalClient = - !client->session() || (client->session()->getTags() & transport::Session::kInternalClient); - - const auto apiParameters = APIParameters::get(opCtx); - auto apiVersion = apiParameters.getAPIVersion().value_or(""); - auto apiStrict = apiParameters.getAPIStrict().value_or(false); - using AllowanceFlags = LiteParsedDocumentSource::AllowedWithApiStrict; int internalUnpackBucketCount = 0; for (auto&& stage : _stageSpecs) { const auto& stageName = stage->getParseTimeName(); const auto& stageInfo = LiteParsedDocumentSource::getInfo(stageName); - uassert(5491300, - str::stream() << "The stage '" << stageName << "' is not allowed in user requests", - !(stageInfo.allowedWithClientType == - LiteParsedDocumentSource::AllowedWithClientType::kInternal && - !isInternalClient)); - // Validate that the stage is API version compatible. - if (performApiVersionChecks && apiStrict) { - switch (stageInfo.allowedWithApiStrict) { - case AllowanceFlags::kNeverInVersion1: { - uassert(ErrorCodes::APIStrictError, - str::stream() - << "stage " << stageName - << " is not allowed with 'apiStrict: true' in API Version " - << apiVersion, - apiVersion != "1"); - break; - } - case AllowanceFlags::kInternal: { - uassert(ErrorCodes::APIStrictError, - str::stream() - << "Internal stage " << stageName - << " cannot be specified with 'apiStrict: true' in API Version " - << apiVersion, - isInternalClient); - break; - } - case AllowanceFlags::kSometimes: { + if (performApiVersionChecks) { + + std::function<void(const APIParameters&)> sometimesCallback = + [&](const APIParameters& apiParameters) { + tassert(5807600, + "Expected callback only if allowed 'sometimes'", + stageInfo.allowedWithApiStrict == AllowedWithApiStrict::kSometimes); stage->assertPermittedInAPIVersion(apiParameters); - break; - } - case AllowanceFlags::kAlways: { - break; - } - } + }; + assertLanguageFeatureIsAllowed(opCtx, + stageName, + stageInfo.allowedWithApiStrict, + stageInfo.allowedWithClientType, + sometimesCallback); } internalUnpackBucketCount += diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index 0fa78436533..ba5197e38e5 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -113,8 +113,8 @@ env.Library( "$BUILD_DIR/mongo/util/fail_point", "collation/collator_factory_icu", "collation/collator_icu", + "common_query_enums_and_helpers", "datetime/init_timezone_data", - "explain_options", "query_planner", "query_request", ], @@ -124,15 +124,20 @@ env.Library( ) env.Library( - target="explain_options", + target="common_query_enums_and_helpers", source=[ + "allowed_contexts.cpp", "explain_options.cpp", "explain_verbosity.idl" ], LIBDEPS=[ "$BUILD_DIR/mongo/base", + "$BUILD_DIR/mongo/db/api_parameters", "$BUILD_DIR/mongo/idl/idl_parser", ], + LIBDEPS_PRIVATE=[ + "$BUILD_DIR/mongo/transport/transport_layer_common", + ], ) env.Library( @@ -372,7 +377,7 @@ env.CppUnitTest( "collation/collator_factory_mock", "collation/collator_interface_mock", "command_request_response", - "explain_options", + "common_query_enums_and_helpers", "hint_parser", "map_reduce_output_format", "query_common", diff --git a/src/mongo/db/query/allowed_contexts.cpp b/src/mongo/db/query/allowed_contexts.cpp new file mode 100644 index 00000000000..1a2eebadc7f --- /dev/null +++ b/src/mongo/db/query/allowed_contexts.cpp @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2021-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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/query/allowed_contexts.h" + +#include "mongo/transport/session.h" + +namespace mongo { + +void assertLanguageFeatureIsAllowed( + const OperationContext* opCtx, + std::string operatorName, + AllowedWithApiStrict allowedWithApiStrict, + AllowedWithClientType allowedWithClientType, + boost::optional<std::function<void(const APIParameters&)>> callbackForSometimesAllowed) { + // An internal client could be one of the following : + // - Does not have any transport session + // - The transport session tag is internal + auto client = opCtx->getClient(); + const auto isInternalClient = client && + (!client->session() || + (client->session()->getTags() & transport::Session::kInternalClient)); + + const auto apiParameters = APIParameters::get(opCtx); + + uassert(5491300, + str::stream() << operatorName << "' is not allowed in user requests", + !(allowedWithClientType == AllowedWithClientType::kInternal && !isInternalClient)); + + const auto apiVersion = apiParameters.getAPIVersion().value_or(""); + const auto apiStrict = apiParameters.getAPIStrict().value_or(false); + if (!apiStrict) { + return; + } + switch (allowedWithApiStrict) { + case AllowedWithApiStrict::kNeverInVersion1: { + uassert(ErrorCodes::APIStrictError, + str::stream() << operatorName + << " is not allowed with 'apiStrict: true' in API Version " + << apiVersion, + apiVersion != "1"); + break; + } + case AllowedWithApiStrict::kInternal: { + uassert(ErrorCodes::APIStrictError, + str::stream() << operatorName + << " cannot be specified with 'apiStrict: true' in API Version " + << apiVersion, + isInternalClient); + break; + } + case AllowedWithApiStrict::kSometimes: { + if (auto callback = callbackForSometimesAllowed) { + (*callback)(apiParameters); + } + break; + } + case AllowedWithApiStrict::kAlways: { + break; + } + } +} +} // namespace mongo diff --git a/src/mongo/db/query/allowed_contexts.h b/src/mongo/db/query/allowed_contexts.h new file mode 100644 index 00000000000..a8c1506d749 --- /dev/null +++ b/src/mongo/db/query/allowed_contexts.h @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2021-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. + */ + +#pragma once + +#include "mongo/base/status.h" +#include "mongo/db/api_parameters.h" +#include "mongo/util/assert_util.h" + +namespace mongo { +/** + * Flags to mark language features with different allowance constraints when API versioning is + * enabled. + */ +enum class AllowedWithApiStrict { + // The stage is always allowed in the pipeline regardless of API versions. + kAlways, + // This stage can be allowed in a stable API version, depending on the parameters. + kSometimes, + // The stage is allowed only for internal client when 'apiStrict' is set to true. + kInternal, + // The stage is never allowed in API version '1' when 'apiStrict' is set to true. + kNeverInVersion1 +}; + +/** + * Determines the type of client which is permitted to use a particular stage in its command + * request. Ensures that only internal clients are permitted to send or deserialize certain + * stages. + */ +enum class AllowedWithClientType { + // The stage can be specified in the command request of any client. + kAny, + // The stage can be specified in the command request of an internal client only. + kInternal, +}; + +/** + * Asserts that the API parameters in 'apiParameters' are compatible with the restrictions on + * 'operatorName' given by 'allowedWithApiStrict' and 'allowedWithClientType'. If the operator is + * allowed 'sometimes', a callback can be provided in 'callbackForSometimesAllowed' to check if the + * conditions are met. + */ +void assertLanguageFeatureIsAllowed(const OperationContext* opCtx, + std::string operatorName, + AllowedWithApiStrict allowedWithApiStrict, + AllowedWithClientType allowedWithClientType, + boost::optional<std::function<void(const APIParameters&)>> + callbackForSometimesAllowed = boost::none); + +} // namespace mongo diff --git a/src/mongo/s/query/document_source_merge_cursors.cpp b/src/mongo/s/query/document_source_merge_cursors.cpp index 737ad721006..0472e3d0a54 100644 --- a/src/mongo/s/query/document_source_merge_cursors.cpp +++ b/src/mongo/s/query/document_source_merge_cursors.cpp @@ -41,7 +41,7 @@ namespace mongo { REGISTER_DOCUMENT_SOURCE(mergeCursors, LiteParsedDocumentSourceDefault::parse, DocumentSourceMergeCursors::createFromBson, - LiteParsedDocumentSource::AllowedWithApiStrict::kInternal); + AllowedWithApiStrict::kInternal); constexpr StringData DocumentSourceMergeCursors::kStageName; |