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) {