1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5
6 #ifndef ROCKSDB_LITE
7
8 #include <cinttypes>
9
10 #include "port/stack_trace.h"
11 #include "rocksdb/db.h"
12 #include "rocksdb/sst_file_reader.h"
13 #include "rocksdb/sst_file_writer.h"
14 #include "table/sst_file_writer_collectors.h"
15 #include "test_util/testharness.h"
16 #include "test_util/testutil.h"
17 #include "utilities/merge_operators.h"
18
19 namespace ROCKSDB_NAMESPACE {
20
EncodeAsString(uint64_t v)21 std::string EncodeAsString(uint64_t v) {
22 char buf[16];
23 snprintf(buf, sizeof(buf), "%08" PRIu64, v);
24 return std::string(buf);
25 }
26
EncodeAsUint64(uint64_t v)27 std::string EncodeAsUint64(uint64_t v) {
28 std::string dst;
29 PutFixed64(&dst, v);
30 return dst;
31 }
32
33 class SstFileReaderTest : public testing::Test {
34 public:
SstFileReaderTest()35 SstFileReaderTest() {
36 options_.merge_operator = MergeOperators::CreateUInt64AddOperator();
37 sst_name_ = test::PerThreadDBPath("sst_file");
38
39 Env* base_env = Env::Default();
40 const char* test_env_uri = getenv("TEST_ENV_URI");
41 if(test_env_uri) {
42 Env* test_env = nullptr;
43 Status s = Env::LoadEnv(test_env_uri, &test_env, &env_guard_);
44 base_env = test_env;
45 EXPECT_OK(s);
46 EXPECT_NE(Env::Default(), base_env);
47 }
48 EXPECT_NE(nullptr, base_env);
49 env_ = base_env;
50 options_.env = env_;
51 }
52
~SstFileReaderTest()53 ~SstFileReaderTest() {
54 Status s = env_->DeleteFile(sst_name_);
55 EXPECT_OK(s);
56 }
57
CreateFile(const std::string & file_name,const std::vector<std::string> & keys)58 void CreateFile(const std::string& file_name,
59 const std::vector<std::string>& keys) {
60 SstFileWriter writer(soptions_, options_);
61 ASSERT_OK(writer.Open(file_name));
62 for (size_t i = 0; i + 2 < keys.size(); i += 3) {
63 ASSERT_OK(writer.Put(keys[i], keys[i]));
64 ASSERT_OK(writer.Merge(keys[i + 1], EncodeAsUint64(i + 1)));
65 ASSERT_OK(writer.Delete(keys[i + 2]));
66 }
67 ASSERT_OK(writer.Finish());
68 }
69
CheckFile(const std::string & file_name,const std::vector<std::string> & keys,bool check_global_seqno=false)70 void CheckFile(const std::string& file_name,
71 const std::vector<std::string>& keys,
72 bool check_global_seqno = false) {
73 ReadOptions ropts;
74 SstFileReader reader(options_);
75 ASSERT_OK(reader.Open(file_name));
76 ASSERT_OK(reader.VerifyChecksum());
77 std::unique_ptr<Iterator> iter(reader.NewIterator(ropts));
78 iter->SeekToFirst();
79 for (size_t i = 0; i + 2 < keys.size(); i += 3) {
80 ASSERT_TRUE(iter->Valid());
81 ASSERT_EQ(iter->key().compare(keys[i]), 0);
82 ASSERT_EQ(iter->value().compare(keys[i]), 0);
83 iter->Next();
84 ASSERT_TRUE(iter->Valid());
85 ASSERT_EQ(iter->key().compare(keys[i + 1]), 0);
86 ASSERT_EQ(iter->value().compare(EncodeAsUint64(i + 1)), 0);
87 iter->Next();
88 }
89 ASSERT_FALSE(iter->Valid());
90 if (check_global_seqno) {
91 auto properties = reader.GetTableProperties();
92 ASSERT_TRUE(properties);
93 auto& user_properties = properties->user_collected_properties;
94 ASSERT_TRUE(
95 user_properties.count(ExternalSstFilePropertyNames::kGlobalSeqno));
96 }
97 }
98
CreateFileAndCheck(const std::vector<std::string> & keys)99 void CreateFileAndCheck(const std::vector<std::string>& keys) {
100 CreateFile(sst_name_, keys);
101 CheckFile(sst_name_, keys);
102 }
103
104 protected:
105 Options options_;
106 EnvOptions soptions_;
107 std::string sst_name_;
108 std::shared_ptr<Env> env_guard_;
109 Env* env_;
110 };
111
112 const uint64_t kNumKeys = 100;
113
TEST_F(SstFileReaderTest,Basic)114 TEST_F(SstFileReaderTest, Basic) {
115 std::vector<std::string> keys;
116 for (uint64_t i = 0; i < kNumKeys; i++) {
117 keys.emplace_back(EncodeAsString(i));
118 }
119 CreateFileAndCheck(keys);
120 }
121
TEST_F(SstFileReaderTest,Uint64Comparator)122 TEST_F(SstFileReaderTest, Uint64Comparator) {
123 options_.comparator = test::Uint64Comparator();
124 std::vector<std::string> keys;
125 for (uint64_t i = 0; i < kNumKeys; i++) {
126 keys.emplace_back(EncodeAsUint64(i));
127 }
128 CreateFileAndCheck(keys);
129 }
130
TEST_F(SstFileReaderTest,ReadFileWithGlobalSeqno)131 TEST_F(SstFileReaderTest, ReadFileWithGlobalSeqno) {
132 std::vector<std::string> keys;
133 for (uint64_t i = 0; i < kNumKeys; i++) {
134 keys.emplace_back(EncodeAsString(i));
135 }
136 // Generate a SST file.
137 CreateFile(sst_name_, keys);
138
139 // Ingest the file into a db, to assign it a global sequence number.
140 Options options;
141 options.create_if_missing = true;
142 std::string db_name = test::PerThreadDBPath("test_db");
143 DB* db;
144 ASSERT_OK(DB::Open(options, db_name, &db));
145 // Bump sequence number.
146 ASSERT_OK(db->Put(WriteOptions(), keys[0], "foo"));
147 ASSERT_OK(db->Flush(FlushOptions()));
148 // Ingest the file.
149 IngestExternalFileOptions ingest_options;
150 ingest_options.write_global_seqno = true;
151 ASSERT_OK(db->IngestExternalFile({sst_name_}, ingest_options));
152 std::vector<std::string> live_files;
153 uint64_t manifest_file_size = 0;
154 ASSERT_OK(db->GetLiveFiles(live_files, &manifest_file_size));
155 // Get the ingested file.
156 std::string ingested_file;
157 for (auto& live_file : live_files) {
158 if (live_file.substr(live_file.size() - 4, std::string::npos) == ".sst") {
159 if (ingested_file.empty() || ingested_file < live_file) {
160 ingested_file = live_file;
161 }
162 }
163 }
164 ASSERT_FALSE(ingested_file.empty());
165 delete db;
166
167 // Verify the file can be open and read by SstFileReader.
168 CheckFile(db_name + ingested_file, keys, true /* check_global_seqno */);
169
170 // Cleanup.
171 ASSERT_OK(DestroyDB(db_name, options));
172 }
173
174 } // namespace ROCKSDB_NAMESPACE
175
176 #ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
177 extern "C" {
178 void RegisterCustomObjects(int argc, char** argv);
179 }
180 #else
RegisterCustomObjects(int,char **)181 void RegisterCustomObjects(int /*argc*/, char** /*argv*/) {}
182 #endif // !ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
183
main(int argc,char ** argv)184 int main(int argc, char** argv) {
185 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
186 ::testing::InitGoogleTest(&argc, argv);
187 RegisterCustomObjects(argc, argv);
188 return RUN_ALL_TESTS();
189 }
190
191 #else
192 #include <stdio.h>
193
main(int,char **)194 int main(int /*argc*/, char** /*argv*/) {
195 fprintf(stderr,
196 "SKIPPED as SstFileReader is not supported in ROCKSDB_LITE\n");
197 return 0;
198 }
199
200 #endif // ROCKSDB_LITE
201