summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnna Wawrzyniak <anna.wawrzyniak@mongodb.com>2022-03-16 23:50:03 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-03-17 00:23:04 +0000
commit3a2231e309e77cb0d9c1d2bd0aa380e35ec6d32a (patch)
tree8de1e3f3a145c6d60c13bf8d114f733e8ae73773
parentcccb587590252e1b2bf2e199e783e52c65141825 (diff)
downloadmongo-3a2231e309e77cb0d9c1d2bd0aa380e35ec6d32a.tar.gz
SERVER-64417 Add configurable printers for SBE
-rw-r--r--src/mongo/db/exec/sbe/SConscript5
-rw-r--r--src/mongo/db/exec/sbe/util/print_options.h87
-rw-r--r--src/mongo/db/exec/sbe/util/stage_results_printer.cpp119
-rw-r--r--src/mongo/db/exec/sbe/util/stage_results_printer.h87
-rw-r--r--src/mongo/db/exec/sbe/util/stage_results_printer_test.cpp74
-rw-r--r--src/mongo/db/exec/sbe/values/slot_printer.cpp65
-rw-r--r--src/mongo/db/exec/sbe/values/slot_printer.h73
-rw-r--r--src/mongo/db/exec/sbe/values/slot_printer_test.cpp72
-rw-r--r--src/mongo/db/exec/sbe/values/value.cpp413
-rw-r--r--src/mongo/db/exec/sbe/values/value.h3
-rw-r--r--src/mongo/db/exec/sbe/values/value_printer.cpp479
-rw-r--r--src/mongo/db/exec/sbe/values/value_printer.h81
-rw-r--r--src/mongo/db/exec/sbe/values/write_value_to_stream_test.cpp36
13 files changed, 1168 insertions, 426 deletions
diff --git a/src/mongo/db/exec/sbe/SConscript b/src/mongo/db/exec/sbe/SConscript
index ec9deb11cd4..e9cda59b2a1 100644
--- a/src/mongo/db/exec/sbe/SConscript
+++ b/src/mongo/db/exec/sbe/SConscript
@@ -17,6 +17,7 @@ env.Library(
source=[
'values/bson.cpp',
'values/value.cpp',
+ 'values/value_printer.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
@@ -57,8 +58,10 @@ sbeEnv.Library(
'stages/unique.cpp',
'stages/unwind.cpp',
'util/debug_print.cpp',
+ 'util/stage_results_printer.cpp',
'values/sbe_pattern_value_cmp.cpp',
'values/slot.cpp',
+ 'values/slot_printer.cpp',
'vm/arith.cpp',
'vm/datetime.cpp',
'vm/vm.cpp',
@@ -193,7 +196,9 @@ env.CppUnitTest(
'sbe_test.cpp',
'sbe_trial_run_tracker_test.cpp',
'sbe_unique_test.cpp',
+ 'util/stage_results_printer_test.cpp',
'values/sbe_pattern_value_cmp_test.cpp',
+ 'values/slot_printer_test.cpp',
'values/value_serialization_test.cpp',
"values/value_test.cpp",
'values/write_value_to_stream_test.cpp'
diff --git a/src/mongo/db/exec/sbe/util/print_options.h b/src/mongo/db/exec/sbe/util/print_options.h
new file mode 100644
index 00000000000..e926d191df1
--- /dev/null
+++ b/src/mongo/db/exec/sbe/util/print_options.h
@@ -0,0 +1,87 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/platform/basic.h"
+#include "mongo/util/ctype.h"
+
+namespace mongo::sbe {
+
+class PrintOptions {
+public:
+ static constexpr size_t kDefaultStringMaxDisplayLength = 160;
+ static constexpr size_t kDefaultBinDataMaxDisplayLength = 80;
+ static constexpr size_t kDefaultArrayObjectOrNestingMaxDepth = 10;
+ static constexpr size_t kDefaultUseTagForAmbiguousValues = false;
+
+ size_t stringMaxDisplayLength() const {
+ return _stringMaxDisplayLength;
+ }
+
+ PrintOptions& stringMaxDisplayLength(size_t value) {
+ _stringMaxDisplayLength = value;
+ return *this;
+ }
+
+ size_t binDataMaxDisplayLength() const {
+ return _binDataMaxDisplayLength;
+ }
+
+ PrintOptions& binDataMaxDisplayLength(size_t value) {
+ _binDataMaxDisplayLength = value;
+ return *this;
+ }
+
+ size_t arrayObjectOrNestingMaxDepth() const {
+ return _arrayObjectOrNestingMaxDepth;
+ }
+
+ PrintOptions& arrayObjectOrNestingMaxDepth(size_t value) {
+ _arrayObjectOrNestingMaxDepth = value;
+ return *this;
+ }
+
+ bool useTagForAmbiguousValues() const {
+ return _useTagForAmbiguousValues;
+ }
+
+ PrintOptions& useTagForAmbiguousValues(bool value) {
+ _useTagForAmbiguousValues = value;
+ return *this;
+ }
+
+private:
+ size_t _stringMaxDisplayLength = kDefaultStringMaxDisplayLength;
+ size_t _binDataMaxDisplayLength = kDefaultBinDataMaxDisplayLength;
+ size_t _arrayObjectOrNestingMaxDepth = kDefaultArrayObjectOrNestingMaxDepth;
+ bool _useTagForAmbiguousValues = kDefaultUseTagForAmbiguousValues;
+};
+
+} // namespace mongo::sbe
diff --git a/src/mongo/db/exec/sbe/util/stage_results_printer.cpp b/src/mongo/db/exec/sbe/util/stage_results_printer.cpp
new file mode 100644
index 00000000000..603b00ef5a0
--- /dev/null
+++ b/src/mongo/db/exec/sbe/util/stage_results_printer.cpp
@@ -0,0 +1,119 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/db/exec/sbe/util/stage_results_printer.h"
+#include "mongo/platform/basic.h"
+
+namespace mongo::sbe {
+
+template <typename T>
+StageResultsPrinter<T>::StageResultsPrinter(T& stream, const PrintOptions& options)
+ : _stream(stream),
+ _options(options),
+ _valuePrinter(value::ValuePrinters::make(stream, options)) {}
+
+template <typename T>
+void StageResultsPrinter<T>::printStageResults(CompileCtx* ctx,
+ const value::SlotVector& slots,
+ const std::vector<std::string>& names,
+ PlanStage* stage) {
+ tassert(6441701, "slots and names sizes must match", slots.size() == names.size());
+ SlotNames slotNames;
+ size_t idx = 0;
+ for (auto slot : slots) {
+ slotNames.emplace_back(slot, names[idx++]);
+ }
+
+ printStageResults(ctx, slotNames, stage);
+}
+
+template <typename T>
+void StageResultsPrinter<T>::printStageResults(CompileCtx* ctx,
+ const SlotNames& slotNames,
+ PlanStage* stage) {
+ std::vector<value::SlotAccessor*> accessors;
+ for (auto slot : slotNames) {
+ accessors.push_back(stage->getAccessor(*ctx, slot.first));
+ }
+
+ printSlotNames(slotNames);
+ _stream << ":"
+ << "\n";
+
+ size_t iter = 0;
+ for (auto st = stage->getNext(); st == PlanState::ADVANCED; st = stage->getNext(), iter++) {
+ if (iter >= _options.arrayObjectOrNestingMaxDepth()) {
+ _stream << "..."
+ << "\n";
+ break;
+ }
+
+ bool first = true;
+ for (auto accessor : accessors) {
+ if (!first) {
+ _stream << ", ";
+ } else {
+ first = false;
+ }
+ auto [tag, val] = accessor->getViewOfValue();
+ _valuePrinter.writeValueToStream(tag, val);
+ }
+ _stream << "\n";
+ }
+}
+
+template <typename T>
+void StageResultsPrinter<T>::printSlotNames(const SlotNames& slotNames) {
+ _stream << "[";
+ bool first = true;
+ for (auto slot : slotNames) {
+ if (!first) {
+ _stream << ", ";
+ } else {
+ first = false;
+ }
+ _stream << slot.second;
+ }
+ _stream << "]";
+}
+
+template class StageResultsPrinter<std::ostream>;
+template class StageResultsPrinter<str::stream>;
+
+StageResultsPrinter<std::ostream> StageResultsPrinters::make(std::ostream& stream,
+ const PrintOptions& options) {
+ return StageResultsPrinter(stream, options);
+}
+
+StageResultsPrinter<str::stream> StageResultsPrinters::make(str::stream& stream,
+ const PrintOptions& options) {
+ return StageResultsPrinter(stream, options);
+}
+
+} // namespace mongo::sbe
diff --git a/src/mongo/db/exec/sbe/util/stage_results_printer.h b/src/mongo/db/exec/sbe/util/stage_results_printer.h
new file mode 100644
index 00000000000..a96fb8e195a
--- /dev/null
+++ b/src/mongo/db/exec/sbe/util/stage_results_printer.h
@@ -0,0 +1,87 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <ostream>
+
+#include "mongo/db/exec/sbe/stages/stages.h"
+#include "mongo/db/exec/sbe/util/print_options.h"
+#include "mongo/db/exec/sbe/values/slot.h"
+#include "mongo/db/exec/sbe/values/value.h"
+#include "mongo/db/exec/sbe/values/value_printer.h"
+#include "mongo/platform/basic.h"
+#include "mongo/util/str.h"
+
+namespace mongo::sbe {
+
+template <typename T>
+class StageResultsPrinter;
+
+/**
+ * Companion static class to StageResultsPrinter template.
+ */
+class StageResultsPrinters {
+ StageResultsPrinters() = delete;
+ StageResultsPrinters(const StageResultsPrinters&) = delete;
+
+public:
+ using SlotNames = std::vector<std::pair<value::SlotId, std::string>>;
+
+ static StageResultsPrinter<std::ostream> make(std::ostream& stream,
+ const PrintOptions& options);
+ static StageResultsPrinter<str::stream> make(str::stream& stream, const PrintOptions& options);
+};
+
+template <typename T>
+class StageResultsPrinter {
+ StageResultsPrinter() = delete;
+ StageResultsPrinter(T& stream, const PrintOptions& options);
+
+ friend class StageResultsPrinters;
+
+public:
+ using SlotNames = StageResultsPrinters::SlotNames;
+
+ void printStageResults(CompileCtx* ctx,
+ const value::SlotVector& slots,
+ const std::vector<std::string>& names,
+ PlanStage* stage);
+
+ void printStageResults(CompileCtx* ctx, const SlotNames& slotNames, PlanStage* stage);
+
+ void printSlotNames(const SlotNames& slotNames);
+
+private:
+ T& _stream;
+ const PrintOptions& _options;
+ value::ValuePrinter<T> _valuePrinter;
+};
+
+} // namespace mongo::sbe
diff --git a/src/mongo/db/exec/sbe/util/stage_results_printer_test.cpp b/src/mongo/db/exec/sbe/util/stage_results_printer_test.cpp
new file mode 100644
index 00000000000..f6cd96a3495
--- /dev/null
+++ b/src/mongo/db/exec/sbe/util/stage_results_printer_test.cpp
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/db/exec/sbe/sbe_plan_stage_test.h"
+#include "mongo/db/exec/sbe/util/stage_results_printer.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo::sbe {
+
+class StageResultsPrinterTestFixture : public PlanStageTestFixture {
+public:
+ void runTest(const std::vector<std::string>& names,
+ const BSONArray& input,
+ const PrintOptions& options,
+ const std::string expected) {
+ auto ctx = makeCompileCtx();
+ auto [scanSlots, scanStage] = generateVirtualScanMulti(names.size(), input);
+
+ prepareTree(ctx.get(), scanStage.get());
+
+ std::stringstream stream;
+ StageResultsPrinters::make(stream, options)
+ .printStageResults(ctx.get(), scanSlots, names, scanStage.get());
+
+ ASSERT_EQ(stream.str(), expected);
+
+ scanStage->close();
+ }
+};
+
+TEST_F(StageResultsPrinterTestFixture, SimpleStage) {
+ runTest({"a", "b"},
+ BSON_ARRAY(BSON_ARRAY(1 << "abc") << BSON_ARRAY(2.5 << "def")),
+ PrintOptions(),
+ "[a, b]:\n"
+ "1, \"abc\"\n"
+ "2.5, \"def\"\n");
+}
+
+TEST_F(StageResultsPrinterTestFixture, SimpleStageWithLimit) {
+ runTest({"a", "b"},
+ BSON_ARRAY(BSON_ARRAY(1 << "abc") << BSON_ARRAY(2.5 << "def")),
+ PrintOptions().arrayObjectOrNestingMaxDepth(1),
+ "[a, b]:\n"
+ "1, \"abc\"\n"
+ "...\n");
+}
+} // namespace mongo::sbe
diff --git a/src/mongo/db/exec/sbe/values/slot_printer.cpp b/src/mongo/db/exec/sbe/values/slot_printer.cpp
new file mode 100644
index 00000000000..6efdf05d3ed
--- /dev/null
+++ b/src/mongo/db/exec/sbe/values/slot_printer.cpp
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include <iostream>
+
+#include "mongo/db/exec/sbe/values/slot_printer.h"
+#include "mongo/platform/basic.h"
+
+namespace mongo::sbe::value {
+
+template <typename T>
+SlotPrinter<T>::SlotPrinter(T& stream, const PrintOptions& options)
+ : _stream(stream), _options(options), _valuePrinter(ValuePrinters::make(stream, options)) {}
+
+template <typename T>
+void SlotPrinter<T>::printMaterializedRow(const MaterializedRow& row) {
+ _stream << "[";
+ for (std::size_t idx = 0; idx < row.size(); idx++) {
+ if (idx > 0) {
+ _stream << ", ";
+ }
+ auto [tag, val] = row.getViewOfValue(idx);
+ _valuePrinter.writeValueToStream(tag, val);
+ }
+ _stream << "]";
+}
+
+template class SlotPrinter<std::ostream>;
+template class SlotPrinter<str::stream>;
+
+SlotPrinter<std::ostream> SlotPrinters::make(std::ostream& stream, const PrintOptions& options) {
+ return SlotPrinter(stream, options);
+}
+
+SlotPrinter<str::stream> SlotPrinters::make(str::stream& stream, const PrintOptions& options) {
+ return SlotPrinter(stream, options);
+}
+
+} // namespace mongo::sbe::value
diff --git a/src/mongo/db/exec/sbe/values/slot_printer.h b/src/mongo/db/exec/sbe/values/slot_printer.h
new file mode 100644
index 00000000000..f681874bdeb
--- /dev/null
+++ b/src/mongo/db/exec/sbe/values/slot_printer.h
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <ostream>
+
+#include "mongo/db/exec/sbe/util/print_options.h"
+#include "mongo/db/exec/sbe/values/slot.h"
+#include "mongo/db/exec/sbe/values/value.h"
+#include "mongo/db/exec/sbe/values/value_printer.h"
+#include "mongo/platform/basic.h"
+#include "mongo/util/str.h"
+
+namespace mongo::sbe::value {
+
+template <typename T>
+class SlotPrinter;
+
+/**
+ * Companion static class to SlotPrinter template.
+ */
+class SlotPrinters {
+ SlotPrinters() = delete;
+ SlotPrinters(const SlotPrinters&) = delete;
+
+public:
+ static SlotPrinter<std::ostream> make(std::ostream& stream, const PrintOptions& options);
+ static SlotPrinter<str::stream> make(str::stream& stream, const PrintOptions& options);
+};
+
+template <typename T>
+class SlotPrinter {
+ SlotPrinter() = delete;
+ SlotPrinter(T& stream, const PrintOptions& options);
+ friend class SlotPrinters;
+
+public:
+ void printMaterializedRow(const MaterializedRow& row);
+
+private:
+ T& _stream;
+ const PrintOptions& _options;
+ value::ValuePrinter<T> _valuePrinter;
+};
+
+} // namespace mongo::sbe::value
diff --git a/src/mongo/db/exec/sbe/values/slot_printer_test.cpp b/src/mongo/db/exec/sbe/values/slot_printer_test.cpp
new file mode 100644
index 00000000000..aca12002279
--- /dev/null
+++ b/src/mongo/db/exec/sbe/values/slot_printer_test.cpp
@@ -0,0 +1,72 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/db/exec/sbe/values/slot_printer.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo::sbe::value {
+
+TEST(SlotPrinterTest, SimpleMaterializedRow0) {
+ MaterializedRow row;
+ row.resize(0);
+
+ std::ostringstream oss;
+ SlotPrinters::make(oss, PrintOptions()).printMaterializedRow(row);
+
+ ASSERT_EQ("[]", oss.str());
+}
+
+TEST(SlotPrinterTest, SimpleMaterializedRow1) {
+ MaterializedRow row;
+ row.resize(1);
+
+ row.reset(0, false, TypeTags::NumberInt64, bitcastFrom<int64_t>(13ll));
+
+ std::ostringstream oss;
+ SlotPrinters::make(oss, PrintOptions()).printMaterializedRow(row);
+
+ ASSERT_EQ("[13]", oss.str());
+}
+
+TEST(SlotPrinterTest, SimpleMaterializedRow2) {
+ MaterializedRow row;
+ row.resize(2);
+
+ row.reset(0, false, TypeTags::NumberInt64, bitcastFrom<int64_t>(13ll));
+
+ auto [tag1, val1] = makeNewString("abc");
+ row.reset(1, true, tag1, val1);
+
+ std::ostringstream oss;
+ SlotPrinters::make(oss, PrintOptions()).printMaterializedRow(row);
+
+ ASSERT_EQ("[13, \"abc\"]", oss.str());
+}
+
+} // namespace mongo::sbe::value
diff --git a/src/mongo/db/exec/sbe/values/value.cpp b/src/mongo/db/exec/sbe/values/value.cpp
index 8c7de0f1b8e..b7e09a8cf38 100644
--- a/src/mongo/db/exec/sbe/values/value.cpp
+++ b/src/mongo/db/exec/sbe/values/value.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/exec/sbe/values/bson.h"
#include "mongo/db/exec/sbe/values/sort_spec.h"
#include "mongo/db/exec/sbe/values/value_builder.h"
+#include "mongo/db/exec/sbe/values/value_printer.h"
#include "mongo/db/query/collation/collator_interface.h"
#include "mongo/db/query/datetime/date_time_support.h"
#include "mongo/db/storage/key_string.h"
@@ -324,427 +325,23 @@ void releaseValue(TypeTags tag, Value val) noexcept {
}
}
-template <typename T>
-void writeTagToStream(T& stream, const TypeTags tag) {
- switch (tag) {
- case TypeTags::Nothing:
- stream << "Nothing";
- break;
- case TypeTags::NumberInt32:
- stream << "NumberInt32";
- break;
- case TypeTags::NumberInt64:
- stream << "NumberInt64";
- break;
- case TypeTags::NumberDouble:
- stream << "NumberDouble";
- break;
- case TypeTags::NumberDecimal:
- stream << "NumberDecimal";
- break;
- case TypeTags::Date:
- stream << "Date";
- break;
- case TypeTags::Timestamp:
- stream << "Timestamp";
- break;
- case TypeTags::Boolean:
- stream << "Boolean";
- break;
- case TypeTags::Null:
- stream << "Null";
- break;
- case TypeTags::StringSmall:
- stream << "StringSmall";
- break;
- case TypeTags::StringBig:
- stream << "StringBig";
- break;
- case TypeTags::Array:
- stream << "Array";
- break;
- case TypeTags::ArraySet:
- stream << "ArraySet";
- break;
- case TypeTags::Object:
- stream << "Object";
- break;
- case TypeTags::ObjectId:
- stream << "ObjectId";
- break;
- case TypeTags::MinKey:
- stream << "MinKey";
- break;
- case TypeTags::MaxKey:
- stream << "MaxKey";
- break;
- case TypeTags::bsonObject:
- stream << "bsonObject";
- break;
- case TypeTags::bsonArray:
- stream << "bsonArray";
- break;
- case TypeTags::bsonString:
- stream << "bsonString";
- break;
- case TypeTags::bsonSymbol:
- stream << "bsonSymbol";
- break;
- case TypeTags::bsonObjectId:
- stream << "bsonObjectId";
- break;
- case TypeTags::bsonBinData:
- stream << "bsonBinData";
- break;
- case TypeTags::LocalLambda:
- stream << "LocalLambda";
- break;
- case TypeTags::bsonUndefined:
- stream << "bsonUndefined";
- break;
- case TypeTags::ksValue:
- stream << "KeyString";
- break;
- case TypeTags::pcreRegex:
- stream << "pcreRegex";
- break;
- case TypeTags::timeZoneDB:
- stream << "timeZoneDB";
- break;
- case TypeTags::RecordId:
- stream << "RecordId";
- break;
- case TypeTags::jsFunction:
- stream << "jsFunction";
- break;
- case TypeTags::shardFilterer:
- stream << "shardFilterer";
- break;
- case TypeTags::collator:
- stream << "collator";
- break;
- case TypeTags::bsonRegex:
- stream << "bsonRegex";
- break;
- case TypeTags::bsonJavascript:
- stream << "bsonJavascript";
- break;
- case TypeTags::bsonDBPointer:
- stream << "bsonDBPointer";
- break;
- case TypeTags::bsonCodeWScope:
- stream << "bsonCodeWScope";
- break;
- case TypeTags::ftsMatcher:
- stream << "ftsMatcher";
- break;
- case TypeTags::sortSpec:
- stream << "sortSpec";
- break;
- default:
- stream << "unknown tag";
- break;
- }
-}
-
-namespace {
-template <typename T>
-void writeStringDataToStream(T& stream, StringData sd, bool isJavaScript = false) {
- if (!isJavaScript) {
- stream << '"';
- }
- if (sd.size() <= kStringMaxDisplayLength) {
- stream << sd;
- if (!isJavaScript) {
- stream << '"';
- }
- } else {
- stream << sd.substr(0, kStringMaxDisplayLength);
- if (!isJavaScript) {
- stream << "\"...";
- } else {
- stream << "...";
- }
- }
-}
-
-template <typename T>
-void writeArrayToStream(T& stream, TypeTags tag, Value val, size_t depth = 1) {
- stream << '[';
- auto shouldTruncate = true;
- size_t iter = 0;
- if (auto ae = ArrayEnumerator{tag, val}; !ae.atEnd()) {
- while (iter < kArrayObjectOrNestingMaxDepth && depth < kArrayObjectOrNestingMaxDepth) {
- auto [aeTag, aeVal] = ae.getViewOfValue();
- if (aeTag == TypeTags::Array || aeTag == TypeTags::Object) {
- ++depth;
- }
- writeValueToStream(stream, aeTag, aeVal, depth);
- ae.advance();
- if (ae.atEnd()) {
- shouldTruncate = false;
- break;
- }
- stream << ", ";
- ++iter;
- }
- if (shouldTruncate || depth > kArrayObjectOrNestingMaxDepth) {
- stream << "...";
- }
- }
- stream << ']';
-}
-
-template <typename T>
-void writeObjectToStream(T& stream, TypeTags tag, Value val, size_t depth = 1) {
- stream << '{';
- auto shouldTruncate = true;
- size_t iter = 0;
- if (auto oe = ObjectEnumerator{tag, val}; !oe.atEnd()) {
- while (iter < kArrayObjectOrNestingMaxDepth && depth < kArrayObjectOrNestingMaxDepth) {
- stream << "\"" << oe.getFieldName() << "\" : ";
- auto [oeTag, oeVal] = oe.getViewOfValue();
- if (oeTag == TypeTags::Array || oeTag == TypeTags::Object) {
- ++depth;
- }
- writeValueToStream(stream, oeTag, oeVal, depth);
-
- oe.advance();
- if (oe.atEnd()) {
- shouldTruncate = false;
- break;
- }
-
- stream << ", ";
- ++iter;
- }
- if (shouldTruncate || depth > kArrayObjectOrNestingMaxDepth) {
- stream << "...";
- }
- }
- stream << '}';
-}
-
-template <typename T>
-void writeObjectToStream(T& stream, const BSONObj& obj) {
- writeObjectToStream(stream, TypeTags::bsonObject, bitcastFrom<const char*>(obj.objdata()));
-}
-
-template <typename T>
-void writeObjectIdToStream(T& stream, TypeTags tag, Value val) {
- auto objId =
- tag == TypeTags::ObjectId ? getObjectIdView(val)->data() : bitcastTo<uint8_t*>(val);
-
- stream << (tag == TypeTags::ObjectId ? "ObjectId(\"" : "bsonObjectId(\"")
- << OID::from(objId).toString() << "\")";
-}
-
-template <typename T>
-void writeCollatorToStream(T& stream, const CollatorInterface* collator) {
- if (collator) {
- stream << "Collator(";
- writeObjectToStream(stream, collator->getSpec().toBSON());
- stream << ')';
- } else {
- stream << "null";
- }
-}
-
-template <typename T>
-void writeBsonRegexToStream(T& stream, const BsonRegex& regex) {
- stream << '/';
- if (regex.pattern.size() <= kStringMaxDisplayLength) {
- stream << regex.pattern;
- } else {
- stream << regex.pattern.substr(0, kStringMaxDisplayLength) << " ... ";
- }
- stream << '/' << regex.flags;
-}
-
-} // namespace
-
-template <typename T>
-void writeValueToStream(T& stream, TypeTags tag, Value val, size_t depth = 1) {
- switch (tag) {
- case TypeTags::NumberInt32:
- stream << bitcastTo<int32_t>(val);
- break;
- case TypeTags::NumberInt64:
- stream << bitcastTo<int64_t>(val);
- break;
- case TypeTags::NumberDouble:
- stream << bitcastTo<double>(val);
- break;
- case TypeTags::NumberDecimal:
- stream << bitcastTo<Decimal128>(val).toString();
- break;
- case TypeTags::Date:
- stream << bitcastTo<int64_t>(val);
- break;
- case TypeTags::Boolean:
- stream << (bitcastTo<bool>(val) ? "true" : "false");
- break;
- case TypeTags::Null:
- stream << "null";
- break;
- case TypeTags::StringSmall:
- case TypeTags::StringBig:
- case TypeTags::bsonString:
- writeStringDataToStream(stream, getStringOrSymbolView(tag, val));
- break;
- case TypeTags::bsonSymbol:
- stream << "Symbol(";
- writeStringDataToStream(stream, getStringOrSymbolView(tag, val));
- stream << ')';
- break;
- case TypeTags::Array:
- case TypeTags::ArraySet:
- case TypeTags::bsonArray:
- writeArrayToStream(stream, tag, val, depth);
- break;
- case TypeTags::Object:
- case TypeTags::bsonObject:
- writeObjectToStream(stream, tag, val, depth);
- break;
- case TypeTags::ObjectId:
- case TypeTags::bsonObjectId:
- writeObjectIdToStream(stream, tag, val);
- break;
- case TypeTags::Nothing:
- stream << "Nothing";
- break;
- case TypeTags::MinKey:
- stream << "minKey";
- break;
- case TypeTags::MaxKey:
- stream << "maxKey";
- break;
- case TypeTags::bsonBinData: {
- auto data =
- reinterpret_cast<const char*>(getBSONBinDataCompat(TypeTags::bsonBinData, val));
- auto len = getBSONBinDataSizeCompat(TypeTags::bsonBinData, val);
- auto type = getBSONBinDataSubtype(TypeTags::bsonBinData, val);
-
- // If the BinData is a correctly sized newUUID, display it as such.
- if (type == newUUID && len == kNewUUIDLength) {
- using namespace fmt::literals;
- StringData sd(data, len);
- // 4 Octets - 2 Octets - 2 Octets - 2 Octets - 6 Octets
- stream << "UUID(\"{}-{}-{}-{}-{}\")"_format(hexblob::encodeLower(sd.substr(0, 4)),
- hexblob::encodeLower(sd.substr(4, 2)),
- hexblob::encodeLower(sd.substr(6, 2)),
- hexblob::encodeLower(sd.substr(8, 2)),
- hexblob::encodeLower(sd.substr(10, 6)));
- break;
- }
-
- stream << "BinData(" << type << ", "
- << hexblob::encode(data, std::min(len, kBinDataMaxDisplayLength))
- << (len > kBinDataMaxDisplayLength ? "...)" : ")");
- break;
- }
- case TypeTags::bsonUndefined:
- stream << "undefined";
- break;
- case TypeTags::LocalLambda:
- stream << "LocalLambda";
- break;
- case TypeTags::ksValue: {
- auto ks = getKeyStringView(val);
- stream << "KS(" << ks->toString() << ")";
- break;
- }
- case TypeTags::Timestamp: {
- Timestamp ts{bitcastTo<uint64_t>(val)};
- stream << ts.toString();
- break;
- }
- case TypeTags::pcreRegex: {
- auto regex = getPcreRegexView(val);
- stream << "PcreRegex(/" << regex->pattern() << "/" << regex->options() << ")";
- break;
- }
- case TypeTags::timeZoneDB: {
- auto tzdb = getTimeZoneDBView(val);
- auto timeZones = tzdb->getTimeZoneStrings();
- stream << "TimeZoneDatabase(" << timeZones.front() << "..." << timeZones.back() + ")";
- break;
- }
- case TypeTags::RecordId:
- stream << "RecordId(" << getRecordIdView(val)->toString() << ")";
- break;
- case TypeTags::jsFunction:
- // TODO: Also include code.
- stream << "jsFunction";
- break;
- case TypeTags::shardFilterer:
- stream << "ShardFilterer";
- break;
- case TypeTags::collator:
- writeCollatorToStream(stream, getCollatorView(val));
- break;
- case TypeTags::bsonRegex: {
- writeBsonRegexToStream(stream, getBsonRegexView(val));
- break;
- }
- case TypeTags::bsonJavascript:
- stream << "Javascript(";
- writeStringDataToStream(stream, getStringView(TypeTags::StringBig, val), true);
- stream << ")";
- break;
- case TypeTags::bsonDBPointer: {
- const auto dbptr = getBsonDBPointerView(val);
- stream << "DBPointer(";
- writeStringDataToStream(stream, dbptr.ns);
- stream << ", ";
- writeObjectIdToStream(
- stream, TypeTags::bsonObjectId, bitcastFrom<const uint8_t*>(dbptr.id));
- stream << ')';
- break;
- }
- case TypeTags::bsonCodeWScope: {
- const auto cws = getBsonCodeWScopeView(val);
- stream << "CodeWScope(" << cws.code << ", ";
- writeObjectToStream(stream, TypeTags::bsonObject, bitcastFrom<const char*>(cws.scope));
- stream << ')';
- break;
- }
- case value::TypeTags::ftsMatcher: {
- auto ftsMatcher = getFtsMatcherView(val);
- stream << "FtsMatcher(";
- writeObjectToStream(stream, ftsMatcher->query().toBSON());
- stream << ')';
- break;
- }
- case TypeTags::sortSpec:
- stream << "SortSpec(";
- writeObjectToStream(stream, getSortSpecView(val)->getPattern());
- stream << ", ";
- writeCollatorToStream(stream, getSortSpecView(val)->getCollator());
- stream << ')';
- break;
- default:
- MONGO_UNREACHABLE;
- }
-}
-
std::ostream& operator<<(std::ostream& os, const TypeTags tag) {
- writeTagToStream(os, tag);
+ ValuePrinters::make(os, PrintOptions()).writeTagToStream(tag);
return os;
}
str::stream& operator<<(str::stream& str, const TypeTags tag) {
- writeTagToStream(str, tag);
+ ValuePrinters::make(str, PrintOptions()).writeTagToStream(tag);
return str;
}
std::ostream& operator<<(std::ostream& os, const std::pair<TypeTags, Value>& value) {
- writeValueToStream(os, value.first, value.second);
+ ValuePrinters::make(os, PrintOptions()).writeValueToStream(value.first, value.second);
return os;
}
str::stream& operator<<(str::stream& str, const std::pair<TypeTags, Value>& value) {
- writeValueToStream(str, value.first, value.second);
+ ValuePrinters::make(str, PrintOptions()).writeValueToStream(value.first, value.second);
return str;
}
diff --git a/src/mongo/db/exec/sbe/values/value.h b/src/mongo/db/exec/sbe/values/value.h
index 6a7cfe810e8..a726b0943e3 100644
--- a/src/mongo/db/exec/sbe/values/value.h
+++ b/src/mongo/db/exec/sbe/values/value.h
@@ -77,10 +77,7 @@ using IndexKeysInclusionSet = std::bitset<Ordering::kMaxCompoundIndexKeys>;
namespace value {
class SortSpec;
-static constexpr size_t kStringMaxDisplayLength = 160;
-static constexpr size_t kBinDataMaxDisplayLength = 80;
static constexpr size_t kNewUUIDLength = 16;
-static constexpr size_t kArrayObjectOrNestingMaxDepth = 10;
/**
* Type dispatch tags.
diff --git a/src/mongo/db/exec/sbe/values/value_printer.cpp b/src/mongo/db/exec/sbe/values/value_printer.cpp
new file mode 100644
index 00000000000..0035132e560
--- /dev/null
+++ b/src/mongo/db/exec/sbe/values/value_printer.cpp
@@ -0,0 +1,479 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+#include "mongo/db/exec/sbe/values/value_printer.h"
+#include "mongo/db/exec/sbe/values/sort_spec.h"
+#include "mongo/db/exec/sbe/values/value.h"
+#include "mongo/platform/basic.h"
+
+namespace mongo::sbe::value {
+
+template <typename T>
+ValuePrinter<T>::ValuePrinter(T& stream, const PrintOptions& options)
+ : stream(stream), options(options) {}
+template <typename T>
+void ValuePrinter<T>::writeTagToStream(TypeTags tag) {
+ switch (tag) {
+ case TypeTags::Nothing:
+ stream << "Nothing";
+ break;
+ case TypeTags::NumberInt32:
+ stream << "NumberInt32";
+ break;
+ case TypeTags::NumberInt64:
+ stream << "NumberInt64";
+ break;
+ case TypeTags::NumberDouble:
+ stream << "NumberDouble";
+ break;
+ case TypeTags::NumberDecimal:
+ stream << "NumberDecimal";
+ break;
+ case TypeTags::Date:
+ stream << "Date";
+ break;
+ case TypeTags::Timestamp:
+ stream << "Timestamp";
+ break;
+ case TypeTags::Boolean:
+ stream << "Boolean";
+ break;
+ case TypeTags::Null:
+ stream << "Null";
+ break;
+ case TypeTags::StringSmall:
+ stream << "StringSmall";
+ break;
+ case TypeTags::StringBig:
+ stream << "StringBig";
+ break;
+ case TypeTags::Array:
+ stream << "Array";
+ break;
+ case TypeTags::ArraySet:
+ stream << "ArraySet";
+ break;
+ case TypeTags::Object:
+ stream << "Object";
+ break;
+ case TypeTags::ObjectId:
+ stream << "ObjectId";
+ break;
+ case TypeTags::MinKey:
+ stream << "MinKey";
+ break;
+ case TypeTags::MaxKey:
+ stream << "MaxKey";
+ break;
+ case TypeTags::bsonObject:
+ stream << "bsonObject";
+ break;
+ case TypeTags::bsonArray:
+ stream << "bsonArray";
+ break;
+ case TypeTags::bsonString:
+ stream << "bsonString";
+ break;
+ case TypeTags::bsonSymbol:
+ stream << "bsonSymbol";
+ break;
+ case TypeTags::bsonObjectId:
+ stream << "bsonObjectId";
+ break;
+ case TypeTags::bsonBinData:
+ stream << "bsonBinData";
+ break;
+ case TypeTags::LocalLambda:
+ stream << "LocalLambda";
+ break;
+ case TypeTags::bsonUndefined:
+ stream << "bsonUndefined";
+ break;
+ case TypeTags::ksValue:
+ stream << "KeyString";
+ break;
+ case TypeTags::pcreRegex:
+ stream << "pcreRegex";
+ break;
+ case TypeTags::timeZoneDB:
+ stream << "timeZoneDB";
+ break;
+ case TypeTags::RecordId:
+ stream << "RecordId";
+ break;
+ case TypeTags::jsFunction:
+ stream << "jsFunction";
+ break;
+ case TypeTags::shardFilterer:
+ stream << "shardFilterer";
+ break;
+ case TypeTags::collator:
+ stream << "collator";
+ break;
+ case TypeTags::bsonRegex:
+ stream << "bsonRegex";
+ break;
+ case TypeTags::bsonJavascript:
+ stream << "bsonJavascript";
+ break;
+ case TypeTags::bsonDBPointer:
+ stream << "bsonDBPointer";
+ break;
+ case TypeTags::bsonCodeWScope:
+ stream << "bsonCodeWScope";
+ break;
+ case TypeTags::ftsMatcher:
+ stream << "ftsMatcher";
+ break;
+ case TypeTags::sortSpec:
+ stream << "sortSpec";
+ break;
+ default:
+ stream << "unknown tag";
+ break;
+ }
+}
+
+template <typename T>
+void ValuePrinter<T>::writeStringDataToStream(StringData sd, bool isJavaScript) {
+ if (!isJavaScript) {
+ stream << '"';
+ }
+ if (sd.size() <= options.stringMaxDisplayLength()) {
+ stream << sd;
+ if (!isJavaScript) {
+ stream << '"';
+ }
+ } else {
+ stream << sd.substr(0, options.stringMaxDisplayLength());
+ if (!isJavaScript) {
+ stream << "\"...";
+ } else {
+ stream << "...";
+ }
+ }
+}
+
+template <typename T>
+void ValuePrinter<T>::writeArrayToStream(TypeTags tag, Value val, size_t depth) {
+ stream << '[';
+ auto shouldTruncate = true;
+ size_t iter = 0;
+ if (auto ae = ArrayEnumerator{tag, val}; !ae.atEnd()) {
+ while (iter < options.arrayObjectOrNestingMaxDepth() &&
+ depth < options.arrayObjectOrNestingMaxDepth()) {
+ auto [aeTag, aeVal] = ae.getViewOfValue();
+ if (aeTag == TypeTags::Array || aeTag == TypeTags::Object) {
+ ++depth;
+ }
+ writeValueToStream(aeTag, aeVal, depth);
+ ae.advance();
+ if (ae.atEnd()) {
+ shouldTruncate = false;
+ break;
+ }
+ stream << ", ";
+ ++iter;
+ }
+ if (shouldTruncate || depth > options.arrayObjectOrNestingMaxDepth()) {
+ stream << "...";
+ }
+ }
+ stream << ']';
+}
+
+template <typename T>
+void ValuePrinter<T>::writeObjectToStream(TypeTags tag, Value val, size_t depth) {
+ stream << '{';
+ auto shouldTruncate = true;
+ size_t iter = 0;
+ if (auto oe = ObjectEnumerator{tag, val}; !oe.atEnd()) {
+ while (iter < options.arrayObjectOrNestingMaxDepth() &&
+ depth < options.arrayObjectOrNestingMaxDepth()) {
+ stream << "\"" << oe.getFieldName() << "\" : ";
+ auto [oeTag, oeVal] = oe.getViewOfValue();
+ if (oeTag == TypeTags::Array || oeTag == TypeTags::Object) {
+ ++depth;
+ }
+ writeValueToStream(oeTag, oeVal, depth);
+
+ oe.advance();
+ if (oe.atEnd()) {
+ shouldTruncate = false;
+ break;
+ }
+
+ stream << ", ";
+ ++iter;
+ }
+ if (shouldTruncate || depth > options.arrayObjectOrNestingMaxDepth()) {
+ stream << "...";
+ }
+ }
+ stream << '}';
+}
+
+template <typename T>
+void ValuePrinter<T>::writeObjectToStream(const BSONObj& obj) {
+ writeObjectToStream(TypeTags::bsonObject, bitcastFrom<const char*>(obj.objdata()));
+}
+
+template <typename T>
+void ValuePrinter<T>::writeObjectIdToStream(TypeTags tag, Value val) {
+ auto objId =
+ tag == TypeTags::ObjectId ? getObjectIdView(val)->data() : bitcastTo<uint8_t*>(val);
+
+ stream << (tag == TypeTags::ObjectId ? "ObjectId(\"" : "bsonObjectId(\"")
+ << OID::from(objId).toString() << "\")";
+}
+
+template <typename T>
+void ValuePrinter<T>::writeCollatorToStream(const CollatorInterface* collator) {
+ if (collator) {
+ stream << "Collator(";
+ writeObjectToStream(collator->getSpec().toBSON());
+ stream << ')';
+ } else {
+ stream << "null";
+ }
+}
+
+template <typename T>
+void ValuePrinter<T>::writeBsonRegexToStream(const BsonRegex& regex) {
+ stream << '/';
+ if (regex.pattern.size() <= options.stringMaxDisplayLength()) {
+ stream << regex.pattern;
+ } else {
+ stream << regex.pattern.substr(0, options.stringMaxDisplayLength()) << " ... ";
+ }
+ stream << '/' << regex.flags;
+}
+
+template <typename T>
+void ValuePrinter<T>::writeValueToStream(TypeTags tag, Value val, size_t depth) {
+ switch (tag) {
+ case TypeTags::NumberInt32:
+ stream << bitcastTo<int32_t>(val);
+ break;
+ case TypeTags::NumberInt64:
+ stream << bitcastTo<int64_t>(val);
+ if (options.useTagForAmbiguousValues()) {
+ stream << "ll";
+ }
+ break;
+ case TypeTags::NumberDouble:
+ stream << bitcastTo<double>(val);
+ if (options.useTagForAmbiguousValues()) {
+ stream << "L";
+ }
+ break;
+ case TypeTags::NumberDecimal:
+ if (options.useTagForAmbiguousValues()) {
+ writeTagToStream(tag);
+ stream << "(";
+ }
+ stream << bitcastTo<Decimal128>(val).toString();
+ if (options.useTagForAmbiguousValues()) {
+ stream << ")";
+ }
+ break;
+ case TypeTags::Date:
+ if (options.useTagForAmbiguousValues()) {
+ writeTagToStream(tag);
+ stream << "(";
+ }
+ stream << bitcastTo<int64_t>(val);
+ if (options.useTagForAmbiguousValues()) {
+ stream << ")";
+ }
+ break;
+ case TypeTags::Boolean:
+ stream << (bitcastTo<bool>(val) ? "true" : "false");
+ break;
+ case TypeTags::Null:
+ stream << "null";
+ break;
+ case TypeTags::StringSmall:
+ case TypeTags::StringBig:
+ case TypeTags::bsonString:
+ writeStringDataToStream(getStringOrSymbolView(tag, val));
+ break;
+ case TypeTags::bsonSymbol:
+ stream << "Symbol(";
+ writeStringDataToStream(getStringOrSymbolView(tag, val));
+ stream << ')';
+ break;
+ case TypeTags::Array:
+ case TypeTags::ArraySet:
+ case TypeTags::bsonArray:
+ writeArrayToStream(tag, val, depth);
+ break;
+ case TypeTags::Object:
+ case TypeTags::bsonObject:
+ writeObjectToStream(tag, val, depth);
+ break;
+ case TypeTags::ObjectId:
+ case TypeTags::bsonObjectId:
+ writeObjectIdToStream(tag, val);
+ break;
+ case TypeTags::Nothing:
+ stream << "Nothing";
+ break;
+ case TypeTags::MinKey:
+ stream << "minKey";
+ break;
+ case TypeTags::MaxKey:
+ stream << "maxKey";
+ break;
+ case TypeTags::bsonBinData: {
+ auto data =
+ reinterpret_cast<const char*>(getBSONBinDataCompat(TypeTags::bsonBinData, val));
+ auto len = getBSONBinDataSizeCompat(TypeTags::bsonBinData, val);
+ auto type = getBSONBinDataSubtype(TypeTags::bsonBinData, val);
+
+ // If the BinData is a correctly sized newUUID, display it as such.
+ if (type == newUUID && len == kNewUUIDLength) {
+ using namespace fmt::literals;
+ StringData sd(data, len);
+ // 4 Octets - 2 Octets - 2 Octets - 2 Octets - 6 Octets
+ stream << "UUID(\"{}-{}-{}-{}-{}\")"_format(hexblob::encodeLower(sd.substr(0, 4)),
+ hexblob::encodeLower(sd.substr(4, 2)),
+ hexblob::encodeLower(sd.substr(6, 2)),
+ hexblob::encodeLower(sd.substr(8, 2)),
+ hexblob::encodeLower(sd.substr(10, 6)));
+ break;
+ }
+
+ stream << "BinData(" << type << ", "
+ << hexblob::encode(data, std::min(len, options.binDataMaxDisplayLength()))
+ << (len > options.binDataMaxDisplayLength() ? "...)" : ")");
+ break;
+ }
+ case TypeTags::bsonUndefined:
+ stream << "undefined";
+ break;
+ case TypeTags::LocalLambda:
+ stream << "LocalLambda";
+ break;
+ case TypeTags::ksValue: {
+ auto ks = getKeyStringView(val);
+ stream << "KS(" << ks->toString() << ")";
+ break;
+ }
+ case TypeTags::Timestamp: {
+ if (options.useTagForAmbiguousValues()) {
+ writeTagToStream(tag);
+ stream << "(";
+ }
+ Timestamp ts{bitcastTo<uint64_t>(val)};
+ stream << ts.toString();
+ if (options.useTagForAmbiguousValues()) {
+ stream << ")";
+ }
+ break;
+ }
+ case TypeTags::pcreRegex: {
+ auto regex = getPcreRegexView(val);
+ stream << "PcreRegex(/" << regex->pattern() << "/" << regex->options() << ")";
+ break;
+ }
+ case TypeTags::timeZoneDB: {
+ auto tzdb = getTimeZoneDBView(val);
+ auto timeZones = tzdb->getTimeZoneStrings();
+ stream << "TimeZoneDatabase(" << timeZones.front() << "..." << timeZones.back() + ")";
+ break;
+ }
+ case TypeTags::RecordId:
+ stream << "RecordId(" << getRecordIdView(val)->toString() << ")";
+ break;
+ case TypeTags::jsFunction:
+ // TODO: Also include code.
+ stream << "jsFunction";
+ break;
+ case TypeTags::shardFilterer:
+ stream << "ShardFilterer";
+ break;
+ case TypeTags::collator:
+ writeCollatorToStream(getCollatorView(val));
+ break;
+ case TypeTags::bsonRegex: {
+ writeBsonRegexToStream(getBsonRegexView(val));
+ break;
+ }
+ case TypeTags::bsonJavascript:
+ stream << "Javascript(";
+ writeStringDataToStream(getStringView(TypeTags::StringBig, val), true);
+ stream << ")";
+ break;
+ case TypeTags::bsonDBPointer: {
+ const auto dbptr = getBsonDBPointerView(val);
+ stream << "DBPointer(";
+ writeStringDataToStream(dbptr.ns);
+ stream << ", ";
+ writeObjectIdToStream(TypeTags::bsonObjectId, bitcastFrom<const uint8_t*>(dbptr.id));
+ stream << ')';
+ break;
+ }
+ case TypeTags::bsonCodeWScope: {
+ const auto cws = getBsonCodeWScopeView(val);
+ stream << "CodeWScope(" << cws.code << ", ";
+ writeObjectToStream(TypeTags::bsonObject, bitcastFrom<const char*>(cws.scope));
+ stream << ')';
+ break;
+ }
+ case value::TypeTags::ftsMatcher: {
+ auto ftsMatcher = getFtsMatcherView(val);
+ stream << "FtsMatcher(";
+ writeObjectToStream(ftsMatcher->query().toBSON());
+ stream << ')';
+ break;
+ }
+ case TypeTags::sortSpec:
+ stream << "SortSpec(";
+ writeObjectToStream(getSortSpecView(val)->getPattern());
+ stream << ", ";
+ writeCollatorToStream(getSortSpecView(val)->getCollator());
+ stream << ')';
+ break;
+ default:
+ MONGO_UNREACHABLE;
+ }
+}
+
+template class ValuePrinter<std::ostream>;
+template class ValuePrinter<str::stream>;
+
+ValuePrinter<std::ostream> ValuePrinters::make(std::ostream& stream, const PrintOptions& options) {
+ return ValuePrinter(stream, options);
+}
+
+ValuePrinter<str::stream> ValuePrinters::make(str::stream& stream, const PrintOptions& options) {
+ return ValuePrinter(stream, options);
+}
+
+} // namespace mongo::sbe::value
diff --git a/src/mongo/db/exec/sbe/values/value_printer.h b/src/mongo/db/exec/sbe/values/value_printer.h
new file mode 100644
index 00000000000..337cc252827
--- /dev/null
+++ b/src/mongo/db/exec/sbe/values/value_printer.h
@@ -0,0 +1,81 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <ostream>
+
+#include "mongo/db/exec/sbe/util/print_options.h"
+#include "mongo/db/exec/sbe/values/value.h"
+#include "mongo/platform/basic.h"
+#include "mongo/util/str.h"
+
+namespace mongo::sbe::value {
+
+template <typename T>
+class ValuePrinter;
+
+/**
+ * Companion static class to ValuePrinter template.
+ */
+class ValuePrinters {
+ ValuePrinters() = delete;
+ ValuePrinters(const ValuePrinters&) = delete;
+
+public:
+ static ValuePrinter<std::ostream> make(std::ostream& stream, const PrintOptions& options);
+ static ValuePrinter<str::stream> make(str::stream& stream, const PrintOptions& options);
+};
+
+/**
+ * Utility for printing values to stream.
+ */
+template <typename T>
+class ValuePrinter {
+ ValuePrinter() = delete;
+ ValuePrinter(T& stream, const PrintOptions& options);
+ friend class ValuePrinters;
+
+public:
+ void writeTagToStream(TypeTags tag);
+ void writeStringDataToStream(StringData sd, bool isJavaScript = false);
+ void writeArrayToStream(TypeTags tag, Value val, size_t depth = 1);
+ void writeObjectToStream(TypeTags tag, Value val, size_t depth = 1);
+ void writeObjectToStream(const BSONObj& obj);
+ void writeObjectIdToStream(TypeTags tag, Value val);
+ void writeCollatorToStream(const CollatorInterface* collator);
+ void writeBsonRegexToStream(const BsonRegex& regex);
+ void writeValueToStream(TypeTags tag, Value val, size_t depth = 1);
+
+public:
+ T& stream;
+ PrintOptions options;
+};
+
+} // namespace mongo::sbe::value
diff --git a/src/mongo/db/exec/sbe/values/write_value_to_stream_test.cpp b/src/mongo/db/exec/sbe/values/write_value_to_stream_test.cpp
index 18f73d6ec15..f3be76978ec 100644
--- a/src/mongo/db/exec/sbe/values/write_value_to_stream_test.cpp
+++ b/src/mongo/db/exec/sbe/values/write_value_to_stream_test.cpp
@@ -31,6 +31,7 @@
* This file contains tests for sbe::value::writeValueToStream.
*/
+#include "mongo/db/exec/sbe/util/print_options.h"
#include "mongo/db/exec/sbe/values/value.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/hex.h"
@@ -101,8 +102,8 @@ TEST(WriteValueToStream, LongBSONBinDataTest) {
const std::pair<value::TypeTags, value::Value> value(value::TypeTags::bsonBinData, val);
std::ostringstream oss;
writeToStream(oss, value);
- auto expectedString =
- "BinData(0, " + hexblob::encode(kStringLong, value::kBinDataMaxDisplayLength) + "...)";
+ auto expectedString = "BinData(0, " +
+ hexblob::encode(kStringLong, PrintOptions::kDefaultBinDataMaxDisplayLength) + "...)";
ASSERT_EQUALS(expectedString, oss.str());
}
@@ -153,8 +154,9 @@ TEST(WriteValueToStream, LongStringBigTest) {
value::ValueGuard guard{tag, val};
std::ostringstream oss;
writeToStream(oss, {tag, val});
- auto expectedString =
- "\"" + std::string(kStringLong).substr(0, value::kStringMaxDisplayLength) + "\"" + "...";
+ auto expectedString = "\"" +
+ std::string(kStringLong).substr(0, PrintOptions::kDefaultStringMaxDisplayLength) + "\"" +
+ "...";
ASSERT_EQUALS(expectedString, oss.str());
}
@@ -164,7 +166,7 @@ TEST(WriteValueToStream, BigArrayTest) {
auto [sTag, sVal] = value::makeNewString("a");
value::ValueGuard sGuard{sTag, sVal};
auto testArr = value::getArrayView(aVal);
- for (size_t i = 0; i < value::kArrayObjectOrNestingMaxDepth + 1; ++i) {
+ for (size_t i = 0; i < PrintOptions::kDefaultArrayObjectOrNestingMaxDepth + 1; ++i) {
testArr->push_back(sTag, sVal);
}
std::ostringstream oss;
@@ -176,7 +178,8 @@ TEST(WriteValueToStream, BigArrayTest) {
TEST(WriteValueToStream, NestedArrayTest) {
auto [aTag, aVal] = value::makeNewArray();
- auto [tag, val] = makeNestedArray(value::kArrayObjectOrNestingMaxDepth, aVal, aVal);
+ auto [tag, val] =
+ makeNestedArray(PrintOptions::kDefaultArrayObjectOrNestingMaxDepth, aVal, aVal);
value::ValueGuard guard{tag, val};
std::ostringstream oss;
writeToStream(oss, {tag, val});
@@ -186,7 +189,8 @@ TEST(WriteValueToStream, NestedArrayTest) {
TEST(WriteValueToStream, NestedObjectTest) {
auto [oTag, oVal] = value::makeNewObject();
- auto [tag, val] = makeNestedObject(value::kArrayObjectOrNestingMaxDepth, oVal, oVal);
+ auto [tag, val] =
+ makeNestedObject(PrintOptions::kDefaultArrayObjectOrNestingMaxDepth, oVal, oVal);
value::ValueGuard guard{tag, val};
std::ostringstream oss;
writeToStream(oss, {tag, val});
@@ -209,7 +213,7 @@ TEST(WriteValueToStream, BigArrayInObjectInArrayTest) {
auto testArr = value::getArrayView(iaVal);
auto [sTag, sVal] = value::makeNewString("a");
- for (size_t i = 0; i < value::kArrayObjectOrNestingMaxDepth + 1; ++i) {
+ for (size_t i = 0; i < PrintOptions::kDefaultArrayObjectOrNestingMaxDepth + 1; ++i) {
testArr->push_back(sTag, sVal);
}
@@ -234,7 +238,7 @@ TEST(WriteValueToStream, BigObjectInArrayInObjectTest) {
auto testObj = value::getObjectView(ioVal);
auto [sTag, sVal] = value::makeNewString("a");
- for (size_t i = 0; i < value::kArrayObjectOrNestingMaxDepth + 1; ++i) {
+ for (size_t i = 0; i < PrintOptions::kDefaultArrayObjectOrNestingMaxDepth + 1; ++i) {
testObj->push_back(std::to_string(i), sTag, sVal);
}
@@ -252,7 +256,7 @@ TEST(WriteValueToStream, SmallArrayTest) {
auto [sTag, sVal] = value::makeNewString("a");
value::ValueGuard sGuard{sTag, sVal};
auto testArr = value::getArrayView(aVal);
- for (size_t i = 0; i < value::kArrayObjectOrNestingMaxDepth - 1; ++i) {
+ for (size_t i = 0; i < PrintOptions::kDefaultArrayObjectOrNestingMaxDepth - 1; ++i) {
testArr->push_back(sTag, sVal);
}
std::ostringstream oss;
@@ -267,7 +271,7 @@ TEST(WriteValueToStream, BigObjTest) {
auto [sTag, sVal] = value::makeNewString("a");
value::ValueGuard sGuard{sTag, sVal};
auto testObj = value::getObjectView(oVal);
- for (size_t i = 0; i < value::kArrayObjectOrNestingMaxDepth + 1; ++i) {
+ for (size_t i = 0; i < PrintOptions::kDefaultArrayObjectOrNestingMaxDepth + 1; ++i) {
testObj->push_back(std::to_string(i), sTag, sVal);
}
std::ostringstream oss;
@@ -284,7 +288,7 @@ TEST(WriteValueToStream, SmallObjTest) {
auto [sTag, sVal] = value::makeNewString("a");
value::ValueGuard sGuard{sTag, sVal};
auto testObj = value::getObjectView(oVal);
- for (size_t i = 0; i < value::kArrayObjectOrNestingMaxDepth - 1; ++i) {
+ for (size_t i = 0; i < PrintOptions::kDefaultArrayObjectOrNestingMaxDepth - 1; ++i) {
testObj->push_back(std::to_string(i), sTag, sVal);
}
std::ostringstream oss;
@@ -340,8 +344,9 @@ TEST(WriteValueToStream, LongBSONStringTest) {
const std::pair<value::TypeTags, value::Value> value(value::TypeTags::bsonString, val);
std::ostringstream oss;
writeToStream(oss, value);
- auto expectedString =
- "\"" + std::string(kStringLong).substr(0, value::kStringMaxDisplayLength) + "\"" + "...";
+ auto expectedString = "\"" +
+ std::string(kStringLong).substr(0, PrintOptions::kDefaultStringMaxDisplayLength) + "\"" +
+ "...";
ASSERT_EQUALS(expectedString, oss.str());
}
@@ -362,7 +367,8 @@ TEST(WriteValueToStream, LongBSONSymbolTest) {
std::ostringstream oss;
writeToStream(oss, value);
auto expectedString = "Symbol(\"" +
- std::string(kStringLong).substr(0, value::kStringMaxDisplayLength) + "\"" + "...)";
+ std::string(kStringLong).substr(0, PrintOptions::kDefaultStringMaxDisplayLength) + "\"" +
+ "...)";
ASSERT_EQUALS(expectedString, oss.str());
}