diff options
Diffstat (limited to 'src/mongo/db/exec/sbe/values/value_printer.cpp')
-rw-r--r-- | src/mongo/db/exec/sbe/values/value_printer.cpp | 479 |
1 files changed, 479 insertions, 0 deletions
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 |