From c42d0cf862adeeeb22b311ef5a9851c2b06b711b Mon Sep 17 00:00:00 2001 From: mrambacher Date: Wed, 16 Feb 2022 11:11:55 -0800 Subject: [PATCH] Add support for decimals to PatternEntry (#9577) Summary: Add support for doubles to ObjectLibrary::PatternEntry. This support will allow patterns containing a non-integer number to be parsed correctly. Added appropriate test cases to cover this new option. Pull Request resolved: https://github.com/facebook/rocksdb/pull/9577 Reviewed By: pdillinger Differential Revision: D34269763 Pulled By: mrambacher fbshipit-source-id: b5ce16cbd3665c2974ec0f3412ef2b403ef8b155 --- HISTORY.md | 2 +- include/rocksdb/utilities/object_registry.h | 8 ++- utilities/object_registry.cc | 56 ++++++++++++++---- utilities/object_registry_test.cc | 63 +++++++++++++++++++++ 4 files changed, 113 insertions(+), 16 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index adcf5d02f..e4e7bed3b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -64,7 +64,7 @@ * Remove deprecated overloads of API DB::GetApproximateSizes. * Remove deprecated option DBOptions::new_table_reader_for_compaction_inputs. * Add Transaction::SetReadTimestampForValidation() and Transaction::SetCommitTimestamp(). Default impl returns NotSupported(). - +* Add support for decimal patterns to ObjectLibrary::PatternEntry ### Behavior Changes * Disallow the combination of DBOptions.use_direct_io_for_flush_and_compaction == true and DBOptions.writable_file_max_buffer_size == 0. This combination can cause WritableFileWriter::Append() to loop forever, and it does not make much sense in direct IO. * `ReadOptions::total_order_seek` no longer affects `DB::Get()`. The original motivation for this interaction has been obsolete since RocksDB has been able to detect whether the current prefix extractor is compatible with that used to generate table files, probably RocksDB 5.14.0. diff --git a/include/rocksdb/utilities/object_registry.h b/include/rocksdb/utilities/object_registry.h index b6751cae8..7e092601b 100644 --- a/include/rocksdb/utilities/object_registry.h +++ b/include/rocksdb/utilities/object_registry.h @@ -75,7 +75,8 @@ class ObjectLibrary { kMatchZeroOrMore, // [suffix].* kMatchAtLeastOne, // [suffix].+ kMatchExact, // [suffix] - kMatchNumeric, // [suffix][0-9]+ + kMatchInteger, // [suffix][0-9]+ + kMatchDecimal, // [suffix][0-9]+[.][0-9]+ }; public: @@ -123,8 +124,9 @@ class ObjectLibrary { // Adds a separator (exact match of separator with trailing numbers) to the // entry - PatternEntry& AddNumber(const std::string& separator) { - separators_.emplace_back(separator, kMatchNumeric); + PatternEntry& AddNumber(const std::string& separator, bool is_int = true) { + separators_.emplace_back(separator, + (is_int) ? kMatchInteger : kMatchDecimal); slength_ += separator.size() + 1; return *this; } diff --git a/utilities/object_registry.cc b/utilities/object_registry.cc index 61820620f..6f643ba9b 100644 --- a/utilities/object_registry.cc +++ b/utilities/object_registry.cc @@ -15,6 +15,39 @@ namespace ROCKSDB_NAMESPACE { #ifndef ROCKSDB_LITE +namespace { +bool MatchesInteger(const std::string &target, size_t start, size_t pos) { + // If it is numeric, everything up to the match must be a number + int digits = 0; + while (start < pos) { + if (!isdigit(target[start++])) { + return false; + } else { + digits++; + } + } + return (digits > 0); +} + +bool MatchesDecimal(const std::string &target, size_t start, size_t pos) { + int digits = 0; + for (bool point = false; start < pos; start++) { + if (target[start] == '.') { + if (point) { + return false; + } else { + point = true; + } + } else if (!isdigit(target[start])) { + return false; + } else { + digits++; + } + } + return (digits > 0); +} +} // namespace + size_t ObjectLibrary::PatternEntry::MatchSeparatorAt( size_t start, Quantifier mode, const std::string &target, size_t tlen, const std::string &separator) const { @@ -36,12 +69,13 @@ size_t ObjectLibrary::PatternEntry::MatchSeparatorAt( } if (pos == std::string::npos) { return pos; - } else if (mode == kMatchNumeric) { - // If it is numeric, everything up to the match must be a number - while (start < pos) { - if (!isdigit(target[start++])) { - return std::string::npos; - } + } else if (mode == kMatchInteger) { + if (!MatchesInteger(target, start, pos)) { + return std::string::npos; + } + } else if (mode == kMatchDecimal) { + if (!MatchesDecimal(target, start, pos)) { + return std::string::npos; } } return pos + slen; @@ -84,12 +118,10 @@ bool ObjectLibrary::PatternEntry::MatchesTarget(const std::string &name, return (start == tlen); } else if (start > tlen || (start == tlen && mode != kMatchZeroOrMore)) { return false; - } else if (mode == kMatchNumeric) { - while (start < tlen) { - if (!isdigit(target[start++])) { - return false; - } - } + } else if (mode == kMatchInteger) { + return MatchesInteger(target, start, tlen); + } else if (mode == kMatchDecimal) { + return MatchesDecimal(target, start, tlen); } } return true; diff --git a/utilities/object_registry_test.cc b/utilities/object_registry_test.cc index 3ab2df6b9..7b9720fef 100644 --- a/utilities/object_registry_test.cc +++ b/utilities/object_registry_test.cc @@ -599,6 +599,69 @@ TEST_F(PatternEntryTest, TestNumericEntry) { ASSERT_FALSE(entry.Matches("A:B")); ASSERT_FALSE(entry.Matches("A:1B")); ASSERT_FALSE(entry.Matches("A:B1")); + + entry.AddSeparator(":", false); + ASSERT_FALSE(entry.Matches("A")); + ASSERT_FALSE(entry.Matches("AA")); + ASSERT_FALSE(entry.Matches("A:")); + ASSERT_FALSE(entry.Matches("AA:")); + ASSERT_TRUE(entry.Matches("A:1:")); + ASSERT_TRUE(entry.Matches("A:11:")); + ASSERT_FALSE(entry.Matches("A:1")); + ASSERT_FALSE(entry.Matches("A:B1:")); + ASSERT_FALSE(entry.Matches("A:1B:")); + ASSERT_FALSE(entry.Matches("A::")); +} + +TEST_F(PatternEntryTest, TestDoubleEntry) { + ObjectLibrary::PatternEntry entry("A", false); + entry.AddNumber(":", false); + ASSERT_FALSE(entry.Matches("A")); + ASSERT_FALSE(entry.Matches("AA")); + ASSERT_FALSE(entry.Matches("A:")); + ASSERT_FALSE(entry.Matches("AA:")); + ASSERT_FALSE(entry.Matches("AA:1")); + ASSERT_FALSE(entry.Matches("AA:11")); + ASSERT_FALSE(entry.Matches("A:B")); + ASSERT_FALSE(entry.Matches("A:1B")); + ASSERT_FALSE(entry.Matches("A:B1")); + ASSERT_TRUE(entry.Matches("A:1")); + ASSERT_TRUE(entry.Matches("A:11")); + ASSERT_TRUE(entry.Matches("A:1.1")); + ASSERT_TRUE(entry.Matches("A:11.11")); + ASSERT_TRUE(entry.Matches("A:1.")); + ASSERT_TRUE(entry.Matches("A:.1")); + ASSERT_TRUE(entry.Matches("A:0.1")); + ASSERT_TRUE(entry.Matches("A:1.0")); + ASSERT_TRUE(entry.Matches("A:1.0")); + + ASSERT_FALSE(entry.Matches("A:1.0.")); + ASSERT_FALSE(entry.Matches("A:1.0.2")); + ASSERT_FALSE(entry.Matches("A:.1.0")); + ASSERT_FALSE(entry.Matches("A:..10")); + ASSERT_FALSE(entry.Matches("A:10..")); + ASSERT_FALSE(entry.Matches("A:.")); + + entry.AddSeparator(":", false); + ASSERT_FALSE(entry.Matches("A:1")); + ASSERT_FALSE(entry.Matches("A:1.0")); + + ASSERT_TRUE(entry.Matches("A:11:")); + ASSERT_TRUE(entry.Matches("A:1.1:")); + ASSERT_TRUE(entry.Matches("A:11.11:")); + ASSERT_TRUE(entry.Matches("A:1.:")); + ASSERT_TRUE(entry.Matches("A:.1:")); + ASSERT_TRUE(entry.Matches("A:0.1:")); + ASSERT_TRUE(entry.Matches("A:1.0:")); + ASSERT_TRUE(entry.Matches("A:1.0:")); + + ASSERT_FALSE(entry.Matches("A:1.0.:")); + ASSERT_FALSE(entry.Matches("A:1.0.2:")); + ASSERT_FALSE(entry.Matches("A:.1.0:")); + ASSERT_FALSE(entry.Matches("A:..10:")); + ASSERT_FALSE(entry.Matches("A:10..:")); + ASSERT_FALSE(entry.Matches("A:.:")); + ASSERT_FALSE(entry.Matches("A::")); } TEST_F(PatternEntryTest, TestIndividualIdEntry) {