Bug fix: Reject empty string as a column family name#14732
Conversation
Summary: Previously, calling `DB::CreateColumnFamily(opts, "", &handle)` returned `Status::OK()` with a usable handle, but the column family was not persisted in the manifest. Any data written to the empty-named CF was silently lost on DB reopen, and `ListColumnFamilies` would not show it. The empty string is also reserved as a sentinel meaning "no/unknown column family" in various RocksDB APIs and serialization formats (e.g. `TablePropertiesCollectorFactory::Context::kUnknownColumnFamily` and table properties), so allowing it as a real CF name is ambiguous in addition to being broken. This change rejects an empty CF name with `Status::InvalidArgument` at the top of `DBImpl::CreateColumnFamilyImpl`, which covers the single-CF `CreateColumnFamily` API as well as both `CreateColumnFamilies` overloads (by-names and by-descriptors). Test Plan: * Added `ColumnFamilyTest.EmptyNameRejected` covering all three Create entry points; verifies `IsInvalidArgument()` and that no spurious handles are returned.
|
@pdillinger has imported this pull request. If you are a Meta employee, you can view this in D104753911. |
✅ clang-tidy: No findings on changed linesCompleted in 254.2s. |
|
@pdillinger has imported this pull request. If you are a Meta employee, you can view this in D104753911. |
🟡 Codex Code ReviewAuto-triggered after CI passed — reviewing commit 2e18528 ❌ Codex review failed before producing findings. ℹ️ About this responseGenerated by Codex CLI. Limitations:
Commands:
|
✅ Claude Code ReviewAuto-triggered after CI passed — reviewing commit 2e18528 SummaryClean, well-targeted bug fix. The empty-name check is correctly placed, uses the right error code, preserves all invariants, and has no performance impact. The test covers the three main API entry points. Two minor test coverage gaps identified. High-severity findings (0): No high-severity findings. Full review (click to expand)Findings🔴 HIGHNone. 🟡 MEDIUMM1. Missing test for
|
| Context | Does code execute? | Assumptions hold? | Action needed? |
|---|---|---|---|
| Internal CF creation (persistent stats) | YES | Uses non-empty constants | Safe, no action |
DB::Open with create_missing_column_families |
YES | Correctly rejects empty names | Safe (test gap noted in M1) |
| WritePreparedTxnDB | YES (creates CFs) | Same check applies | Safe |
| ReadOnly DB | NO (can't create CFs) | N/A | N/A |
Table properties sentinel ("" = unknown CF) |
N/A (read path, not creation) | No conflict | Safe |
Assumption stress-test:
- Claim: "Empty string is reserved as sentinel." Verified:
builder.cc:57assertskUnknownColumnFamilyID ↔ empty name;table_properties.cc:150substitutes "N/A" for empty;meta_blocks.cc:164skips serializing empty names. The claim holds. - Claim: "Prior behavior was silently broken." Verified: Empty-named CFs bypass manifest persistence because the name is empty in serialization. Data loss on reopen confirmed by the PR description and corroborated by the serialization skip logic.
Positive Observations
- The check is placed optimally — before any expensive operations (option validation, directory creation, mutex acquisition).
*handle = nullptris set before the early return, maintaining the contract that handle is null on error.- Error message is clear and follows existing conventions (cf. "Column family already exists" at line 4292).
- The
Status s;reordering is semantically neutral (local default-constructed variable). - All four review agents independently confirmed correctness, thread safety, and performance neutrality.
ℹ️ About this response
Generated by Claude Code.
Review methodology: claude_md/code_review.md
Limitations:
- Claude may miss context from files not in the diff
- Large PRs may be truncated
- Always apply human judgment to AI suggestions
Commands:
/claude-review [context]— Request a code review/claude-query <question>— Ask about the PR or codebase
|
@pdillinger merged this pull request in 59633e3. |
Summary:
Previously, calling
DB::CreateColumnFamily(opts, "", &handle)returnedStatus::OK()with a usable handle, but the column family was not persisted in the manifest. Any data written to the empty-named CF was silently lost on DB reopen, andListColumnFamilieswould not show it.The empty string is also reserved as a sentinel meaning "no/unknown column family" in various RocksDB APIs and serialization formats (e.g.
TablePropertiesCollectorFactory::Context::kUnknownColumnFamilyand table properties), so allowing it as a real CF name is ambiguous in addition to being broken.This change rejects an empty CF name with
Status::InvalidArgumentat the top ofDBImpl::CreateColumnFamilyImpl, which covers the single-CFCreateColumnFamilyAPI as well as bothCreateColumnFamiliesoverloads (by-names and by-descriptors).Test Plan:
ColumnFamilyTest.EmptyNameRejectedcovering all three Create entry points; verifiesIsInvalidArgument()and that no spurious handles are returned.