From 3a2231e309e77cb0d9c1d2bd0aa380e35ec6d32a Mon Sep 17 00:00:00 2001 From: Anna Wawrzyniak Date: Wed, 16 Mar 2022 23:50:03 +0000 Subject: SERVER-64417 Add configurable printers for SBE --- src/mongo/db/exec/sbe/SConscript | 5 + src/mongo/db/exec/sbe/util/print_options.h | 87 ++++ .../db/exec/sbe/util/stage_results_printer.cpp | 119 +++++ src/mongo/db/exec/sbe/util/stage_results_printer.h | 87 ++++ .../exec/sbe/util/stage_results_printer_test.cpp | 74 ++++ src/mongo/db/exec/sbe/values/slot_printer.cpp | 65 +++ src/mongo/db/exec/sbe/values/slot_printer.h | 73 ++++ src/mongo/db/exec/sbe/values/slot_printer_test.cpp | 72 ++++ src/mongo/db/exec/sbe/values/value.cpp | 413 +----------------- src/mongo/db/exec/sbe/values/value.h | 3 - src/mongo/db/exec/sbe/values/value_printer.cpp | 479 +++++++++++++++++++++ src/mongo/db/exec/sbe/values/value_printer.h | 81 ++++ .../exec/sbe/values/write_value_to_stream_test.cpp | 36 +- 13 files changed, 1168 insertions(+), 426 deletions(-) create mode 100644 src/mongo/db/exec/sbe/util/print_options.h create mode 100644 src/mongo/db/exec/sbe/util/stage_results_printer.cpp create mode 100644 src/mongo/db/exec/sbe/util/stage_results_printer.h create mode 100644 src/mongo/db/exec/sbe/util/stage_results_printer_test.cpp create mode 100644 src/mongo/db/exec/sbe/values/slot_printer.cpp create mode 100644 src/mongo/db/exec/sbe/values/slot_printer.h create mode 100644 src/mongo/db/exec/sbe/values/slot_printer_test.cpp create mode 100644 src/mongo/db/exec/sbe/values/value_printer.cpp create mode 100644 src/mongo/db/exec/sbe/values/value_printer.h 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 + * . + * + * 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 + * . + * + * 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 +StageResultsPrinter::StageResultsPrinter(T& stream, const PrintOptions& options) + : _stream(stream), + _options(options), + _valuePrinter(value::ValuePrinters::make(stream, options)) {} + +template +void StageResultsPrinter::printStageResults(CompileCtx* ctx, + const value::SlotVector& slots, + const std::vector& 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 +void StageResultsPrinter::printStageResults(CompileCtx* ctx, + const SlotNames& slotNames, + PlanStage* stage) { + std::vector 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 +void StageResultsPrinter::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; +template class StageResultsPrinter; + +StageResultsPrinter StageResultsPrinters::make(std::ostream& stream, + const PrintOptions& options) { + return StageResultsPrinter(stream, options); +} + +StageResultsPrinter 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 + * . + * + * 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 + +#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 +class StageResultsPrinter; + +/** + * Companion static class to StageResultsPrinter template. + */ +class StageResultsPrinters { + StageResultsPrinters() = delete; + StageResultsPrinters(const StageResultsPrinters&) = delete; + +public: + using SlotNames = std::vector>; + + static StageResultsPrinter make(std::ostream& stream, + const PrintOptions& options); + static StageResultsPrinter make(str::stream& stream, const PrintOptions& options); +}; + +template +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& 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 _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 + * . + * + * 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& 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 + * . + * + * 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 + +#include "mongo/db/exec/sbe/values/slot_printer.h" +#include "mongo/platform/basic.h" + +namespace mongo::sbe::value { + +template +SlotPrinter::SlotPrinter(T& stream, const PrintOptions& options) + : _stream(stream), _options(options), _valuePrinter(ValuePrinters::make(stream, options)) {} + +template +void SlotPrinter::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; +template class SlotPrinter; + +SlotPrinter SlotPrinters::make(std::ostream& stream, const PrintOptions& options) { + return SlotPrinter(stream, options); +} + +SlotPrinter 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 + * . + * + * 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 + +#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 +class SlotPrinter; + +/** + * Companion static class to SlotPrinter template. + */ +class SlotPrinters { + SlotPrinters() = delete; + SlotPrinters(const SlotPrinters&) = delete; + +public: + static SlotPrinter make(std::ostream& stream, const PrintOptions& options); + static SlotPrinter make(str::stream& stream, const PrintOptions& options); +}; + +template +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 _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 + * . + * + * 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(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(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 -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 -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 -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 -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 -void writeObjectToStream(T& stream, const BSONObj& obj) { - writeObjectToStream(stream, TypeTags::bsonObject, bitcastFrom(obj.objdata())); -} - -template -void writeObjectIdToStream(T& stream, TypeTags tag, Value val) { - auto objId = - tag == TypeTags::ObjectId ? getObjectIdView(val)->data() : bitcastTo(val); - - stream << (tag == TypeTags::ObjectId ? "ObjectId(\"" : "bsonObjectId(\"") - << OID::from(objId).toString() << "\")"; -} - -template -void writeCollatorToStream(T& stream, const CollatorInterface* collator) { - if (collator) { - stream << "Collator("; - writeObjectToStream(stream, collator->getSpec().toBSON()); - stream << ')'; - } else { - stream << "null"; - } -} - -template -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 -void writeValueToStream(T& stream, TypeTags tag, Value val, size_t depth = 1) { - switch (tag) { - case TypeTags::NumberInt32: - stream << bitcastTo(val); - break; - case TypeTags::NumberInt64: - stream << bitcastTo(val); - break; - case TypeTags::NumberDouble: - stream << bitcastTo(val); - break; - case TypeTags::NumberDecimal: - stream << bitcastTo(val).toString(); - break; - case TypeTags::Date: - stream << bitcastTo(val); - break; - case TypeTags::Boolean: - stream << (bitcastTo(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(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(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(dbptr.id)); - stream << ')'; - break; - } - case TypeTags::bsonCodeWScope: { - const auto cws = getBsonCodeWScopeView(val); - stream << "CodeWScope(" << cws.code << ", "; - writeObjectToStream(stream, TypeTags::bsonObject, bitcastFrom(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& 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& 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; 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 + * . + * + * 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 +ValuePrinter::ValuePrinter(T& stream, const PrintOptions& options) + : stream(stream), options(options) {} +template +void ValuePrinter::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 +void ValuePrinter::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 +void ValuePrinter::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 +void ValuePrinter::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 +void ValuePrinter::writeObjectToStream(const BSONObj& obj) { + writeObjectToStream(TypeTags::bsonObject, bitcastFrom(obj.objdata())); +} + +template +void ValuePrinter::writeObjectIdToStream(TypeTags tag, Value val) { + auto objId = + tag == TypeTags::ObjectId ? getObjectIdView(val)->data() : bitcastTo(val); + + stream << (tag == TypeTags::ObjectId ? "ObjectId(\"" : "bsonObjectId(\"") + << OID::from(objId).toString() << "\")"; +} + +template +void ValuePrinter::writeCollatorToStream(const CollatorInterface* collator) { + if (collator) { + stream << "Collator("; + writeObjectToStream(collator->getSpec().toBSON()); + stream << ')'; + } else { + stream << "null"; + } +} + +template +void ValuePrinter::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 +void ValuePrinter::writeValueToStream(TypeTags tag, Value val, size_t depth) { + switch (tag) { + case TypeTags::NumberInt32: + stream << bitcastTo(val); + break; + case TypeTags::NumberInt64: + stream << bitcastTo(val); + if (options.useTagForAmbiguousValues()) { + stream << "ll"; + } + break; + case TypeTags::NumberDouble: + stream << bitcastTo(val); + if (options.useTagForAmbiguousValues()) { + stream << "L"; + } + break; + case TypeTags::NumberDecimal: + if (options.useTagForAmbiguousValues()) { + writeTagToStream(tag); + stream << "("; + } + stream << bitcastTo(val).toString(); + if (options.useTagForAmbiguousValues()) { + stream << ")"; + } + break; + case TypeTags::Date: + if (options.useTagForAmbiguousValues()) { + writeTagToStream(tag); + stream << "("; + } + stream << bitcastTo(val); + if (options.useTagForAmbiguousValues()) { + stream << ")"; + } + break; + case TypeTags::Boolean: + stream << (bitcastTo(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(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(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(dbptr.id)); + stream << ')'; + break; + } + case TypeTags::bsonCodeWScope: { + const auto cws = getBsonCodeWScopeView(val); + stream << "CodeWScope(" << cws.code << ", "; + writeObjectToStream(TypeTags::bsonObject, bitcastFrom(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; +template class ValuePrinter; + +ValuePrinter ValuePrinters::make(std::ostream& stream, const PrintOptions& options) { + return ValuePrinter(stream, options); +} + +ValuePrinter 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 + * . + * + * 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 + +#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 +class ValuePrinter; + +/** + * Companion static class to ValuePrinter template. + */ +class ValuePrinters { + ValuePrinters() = delete; + ValuePrinters(const ValuePrinters&) = delete; + +public: + static ValuePrinter make(std::ostream& stream, const PrintOptions& options); + static ValuePrinter make(str::stream& stream, const PrintOptions& options); +}; + +/** + * Utility for printing values to stream. + */ +template +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(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(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()); } -- cgit v1.2.1