summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyle Suarez <kyle.suarez@mongodb.com>2022-05-16 19:58:12 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-05-16 21:03:13 +0000
commit807371ef59dbdbe3f6016d02df5b5df488df70a7 (patch)
treecfca0954394dac5c3492ed5469a08979ca5c1e9b
parent5cc18bcd682fc59cd3714800a69f34246049caee (diff)
downloadmongo-807371ef59dbdbe3f6016d02df5b5df488df70a7.tar.gz
SERVER-65699 delete test-only 'sbe' command
-rw-r--r--etc/backports_required_for_multiversion_tests.yml4
-rw-r--r--jstests/core/sbe/sbe_cmd.js79
-rw-r--r--jstests/core/txns/sbe_cmd_disallowed_in_txn.js51
-rw-r--r--jstests/core/views/views_all_commands.js1
-rw-r--r--jstests/replsets/db_reads_while_recovering_all_commands.js1
-rw-r--r--src/mongo/db/commands/SConscript1
-rw-r--r--src/mongo/db/exec/SConscript14
-rw-r--r--src/mongo/db/exec/sbe/SConscript15
-rw-r--r--src/mongo/db/exec/sbe/parser/parser.cpp2093
-rw-r--r--src/mongo/db/exec/sbe/parser/parser.h249
-rw-r--r--src/mongo/db/exec/sbe/parser/sbe_parser_test.cpp617
-rw-r--r--src/mongo/db/exec/sbe_cmd.cpp190
-rw-r--r--src/mongo/shell/db.js9
13 files changed, 0 insertions, 3324 deletions
diff --git a/etc/backports_required_for_multiversion_tests.yml b/etc/backports_required_for_multiversion_tests.yml
index 2ba3f9189e9..56151a12bde 100644
--- a/etc/backports_required_for_multiversion_tests.yml
+++ b/etc/backports_required_for_multiversion_tests.yml
@@ -102,8 +102,6 @@ last-continuous:
test_file: jstests/aggregation/range.js
- ticket: SERVER-59923
test_file: jstests/sharding/test_resharding_test_fixture_shutdown_retry_needed.js
- - ticket: SERVER-60567
- test_file: jstests/core/sbe/sbe_cmd.js
- ticket: SERVER-61087
test_file: jstests/core/sbe/sbe_ixscan_explain.js
- ticket: SERVER-56127
@@ -465,8 +463,6 @@ last-lts:
test_file: jstests/aggregation/range.js
- ticket: SERVER-59923
test_file: jstests/sharding/test_resharding_test_fixture_shutdown_retry_needed.js
- - ticket: SERVER-60567
- test_file: jstests/core/sbe/sbe_cmd.js
- ticket: SERVER-61087
test_file: jstests/core/sbe/sbe_ixscan_explain.js
- ticket: SERVER-56127
diff --git a/jstests/core/sbe/sbe_cmd.js b/jstests/core/sbe/sbe_cmd.js
deleted file mode 100644
index 017efae36ab..00000000000
--- a/jstests/core/sbe/sbe_cmd.js
+++ /dev/null
@@ -1,79 +0,0 @@
-// Tests internal 'sbe' command.
-// @tags: [
-// assumes_against_mongod_not_mongos,
-// does_not_support_stepdowns,
-// uses_testing_only_commands,
-// ]
-
-(function() {
-"use strict";
-
-load("jstests/aggregation/extras/utils.js"); // For assertArrayEq.
-load("jstests/libs/sbe_util.js"); // For checkSBEEnabled.
-
-const isSBEEnabled = checkSBEEnabled(db);
-if (!isSBEEnabled) {
- jsTestLog("Skipping test because the SBE feature flag is disabled");
- return;
-}
-
-// The sbe command requires lock-free reads, so determine if they're enabled before proceeding:
-// (1) ephemeralForTest automatically uses enableMajorityReadConcern=false, which disables lock-free
-// reads.
-// (2) lock-free reads are only supported in server versions 4.9+.
-const maxWireVersion = assert.commandWorked(db.runCommand({isMaster: 1})).maxWireVersion;
-const isLockFreeReadsEnabled = jsTest.options().storageEngine !== "ephemeralForTest" &&
- maxWireVersion >= 12 /* WIRE_VERSION_49 */;
-if (!isLockFreeReadsEnabled) {
- jsTestLog("Skipping test because lock-free reads are not enabled.");
- return;
-}
-
-const coll = db.jstests_sbe_cmd;
-coll.drop();
-const basicFind = coll.find().explain();
-if (!basicFind.queryPlanner.winningPlan.hasOwnProperty("slotBasedPlan")) {
- jsTestLog("Skipping test because the SBE feature flag is disabled");
- return;
-}
-
-// Helper that executes a given 'query', gets the generated 'slotBasedPlan' from explain output,
-// and runs that SBE plan through the internal 'sbe' command which executes the plan string
-// directly to verify that the command works.
-function assertSbeCommandWorked({query, projection = {}} = {}) {
- const queryResult = coll.find(query, projection);
- const expl = queryResult.explain();
-
- assert(expl.queryPlanner.winningPlan.hasOwnProperty("slotBasedPlan"));
- const slotBasedPlan = expl.queryPlanner.winningPlan.slotBasedPlan.stages;
-
- // Verify that the sbe command works and that the SBE plan string is parsed successfully.
- assert(db._sbe(slotBasedPlan));
-}
-
-assert.commandWorked(coll.insertMany([
- {_id: 0, a: 1, b: 1, c: 1},
- {_id: 1, a: 1, b: 1, c: 2},
- {_id: 2, a: 1, b: 1, c: 3},
- {_id: 3, a: 1, b: 2, c: 3}
-]));
-
-// Test query: {}.
-assertSbeCommandWorked({query: {}});
-// Test query {b: {$type: "string"}}.
-assertSbeCommandWorked({query: {b: {$type: "number"}}});
-// Test query: {a: {$exists: true}}.
-assertSbeCommandWorked({query: {a: {$exists: true}}});
-
-// Verify that the sbe command can detect if a collection has been dropped.
-const explain = coll.find().explain();
-assert(explain.queryPlanner.winningPlan.hasOwnProperty("slotBasedPlan"), explain);
-const slotBasedPlan = explain.queryPlanner.winningPlan.slotBasedPlan.stages;
-
-// The command response should be OK as long as the collection exists.
-assert(db._sbe(slotBasedPlan));
-
-// After the collection is dropped, the parser should detect that the collection doesn't exist.
-assert(coll.drop());
-assert.throwsWithCode(() => db._sbe(slotBasedPlan), 6056700);
-}());
diff --git a/jstests/core/txns/sbe_cmd_disallowed_in_txn.js b/jstests/core/txns/sbe_cmd_disallowed_in_txn.js
deleted file mode 100644
index 25a45abf6db..00000000000
--- a/jstests/core/txns/sbe_cmd_disallowed_in_txn.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// Tests that the internal 'sbe' command is disallowed inside a transaction.
-//
-// @tags: [
-// assumes_against_mongod_not_mongos,
-// does_not_support_stepdowns,
-// uses_testing_only_commands,
-// uses_transactions,
-// ]
-
-(function() {
-"use strict";
-
-load("jstests/libs/sbe_util.js"); // For checkSBEEnabled.
-
-const isSBEEnabled = checkSBEEnabled(db);
-if (!isSBEEnabled) {
- jsTestLog("Skipping test because the SBE feature flag is disabled");
- return;
-}
-
-const dbName = "sbe_cmd_disallowed_in_txn_db";
-const collName = "sbe_cmd_disallowed_in_txn_coll";
-const testDb = db.getSiblingDB(dbName);
-testDb.dropDatabase();
-
-const coll = testDb[collName];
-assert.commandWorked(coll.insertMany([
- {_id: 0, a: 1, b: 1, c: 1},
- {_id: 1, a: 1, b: 1, c: 2},
- {_id: 2, a: 1, b: 1, c: 3},
- {_id: 3, a: 1, b: 2, c: 3}
-]));
-
-// Use explain to obtain a test SBE command string.
-const explain = coll.find({a: 1, b: 2}).explain();
-if (!explain.queryPlanner.winningPlan.hasOwnProperty("slotBasedPlan")) {
- jsTestLog("Skipping test because the SBE feature flag is disabled");
- return;
-}
-const slotBasedPlan = explain.queryPlanner.winningPlan.slotBasedPlan;
-assert(slotBasedPlan.hasOwnProperty("stages"), explain);
-const sbeString = slotBasedPlan.stages;
-
-const session = testDb.getMongo().startSession();
-const sessionDb = session.getDatabase(dbName);
-
-session.startTransaction();
-assert.throwsWithCode(() => sessionDb._sbe(slotBasedPlan).itcount(),
- ErrorCodes.OperationNotSupportedInTransaction);
-session.abortTransaction();
-}());
diff --git a/jstests/core/views/views_all_commands.js b/jstests/core/views/views_all_commands.js
index 63ef47a9514..bbdb2623e75 100644
--- a/jstests/core/views/views_all_commands.js
+++ b/jstests/core/views/views_all_commands.js
@@ -604,7 +604,6 @@ let viewsCommandTests = {
rotateCertificates: {skip: isUnrelated},
saslContinue: {skip: isUnrelated},
saslStart: {skip: isUnrelated},
- sbe: {skip: isAnInternalCommand},
serverStatus: {command: {serverStatus: 1}, skip: isUnrelated},
setChangeStreamOptions: {skip: isUnrelated}, // TODO SERVER-65353 remove in 6.1.
setIndexCommitQuorum: {skip: isUnrelated},
diff --git a/jstests/replsets/db_reads_while_recovering_all_commands.js b/jstests/replsets/db_reads_while_recovering_all_commands.js
index e4984ba3b8e..ec95e98892e 100644
--- a/jstests/replsets/db_reads_while_recovering_all_commands.js
+++ b/jstests/replsets/db_reads_while_recovering_all_commands.js
@@ -330,7 +330,6 @@ const allCommands = {
rotateCertificates: {skip: isNotAUserDataRead},
saslContinue: {skip: isPrimaryOnly},
saslStart: {skip: isPrimaryOnly},
- sbe: {skip: isAnInternalCommand},
serverStatus: {skip: isNotAUserDataRead},
setAuditConfig: {skip: isNotAUserDataRead},
setChangeStreamOptions: {skip: isPrimaryOnly}, // TODO SERVER-65353 remove in 6.1.
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index c6d2d8232e0..590b145470f 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -560,7 +560,6 @@ env.Library(
'$BUILD_DIR/mongo/db/commands',
'$BUILD_DIR/mongo/db/curop_failpoint_helpers',
'$BUILD_DIR/mongo/db/dbhelpers',
- '$BUILD_DIR/mongo/db/exec/sbe_cmd',
'$BUILD_DIR/mongo/db/exec/stagedebug_cmd',
'$BUILD_DIR/mongo/db/fle_crud_mongod',
'$BUILD_DIR/mongo/db/index_builds_coordinator_interface',
diff --git a/src/mongo/db/exec/SConscript b/src/mongo/db/exec/SConscript
index bf6e0edb75a..c2caf69f0f2 100644
--- a/src/mongo/db/exec/SConscript
+++ b/src/mongo/db/exec/SConscript
@@ -116,20 +116,6 @@ env.Library(
],
)
-env.Library(
- target='sbe_cmd',
- source=[
- 'sbe_cmd.cpp'
- ],
- LIBDEPS=[
- "$BUILD_DIR/mongo/db/query_exec",
- "sbe/query_sbe_parser",
- ],
- LIBDEPS_PRIVATE=[
- '$BUILD_DIR/mongo/db/commands/test_commands_enabled',
- ],
-)
-
env.CppUnitTest(
target='db_exec_test',
source=[
diff --git a/src/mongo/db/exec/sbe/SConscript b/src/mongo/db/exec/sbe/SConscript
index 21bcb0f1a53..b99625b0833 100644
--- a/src/mongo/db/exec/sbe/SConscript
+++ b/src/mongo/db/exec/sbe/SConscript
@@ -128,19 +128,6 @@ env.Library(
)
env.Library(
- target='query_sbe_parser',
- source=[
- 'parser/parser.cpp',
- ],
- LIBDEPS=[
- 'query_sbe',
- 'query_sbe_abt',
- 'query_sbe_stages',
- 'query_sbe_storage',
- ]
- )
-
-env.Library(
target='query_sbe_abt',
source=[
'abt/abt_lower.cpp',
@@ -201,7 +188,6 @@ env.CppUnitTest(
'expressions/sbe_trigonometric_expressions_test.cpp',
'expressions/sbe_trunc_builtin_test.cpp',
'expressions/sbe_ts_second_ts_increment_test.cpp',
- 'parser/sbe_parser_test.cpp',
'sbe_column_scan_test.cpp',
'sbe_filter_test.cpp',
'sbe_hash_agg_test.cpp',
@@ -234,7 +220,6 @@ env.CppUnitTest(
'$BUILD_DIR/mongo/db/query/collation/collator_interface_mock',
'$BUILD_DIR/mongo/db/service_context_d_test_fixture',
'$BUILD_DIR/mongo/db/service_context_test_fixture',
- 'query_sbe_parser',
'sbe_plan_stage_test',
],
)
diff --git a/src/mongo/db/exec/sbe/parser/parser.cpp b/src/mongo/db/exec/sbe/parser/parser.cpp
deleted file mode 100644
index 5dda6c508b1..00000000000
--- a/src/mongo/db/exec/sbe/parser/parser.cpp
+++ /dev/null
@@ -1,2093 +0,0 @@
-/**
- * Copyright (C) 2019-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.
- */
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kQuery
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/exec/sbe/parser/parser.h"
-
-#include <charconv>
-
-#include "mongo/db/exec/sbe/abt/abt_lower.h"
-#include "mongo/db/exec/sbe/stages/branch.h"
-#include "mongo/db/exec/sbe/stages/co_scan.h"
-#include "mongo/db/exec/sbe/stages/column_scan.h"
-#include "mongo/db/exec/sbe/stages/exchange.h"
-#include "mongo/db/exec/sbe/stages/filter.h"
-#include "mongo/db/exec/sbe/stages/hash_agg.h"
-#include "mongo/db/exec/sbe/stages/hash_join.h"
-#include "mongo/db/exec/sbe/stages/ix_scan.h"
-#include "mongo/db/exec/sbe/stages/limit_skip.h"
-#include "mongo/db/exec/sbe/stages/loop_join.h"
-#include "mongo/db/exec/sbe/stages/makeobj.h"
-#include "mongo/db/exec/sbe/stages/project.h"
-#include "mongo/db/exec/sbe/stages/scan.h"
-#include "mongo/db/exec/sbe/stages/sort.h"
-#include "mongo/db/exec/sbe/stages/sorted_merge.h"
-#include "mongo/db/exec/sbe/stages/traverse.h"
-#include "mongo/db/exec/sbe/stages/union.h"
-#include "mongo/db/exec/sbe/stages/unique.h"
-#include "mongo/db/exec/sbe/stages/unwind.h"
-#include "mongo/db/query/optimizer/rewrites/const_eval.h"
-#include "mongo/db/query/optimizer/rewrites/path_lower.h"
-#include "mongo/logv2/log.h"
-#include "mongo/util/str.h"
-
-namespace mongo {
-namespace sbe {
-
-static std::string format_error_message(size_t ln, size_t col, const std::string& msg) {
- return str::stream() << ln << ":" << col << ": " << msg << '\n';
-}
-
-static constexpr auto kSyntax = R"(
- ROOT <- OPERATOR
-
- PLAN_NODE_ID <- ('['([0-9])+']')
-
- OPERATOR <- PLAN_NODE_ID? (SCAN / PSCAN / SEEK / IXSCAN / IXSEEK / PROJECT / FILTER / CFILTER /
- MKOBJ / MKBSON / GROUP / HJOIN / NLJOIN / LIMIT / SKIP / COSCAN / TRAVERSE /
- EXCHANGE / SORT / UNWIND / UNION / BRANCH / SIMPLE_PROJ / PFO /
- ESPOOL / LSPOOL / CSPOOL / SSPOOL / UNIQUE / SORTED_MERGE / COLUMNSCAN)
-
- FORWARD_FLAG <- <'true'> / <'false'>
-
- NEED_SLOT_FOR_OPLOG_TS <- <'true'> / <'false'>
-
- SCAN <- 'scan' IDENT? # optional variable name of the root object (record) delivered by the scan
- IDENT? # optional variable name of the record id delivered by the scan
- IDENT? # optional variable name of the snapshot id read by the scan
- IDENT? # optional variable name of the index name read by the scan
- IDENT? # optional variable name of the index key read by the scan
- IDENT? # optional variable name of the index key pattern read by the scan
- IDENT_LIST_WITH_RENAMES # list of projected fields (may be empty)
- IDENT # collection name to scan
- FORWARD_FLAG # forward scan or not
- NEED_SLOT_FOR_OPLOG_TS # Whether a slot needs to be created for oplog timestamp
-
- PSCAN <- 'pscan' IDENT? # optional variable name of the root object (record) delivered by the scan
- IDENT? # optional variable name of the record id delivered by the scan
- IDENT? # optional variable name of the snapshot id read by the scan
- IDENT? # optional variable name of the index name read by the scan
- IDENT? # optional variable name of the index key read by the scan
- IDENT? # optional variable name of the index key pattern read by the scan
- IDENT_LIST_WITH_RENAMES # list of projected fields (may be empty)
- IDENT # collection name to scan
-
- SEEK <- 'seek' IDENT # variable name of the key
- IDENT? # optional variable name of the root object (record) delivered by the scan
- IDENT? # optional variable name of the record id delivered by the scan
- IDENT? # optional variable name of the snapshot id delivered by the scan
- IDENT? # optional variable name of the index name read by the scan
- IDENT? # optional variable name of the index key read by the scan
- IDENT? # optional variable name of the index key pattern read by the scan
- IDENT_LIST_WITH_RENAMES # list of projected fields (may be empty)
- IDENT # collection name to scan
- FORWARD_FLAG # forward scan or not
- NEED_SLOT_FOR_OPLOG_TS # Whether a slot needs to be created for oplog timestamp
-
- IXSCAN <- 'ixscan' IDENT? # optional variable name of the root object (record) delivered by the scan
- IDENT? # optional variable name of the record id delivered by the scan
- IDENT? # optional variable name of the snapshot id delivered by the scan
- IX_KEY_LIST_WITH_RENAMES # list of projected fields (may be empty)
- IDENT # collection name
- IDENT # index name to scan
- FORWARD_FLAG # forward scan or not
-
- IXSEEK <- 'ixseek' IDENT # variable name of the low key
- IDENT # variable name of the high key
- IDENT? # optional variable name of the root object (record) delivered by the scan
- IDENT? # optional variable name of the record id delivered by the scan
- IDENT? # optional variable name of the snapshot id delivered by the scan
- IX_KEY_LIST_WITH_RENAMES # list of projected fields (may be empty)
- IDENT # collection name
- IDENT # index name to scan
- FORWARD_FLAG # forward seek or not
-
- COLUMNSCAN <- 'columnscan' STRING_LIST # paths
- IDENT_LIST # output variables
- IDENT # output record
- IDENT # output RID
- IDENT # collection name
- IDENT # index name to scan
-
- PROJECT <- 'project' PROJECT_LIST OPERATOR
- SIMPLE_PROJ <- '$p' IDENT # output
- IDENT # input
- IDENT_LIST # correlated slots
- PFV # path
- OPERATOR # input
- PFV <- (IDENT '.' PFV) / ( '|' EXPR / IDENT)
-
- FILTER <- 'filter' '{' EXPR '}' OPERATOR
- CFILTER <- 'cfilter' '{' EXPR '}' OPERATOR
-
- MKOBJ_FLAG <- <'true'> / <'false'>
- MKOBJ_DROP_KEEP_FLAG <- <'drop'> / <'keep'>
- MKOBJ <- 'mkobj' IDENT
- (IDENT # Old root
- IDENT_LIST # field names
- MKOBJ_DROP_KEEP_FLAG)? # drop or keep
- IDENT_LIST_WITH_RENAMES # project list
- MKOBJ_FLAG # Force new object
- MKOBJ_FLAG # Return old object
- OPERATOR # child
-
- MKBSON <- 'mkbson' IDENT
- (IDENT # Old root
- IDENT_LIST # field names
- MKOBJ_DROP_KEEP_FLAG)? # drop or keep
- IDENT_LIST_WITH_RENAMES # project list
- MKOBJ_FLAG # Force new object
- MKOBJ_FLAG # Return old object
- OPERATOR # child
-
- GROUP <- 'group' IDENT_LIST
- PROJECT_LIST
- IDENT? # optional collator slot
- OPERATOR
- HJOIN <- 'hj' IDENT? # optional collator slot
- LEFT
- RIGHT
- LEFT <- 'left' IDENT_LIST IDENT_LIST OPERATOR
- RIGHT <- 'right' IDENT_LIST IDENT_LIST OPERATOR
- NLJOIN <- 'nlj' IDENT_LIST # projected outer variables
- IDENT_LIST # correlated parameters
- ('{' EXPR '}')? # optional predicate
- 'left' OPERATOR # outer side
- 'right' OPERATOR # inner side
-
- LIMIT <- 'limit' NUMBER OPERATOR
- SKIP <- 'limitskip' (NUMBER / 'none') NUMBER OPERATOR
- COSCAN <- 'coscan'
- TRAVERSE <- 'traverse' IDENT # output of traverse
- IDENT # output of traverse as seen inside the 'in' branch
- IDENT # input of traverse
- IDENT_LIST? # optional correlated slots
- (('{' (EXPR) '}') / EMPTY_EXPR_TOK) # optional fold expression
- (('{' (EXPR) '}') / EMPTY_EXPR_TOK) # optional final expression
- 'from' OPERATOR
- 'in' OPERATOR
- EXCHANGE <- 'exchange' IDENT_LIST NUMBER IDENT OPERATOR
- SORT <- 'sort' IDENT_LIST
- SORT_DIR_LIST # sort directions
- IDENT_LIST
- NUMBER? # optional 'limit'
- OPERATOR
- UNWIND <- 'unwind' IDENT IDENT IDENT UNWIND_FLAG OPERATOR
- UNWIND_FLAG <- <'true'> / <'false'>
- UNION <- 'union' IDENT_LIST UNION_BRANCH_LIST
-
- UNION_BRANCH_LIST <- '[' (UNION_BRANCH (',' UNION_BRANCH)* )?']'
- UNION_BRANCH <- IDENT_LIST OPERATOR
-
- SORTED_MERGE <- 'smerge' IDENT_LIST SORT_DIR_LIST SORTED_MERGE_BRANCH_LIST
- SORTED_MERGE_BRANCH_LIST <- '[' (SORTED_MERGE_BRANCH (',' SORTED_MERGE_BRANCH)* )?']'
- SORTED_MERGE_BRANCH <- IDENT_LIST # key slots
- IDENT_LIST # value slots
- OPERATOR
-
- BRANCH <- 'branch' '{' EXPR '}' # boolean condition/switch
- IDENT_LIST # output of the operator
- IDENT_LIST # output of the then branch
- OPERATOR # then branch
- IDENT_LIST # output of the else branch
- OPERATOR # else branch
- PFO <- '$pfo' IDENT # output
- IDENT # input
- IDENT_LIST # correlated slots
- PATH # path
- OPERATOR # input operator
- PATH <- '{' PF (',' PF)* '}'
- PF <- (IDENT ':' PF_ACTION) / PF_DROPALL
- PF_ACTION <- PATH / PF_EXPR / PF_MEXPR / PF_DROP / PF_INCL
- PF_DROP <- '0'
- PF_INCL <- '1'
- PF_DROPALL <- '~'
- PF_EXPR <- '=' EXPR
- PF_MEXPR <- '|' EXPR
-
- ESPOOL <- 'espool' IDENT # buffer
- IDENT_LIST # slots
- OPERATOR # input stage
-
- LSPOOL <- 'lspool' IDENT # buffer
- IDENT_LIST # slots
- ('{' EXPR '}')? # optional predicate
- OPERATOR # input stage
-
- CSPOOL <- 'cspool' IDENT # buffer
- IDENT_LIST # slots
-
- SSPOOL <- 'sspool' IDENT # buffer
- IDENT_LIST # slots
-
- UNIQUE <- 'unique' IDENT_LIST # slots
- OPERATOR # input
-
- PROJECT_LIST <- '[' (ASSIGN (',' ASSIGN)* )?']'
- ASSIGN <- IDENT '=' EXPR
-
- EXPR <- EQOP_EXPR? LOG_TOK EXPR / EQOP_EXPR
- LOG_TOK <- <'&&'> / <'||'> / <'!'>
-
- EQOP_EXPR <- RELOP_EXPR EQ_TOK ('[' EXPR ']')? EQOP_EXPR / RELOP_EXPR
- EQ_TOK <- <'=='> / <'!='>
-
- RELOP_EXPR <- ADD_EXPR REL_TOK ('[' EXPR ']')? RELOP_EXPR / ADD_EXPR
- REL_TOK <- <'<=>'> / <'<='> / <'<'> / <'>='> / <'>'>
-
- ADD_EXPR <- MUL_EXPR ADD_TOK ADD_EXPR / MUL_EXPR
- ADD_TOK <- <'+'> / <'-'>
-
- MUL_EXPR <- PRIMARY_EXPR MUL_TOK MUL_EXPR / PRIMARY_EXPR
- MUL_TOK <- <'*'> / <'/'>
-
- PRIMARY_EXPR <- '(' EXPR ')' / CONST_TOK / IF_EXPR / LET_EXPR / FUN_CALL / LAMBDA_EXPR / IDENT / NUMBER / STRING
- CONST_TOK <- <'true'> / <'false'> / <'null'> / <'#'> / EMPTY_EXPR_TOK
- EMPTY_EXPR_TOK <- <'{}'>
-
- IF_EXPR <- 'if' '(' EXPR ',' EXPR ',' EXPR ')'
-
- LET_EXPR <- 'let' FRAME_PROJECT_LIST EXPR
- FRAME_PROJECT_LIST <- '[' (ASSIGN (',' ASSIGN)* )?']'
-
- LAMBDA_EXPR <- '\\' IDENT '.' EXPR
-
- FUN_CALL <- IDENT # function call identifier
- '(' (EXPR (',' EXPR)*)? ')' # function call arguments
-
- IDENT_LIST_WITH_RENAMES <- '[' (IDENT_WITH_RENAME (',' IDENT_WITH_RENAME)*)? ']'
- IDENT_WITH_RENAME <- IDENT ('=' IDENT)?
-
- IX_KEY_LIST_WITH_RENAMES <- '[' (IX_KEY_WITH_RENAME (',' IX_KEY_WITH_RENAME)*)? ']'
- IX_KEY_WITH_RENAME <- IDENT '=' NUMBER
-
- IDENT_LIST <- '[' (IDENT (',' IDENT)*)? ']'
- IDENT <- RAW_IDENT/ESC_IDENT
-
- STRING <- < '"' (!'"' .)* '"' > / < '\'' (!'\'' .)* '\'' >
- STRING_LIST <- '[' (STRING (',' STRING)*)? ']'
-
- NUMBER <- < [0-9]+ >
-
- RAW_IDENT <- < !STAGE_KEYWORDS ([$a-zA-Z_] [$a-zA-Z0-9-_]*) >
-
- ESC_IDENT <- < '@' '"' (!'"' .)* '"' >
-
- SORT_DIR <- <'asc'> / <'desc'>
- SORT_DIR_LIST <- '[' (SORT_DIR (',' SORT_DIR)* )? ']'
-
- STAGE_KEYWORDS <- <'left'> / <'right'> # reserved keywords to avoid collision with IDENT names
-
- %whitespace <- ([ \t\r\n]* ('#' (!'\n' .)* '\n' [ \t\r\n]*)*)
- %word <- [a-z]+
- )";
-
-std::pair<IndexKeysInclusionSet, sbe::value::SlotVector> Parser::lookupIndexKeyRenames(
- const std::vector<std::string>& renames, const std::vector<size_t>& indexKeys) {
- IndexKeysInclusionSet indexKeysInclusion;
-
- // Each indexKey is associated with the parallel remap from the 'renames' vector. This
- // map explicitly binds each indexKey with its remap and sorts them by 'indexKey' order.
- std::map<int, sbe::value::SlotId> slotsByKeyIndex;
- invariant(renames.size() == indexKeys.size());
- for (size_t idx = 0; idx < renames.size(); idx++) {
- uassert(4872100,
- str::stream() << "Cannot project index key at position " << indexKeys[idx],
- indexKeys[idx] < indexKeysInclusion.size());
- slotsByKeyIndex[indexKeys[idx]] = lookupSlotStrict(renames[idx]);
- }
-
- sbe::value::SlotVector slots;
- for (auto&& [indexKey, slot] : slotsByKeyIndex) {
- indexKeysInclusion.set(indexKey);
- slots.push_back(slot);
- }
-
- return {indexKeysInclusion, std::move(slots)};
-}
-
-std::vector<sbe::value::SortDirection> parseSortDirList(const AstQuery& ast) {
- std::vector<value::SortDirection> dirs;
- for (const auto& node : ast.nodes) {
- if (node->token == "asc") {
- dirs.push_back(value::SortDirection::Ascending);
- } else if (node->token == "desc") {
- dirs.push_back(value::SortDirection::Descending);
- } else {
- MONGO_UNREACHABLE;
- }
- }
- return dirs;
-}
-
-MakeObjFieldBehavior parseFieldBehavior(StringData val) {
- if (val == "drop") {
- return MakeObjFieldBehavior::drop;
- } else if (val == "keep") {
- return MakeObjFieldBehavior::keep;
- }
- MONGO_UNREACHABLE_TASSERT(5389100);
-}
-
-void Parser::walkChildren(AstQuery& ast) {
- for (const auto& node : ast.nodes) {
- walk(*node);
- }
-}
-
-void Parser::walkIdent(AstQuery& ast) {
- auto str = ast.nodes[0]->token;
- // Drop @" .. ".
- if (!str.empty() && str[0] == '@') {
- str = str.substr(2, str.size() - 3);
- }
- ast.identifier = std::move(str);
-}
-
-void Parser::walkIdentList(AstQuery& ast) {
- walkChildren(ast);
- for (auto& node : ast.nodes) {
- ast.identifiers.emplace_back(std::move(node->identifier));
- }
-}
-
-void Parser::walkStringList(AstQuery& ast) {
- for (auto& node : ast.nodes) {
- auto str = node->token;
- // Drop quotes.
- ast.identifiers.emplace_back(str = str.substr(1, str.size() - 2));
- }
-}
-
-void Parser::walkIdentWithRename(AstQuery& ast) {
- walkChildren(ast);
- std::string identifier;
- std::string rename;
-
- if (ast.nodes.size() == 1) {
- rename = ast.nodes[0]->identifier;
- identifier = rename;
- } else {
- rename = ast.nodes[0]->identifier;
- identifier = ast.nodes[1]->identifier;
- }
-
- ast.identifier = std::move(identifier);
- ast.rename = std::move(rename);
-}
-
-void Parser::walkIdentListWithRename(AstQuery& ast) {
- walkChildren(ast);
-
- for (auto& node : ast.nodes) {
- ast.identifiers.emplace_back(std::move(node->identifier));
- ast.renames.emplace_back(std::move(node->rename));
- }
-}
-
-void Parser::walkIxKeyWithRename(AstQuery& ast) {
- walkChildren(ast);
-
- ast.rename = ast.nodes[0]->identifier;
-
- // This token cannot be negative, because the parser does not accept a "-" prefix.
- ast.indexKey = static_cast<size_t>(std::stoi(ast.nodes[1]->token));
-}
-
-void Parser::walkIxKeyListWithRename(AstQuery& ast) {
- walkChildren(ast);
-
- for (auto& node : ast.nodes) {
- ast.indexKeys.emplace_back(std::move(node->indexKey));
- ast.renames.emplace_back(std::move(node->rename));
- }
-}
-
-void Parser::walkProjectList(AstQuery& ast) {
- walkChildren(ast);
-
- for (size_t idx = 0; idx < ast.nodes.size(); ++idx) {
- ast.projects[ast.nodes[idx]->identifier] = std::move(ast.nodes[idx]->expr);
- }
-}
-
-void Parser::walkAssign(AstQuery& ast) {
- walkChildren(ast);
-
- ast.identifier = ast.nodes[0]->identifier;
- ast.expr = std::move(ast.nodes[1]->expr);
-}
-
-void Parser::walkExpr(AstQuery& ast) {
- walkChildren(ast);
- if (ast.nodes.size() == 1) {
- ast.expr = std::move(ast.nodes[0]->expr);
- } else if (ast.nodes.size() == 2) {
- // Check if it is an expression with a unary op.
- EPrimUnary::Op op;
- if (ast.nodes[0]->token == "!") {
- op = EPrimUnary::logicNot;
- } else {
- MONGO_UNREACHABLE_TASSERT(5088501);
- }
-
- ast.expr = makeE<EPrimUnary>(op, std::move(ast.nodes[1]->expr));
- } else {
- // Otherwise we have an expression with a binary op.
- EPrimBinary::Op op;
- if (ast.nodes[1]->token == "&&") {
- op = EPrimBinary::logicAnd;
- } else if (ast.nodes[1]->token == "||") {
- op = EPrimBinary::logicOr;
- } else {
- MONGO_UNREACHABLE_TASSERT(5088502);
- }
-
- ast.expr =
- makeE<EPrimBinary>(op, std::move(ast.nodes[0]->expr), std::move(ast.nodes[2]->expr));
- }
-}
-
-void Parser::walkEqopExpr(AstQuery& ast) {
- walkChildren(ast);
- if (ast.nodes.size() == 1) {
- ast.expr = std::move(ast.nodes[0]->expr);
- } else {
- EPrimBinary::Op op;
- if (ast.nodes[1]->token == "==") {
- op = EPrimBinary::eq;
- } else if (ast.nodes[1]->token == "!=") {
- op = EPrimBinary::neq;
- }
-
- if (ast.nodes.size() == 4) {
- ast.expr = makeE<EPrimBinary>(op,
- std::move(ast.nodes[0]->expr),
- std::move(ast.nodes[3]->expr),
- std::move(ast.nodes[2]->expr));
- } else {
- ast.expr = makeE<EPrimBinary>(
- op, std::move(ast.nodes[0]->expr), std::move(ast.nodes[2]->expr));
- }
- }
-}
-
-void Parser::walkRelopExpr(AstQuery& ast) {
- walkChildren(ast);
- if (ast.nodes.size() == 1) {
- ast.expr = std::move(ast.nodes[0]->expr);
- } else {
- EPrimBinary::Op op;
- if (ast.nodes[1]->token == "<=>") {
- op = EPrimBinary::cmp3w;
- } else if (ast.nodes[1]->token == "<=") {
- op = EPrimBinary::lessEq;
- } else if (ast.nodes[1]->token == "<") {
- op = EPrimBinary::less;
- } else if (ast.nodes[1]->token == ">=") {
- op = EPrimBinary::greaterEq;
- } else if (ast.nodes[1]->token == ">") {
- op = EPrimBinary::greater;
- } else {
- MONGO_UNREACHABLE;
- }
-
- if (ast.nodes.size() == 4) {
- ast.expr = makeE<EPrimBinary>(op,
- std::move(ast.nodes[0]->expr),
- std::move(ast.nodes[3]->expr),
- std::move(ast.nodes[2]->expr));
- } else {
- ast.expr = makeE<EPrimBinary>(
- op, std::move(ast.nodes[0]->expr), std::move(ast.nodes[2]->expr));
- }
- }
-}
-
-void Parser::walkAddExpr(AstQuery& ast) {
- walkChildren(ast);
- if (ast.nodes.size() == 1) {
- ast.expr = std::move(ast.nodes[0]->expr);
- } else {
- ast.expr =
- makeE<EPrimBinary>(ast.nodes[1]->token == "+" ? EPrimBinary::add : EPrimBinary::sub,
- std::move(ast.nodes[0]->expr),
- std::move(ast.nodes[2]->expr));
- }
-}
-
-void Parser::walkMulExpr(AstQuery& ast) {
- walkChildren(ast);
- if (ast.nodes.size() == 1) {
- ast.expr = std::move(ast.nodes[0]->expr);
- } else {
- ast.expr =
- makeE<EPrimBinary>(ast.nodes[1]->token == "*" ? EPrimBinary::mul : EPrimBinary::div,
- std::move(ast.nodes[0]->expr),
- std::move(ast.nodes[2]->expr));
- }
-}
-
-void Parser::walkPrimaryExpr(AstQuery& ast) {
- using namespace peg::udl;
-
- walkChildren(ast);
-
- if (ast.nodes[0]->tag == "IDENT"_) {
- // Lookup local binds (let) first.
- auto symbol = lookupSymbol(ast.nodes[0]->identifier);
- if (symbol) {
- ast.expr = makeE<EVariable>(symbol->id, symbol->slotId);
- } else {
- ast.expr = makeE<EVariable>(lookupSlotStrict(ast.nodes[0]->identifier));
- }
- } else if (ast.nodes[0]->tag == "NUMBER"_) {
- ast.expr = makeE<EConstant>(value::TypeTags::NumberInt64,
- value::bitcastFrom<int64_t>(std::stoll(ast.nodes[0]->token)));
- } else if (ast.nodes[0]->tag == "CONST_TOK"_) {
- if (ast.nodes[0]->token == "true") {
- ast.expr = makeE<EConstant>(value::TypeTags::Boolean, value::bitcastFrom<bool>(true));
- } else if (ast.nodes[0]->token == "false") {
- ast.expr = makeE<EConstant>(value::TypeTags::Boolean, value::bitcastFrom<bool>(false));
- } else if (ast.nodes[0]->token == "null") {
- ast.expr = makeE<EConstant>(value::TypeTags::Null, 0);
- } else if (ast.nodes[0]->token == "#") {
- ast.expr = makeE<EConstant>(value::TypeTags::Nothing, 0);
- }
- } else if (ast.nodes[0]->tag == "EXPR"_) {
- ast.expr = std::move(ast.nodes[0]->expr);
- } else if (ast.nodes[0]->tag == "IF_EXPR"_) {
- ast.expr = std::move(ast.nodes[0]->expr);
- } else if (ast.nodes[0]->tag == "LET_EXPR"_) {
- ast.expr = std::move(ast.nodes[0]->expr);
- } else if (ast.nodes[0]->tag == "LAMBDA_EXPR"_) {
- ast.expr = std::move(ast.nodes[0]->expr);
- } else if (ast.nodes[0]->tag == "FUN_CALL"_) {
- ast.expr = std::move(ast.nodes[0]->expr);
- } else if (ast.nodes[0]->tag == "STRING"_) {
- std::string str = ast.nodes[0]->token;
- // Drop quotes.
- str = str.substr(1, str.size() - 2);
- ast.expr = makeE<EConstant>(str);
- }
-}
-void Parser::walkLetExpr(AstQuery& ast) {
- auto frame = newFrameSymbolTable();
- walkChildren(ast);
-
- auto plist = ast.nodes[0];
- EExpression::Vector binds;
-
- binds.resize(frame->table.size());
- for (auto& symbol : frame->table) {
- auto it = plist->projects.find(symbol.first);
- invariant(it != plist->projects.end());
- binds[symbol.second] = std::move(it->second);
- }
-
- ast.expr = makeE<ELocalBind>(frame->id, std::move(binds), std::move(ast.nodes[1]->expr));
-
- popFrameSymbolTable();
-}
-
-void Parser::walkLambdaExpr(AstQuery& ast) {
- auto frame = newFrameSymbolTable();
-
- walk(*ast.nodes[0]);
- frame->table[ast.nodes[0]->identifier] = value::SlotId{0};
-
- walk(*ast.nodes[1]);
-
- ast.expr = makeE<ELocalLambda>(frame->id, std::move(ast.nodes[1]->expr));
-
- popFrameSymbolTable();
-}
-
-void Parser::walkFrameProjectList(AstQuery& ast) {
- value::SlotId slotId{0};
- for (size_t idx = 0; idx < ast.nodes.size(); ++idx) {
- walk(*ast.nodes[idx]);
- currentFrameSymbolTable()->table[ast.nodes[idx]->identifier] = slotId++;
-
- ast.projects[ast.nodes[idx]->identifier] = std::move(ast.nodes[idx]->expr);
- }
-}
-
-void Parser::walkIfExpr(AstQuery& ast) {
- walkChildren(ast);
-
- ast.expr = makeE<EIf>(std::move(ast.nodes[0]->expr),
- std::move(ast.nodes[1]->expr),
- std::move(ast.nodes[2]->expr));
-}
-
-void Parser::walkFunCall(AstQuery& ast) {
- walkChildren(ast);
- EExpression::Vector args;
-
- for (size_t idx = 1; idx < ast.nodes.size(); ++idx) {
- args.emplace_back(std::move(ast.nodes[idx]->expr));
- }
-
- ast.expr = makeE<EFunction>(ast.nodes[0]->identifier, std::move(args));
-}
-
-void Parser::walkScan(AstQuery& ast) {
- walkChildren(ast);
-
- std::string recordName;
- std::string recordIdName;
- std::string snapshotIdName;
- std::string indexIdName;
- std::string indexKeyName;
- std::string indexKeyPatternName;
- int projectsPos;
-
- if (ast.nodes.size() == 10) {
- recordName = std::move(ast.nodes[0]->identifier);
- recordIdName = std::move(ast.nodes[1]->identifier);
- snapshotIdName = std::move(ast.nodes[2]->identifier);
- indexIdName = std::move(ast.nodes[3]->identifier);
- indexKeyName = std::move(ast.nodes[4]->identifier);
- indexKeyPatternName = std::move(ast.nodes[5]->identifier);
- projectsPos = 6;
- } else if (ast.nodes.size() == 6) {
- recordName = std::move(ast.nodes[0]->identifier);
- recordIdName = std::move(ast.nodes[1]->identifier);
- projectsPos = 2;
- } else if (ast.nodes.size() == 5) {
- recordName = std::move(ast.nodes[0]->identifier);
- projectsPos = 1;
- } else if (ast.nodes.size() == 4) {
- projectsPos = 0;
- } else {
- uasserted(5290715, "Wrong number of arguments for SCAN");
- }
- auto lastPos = ast.nodes.size() - 1;
-
- // The 'collName' should be third from last.
- auto collName = std::move(ast.nodes[lastPos - 2]->identifier);
-
- // The 'FORWARD' should be second last.
- const auto forward = (ast.nodes[lastPos - 1]->token == "true") ? true : false;
-
- // The 'NEED_SLOT_FOR_OPLOG_TS' always comes at the end.
- const auto oplogTs = (ast.nodes[lastPos]->token == "true")
- ? boost::optional<value::SlotId>(_env->registerSlot(
- "oplogTs"_sd, value::TypeTags::Nothing, 0, false, &_slotIdGenerator))
- : boost::none;
-
- ast.stage = makeS<ScanStage>(getCollectionUuid(collName),
- lookupSlot(recordName),
- lookupSlot(recordIdName),
- lookupSlot(snapshotIdName),
- lookupSlot(indexIdName),
- lookupSlot(indexKeyName),
- lookupSlot(indexKeyPatternName),
- oplogTs,
- ast.nodes[projectsPos]->identifiers,
- lookupSlots(ast.nodes[projectsPos]->renames),
- boost::none,
- forward,
- nullptr,
- getCurrentPlanNodeId(),
- ScanCallbacks{});
-}
-
-void Parser::walkParallelScan(AstQuery& ast) {
- walkChildren(ast);
-
- std::string recordName;
- std::string recordIdName;
- std::string snapshotIdName;
- std::string indexIdName;
- std::string indexKeyName;
- std::string indexKeyPatternName;
- std::string collName;
- int projectsPos;
-
- if (ast.nodes.size() == 8) {
- recordName = std::move(ast.nodes[0]->identifier);
- recordIdName = std::move(ast.nodes[1]->identifier);
- snapshotIdName = std::move(ast.nodes[2]->identifier);
- indexIdName = std::move(ast.nodes[3]->identifier);
- indexKeyName = std::move(ast.nodes[4]->identifier);
- indexKeyPatternName = std::move(ast.nodes[5]->identifier);
- projectsPos = 6;
- collName = std::move(ast.nodes[7]->identifier);
- } else if (ast.nodes.size() == 4) {
- recordName = std::move(ast.nodes[0]->identifier);
- recordIdName = std::move(ast.nodes[1]->identifier);
- projectsPos = 2;
- collName = std::move(ast.nodes[3]->identifier);
- } else if (ast.nodes.size() == 3) {
- recordName = std::move(ast.nodes[0]->identifier);
- projectsPos = 1;
- collName = std::move(ast.nodes[2]->identifier);
- } else if (ast.nodes.size() == 2) {
- projectsPos = 0;
- collName = std::move(ast.nodes[1]->identifier);
- } else {
- uasserted(5290716, "Wrong number of arguments for PSCAN");
- }
-
- ast.stage = makeS<ParallelScanStage>(getCollectionUuid(collName),
- lookupSlot(recordName),
- lookupSlot(recordIdName),
- lookupSlot(snapshotIdName),
- lookupSlot(indexIdName),
- lookupSlot(indexKeyName),
- lookupSlot(indexKeyPatternName),
- ast.nodes[projectsPos]->identifiers,
- lookupSlots(ast.nodes[projectsPos]->renames),
- nullptr,
- getCurrentPlanNodeId(),
- ScanCallbacks{});
-}
-
-void Parser::walkSeek(AstQuery& ast) {
- walkChildren(ast);
-
- std::string recordName;
- std::string recordIdName;
- std::string snapshotIdName;
- std::string indexIdName;
- std::string indexKeyName;
- std::string indexKeyPatternName;
-
- int projectsPos;
-
- if (ast.nodes.size() == 11) {
- recordName = std::move(ast.nodes[1]->identifier);
- recordIdName = std::move(ast.nodes[2]->identifier);
- snapshotIdName = std::move(ast.nodes[3]->identifier);
- indexIdName = std::move(ast.nodes[4]->identifier);
- indexKeyName = std::move(ast.nodes[5]->identifier);
- indexKeyPatternName = std::move(ast.nodes[6]->identifier);
- projectsPos = 7;
- } else if (ast.nodes.size() == 7) {
- recordName = std::move(ast.nodes[1]->identifier);
- recordIdName = std::move(ast.nodes[2]->identifier);
- projectsPos = 3;
- } else if (ast.nodes.size() == 6) {
- recordName = std::move(ast.nodes[1]->identifier);
- projectsPos = 2;
- } else if (ast.nodes.size() == 5) {
- projectsPos = 1;
- } else {
- uasserted(5290717, "Wrong number of arguments for SEEK");
- }
- auto lastPos = ast.nodes.size() - 1;
-
- // The 'collName' should be third from last.
- auto collName = std::move(ast.nodes[lastPos - 2]->identifier);
-
- // The 'FORWARD' should be second last.
- const auto forward = (ast.nodes[lastPos - 1]->token == "true") ? true : false;
-
- // The 'NEED_SLOT_FOR_OPLOG_TS' always comes at the end.
- const auto oplogTs = (ast.nodes[lastPos]->token == "true")
- ? boost::optional<value::SlotId>(_env->registerSlot(
- "oplogTs"_sd, value::TypeTags::Nothing, 0, false, &_slotIdGenerator))
- : boost::none;
-
- ast.stage = makeS<ScanStage>(getCollectionUuid(collName),
- lookupSlot(recordName),
- lookupSlot(recordIdName),
- lookupSlot(snapshotIdName),
- lookupSlot(indexIdName),
- lookupSlot(indexKeyName),
- lookupSlot(indexKeyPatternName),
- oplogTs,
- ast.nodes[projectsPos]->identifiers,
- lookupSlots(ast.nodes[projectsPos]->renames),
- lookupSlot(ast.nodes[0]->identifier),
- forward,
- nullptr,
- getCurrentPlanNodeId(),
- ScanCallbacks{});
-}
-
-void Parser::walkIndexScan(AstQuery& ast) {
- walkChildren(ast);
-
- std::string recordName;
- std::string recordIdName;
- std::string snapshotIdName;
- std::string collName;
- std::string indexName;
- int projectsPos;
- int forwardPos;
-
- if (ast.nodes.size() == 7) {
- recordName = std::move(ast.nodes[0]->identifier);
- recordIdName = std::move(ast.nodes[1]->identifier);
- snapshotIdName = std::move(ast.nodes[2]->identifier);
- projectsPos = 3;
- collName = std::move(ast.nodes[4]->identifier);
- indexName = std::move(ast.nodes[5]->identifier);
- forwardPos = 6;
- } else if (ast.nodes.size() == 6) {
- recordName = std::move(ast.nodes[0]->identifier);
- recordIdName = std::move(ast.nodes[1]->identifier);
- projectsPos = 2;
- collName = std::move(ast.nodes[3]->identifier);
- indexName = std::move(ast.nodes[4]->identifier);
- forwardPos = 5;
- } else if (ast.nodes.size() == 5) {
- recordName = std::move(ast.nodes[0]->identifier);
- projectsPos = 1;
- collName = std::move(ast.nodes[2]->identifier);
- indexName = std::move(ast.nodes[3]->identifier);
- forwardPos = 4;
- } else if (ast.nodes.size() == 4) {
- projectsPos = 0;
- collName = std::move(ast.nodes[1]->identifier);
- indexName = std::move(ast.nodes[2]->identifier);
- forwardPos = 3;
- } else {
- MONGO_UNREACHABLE
- }
-
- const auto forward = (ast.nodes[forwardPos]->token == "true") ? true : false;
-
- auto [indexKeysInclusion, vars] =
- lookupIndexKeyRenames(ast.nodes[projectsPos]->renames, ast.nodes[projectsPos]->indexKeys);
-
- ast.stage = makeS<IndexScanStage>(getCollectionUuid(collName),
- indexName,
- forward,
- lookupSlot(recordName),
- lookupSlot(recordIdName),
- lookupSlot(snapshotIdName),
- indexKeysInclusion,
- vars,
- boost::none,
- boost::none,
- nullptr,
- getCurrentPlanNodeId());
-}
-
-void Parser::walkIndexSeek(AstQuery& ast) {
- walkChildren(ast);
-
- std::string recordName;
- std::string recordIdName;
- std::string snapshotIdName;
- std::string collName;
- std::string indexName;
- int projectsPos;
- int forwardPos;
-
- if (ast.nodes.size() == 9) {
- recordName = std::move(ast.nodes[2]->identifier);
- recordIdName = std::move(ast.nodes[3]->identifier);
- snapshotIdName = std::move(ast.nodes[4]->identifier);
- projectsPos = 5;
- collName = std::move(ast.nodes[6]->identifier);
- indexName = std::move(ast.nodes[7]->identifier);
- forwardPos = 8;
- } else if (ast.nodes.size() == 8) {
- recordName = std::move(ast.nodes[2]->identifier);
- recordIdName = std::move(ast.nodes[3]->identifier);
- projectsPos = 4;
- collName = std::move(ast.nodes[5]->identifier);
- indexName = std::move(ast.nodes[6]->identifier);
- forwardPos = 7;
- } else if (ast.nodes.size() == 7) {
- recordName = std::move(ast.nodes[2]->identifier);
- projectsPos = 3;
- collName = std::move(ast.nodes[4]->identifier);
- indexName = std::move(ast.nodes[5]->identifier);
- forwardPos = 6;
- } else if (ast.nodes.size() == 6) {
- projectsPos = 2;
- collName = std::move(ast.nodes[3]->identifier);
- indexName = std::move(ast.nodes[4]->identifier);
- forwardPos = 5;
- } else {
- MONGO_UNREACHABLE
- }
-
- const auto forward = (ast.nodes[forwardPos]->token == "true") ? true : false;
-
- auto [indexKeysInclusion, vars] =
- lookupIndexKeyRenames(ast.nodes[projectsPos]->renames, ast.nodes[projectsPos]->indexKeys);
-
- ast.stage = makeS<IndexScanStage>(getCollectionUuid(collName),
- indexName,
- forward,
- lookupSlot(recordName),
- lookupSlot(recordIdName),
- lookupSlot(snapshotIdName),
- indexKeysInclusion,
- vars,
- lookupSlot(ast.nodes[0]->identifier),
- lookupSlot(ast.nodes[1]->identifier),
- nullptr,
- getCurrentPlanNodeId());
-}
-
-namespace {
-struct StringTreeNode {
- std::string value;
- std::vector<StringTreeNode> children;
-
- void append(const FieldRef& fr, size_t level) {
- if (level < fr.numParts()) {
- auto part = fr.getPart(level);
- for (auto& child : children) {
- if (child.value == part) {
- child.append(fr, level + 1);
- return;
- }
- }
- children.emplace_back(StringTreeNode{part.toString()}).append(fr, level + 1);
- }
- }
-
- bool isLeaf() const {
- return children.empty();
- }
-
- // Convert a tree into ABT.
- // example: paths "a.b" and "a.c" are transalted to:
- //
- // Field "a" * Keep "a"
- // |
- // Traverse
- // |
- // Obj * Keep "b", "c"
- optimizer::ABT convertToABT(size_t level) {
- // ABT is built bottom up.
- // Non-root level requires Obj as it must operate only on objects. The top level is
- // guaranteed to be an object so we do not need to check for it.
- auto result = level ? optimizer::make<optimizer::PathObj>()
- : optimizer::make<optimizer::PathIdentity>();
-
- // Stich together a vector of ABTs by using PathComposeM if needed.
- optimizer::PathKeep::NameSet keepNames;
- for (auto& child : children) {
- if (!child.isLeaf()) {
- optimizer::maybeComposePath(result, child.convertToABT(level + 1));
- }
- keepNames.insert(child.value);
- }
-
- // Keep only referenced fields.
- optimizer::maybeComposePath(result, optimizer::make<optimizer::PathKeep>(keepNames));
-
- if (level) {
- result = optimizer::make<optimizer::PathField>(
- value, optimizer::make<optimizer::PathTraverse>(std::move(result)));
- }
-
- return result;
- }
-};
-
-std::unique_ptr<EExpression> abtToExpr(optimizer::ABT& abt, optimizer::SlotVarMap& slotMap) {
- auto env = optimizer::VariableEnvironment::build(abt);
-
- optimizer::PrefixId prefixId;
- // Convert paths into ABT expressions.
- optimizer::EvalPathLowering pathLower{prefixId, env};
- pathLower.optimize(abt);
-
- // Run the constant folding to eliminate lambda applications as they are not directly
- // supported by the SBE VM.
- optimizer::ConstEval constEval{env};
- constEval.optimize(abt);
-
- // And finally convert to the SBE expression.
- optimizer::SBEExpressionLowering exprLower{env, slotMap};
- return exprLower.optimize(abt);
-}
-} // namespace
-
-void Parser::walkColumnScan(AstQuery& ast) {
- walkChildren(ast);
-
- auto paths = ast.nodes[0]->identifiers;
- auto outputs = lookupSlots(ast.nodes[1]->identifiers);
- auto record = lookupSlot(ast.nodes[2]->identifier);
- auto recordId = lookupSlot(ast.nodes[3]->identifier);
- auto collName = ast.nodes[4]->identifier;
- auto indexName = ast.nodes[5]->identifier;
-
- // Merge all path strings into a tree.
- StringTreeNode pathTree;
- for (auto& path : paths) {
- FieldRef fr{path};
- pathTree.append(fr, 0);
- }
-
- // Generate some unique-ish string.
- auto rowStoreInput = std::string{"internal"} + std::to_string(ast.position);
- auto rowStoreSlot = *lookupSlot(rowStoreInput);
- optimizer::SlotVarMap slotMap{};
- slotMap[rowStoreInput] = rowStoreSlot;
-
- // Generate expression that reconstructs the whole object (runs against the row store bson for
- // now).
- auto evalPath = optimizer::make<optimizer::EvalPath>(
- pathTree.convertToABT(0), optimizer::make<optimizer::Variable>(rowStoreInput));
-
- auto evalPathExpr = abtToExpr(evalPath, slotMap);
-
- // Generate expressions for individual paths (also run against the row store for now).
- // example: path "a.b.c." is transalted to:
- // Get "a" Traverse Get "b" Traverse Get "c" Id
- std::vector<std::unique_ptr<EExpression>> pathExprs;
- for (auto& path : paths) {
- FieldRef fr{path};
- auto temp = optimizer::make<optimizer::PathIdentity>();
- for (size_t idx = fr.numParts(); idx-- > 0;) {
- temp = optimizer::make<optimizer::PathGet>(fr.getPart(idx).toString(), std::move(temp));
- if (idx) {
- temp = optimizer::make<optimizer::PathTraverse>(std::move(temp));
- }
- }
- auto evalPath = optimizer::make<optimizer::EvalPath>(
- std::move(temp), optimizer::make<optimizer::Variable>(rowStoreInput));
-
- pathExprs.emplace_back(abtToExpr(evalPath, slotMap));
- }
-
- ast.stage = makeS<ColumnScanStage>(getCollectionUuid(collName),
- indexName,
- std::move(outputs),
- std::move(paths),
- record,
- recordId,
- std::move(evalPathExpr),
- std::move(pathExprs),
- rowStoreSlot,
- nullptr,
- getCurrentPlanNodeId());
-}
-
-void Parser::walkProject(AstQuery& ast) {
- walkChildren(ast);
-
- ast.stage = makeS<ProjectStage>(std::move(ast.nodes[1]->stage),
- lookupSlots(std::move(ast.nodes[0]->projects)),
- getCurrentPlanNodeId());
-}
-
-void Parser::walkFilter(AstQuery& ast) {
- walkChildren(ast);
-
- ast.stage = makeS<FilterStage<false>>(
- std::move(ast.nodes[1]->stage), std::move(ast.nodes[0]->expr), getCurrentPlanNodeId());
-}
-
-void Parser::walkCFilter(AstQuery& ast) {
- walkChildren(ast);
-
- ast.stage = makeS<FilterStage<true>>(
- std::move(ast.nodes[1]->stage), std::move(ast.nodes[0]->expr), getCurrentPlanNodeId());
-}
-
-void Parser::walkSort(AstQuery& ast) {
- walkChildren(ast);
-
- int inputStagePos;
- size_t limit = std::numeric_limits<std::size_t>::max();
-
- // Check if 'limit' was specified.
- if (ast.nodes.size() == 5) {
- limit = std::stoi(ast.nodes[3]->token);
- inputStagePos = 4;
- } else {
- inputStagePos = 3;
- }
- auto dirs = parseSortDirList(*ast.nodes[1]);
-
- ast.stage = makeS<SortStage>(std::move(ast.nodes[inputStagePos]->stage),
- lookupSlots(ast.nodes[0]->identifiers),
- std::move(dirs),
- lookupSlots(ast.nodes[2]->identifiers),
- limit,
- std::numeric_limits<std::size_t>::max(),
- true /* allowDiskUse */,
- getCurrentPlanNodeId());
-}
-
-void Parser::walkUnion(AstQuery& ast) {
- walkChildren(ast);
-
- PlanStage::Vector inputStages;
- std::vector<value::SlotVector> inputVals;
- value::SlotVector outputVals{lookupSlots(ast.nodes[0]->identifiers)};
-
- for (size_t idx = 0; idx < ast.nodes[1]->nodes.size(); idx++) {
- inputVals.push_back(lookupSlots(ast.nodes[1]->nodes[idx]->identifiers));
- inputStages.push_back(std::move(ast.nodes[1]->nodes[idx]->stage));
- }
-
- uassert(ErrorCodes::FailedToParse,
- "Union output values and input values mismatch",
- std::all_of(
- inputVals.begin(), inputVals.end(), [size = outputVals.size()](const auto& slots) {
- return slots.size() == size;
- }));
-
- ast.stage = makeS<UnionStage>(std::move(inputStages),
- std::move(inputVals),
- std::move(outputVals),
- getCurrentPlanNodeId());
-}
-
-void Parser::walkUnionBranch(AstQuery& ast) {
- walkChildren(ast);
-
- ast.identifiers = std::move(ast.nodes[0]->identifiers);
- ast.stage = std::move(ast.nodes[1]->stage);
-}
-
-void Parser::walkSortedMerge(AstQuery& ast) {
- walkChildren(ast);
-
- PlanStage::Vector inputStages;
- std::vector<value::SlotVector> inputKeys;
- std::vector<value::SlotVector> inputVals;
- value::SlotVector outputVals{lookupSlots(ast.nodes[0]->identifiers)};
-
- auto dirs = parseSortDirList(*ast.nodes[1]);
-
- const int kBranchesIdx = 2;
- for (size_t idx = 0; idx < ast.nodes[kBranchesIdx]->nodes.size(); idx++) {
- inputKeys.push_back(
- lookupSlots(ast.nodes[kBranchesIdx]->nodes[idx]->nodes[0]->identifiers));
- inputVals.push_back(
- lookupSlots(ast.nodes[kBranchesIdx]->nodes[idx]->nodes[1]->identifiers));
- inputStages.push_back(std::move(ast.nodes[kBranchesIdx]->nodes[idx]->stage));
- }
-
- uassert(ErrorCodes::FailedToParse,
- "SortedMerge output values and input values mismatch",
- std::all_of(
- inputVals.begin(), inputVals.end(), [size = outputVals.size()](const auto& slots) {
- return slots.size() == size;
- }));
- uassert(ErrorCodes::FailedToParse,
- "SortedMerge dirs/keys mismatch",
- std::all_of(inputKeys.begin(),
- inputKeys.end(),
- [size = dirs.size()](const auto& slots) { return slots.size() == size; }));
-
- ast.stage = makeS<SortedMergeStage>(std::move(inputStages),
- std::move(inputKeys),
- std::move(dirs),
- std::move(inputVals),
- std::move(outputVals),
- getCurrentPlanNodeId());
-}
-
-void Parser::walkSortedMergeBranch(AstQuery& ast) {
- walkChildren(ast);
-
- ast.stage = std::move(ast.nodes[2]->stage);
- invariant(ast.stage);
-}
-
-void Parser::walkUnwind(AstQuery& ast) {
- walkChildren(ast);
-
- bool preserveNullAndEmptyArrays = (ast.nodes[3]->token == "true") ? true : false;
- ast.stage = makeS<UnwindStage>(std::move(ast.nodes[4]->stage),
- lookupSlotStrict(ast.nodes[2]->identifier),
- lookupSlotStrict(ast.nodes[0]->identifier),
- lookupSlotStrict(ast.nodes[1]->identifier),
- preserveNullAndEmptyArrays,
- getCurrentPlanNodeId());
-}
-
-void Parser::walkMkObj(AstQuery& ast) {
- using namespace peg::udl;
- using namespace std::literals;
-
- walkChildren(ast);
-
- std::string newRootName = ast.nodes[0]->identifier;
- std::string oldRootName;
- std::vector<std::string> fields;
- boost::optional<MakeObjFieldBehavior> fieldBehavior;
-
- size_t projectListPos = 1;
- size_t forceNewObjPos = 2;
- size_t retOldObjPos = 3;
- size_t inputPos = 4;
-
- if (ast.nodes.size() != 5) {
- oldRootName = ast.nodes[1]->identifier;
- fields = std::move(ast.nodes[2]->identifiers);
- fieldBehavior = parseFieldBehavior(ast.nodes[3]->token);
- projectListPos = 4;
- forceNewObjPos = 5;
- retOldObjPos = 6;
- inputPos = 7;
- }
-
- const bool forceNewObj = ast.nodes[forceNewObjPos]->token == "true";
- const bool retOldObj = ast.nodes[retOldObjPos]->token == "true";
-
- if (ast.tag == "MKOBJ"_) {
- ast.stage =
- makeS<MakeObjStage>(std::move(ast.nodes[inputPos]->stage),
- lookupSlotStrict(newRootName),
- lookupSlot(oldRootName),
- fieldBehavior,
- std::move(fields),
- std::move(ast.nodes[projectListPos]->renames),
- lookupSlots(std::move(ast.nodes[projectListPos]->identifiers)),
- forceNewObj,
- retOldObj,
- getCurrentPlanNodeId());
- } else {
- ast.stage =
- makeS<MakeBsonObjStage>(std::move(ast.nodes[inputPos]->stage),
- lookupSlotStrict(newRootName),
- lookupSlot(oldRootName),
- fieldBehavior,
- std::move(fields),
- std::move(ast.nodes[projectListPos]->renames),
- lookupSlots(std::move(ast.nodes[projectListPos]->identifiers)),
- forceNewObj,
- retOldObj,
- getCurrentPlanNodeId());
- }
-}
-
-void Parser::walkGroup(AstQuery& ast) {
- walkChildren(ast);
-
- size_t collatorSlotPos = 0;
- size_t inputPos = 0;
- // Check if an optional collator slot was provided.
- if (ast.nodes.size() == 4) {
- inputPos = 3;
- collatorSlotPos = 2;
- } else {
- inputPos = 2;
- }
-
- ast.stage = makeS<HashAggStage>(
- std::move(ast.nodes[inputPos]->stage),
- lookupSlots(std::move(ast.nodes[0]->identifiers)),
- lookupSlots(std::move(ast.nodes[1]->projects)),
- makeSV(),
- true,
- collatorSlotPos ? lookupSlot(std::move(ast.nodes[collatorSlotPos]->identifier))
- : boost::none,
- true, // allowDiskUse
- getCurrentPlanNodeId());
-}
-
-void Parser::walkHashJoin(AstQuery& ast) {
- walkChildren(ast);
-
- boost::optional<value::SlotId> collatorSlot;
- auto outerNode = ast.nodes[0];
- auto innerNode = ast.nodes[1];
- if (ast.nodes.size() == 3) {
- outerNode = ast.nodes[1];
- innerNode = ast.nodes[2];
- collatorSlot = lookupSlot(ast.nodes[0]->identifier);
- }
-
- ast.stage =
- makeS<HashJoinStage>(std::move(outerNode->nodes[2]->stage), // outer
- std::move(innerNode->nodes[2]->stage), // inner
- lookupSlots(outerNode->nodes[0]->identifiers), // outer conditions
- lookupSlots(outerNode->nodes[1]->identifiers), // outer projections
- lookupSlots(innerNode->nodes[0]->identifiers), // inner conditions
- lookupSlots(innerNode->nodes[1]->identifiers), // inner projections
- collatorSlot, // collator
- getCurrentPlanNodeId());
-}
-
-void Parser::walkNLJoin(AstQuery& ast) {
- walkChildren(ast);
- size_t outerPos;
- size_t innerPos;
- std::unique_ptr<EExpression> predicate;
-
- if (ast.nodes.size() == 5) {
- predicate = std::move(ast.nodes[2]->expr);
- outerPos = 3;
- innerPos = 4;
- } else {
- outerPos = 2;
- innerPos = 3;
- }
-
- ast.stage = makeS<LoopJoinStage>(std::move(ast.nodes[outerPos]->stage),
- std::move(ast.nodes[innerPos]->stage),
- lookupSlots(ast.nodes[0]->identifiers),
- lookupSlots(ast.nodes[1]->identifiers),
- std::move(predicate),
- getCurrentPlanNodeId());
-}
-
-void Parser::walkLimit(AstQuery& ast) {
- walkChildren(ast);
-
- ast.stage = makeS<LimitSkipStage>(std::move(ast.nodes[1]->stage),
- std::stoi(ast.nodes[0]->token),
- boost::none,
- getCurrentPlanNodeId());
-}
-
-void Parser::walkSkip(AstQuery& ast) {
- walkChildren(ast);
-
- if (ast.nodes.size() == 3) {
- // This is a case where we have both a 'limit' and a 'skip'.
- ast.stage = makeS<LimitSkipStage>(std::move(ast.nodes[2]->stage),
- std::stoi(ast.nodes[0]->token), // limit
- std::stoi(ast.nodes[1]->token), // skip
- getCurrentPlanNodeId());
- } else {
- ast.stage = makeS<LimitSkipStage>(std::move(ast.nodes[1]->stage),
- boost::none,
- std::stoi(ast.nodes[0]->token),
- getCurrentPlanNodeId());
- }
-}
-
-void Parser::walkCoScan(AstQuery& ast) {
- walkChildren(ast);
-
- ast.stage = makeS<CoScanStage>(getCurrentPlanNodeId(), nullptr);
-}
-
-void Parser::walkTraverse(AstQuery& ast) {
- walkChildren(ast);
- size_t inPos;
- size_t fromPos;
- size_t foldPos = 0;
- size_t finalPos = 0;
- size_t correlatedPos = 0;
-
- // The ast for the traverse stage has three nodes of 'inField', 'outField', and 'outFieldInner',
- // two nodes for an 'from' and 'in' child stages, and two nodes for 'foldExpr' and 'finalExpr'.
- // Note that even if one of these expressions is not specified, we output a '{}' token in order
- // to parse the string correctly. This leaves us with two cases: correlated slot vector
- // specified or unspecified. If 'outerCorrelated' is specified, then we have an extra child node
- // and thus eight children, otherwise we are in the original case.
- if (ast.nodes.size() == 8) {
- correlatedPos = 3;
- foldPos = 4;
- finalPos = 5;
- fromPos = 6;
- inPos = 7;
- } else {
- foldPos = 3;
- finalPos = 4;
- fromPos = 5;
- inPos = 6;
- }
- ast.stage = makeS<TraverseStage>(
- std::move(ast.nodes[fromPos]->stage),
- std::move(ast.nodes[inPos]->stage),
- lookupSlotStrict(ast.nodes[2]->identifier),
- lookupSlotStrict(ast.nodes[0]->identifier),
- lookupSlotStrict(ast.nodes[1]->identifier),
- correlatedPos ? lookupSlots(ast.nodes[correlatedPos]->identifiers) : makeSV(),
- std::move(ast.nodes[foldPos]->expr),
- std::move(ast.nodes[finalPos]->expr),
- getCurrentPlanNodeId(),
- boost::none);
-}
-
-void Parser::walkExchange(AstQuery& ast) {
- walkChildren(ast);
- ExchangePolicy policy = [&ast] {
- if (ast.nodes[2]->identifier == "round") {
- return ExchangePolicy::roundrobin;
- }
-
- if (ast.nodes[2]->identifier == "bcast") {
- return ExchangePolicy::broadcast;
- }
- uasserted(4885901, "unknown exchange policy");
- }();
- ast.stage = makeS<ExchangeConsumer>(std::move(ast.nodes[3]->stage),
- std::stoll(ast.nodes[1]->token),
- lookupSlots(ast.nodes[0]->identifiers),
- policy,
- nullptr,
- nullptr,
- getCurrentPlanNodeId());
-}
-
-void Parser::walkBranch(AstQuery& ast) {
- walkChildren(ast);
-
- value::SlotVector outputVals{lookupSlots(ast.nodes[1]->identifiers)};
- value::SlotVector inputThenVals{lookupSlots(ast.nodes[2]->identifiers)};
- value::SlotVector inputElseVals{lookupSlots(ast.nodes[4]->identifiers)};
-
- ast.stage = makeS<BranchStage>(std::move(ast.nodes[3]->stage),
- std::move(ast.nodes[5]->stage),
- std::move(ast.nodes[0]->expr),
- std::move(inputThenVals),
- std::move(inputElseVals),
- std::move(outputVals),
- getCurrentPlanNodeId());
-}
-
-std::unique_ptr<PlanStage> Parser::walkPathValue(AstQuery& ast,
- value::SlotId inputSlot,
- std::unique_ptr<PlanStage> inputStage,
- value::SlotVector correlated,
- value::SlotId outputSlot) {
- using namespace peg::udl;
- using namespace std::literals;
-
- if (ast.nodes.size() == 1) {
- if (ast.nodes[0]->tag == "EXPR"_) {
- auto [it, inserted] = _symbolsLookupTable.insert({"__self", inputSlot});
- invariant(inserted);
- walk(*ast.nodes[0]);
- _symbolsLookupTable.erase(it);
- return makeProjectStage(std::move(inputStage),
- getCurrentPlanNodeId(),
- outputSlot,
- std::move(ast.nodes[0]->expr));
- } else {
- walk(*ast.nodes[0]);
- return makeProjectStage(
- std::move(inputStage),
- getCurrentPlanNodeId(),
- outputSlot,
- makeE<EFunction>("getField"_sd,
- makeEs(makeE<EVariable>(inputSlot),
- makeE<EConstant>(ast.nodes[0]->identifier))));
- }
- } else {
- walk(*ast.nodes[0]);
- auto traverseIn = _slotIdGenerator.generate();
- auto from =
- makeProjectStage(std::move(inputStage),
- getCurrentPlanNodeId(),
- traverseIn,
- makeE<EFunction>("getField"_sd,
- makeEs(makeE<EVariable>(inputSlot),
- makeE<EConstant>(ast.nodes[0]->identifier))));
- auto in = makeS<LimitSkipStage>(
- makeS<CoScanStage>(getCurrentPlanNodeId()), 1, boost::none, getCurrentPlanNodeId());
- auto stage = makeS<TraverseStage>(
- std::move(from),
- walkPathValue(*ast.nodes[1], traverseIn, std::move(in), {}, outputSlot),
- traverseIn,
- outputSlot,
- outputSlot,
- std::move(correlated),
- nullptr,
- nullptr,
- getCurrentPlanNodeId(),
- boost::none);
-
- return stage;
- }
-}
-
-void Parser::walkSimpleProj(AstQuery& ast) {
- walk(*ast.nodes[0]);
- walk(*ast.nodes[1]);
- walk(*ast.nodes[2]);
- walk(*ast.nodes[4]);
-
- auto inputStage = std::move(ast.nodes[4]->stage);
- auto outputSlot = lookupSlotStrict(ast.nodes[0]->identifier);
- auto inputSlot = lookupSlotStrict(ast.nodes[1]->identifier);
-
- ast.stage = walkPathValue(*ast.nodes[3],
- inputSlot,
- std::move(inputStage),
- lookupSlots(ast.nodes[2]->identifiers),
- outputSlot);
-}
-
-bool needNewObject(AstQuery& ast) {
- using namespace peg::udl;
-
- switch (ast.tag) {
- case "PATH"_: {
- // If anything in the path needs a new object then this path needs a new object.
- bool newObj = false;
- for (const auto& node : ast.nodes) {
- newObj |= needNewObject(*node);
- }
- return newObj;
- }
- case "PF"_: {
- if (ast.nodes.size() == 1) {
- // PF_DROPALL
- return false;
- }
-
- // Follow the action on the field.
- return needNewObject(*ast.nodes[1]);
- }
- case "PF_ACTION"_: {
-
- return needNewObject(*ast.nodes[0]);
- }
- case "PF_DROP"_: {
- return false;
- }
- case "PF_INCL"_: {
- return false;
- }
- case "PF_EXPR"_: {
- // This is the only place that forces a new object.
- return true;
- }
- case "PF_MEXPR"_: {
- return false;
- }
- default:;
- }
- MONGO_UNREACHABLE;
-}
-
-bool returnOldObject(AstQuery& ast) {
- using namespace peg::udl;
-
- switch (ast.tag) {
- case "PATH"_: {
- // If anything in the path needs a new object then this path needs a new object.
- bool retOldObj = true;
- for (const auto& node : ast.nodes) {
- retOldObj &= returnOldObject(*node);
- }
- return retOldObj;
- }
- case "PF"_: {
- if (ast.nodes.size() == 1) {
- // PF_DROPALL
- return true;
- }
-
- // Follow the action on the field.
- return returnOldObject(*ast.nodes[1]);
- }
- case "PF_ACTION"_: {
-
- return returnOldObject(*ast.nodes[0]);
- }
- case "PF_DROP"_: {
- return true;
- }
- case "PF_INCL"_: {
- return false;
- }
- case "PF_EXPR"_: {
- return false;
- }
- case "PF_MEXPR"_: {
- return true;
- }
- default:;
- }
- MONGO_UNREACHABLE;
-}
-
-std::unique_ptr<PlanStage> Parser::walkPath(AstQuery& ast,
- value::SlotId inputSlot,
- value::SlotId outputSlot) {
- using namespace peg::udl;
-
- // Do we have to unconditionally create a new object?
- // The algorithm is recursive - if any action anythere is the path needs a new object then the
- // request is bubbled all the way up.
- bool newObj = needNewObject(ast);
- bool restrictAll = false;
- bool retOldObj = returnOldObject(ast);
-
- std::vector<std::string> fieldNames;
- std::vector<std::string> fieldRestrictNames;
- value::SlotVector fieldVars;
- std::unique_ptr<PlanStage> stage = makeS<LimitSkipStage>(
- makeS<CoScanStage>(getCurrentPlanNodeId()), 1, boost::none, getCurrentPlanNodeId());
-
- for (size_t idx = ast.nodes.size(); idx-- > 0;) {
- const auto& pf = ast.nodes[idx];
- if (pf->nodes[0]->name == "PF_DROPALL") {
- restrictAll = true;
- } else {
- walk(*pf->nodes[0]);
- const auto& action = *pf->nodes[1];
- switch (action.nodes[0]->tag) {
- case "PF_DROP"_: {
- fieldRestrictNames.emplace(fieldRestrictNames.begin(),
- std::move(pf->nodes[0]->identifier));
-
- break;
- }
- case "PF_INCL"_: {
- fieldNames.emplace(fieldNames.begin(), std::move(pf->nodes[0]->identifier));
- fieldVars.emplace(fieldVars.begin(), _slotIdGenerator.generate());
- stage = makeProjectStage(
- std::move(stage),
- getCurrentPlanNodeId(),
- fieldVars.front(),
- makeE<EFunction>("getField",
- makeEs(makeE<EVariable>(inputSlot),
- makeE<EConstant>(fieldNames.front()))));
- break;
- }
- case "PF_EXPR"_: {
- fieldNames.emplace(fieldNames.begin(), std::move(pf->nodes[0]->identifier));
- walk(*action.nodes[0]);
- fieldVars.emplace(fieldVars.begin(), _slotIdGenerator.generate());
- stage = makeProjectStage(std::move(stage),
- getCurrentPlanNodeId(),
- fieldVars.front(),
- std::move(action.nodes[0]->nodes[0]->expr));
- break;
- }
- case "PF_MEXPR"_: {
- auto [it, inserted] = _symbolsLookupTable.insert({"__self", inputSlot});
- invariant(inserted);
-
- fieldNames.emplace(fieldNames.begin(), std::move(pf->nodes[0]->identifier));
- walk(*action.nodes[0]);
- fieldVars.emplace(fieldVars.begin(), _slotIdGenerator.generate());
- stage = makeProjectStage(std::move(stage),
- getCurrentPlanNodeId(),
- fieldVars.front(),
- std::move(action.nodes[0]->nodes[0]->expr));
-
- _symbolsLookupTable.erase(it);
- break;
- }
- case "PATH"_: {
- fieldNames.emplace(fieldNames.begin(), std::move(pf->nodes[0]->identifier));
- fieldVars.emplace(fieldVars.begin(), _slotIdGenerator.generate());
- auto traverseOut = fieldVars.front();
- auto traverseIn = _slotIdGenerator.generate();
- stage = makeProjectStage(
- std::move(stage),
- getCurrentPlanNodeId(),
- traverseIn,
- makeE<EFunction>("getField",
- makeEs(makeE<EVariable>(inputSlot),
- makeE<EConstant>(fieldNames.front()))));
-
- stage =
- makeS<TraverseStage>(std::move(stage),
- walkPath(*action.nodes[0], traverseIn, traverseOut),
- traverseIn,
- traverseOut,
- traverseOut,
- sbe::makeSV(),
- nullptr,
- nullptr,
- getCurrentPlanNodeId(),
- boost::none);
- break;
- }
- }
- }
- }
-
- if (restrictAll) {
- fieldRestrictNames.clear();
- fieldRestrictNames.emplace_back("");
- }
- stage = makeS<MakeObjStage>(std::move(stage),
- outputSlot,
- inputSlot,
- sbe::MakeObjStage::FieldBehavior::drop,
- std::move(fieldRestrictNames),
- std::move(fieldNames),
- std::move(fieldVars),
- newObj,
- retOldObj,
- getCurrentPlanNodeId());
-
- return stage;
-}
-
-void Parser::walkPFO(AstQuery& ast) {
- walk(*ast.nodes[0]);
- walk(*ast.nodes[1]);
- walk(*ast.nodes[2]);
- walk(*ast.nodes[4]);
-
-
- ast.stage = makeS<TraverseStage>(std::move(ast.nodes[4]->stage),
- walkPath(*ast.nodes[3],
- lookupSlotStrict(ast.nodes[1]->identifier),
- lookupSlotStrict(ast.nodes[0]->identifier)),
- lookupSlotStrict(ast.nodes[1]->identifier),
- lookupSlotStrict(ast.nodes[0]->identifier),
- lookupSlotStrict(ast.nodes[0]->identifier),
- lookupSlots(ast.nodes[2]->identifiers),
- nullptr,
- nullptr,
- getCurrentPlanNodeId(),
- boost::none);
-}
-
-void Parser::walkLazyProducerSpool(AstQuery& ast) {
- walkChildren(ast);
-
- std::unique_ptr<EExpression> predicate;
- size_t inputPos;
-
- if (ast.nodes.size() == 4) {
- predicate = std::move(ast.nodes[2]->expr);
- inputPos = 3;
- } else {
- inputPos = 2;
- }
-
- ast.stage = makeS<SpoolLazyProducerStage>(std::move(ast.nodes[inputPos]->stage),
- lookupSpoolBuffer(ast.nodes[0]->identifier),
- lookupSlots(ast.nodes[1]->identifiers),
- std::move(predicate),
- getCurrentPlanNodeId());
-}
-
-void Parser::walkEagerProducerSpool(AstQuery& ast) {
- walkChildren(ast);
-
- ast.stage = makeS<SpoolEagerProducerStage>(std::move(ast.nodes[2]->stage),
- lookupSpoolBuffer(ast.nodes[0]->identifier),
- lookupSlots(ast.nodes[1]->identifiers),
- getCurrentPlanNodeId());
-}
-
-void Parser::walkConsumerSpool(AstQuery& ast) {
- walkChildren(ast);
-
- ast.stage = makeS<SpoolConsumerStage<false>>(lookupSpoolBuffer(ast.nodes[0]->identifier),
- lookupSlots(ast.nodes[1]->identifiers),
- getCurrentPlanNodeId());
-}
-
-void Parser::walkStackConsumerSpool(AstQuery& ast) {
- walkChildren(ast);
-
- ast.stage = makeS<SpoolConsumerStage<true>>(lookupSpoolBuffer(ast.nodes[0]->identifier),
- lookupSlots(ast.nodes[1]->identifiers),
- getCurrentPlanNodeId());
-}
-
-void Parser::walkPlanNodeId(AstQuery& ast) {
- auto& str = ast.token;
- auto idBegin = str.data() + 1;
- auto idEnd = str.data() + str.length();
- PlanNodeId id = 0;
- auto [ptr, ec] = std::from_chars(idBegin, idEnd, id);
- uassert(5107701, "Invalid plan node id literal.", ec == std::errc());
- planNodeIdStack.push(id);
-}
-
-void Parser::walkUnique(AstQuery& ast) {
- walkChildren(ast);
-
- ast.stage = makeS<UniqueStage>(std::move(ast.nodes[1]->stage),
- lookupSlots(ast.nodes[0]->identifiers),
- getCurrentPlanNodeId());
-}
-
-void Parser::walk(AstQuery& ast) {
- using namespace peg::udl;
-
- switch (ast.tag) {
- case "OPERATOR"_: {
- walkChildren(ast);
- size_t stageIndex = 0;
- if (ast.nodes.size() == 2) {
- // First child contains plan node id. Stage is stored in the second child.
- stageIndex = 1;
- // Remove current plan node id because it was only relevant to the parsed stage.
- planNodeIdStack.pop();
- }
- ast.stage = std::move(ast.nodes[stageIndex]->stage);
- break;
- }
- case "ROOT"_:
- walkChildren(ast);
- ast.stage = std::move(ast.nodes[0]->stage);
- break;
- case "SCAN"_:
- walkScan(ast);
- break;
- case "PSCAN"_:
- walkParallelScan(ast);
- break;
- case "SEEK"_:
- walkSeek(ast);
- break;
- case "IXSCAN"_:
- walkIndexScan(ast);
- break;
- case "IXSEEK"_:
- walkIndexSeek(ast);
- break;
- case "COLUMNSCAN"_:
- walkColumnScan(ast);
- break;
- case "PROJECT"_:
- walkProject(ast);
- break;
- case "FILTER"_:
- walkFilter(ast);
- break;
- case "CFILTER"_:
- walkCFilter(ast);
- break;
- case "SORT"_:
- walkSort(ast);
- break;
- case "UNION"_:
- walkUnion(ast);
- break;
- case "UNION_BRANCH_LIST"_:
- walkChildren(ast);
- break;
- case "UNION_BRANCH"_:
- walkUnionBranch(ast);
- break;
- case "SORTED_MERGE"_:
- walkSortedMerge(ast);
- break;
- case "SORTED_MERGE_BRANCH_LIST"_:
- walkChildren(ast);
- break;
- case "SORTED_MERGE_BRANCH"_:
- walkSortedMergeBranch(ast);
- break;
- case "UNWIND"_:
- walkUnwind(ast);
- break;
- case "MKOBJ"_:
- case "MKBSON"_:
- walkMkObj(ast);
- break;
- case "GROUP"_:
- walkGroup(ast);
- break;
- case "HJOIN"_:
- walkHashJoin(ast);
- break;
- case "NLJOIN"_:
- walkNLJoin(ast);
- break;
- case "LIMIT"_:
- walkLimit(ast);
- break;
- case "SKIP"_:
- walkSkip(ast);
- break;
- case "COSCAN"_:
- walkCoScan(ast);
- break;
- case "TRAVERSE"_:
- walkTraverse(ast);
- break;
- case "EXCHANGE"_:
- walkExchange(ast);
- break;
- case "IDENT"_:
- walkIdent(ast);
- break;
- case "IDENT_LIST"_:
- walkIdentList(ast);
- break;
- case "STRING_LIST"_:
- walkStringList(ast);
- break;
- case "IDENT_WITH_RENAME"_:
- walkIdentWithRename(ast);
- break;
- case "IDENT_LIST_WITH_RENAMES"_:
- walkIdentListWithRename(ast);
- break;
- case "IX_KEY_WITH_RENAME"_:
- walkIxKeyWithRename(ast);
- break;
- case "IX_KEY_LIST_WITH_RENAMES"_:
- walkIxKeyListWithRename(ast);
- break;
- case "PROJECT_LIST"_:
- walkProjectList(ast);
- break;
- case "ASSIGN"_:
- walkAssign(ast);
- break;
- case "EXPR"_:
- walkExpr(ast);
- break;
- case "EQOP_EXPR"_:
- walkEqopExpr(ast);
- break;
- case "RELOP_EXPR"_:
- walkRelopExpr(ast);
- break;
- case "ADD_EXPR"_:
- walkAddExpr(ast);
- break;
- case "MUL_EXPR"_:
- walkMulExpr(ast);
- break;
- case "PRIMARY_EXPR"_:
- walkPrimaryExpr(ast);
- break;
- case "IF_EXPR"_:
- walkIfExpr(ast);
- break;
- case "LET_EXPR"_:
- walkLetExpr(ast);
- break;
- case "LAMBDA_EXPR"_:
- walkLambdaExpr(ast);
- break;
- case "FRAME_PROJECT_LIST"_:
- walkFrameProjectList(ast);
- break;
- case "FUN_CALL"_:
- walkFunCall(ast);
- break;
- case "BRANCH"_:
- walkBranch(ast);
- break;
- case "SIMPLE_PROJ"_:
- walkSimpleProj(ast);
- break;
- case "PFO"_:
- walkPFO(ast);
- break;
- case "ESPOOL"_:
- walkEagerProducerSpool(ast);
- break;
- case "LSPOOL"_:
- walkLazyProducerSpool(ast);
- break;
- case "CSPOOL"_:
- walkConsumerSpool(ast);
- break;
- case "SSPOOL"_:
- walkStackConsumerSpool(ast);
- break;
- case "PLAN_NODE_ID"_:
- walkPlanNodeId(ast);
- break;
- case "UNIQUE"_:
- walkUnique(ast);
- break;
- default:
- walkChildren(ast);
- }
-}
-
-Parser::Parser(RuntimeEnvironment* env) : _env(env) {
- invariant(_env);
-
- _parser.log = [&](size_t ln, size_t col, const std::string& msg) {
- LOGV2(4885902, "{msg}", "msg"_attr = format_error_message(ln, col, msg));
- };
-
- if (!_parser.load_grammar(kSyntax)) {
- uasserted(4885903, "Invalid syntax definition.");
- }
-
- _parser.enable_packrat_parsing();
-
- _parser.enable_ast<AstQuery>();
-}
-
-std::unique_ptr<PlanStage> Parser::parse(OperationContext* opCtx,
- StringData defaultDb,
- StringData line) {
- std::shared_ptr<AstQuery> ast;
-
- _opCtx = opCtx;
- _defaultDb = defaultDb.toString();
-
- auto result = _parser.parse_n(line.rawData(), line.size(), ast);
- uassert(4885904, str::stream() << "Syntax error in query: " << line, result);
-
- walk(*ast);
- uassert(4885905, "Query does not have the root.", ast->stage);
-
- return std::move(ast->stage);
-}
-
-UUID Parser::getCollectionUuid(const std::string& collName) {
- if (!_opCtx) {
- // The SBE plan cannot actually run without a valid UUID, but it's useful to allow the
- // parser to run in isolation for unit testing.
- auto uuid = UUID::parse("00000000-0000-0000-0000-000000000000");
- invariant(uuid.isOK());
- return uuid.getValue();
- }
-
- auto catalog = CollectionCatalog::get(_opCtx);
-
- // Try to parse the collection name as a UUID directly, otherwise fallback to lookup by
- // NamespaceString.
- auto parsedUuid = UUID::parse(collName);
- if (parsedUuid.isOK()) {
- auto uuid = parsedUuid.getValue();
-
- // Verify that the UUID corresponds to an existing collection.
- auto collPtr = catalog->lookupCollectionByUUID(_opCtx, uuid);
- uassert(6056700,
- str::stream() << "SBE command parser could not find collection: " << collName,
- collPtr);
- return uuid;
- }
-
- auto uuid = catalog->lookupUUIDByNSS(_opCtx, NamespaceString{collName});
- uassert(5162900,
- str::stream() << "SBE command parser could not find collection: " << collName,
- uuid);
- return *uuid;
-}
-
-PlanNodeId Parser::getCurrentPlanNodeId() {
- if (planNodeIdStack.empty()) {
- return kEmptyPlanNodeId;
- }
- return planNodeIdStack.top();
-}
-
-} // namespace sbe
-} // namespace mongo
diff --git a/src/mongo/db/exec/sbe/parser/parser.h b/src/mongo/db/exec/sbe/parser/parser.h
deleted file mode 100644
index 548ba32b301..00000000000
--- a/src/mongo/db/exec/sbe/parser/parser.h
+++ /dev/null
@@ -1,249 +0,0 @@
-/**
- * Copyright (C) 2019-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
-
-#define PEGLIB_USE_STD_ANY 0
-#include <third_party/peglib/peglib.h>
-
-#include <stack>
-
-#include "mongo/db/exec/sbe/expressions/expression.h"
-#include "mongo/db/exec/sbe/stages/ix_scan.h"
-#include "mongo/db/exec/sbe/stages/spool.h"
-#include "mongo/db/exec/sbe/stages/stages.h"
-#include "mongo/db/exec/sbe/values/slot.h"
-#include "mongo/db/query/sbe_stage_builder.h"
-
-namespace mongo {
-namespace sbe {
-struct ParsedQueryTree {
- std::string identifier;
- size_t indexKey;
- std::string rename;
- std::vector<std::string> identifiers;
- std::vector<size_t> indexKeys;
- std::vector<std::string> renames;
-
- std::unique_ptr<PlanStage> stage;
- std::unique_ptr<EExpression> expr;
-
- // Sorted map for projects. Since by default the map will be sorted by the string, we will get
- // deterministic order as we insert symbol string keys and expression values.
- std::map<std::string, std::unique_ptr<EExpression>> projects;
-};
-
-using AstQuery = peg::AstBase<ParsedQueryTree>;
-
-class Parser {
-public:
- Parser(RuntimeEnvironment* env);
- std::unique_ptr<PlanStage> parse(OperationContext* opCtx,
- StringData defaultDb,
- StringData line);
-
- std::pair<boost::optional<value::SlotId>, boost::optional<value::SlotId>> getTopLevelSlots()
- const {
- return {_resultSlot, _recordIdSlot};
- }
-
-private:
- using SymbolTable = std::map<std::string, value::SlotId>;
- using SpoolBufferLookupTable = std::map<std::string, SpoolId>;
- peg::parser _parser;
- OperationContext* _opCtx{nullptr};
- std::string _defaultDb;
- SymbolTable _symbolsLookupTable;
- SpoolBufferLookupTable _spoolBuffersLookupTable;
- value::SlotIdGenerator _slotIdGenerator;
- value::SpoolIdGenerator _spoolIdGenerator;
- FrameId _frameId{0};
- RuntimeEnvironment* _env;
- struct FrameSymbolTable {
- FrameId id;
- SymbolTable table;
- };
- struct FrameSymbol {
- FrameId id;
- value::SlotId slotId;
- };
- std::vector<std::unique_ptr<FrameSymbolTable>> _frameLookupTable;
- boost::optional<value::SlotId> _resultSlot;
- boost::optional<value::SlotId> _recordIdSlot;
- std::stack<PlanNodeId> planNodeIdStack;
-
- FrameSymbolTable* newFrameSymbolTable() {
- auto table = std::make_unique<FrameSymbolTable>();
- table->id = ++_frameId;
-
- _frameLookupTable.emplace_back(std::move(table));
-
- return _frameLookupTable.back().get();
- }
- FrameSymbolTable* currentFrameSymbolTable() {
- return _frameLookupTable.back().get();
- }
- void popFrameSymbolTable() {
- _frameLookupTable.pop_back();
- }
-
- boost::optional<FrameSymbol> lookupSymbol(const std::string& name) {
- for (size_t idx = _frameLookupTable.size(); idx-- > 0;) {
- if (auto it = _frameLookupTable[idx]->table.find(name);
- it != _frameLookupTable[idx]->table.end()) {
- return FrameSymbol{_frameLookupTable[idx]->id, it->second};
- }
- }
-
- return boost::none;
- }
- boost::optional<value::SlotId> lookupSlot(const std::string& name) {
- if (name.empty() || name == DebugPrinter::kNoneKeyword) {
- return boost::none;
- } else if (_symbolsLookupTable.find(name) == _symbolsLookupTable.end()) {
- _symbolsLookupTable[name] = _slotIdGenerator.generate();
-
- if (name == "$$RESULT") {
- _resultSlot = _symbolsLookupTable[name];
- } else if (name == "$$RID") {
- _recordIdSlot = _symbolsLookupTable[name];
- }
- }
- return _symbolsLookupTable[name];
- }
-
- value::SlotId lookupSlotStrict(const std::string& name) {
- auto slot = lookupSlot(name);
- uassert(4885906, str::stream() << "Unable lookup SlotId for [" << name << "]", slot);
- return *slot;
- }
-
- value::SlotVector lookupSlots(const std::vector<std::string>& names) {
- value::SlotVector result;
- std::transform(names.begin(),
- names.end(),
- std::back_inserter(result),
- [this](const auto& name) { return lookupSlotStrict(name); });
- return result;
- }
-
- template <typename T>
- sbe::value::SlotMap<T> lookupSlots(std::map<std::string, T> indentifiers) {
- sbe::value::SlotMap<T> result;
- for (auto&& [name, value] : indentifiers) {
- result[lookupSlotStrict(name)] = std::move(value);
- }
- return result;
- }
-
- std::pair<IndexKeysInclusionSet, sbe::value::SlotVector> lookupIndexKeyRenames(
- const std::vector<std::string>& renames, const std::vector<size_t>& indexKeys);
-
- SpoolId lookupSpoolBuffer(const std::string& name) {
- if (_spoolBuffersLookupTable.find(name) == _spoolBuffersLookupTable.end()) {
- _spoolBuffersLookupTable[name] = _spoolIdGenerator.generate();
- }
- return _spoolBuffersLookupTable[name];
- }
-
- void walkChildren(AstQuery& ast);
- void walkIdent(AstQuery& ast);
- void walkIdentList(AstQuery& ast);
- void walkStringList(AstQuery& ast);
- void walkIdentWithRename(AstQuery& ast);
- void walkIdentListWithRename(AstQuery& ast);
- void walkIxKeyWithRename(AstQuery& ast);
- void walkIxKeyListWithRename(AstQuery& ast);
-
- void walkProjectList(AstQuery& ast);
- void walkAssign(AstQuery& ast);
- void walkExpr(AstQuery& ast);
- void walkEqopExpr(AstQuery& ast);
- void walkRelopExpr(AstQuery& ast);
- void walkAddExpr(AstQuery& ast);
- void walkMulExpr(AstQuery& ast);
- void walkPrimaryExpr(AstQuery& ast);
- void walkIfExpr(AstQuery& ast);
- void walkLetExpr(AstQuery& ast);
- void walkLambdaExpr(AstQuery& ast);
- void walkFrameProjectList(AstQuery& ast);
- void walkFunCall(AstQuery& ast);
- void walkUnionBranch(AstQuery& ast);
- void walkSortedMergeBranch(AstQuery& ast);
- void walkSortDirList(AstQuery& ast);
-
- void walkScan(AstQuery& ast);
- void walkParallelScan(AstQuery& ast);
- void walkSeek(AstQuery& ast);
- void walkIndexScan(AstQuery& ast);
- void walkIndexSeek(AstQuery& ast);
- void walkColumnScan(AstQuery& ast);
- void walkProject(AstQuery& ast);
- void walkFilter(AstQuery& ast);
- void walkCFilter(AstQuery& ast);
- void walkSort(AstQuery& ast);
- void walkUnion(AstQuery& ast);
- void walkUnwind(AstQuery& ast);
- void walkMkObj(AstQuery& ast);
- void walkGroup(AstQuery& ast);
- void walkHashJoin(AstQuery& ast);
- void walkNLJoin(AstQuery& ast);
- void walkLimit(AstQuery& ast);
- void walkSkip(AstQuery& ast);
- void walkCoScan(AstQuery& ast);
- void walkTraverse(AstQuery& ast);
- void walkExchange(AstQuery& ast);
- void walkBranch(AstQuery& ast);
- void walkSimpleProj(AstQuery& ast);
- void walkPFO(AstQuery& ast);
- void walkLazyProducerSpool(AstQuery& ast);
- void walkEagerProducerSpool(AstQuery& ast);
- void walkConsumerSpool(AstQuery& ast);
- void walkStackConsumerSpool(AstQuery& ast);
- void walkPlanNodeId(AstQuery& ast);
- void walkUnique(AstQuery& ast);
- void walkSortedMerge(AstQuery& ast);
-
- void walk(AstQuery& ast);
-
- std::unique_ptr<PlanStage> walkPath(AstQuery& ast,
- value::SlotId inputSlot,
- value::SlotId outputSlot);
- std::unique_ptr<PlanStage> walkPathValue(AstQuery& ast,
- value::SlotId inputSlot,
- std::unique_ptr<PlanStage> inputStage,
- value::SlotVector correlated,
- value::SlotId outputSlot);
-
- UUID getCollectionUuid(const std::string& collName);
-
- PlanNodeId getCurrentPlanNodeId();
-};
-} // namespace sbe
-} // namespace mongo
diff --git a/src/mongo/db/exec/sbe/parser/sbe_parser_test.cpp b/src/mongo/db/exec/sbe/parser/sbe_parser_test.cpp
deleted file mode 100644
index bf21402d279..00000000000
--- a/src/mongo/db/exec/sbe/parser/sbe_parser_test.cpp
+++ /dev/null
@@ -1,617 +0,0 @@
-/**
- * Copyright (C) 2020-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/exec/sbe/expressions/expression.h"
-#include "mongo/db/exec/sbe/parser/parser.h"
-#include "mongo/db/exec/sbe/stages/branch.h"
-#include "mongo/db/exec/sbe/stages/co_scan.h"
-#include "mongo/db/exec/sbe/stages/exchange.h"
-#include "mongo/db/exec/sbe/stages/filter.h"
-#include "mongo/db/exec/sbe/stages/hash_agg.h"
-#include "mongo/db/exec/sbe/stages/hash_join.h"
-#include "mongo/db/exec/sbe/stages/ix_scan.h"
-#include "mongo/db/exec/sbe/stages/limit_skip.h"
-#include "mongo/db/exec/sbe/stages/loop_join.h"
-#include "mongo/db/exec/sbe/stages/makeobj.h"
-#include "mongo/db/exec/sbe/stages/project.h"
-#include "mongo/db/exec/sbe/stages/scan.h"
-#include "mongo/db/exec/sbe/stages/sort.h"
-#include "mongo/db/exec/sbe/stages/sorted_merge.h"
-#include "mongo/db/exec/sbe/stages/spool.h"
-#include "mongo/db/exec/sbe/stages/traverse.h"
-#include "mongo/db/exec/sbe/stages/union.h"
-#include "mongo/db/exec/sbe/stages/unique.h"
-#include "mongo/db/exec/sbe/stages/unwind.h"
-#include "mongo/db/exec/sbe/util/debug_print.h"
-#include "mongo/db/exec/sbe/values/value.h"
-#include "mongo/db/namespace_string.h"
-#include "mongo/db/query/sbe_stage_builder_helpers.h"
-#include "mongo/unittest/unittest.h"
-
-#include <regex>
-
-namespace mongo {
-namespace {
-/**
- * Normalizes an SBE plan string by iterating and deterministically replacing every occurance
- * of every unique slot ID with a monotonically increasing slot counter. The SBE plan string
- * remains logically unchanged, but allows us to compare SBE plans string-to-string in test.
- */
-std::string normalizeSbePlanString(const std::string& sbePlanString) {
- str::stream normalizedPlanString;
-
- std::map<std::string, size_t> slotsMap;
- sbe::value::SlotId slotCounter = 1;
-
- const std::regex slotMatchRegex("s[0-9]+");
-
- auto remapSlotsCallback = [&](const std::string& token) {
- std::istringstream iss(token);
- std::string n;
- if (iss >> n) {
- if (std::regex_match(token, slotMatchRegex)) {
- // If already saw this slot, replace with the one that was remapped, otherwise
- // record this new encountered slot in the map and update the counter.
- if (slotsMap.find(token) != slotsMap.end()) {
- normalizedPlanString << "s" << std::to_string(slotsMap[token]);
- } else {
- slotsMap[token] = slotCounter;
- normalizedPlanString << "s" << std::to_string(slotCounter++);
- }
- } else {
- normalizedPlanString << token;
- }
- } else {
- normalizedPlanString << token;
- }
- };
-
- std::sregex_token_iterator begin(
- sbePlanString.begin(), sbePlanString.end(), slotMatchRegex, {-1, 0}),
- end;
- std::for_each(begin, end, remapSlotsCallback);
- return normalizedPlanString;
-}
-
-/**
- * SIMPLE_PROJ and PFO stages are transformed into multiple stages by the parser. They do
- * not have a direct PlanStage analogue and are not included in these tests.
- */
-class SBEParserTest : public unittest::Test {
-protected:
- SBEParserTest() : planNodeId(12345) {
- auto fakeUuid = unittest::assertGet(UUID::parse("00000000-0000-0000-0000-000000000000"));
- stages = sbe::makeSs(
- // IXSCAN with 'recordSlot' and 'recordIdSlot' slots only.
- sbe::makeS<sbe::IndexScanStage>(fakeUuid,
- "_id",
- true,
- sbe::value::SlotId{3},
- sbe::value::SlotId{4},
- boost::none,
- sbe::IndexKeysInclusionSet{},
- sbe::makeSV(),
- boost::none,
- boost::none,
- nullptr,
- planNodeId),
- // IXSCAN with 'seekKeySlotLow' / 'seekKeySlotHigh' present.
- sbe::makeS<sbe::IndexScanStage>(fakeUuid,
- "_id",
- true,
- sbe::value::SlotId{3},
- sbe::value::SlotId{4},
- boost::none,
- sbe::IndexKeysInclusionSet{},
- sbe::makeSV(),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- nullptr,
- planNodeId),
- // IXSCAN with 'recordIdSlot' missing and 'seekKeySlotLow' present.
- sbe::makeS<sbe::IndexScanStage>(fakeUuid,
- "_id",
- true,
- sbe::value::SlotId{1},
- boost::none,
- boost::none,
- sbe::IndexKeysInclusionSet{},
- sbe::makeSV(),
- sbe::value::SlotId{2},
- boost::none,
- nullptr,
- planNodeId),
- // IXSCAN with all slots except seek keys present.
- sbe::makeS<sbe::IndexScanStage>(fakeUuid,
- "_id",
- true,
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::value::SlotId{3},
- sbe::IndexKeysInclusionSet{},
- sbe::makeSV(),
- boost::none,
- boost::none,
- nullptr,
- planNodeId),
- // IXSCAN with all slots present.
- sbe::makeS<sbe::IndexScanStage>(fakeUuid,
- "_id",
- true,
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::value::SlotId{3},
- sbe::IndexKeysInclusionSet{},
- sbe::makeSV(),
- sbe::value::SlotId{4},
- sbe::value::SlotId{5},
- nullptr,
- planNodeId),
- // SCAN with 'recordSlot' and 'recordIdSlot' slots only.
- sbe::makeS<sbe::ScanStage>(fakeUuid,
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- boost::none,
- boost::none,
- boost::none,
- boost::none,
- boost::none,
- std::vector<std::string>{},
- sbe::makeSV(),
- boost::none,
- true /* forward */,
- nullptr,
- planNodeId,
- sbe::ScanCallbacks{}),
- // SCAN with 'recordSlot', 'recordIdSlot' and 'seekKeySlot' slots.
- sbe::makeS<sbe::ScanStage>(fakeUuid,
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- boost::none,
- boost::none,
- boost::none,
- boost::none,
- boost::none,
- std::vector<std::string>{},
- sbe::makeSV(),
- sbe::value::SlotId{3},
- true /* forward */,
- nullptr,
- planNodeId,
- sbe::ScanCallbacks{}),
- // SCAN with all slots present.
- sbe::makeS<sbe::ScanStage>(
- fakeUuid,
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::value::SlotId{3},
- sbe::value::SlotId{4},
- sbe::value::SlotId{5},
- sbe::value::SlotId{6},
- sbe::value::SlotId{7},
- std::vector<std::string>{repl::OpTime::kTimestampFieldName.toString()},
- sbe::makeSV(sbe::value::SlotId{7}),
- sbe::value::SlotId{8},
- true /* forward */,
- nullptr,
- planNodeId,
- sbe::ScanCallbacks{}),
- // PSCAN with both 'recordSlot' and 'recordIdSlot' slots present.
- sbe::makeS<sbe::ParallelScanStage>(fakeUuid,
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- boost::none,
- boost::none,
- boost::none,
- boost::none,
- std::vector<std::string>{"a", "b"},
- sbe::makeSV(1, 2),
- nullptr,
- planNodeId,
- sbe::ScanCallbacks{}),
- // PSCAN with 'recordSlot' missing.
- sbe::makeS<sbe::ParallelScanStage>(fakeUuid,
- sbe::value::SlotId{1},
- boost::none,
- boost::none,
- boost::none,
- boost::none,
- boost::none,
- std::vector<std::string>{"a", "b"},
- sbe::makeSV(1, 2),
- nullptr,
- planNodeId,
- sbe::ScanCallbacks{}),
- // PSCAN with all slots.
- sbe::makeS<sbe::ParallelScanStage>(fakeUuid,
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::value::SlotId{3},
- sbe::value::SlotId{4},
- sbe::value::SlotId{5},
- sbe::value::SlotId{6},
- std::vector<std::string>{"a", "b"},
- sbe::makeSV(1, 2),
- nullptr,
- planNodeId,
- sbe::ScanCallbacks{}),
- // COSCAN
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- // PROJECT
- sbe::makeProjectStage(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- planNodeId,
- sbe::value::SlotId{1},
- stage_builder::makeConstant(sbe::value::TypeTags::NumberInt32, 123),
- sbe::value::SlotId{2},
- stage_builder::makeConstant(sbe::value::TypeTags::NumberInt32, 456)),
- // TRAVERSE with only 'from' and 'in' child stages present
- sbe::makeS<sbe::TraverseStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::value::SlotId{3},
- sbe::makeSV(),
- nullptr,
- nullptr,
- planNodeId,
- 1 /* nestedArraysDepth */
- ),
- // TRAVERSE with 'outerCorrelated' slot vector present.
- sbe::makeS<sbe::TraverseStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::value::SlotId{3},
- sbe::makeSV(4, 5, 6),
- nullptr,
- nullptr,
- planNodeId,
- 1 /* nestedArraysDepth */
- ),
- // TRAVERSE with both 'foldExpr' and 'finalExpr' present.
- sbe::makeS<sbe::TraverseStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::value::SlotId{3},
- sbe::makeSV(4, 5, 6),
- sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::NumberInt32, 123),
- sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::NumberInt32, 456),
- planNodeId,
- 1 /* nestedArraysDepth */
- ),
- // TRAVERSE with 'foldExpr' present but 'finalExpr' missing.
- sbe::makeS<sbe::TraverseStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::value::SlotId{3},
- sbe::makeSV(4, 5, 6),
- sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::NumberInt32, 123),
- nullptr,
- planNodeId,
- 1 /* nestedArraysDepth */
- ),
- // TRAVERSE with 'finalExpr' present but 'foldExpr' missing.
- sbe::makeS<sbe::TraverseStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::value::SlotId{3},
- sbe::makeSV(4, 5, 6),
- nullptr,
- sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::NumberInt32, 123),
- planNodeId,
- 1 /* nestedArraysDepth */
- ),
- // MAKEOBJ
- sbe::makeS<sbe::MakeObjStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::MakeObjFieldBehavior::keep,
- std::vector<std::string>{"a", "b"},
- std::vector<std::string>{"c", "d"},
- sbe::makeSV(3, 4),
- false,
- false,
- planNodeId),
- // GROUP
- sbe::makeS<sbe::HashAggStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeSV(),
- sbe::makeEM(sbe::value::SlotId{2},
- stage_builder::makeFunction(
- "min", sbe::makeE<sbe::EVariable>(sbe::value::SlotId{1})),
- sbe::value::SlotId{3},
- stage_builder::makeFunction(
- "max", sbe::makeE<sbe::EVariable>(sbe::value::SlotId{1}))),
- sbe::makeSV(),
- true,
- boost::none, /* optional collator slot */
- true, /* allowDiskUse */
- planNodeId),
- // GROUP with a collator slot.
- sbe::makeS<sbe::HashAggStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeSV(),
- sbe::makeEM(sbe::value::SlotId{2},
- stage_builder::makeFunction(
- "min", sbe::makeE<sbe::EVariable>(sbe::value::SlotId{1})),
- sbe::value::SlotId{3},
- stage_builder::makeFunction(
- "max", sbe::makeE<sbe::EVariable>(sbe::value::SlotId{1}))),
- sbe::makeSV(),
- true,
- sbe::value::SlotId{4}, /* optional collator slot */
- true, /* allowDiskUse */
- planNodeId),
- // LIMIT
- sbe::makeS<sbe::LimitSkipStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId), 100, boost::none, planNodeId),
- // SKIP
- sbe::makeS<sbe::LimitSkipStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId), boost::none, 100, planNodeId),
- // LIMIT SKIP
- sbe::makeS<sbe::LimitSkipStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId), 100, 200, planNodeId),
- // SORT
- sbe::makeS<sbe::SortStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeSV(1),
- std::vector<sbe::value::SortDirection>{sbe::value::SortDirection::Ascending},
- sbe::makeSV(2),
- std::numeric_limits<size_t>::max(),
- std::numeric_limits<size_t>::max(),
- true,
- planNodeId),
- // SORT with sort direction 'Descending'.
- sbe::makeS<sbe::SortStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeSV(1),
- std::vector<sbe::value::SortDirection>{sbe::value::SortDirection::Descending},
- sbe::makeSV(2),
- std::numeric_limits<size_t>::max(),
- std::numeric_limits<size_t>::max(),
- true,
- planNodeId),
- // SORT with 'limit' other than size_t max.
- sbe::makeS<sbe::SortStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeSV(1),
- std::vector<sbe::value::SortDirection>{sbe::value::SortDirection::Ascending},
- sbe::makeSV(2),
- 100 /* limit other than std::numeric_limits<size_t>::max() */,
- std::numeric_limits<size_t>::max(),
- true,
- planNodeId),
- // HJOIN
- sbe::makeS<sbe::HashJoinStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeSV(1, 2) /* outer conditions */,
- sbe::makeSV(3, 4) /* outer projections */,
- sbe::makeSV(1, 2) /* inner conditions */,
- sbe::makeSV(5, 6) /* inner projections */,
- boost::none, /* optional collator slot */
- planNodeId),
- // HJOIN with a collator slot.
- sbe::makeS<sbe::HashJoinStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeSV(1, 2) /* outer conditions */,
- sbe::makeSV(3, 4) /* outer projections */,
- sbe::makeSV(1, 2) /* inner conditions */,
- sbe::makeSV(5, 6) /* inner projections */,
- sbe::value::SlotId{7}, /* optional collator slot */
- planNodeId),
- // FILTER
- sbe::makeS<sbe::FilterStage<false>>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Boolean,
- sbe::value::bitcastFrom<bool>(true)),
- planNodeId),
- // CFILTER
- sbe::makeS<sbe::FilterStage<true>>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Boolean,
- sbe::value::bitcastFrom<bool>(true)),
- planNodeId),
- // NLJOIN
- sbe::makeS<sbe::LoopJoinStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeSV(1, 2),
- sbe::makeSV(3, 4),
- sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Boolean,
- sbe::value::bitcastFrom<bool>(true)),
- planNodeId),
- // EXCHANGE
- sbe::makeS<sbe::ExchangeConsumer>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- 2,
- sbe::makeSV(1, 2),
- sbe::ExchangePolicy::roundrobin,
- nullptr,
- nullptr,
- planNodeId),
- // UNWIND
- sbe::makeS<sbe::UnwindStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::value::SlotId{3},
- true,
- planNodeId),
- // BRANCH
- sbe::makeS<sbe::BranchStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Boolean,
- sbe::value::bitcastFrom<bool>(true)),
- sbe::makeSV(1, 2),
- sbe::makeSV(3, 4),
- sbe::makeSV(5, 6),
- planNodeId),
- sbe::makeS<sbe::MakeObjStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::MakeObjStage::FieldBehavior::drop,
- std::vector<std::string>{"restricted", "fields"},
- std::vector<std::string>{"projected", "fields"},
- sbe::makeSV(3, 4),
- false,
- false,
- planNodeId),
- sbe::makeS<sbe::MakeObjStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::MakeObjStage::FieldBehavior::keep,
- std::vector<std::string>{"kept", "fields"},
- std::vector<std::string>{"projected", "fields"},
- sbe::makeSV(3, 4),
- false,
- false,
- planNodeId),
- sbe::makeS<sbe::MakeObjStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- boost::none,
- boost::none,
- std::vector<std::string>{},
- std::vector<std::string>{"projected", "fields"},
- sbe::makeSV(3, 4),
- false,
- false,
- planNodeId),
- sbe::makeS<sbe::MakeBsonObjStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- boost::none,
- boost::none,
- std::vector<std::string>{},
- std::vector<std::string>{"projected", "fields"},
- sbe::makeSV(3, 4),
- false,
- false,
- planNodeId),
- sbe::makeS<sbe::MakeBsonObjStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::value::SlotId{1},
- sbe::value::SlotId{2},
- sbe::MakeBsonObjStage::FieldBehavior::drop,
- std::vector<std::string>{"restricted", "fields"},
- std::vector<std::string>{"projected", "fields"},
- sbe::makeSV(3, 4),
- false,
- false,
- planNodeId),
- // ESPOOL
- sbe::makeS<sbe::SpoolEagerProducerStage>(sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::SpoolId{1},
- sbe::makeSV(1, 2),
- planNodeId),
- // LSPOOL
- sbe::makeS<sbe::SpoolLazyProducerStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId),
- sbe::SpoolId{1},
- sbe::makeSV(1, 2),
- sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Boolean,
- sbe::value::bitcastFrom<bool>(true)),
- planNodeId),
- // CSPOOL
- sbe::makeS<sbe::SpoolConsumerStage<false>>(
- sbe::SpoolId{1}, sbe::makeSV(1, 2), planNodeId),
- // SSPOOL
- sbe::makeS<sbe::SpoolConsumerStage<true>>(
- sbe::SpoolId{1}, sbe::makeSV(1, 2), planNodeId),
-
- // UNIQUE
- sbe::makeS<sbe::UniqueStage>(
- sbe::makeS<sbe::CoScanStage>(planNodeId), sbe::makeSV(1, 2), planNodeId),
-
- // UNION
- sbe::makeS<sbe::UnionStage>(
- ([this]() {
- sbe::PlanStage::Vector ret;
- ret.push_back(sbe::makeS<sbe::CoScanStage>(planNodeId));
- ret.push_back(sbe::makeS<sbe::CoScanStage>(planNodeId));
- return ret;
- }()),
- std::vector<sbe::value::SlotVector>{sbe::makeSV(1, 2), sbe::makeSV(3, 4)},
- sbe::makeSV(5, 6),
- planNodeId),
-
- // SORTED_MERGE
- sbe::makeS<sbe::SortedMergeStage>(
- ([this]() {
- sbe::PlanStage::Vector ret;
- ret.push_back(sbe::makeS<sbe::CoScanStage>(planNodeId));
- ret.push_back(sbe::makeS<sbe::CoScanStage>(planNodeId));
- return ret;
- }()),
- std::vector<sbe::value::SlotVector>{sbe::makeSV(1, 2), sbe::makeSV(3, 4)},
- std::vector<sbe::value::SortDirection>{sbe::value::SortDirection::Ascending,
- sbe::value::SortDirection::Ascending},
- std::vector<sbe::value::SlotVector>{sbe::makeSV(1, 2), sbe::makeSV(3, 4)},
- sbe::makeSV(5, 6),
- planNodeId));
- }
-
- PlanNodeId planNodeId;
- sbe::PlanStage::Vector stages;
-};
-
-TEST_F(SBEParserTest, TestIdenticalDebugOutputAfterParse) {
- sbe::DebugPrinter printer;
-
- for (const auto& stage : stages) {
- auto env = std::make_unique<sbe::RuntimeEnvironment>();
- sbe::Parser parser(env.get());
- const auto stageText = printer.print(*stage);
-
- const auto parsedStage = parser.parse(nullptr, "testDb", stageText);
- const auto stageTextAfterParse = printer.print(*parsedStage);
-
- ASSERT_EQ(normalizeSbePlanString(stageText), normalizeSbePlanString(stageTextAfterParse));
- }
-}
-
-TEST_F(SBEParserTest, TestPlanNodeIdIsParsed) {
- sbe::DebugPrinter printer;
- auto env = std::make_unique<sbe::RuntimeEnvironment>();
- sbe::Parser parser(env.get());
-
- for (const auto& stage : stages) {
- const auto stageText = printer.print(*stage);
- const auto parsedStage = parser.parse(nullptr, "testDb", stageText);
- ASSERT_EQ(parsedStage->getCommonStats()->nodeId, planNodeId);
- }
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/db/exec/sbe_cmd.cpp b/src/mongo/db/exec/sbe_cmd.cpp
deleted file mode 100644
index 678bcab4389..00000000000
--- a/src/mongo/db/exec/sbe_cmd.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/**
- * Copyright (C) 2019-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/base/init.h"
-#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/commands.h"
-#include "mongo/db/commands/test_commands_enabled.h"
-#include "mongo/db/cursor_manager.h"
-#include "mongo/db/db_raii.h"
-#include "mongo/db/exec/sbe/parser/parser.h"
-#include "mongo/db/exec/sbe/stages/stages.h"
-#include "mongo/db/query/cursor_request.h"
-#include "mongo/db/query/cursor_response.h"
-#include "mongo/db/query/find_common.h"
-#include "mongo/db/query/plan_executor_factory.h"
-
-namespace mongo {
-/**
- * A command for manually constructing a SBE query tree and running it.
- *
- * db.runCommand({sbe: "sbe query text"})
- *
- * This command is only for testing/experimentation, and requires the 'enableTestCommands' flag to
- * be turned on.
- */
-class SBECommand final : public BasicCommand {
-public:
- SBECommand() : BasicCommand("sbe") {}
-
- AllowedOnSecondary secondaryAllowed(ServiceContext* context) const override {
- return AllowedOnSecondary::kOptIn;
- }
-
- bool supportsWriteConcern(const BSONObj& cmd) const override {
- return false;
- }
-
- bool run(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj,
- BSONObjBuilder& result) override {
- CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
-
- // If SBE is disabled, then also disallow the SBE command.
- uassert(5772200,
- "the SBE command requires the SBE engine to be enabled",
- !internalQueryForceClassicEngine.load());
-
- // The SBE command may read from multiple collections, but no logic is in place to acquire
- // locks on all of the necessary collections and databases. Therefore, its implementation
- // depends on lock free reads being enabled (which is the case by default).
- uassert(5772201,
- "the SBE command requires lock free reads",
- !storageGlobalParams.disableLockFreeReads);
-
- long long batchSize;
- uassertStatusOK(CursorRequest::parseCommandCursorOptions(
- cmdObj, query_request_helper::kDefaultBatchSize, &batchSize));
-
- // Acquire the global lock, acquire a snapshot, and stash our version of the collection
- // catalog. The is necessary because SBE plan executors currently use the external lock
- // policy. Note that this must be done before parsing because the parser may look up
- // collections in the stashed catalog.
- //
- // Unlike the similar 'AutoGetCollection*' variants of this db_raii object, this will not
- // handle re-establishing a view of the catalog which is consistent with the new storage
- // snapshot after a yield. For this reason, the SBE command cannot yield.
- //
- // Also, this will not handle all read concerns. This is ok because the SBE command is
- // experimental and need not support the various read concerns.
- AutoGetDbForReadLockFree autoGet{opCtx, dbname};
-
- auto env = std::make_unique<sbe::RuntimeEnvironment>();
- sbe::Parser parser(env.get());
- auto root = parser.parse(opCtx, dbname, cmdObj["sbe"].String());
- auto [resultSlot, recordIdSlot] = parser.getTopLevelSlots();
-
- std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> exec;
- BSONArrayBuilder firstBatch;
-
- // Create a trivial cannonical query for the 'sbe' command execution.
- NamespaceString nss{dbname};
- auto statusWithCQ =
- CanonicalQuery::canonicalize(opCtx, std::make_unique<FindCommandRequest>(nss));
- std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
-
- stage_builder::PlanStageData data{std::move(env)};
-
- if (resultSlot) {
- data.outputs.set(stage_builder::PlanStageSlots::kResult, *resultSlot);
- }
- if (recordIdSlot) {
- data.outputs.set(stage_builder::PlanStageSlots::kRecordId, *recordIdSlot);
- }
-
- root->attachToOperationContext(opCtx);
- root->prepare(data.ctx);
- exec = uassertStatusOK(
- plan_executor_factory::make(opCtx,
- std::move(cq),
- nullptr,
- {std::move(root), std::move(data)},
- {},
- MultipleCollectionAccessor(CollectionPtr::null),
- false, /* returnOwnedBson */
- nss,
- nullptr));
- for (long long objCount = 0; objCount < batchSize; objCount++) {
- BSONObj next;
- PlanExecutor::ExecState state = exec->getNext(&next, nullptr);
- if (state == PlanExecutor::IS_EOF) {
- break;
- }
- invariant(state == PlanExecutor::ADVANCED);
-
- // If we can't fit this result inside the current batch, then we stash it for later.
- if (!FindCommon::haveSpaceForNext(next, objCount, firstBatch.len())) {
- exec->stashResult(next);
- break;
- }
-
- firstBatch.append(next);
- }
-
- if (exec->isEOF()) {
- appendCursorResponseObject(0LL, nss.ns(), firstBatch.arr(), boost::none, &result);
- return true;
- }
-
- exec->saveState();
- exec->detachFromOperationContext();
- const auto pinnedCursor = CursorManager::get(opCtx)->registerCursor(
- opCtx,
- {std::move(exec),
- nss,
- AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
- APIParameters::get(opCtx),
- opCtx->getWriteConcern(),
- repl::ReadConcernArgs::get(opCtx),
- ReadPreferenceSetting::get(opCtx),
- cmdObj,
- {}});
-
- appendCursorResponseObject(
- pinnedCursor.getCursor()->cursorid(), nss.ns(), firstBatch.arr(), boost::none, &result);
-
- return true;
- }
-
- // This is a test-only command so shouldn't be enabled in production, but we try to require
- // auth on new test commands anyway, just in case someone enables them by mistake.
- Status checkAuthForOperation(OperationContext* opCtx,
- const std::string& dbname,
- const BSONObj& cmdObj) const override {
- auto authSession = AuthorizationSession::get(opCtx->getClient());
- if (!authSession->isAuthorizedForAnyActionOnAnyResourceInDB(dbname)) {
- return Status(ErrorCodes::Unauthorized, "Unauthorized");
- }
- return Status::OK();
- }
-};
-
-MONGO_REGISTER_TEST_COMMAND(SBECommand);
-} // namespace mongo
diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js
index cd56dd15926..d3800871af1 100644
--- a/src/mongo/shell/db.js
+++ b/src/mongo/shell/db.js
@@ -1856,12 +1856,3 @@ DB.prototype.dropEncryptedCollection = function(name) {
return this.getCollection(name).drop();
};
}());
-
-DB.prototype._sbe = function(query) {
- const res = this.runCommand({sbe: query});
- if (!res.ok) {
- throw _getErrorWithCode(res, "sbe failed: " + tojson(res));
- }
-
- return new DBCommandCursor(this, res);
-};