1 /**
2  * @file unit-cppapi-metadata.cc
3  *
4  * @section LICENSE
5  *
6  * The MIT License
7  *
8  * @copyright Copyright (c) 2017-2021 TileDB, Inc.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  * @section DESCRIPTION
29  *
30  * Tests the C++ API for array metadata.
31  */
32 
33 #include "test/src/helpers.h"
34 #include "test/src/vfs_helpers.h"
35 #include "tiledb/sm/c_api/tiledb.h"
36 #include "tiledb/sm/c_api/tiledb_struct_def.h"
37 #include "tiledb/sm/config/config.h"
38 #include "tiledb/sm/cpp_api/tiledb"
39 #include "tiledb/sm/enums/encryption_type.h"
40 
41 #ifdef _WIN32
42 #include "tiledb/sm/filesystem/win.h"
43 #else
44 #include "tiledb/sm/filesystem/posix.h"
45 #endif
46 
47 #include <catch.hpp>
48 #include <chrono>
49 #include <iostream>
50 #include <thread>
51 
52 using namespace tiledb;
53 using namespace tiledb::test;
54 
55 /* ********************************* */
56 /*         STRUCT DEFINITION         */
57 /* ********************************* */
58 
59 struct CPPMetadataFx {
60   tiledb_ctx_t* ctx_;
61   tiledb_vfs_t* vfs_;
62   const std::vector<std::unique_ptr<SupportedFs>> fs_vec_;
63   std::string temp_dir_;
64   std::string array_name_;
65   const char* ARRAY_NAME = "test_metadata";
66   tiledb_array_t* array_ = nullptr;
67   const char* key_ = "0123456789abcdeF0123456789abcdeF";
68   const uint32_t key_len_ =
69       (uint32_t)strlen("0123456789abcdeF0123456789abcdeF");
70   const tiledb_encryption_type_t enc_type_ = TILEDB_AES_256_GCM;
71 
72   void create_default_array_1d();
73   void create_default_array_1d_with_key();
74 
75   CPPMetadataFx();
76   ~CPPMetadataFx();
77 };
78 
CPPMetadataFx()79 CPPMetadataFx::CPPMetadataFx()
80     : fs_vec_(vfs_test_get_fs_vec()) {
81   // Initialize vfs test
82   REQUIRE(vfs_test_init(fs_vec_, &ctx_, &vfs_).ok());
83 
84 // Create temporary directory based on the supported filesystem
85 #ifdef _WIN32
86   SupportedFsLocal windows_fs;
87   temp_dir_ = windows_fs.file_prefix() + windows_fs.temp_dir();
88 #else
89   SupportedFsLocal posix_fs;
90   temp_dir_ = posix_fs.file_prefix() + posix_fs.temp_dir();
91 #endif
92 
93   create_dir(temp_dir_, ctx_, vfs_);
94 
95   array_name_ = temp_dir_ + ARRAY_NAME;
96   int rc = tiledb_array_alloc(ctx_, array_name_.c_str(), &array_);
97   CHECK(rc == TILEDB_OK);
98 }
99 
~CPPMetadataFx()100 CPPMetadataFx::~CPPMetadataFx() {
101   tiledb_array_free(&array_);
102   remove_dir(temp_dir_, ctx_, vfs_);
103   tiledb_ctx_free(&ctx_);
104   tiledb_vfs_free(&vfs_);
105 }
106 
create_default_array_1d()107 void CPPMetadataFx::create_default_array_1d() {
108   uint64_t domain[] = {1, 10};
109   uint64_t tile_extent = 5;
110   create_array(
111       ctx_,
112       array_name_,
113       TILEDB_DENSE,
114       {"d"},
115       {TILEDB_UINT64},
116       {domain},
117       {&tile_extent},
118       {"a", "b", "c"},
119       {TILEDB_INT32, TILEDB_CHAR, TILEDB_FLOAT32},
120       {1, TILEDB_VAR_NUM, 2},
121       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1),
122        tiledb::test::Compressor(TILEDB_FILTER_ZSTD, -1),
123        tiledb::test::Compressor(TILEDB_FILTER_LZ4, -1)},
124       TILEDB_ROW_MAJOR,
125       TILEDB_ROW_MAJOR,
126       2);
127 }
128 
create_default_array_1d_with_key()129 void CPPMetadataFx::create_default_array_1d_with_key() {
130   uint64_t domain[] = {1, 10};
131   uint64_t tile_extent = 5;
132   create_array(
133       ctx_,
134       array_name_,
135       enc_type_,
136       key_,
137       key_len_,
138       TILEDB_DENSE,
139       {"d"},
140       {TILEDB_UINT64},
141       {domain},
142       {&tile_extent},
143       {"a", "b", "c"},
144       {TILEDB_INT32, TILEDB_CHAR, TILEDB_FLOAT32},
145       {1, TILEDB_VAR_NUM, 2},
146       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1),
147        tiledb::test::Compressor(TILEDB_FILTER_ZSTD, -1),
148        tiledb::test::Compressor(TILEDB_FILTER_LZ4, -1)},
149       TILEDB_ROW_MAJOR,
150       TILEDB_ROW_MAJOR,
151       2);
152 }
153 
154 /* ********************************* */
155 /*                TESTS              */
156 /* ********************************* */
157 
158 TEST_CASE_METHOD(
159     CPPMetadataFx,
160     "C++ API: Metadata, basic errors",
161     "[cppapi][metadata][errors]") {
162   // Create default array
163   create_default_array_1d();
164 
165   // Put metadata in an array opened for reads - error
166   Context ctx;
167   Array array(ctx, std::string(array_name_), TILEDB_READ);
168   int v = 5;
169   CHECK_THROWS(array.put_metadata("key", TILEDB_INT32, 1, &v));
170   array.close();
171 
172   // Reopen array in WRITE mode
173   array.open(TILEDB_WRITE);
174 
175   // Write value type ANY
176   CHECK_THROWS(array.put_metadata("key", TILEDB_ANY, 1, &v));
177 
178   // Write a correct item
179   array.put_metadata("key", TILEDB_INT32, 1, &v);
180 
181   // Close array
182   array.close();
183 }
184 
185 TEST_CASE_METHOD(
186     CPPMetadataFx,
187     "C++ API: Metadata, write/read",
188     "[cppapi][metadata][read]") {
189   // Create default array
190   create_default_array_1d();
191 
192   // Open array in write mode
193   Context ctx;
194   Array array(ctx, std::string(array_name_), TILEDB_WRITE);
195 
196   // Write items
197   int32_t v = 5;
198   array.put_metadata("aaa", TILEDB_INT32, 1, &v);
199   float f[] = {1.1f, 1.2f};
200   array.put_metadata("bb", TILEDB_FLOAT32, 2, f);
201 
202   // Write null value
203   array.put_metadata("zero_val", TILEDB_FLOAT32, 1, NULL);
204 
205   // Close array
206   array.close();
207 
208   // Open the array in read mode
209   array.open(TILEDB_READ);
210 
211   // Read
212   const void* v_r;
213   tiledb_datatype_t v_type;
214   uint32_t v_num;
215   array.get_metadata("aaa", &v_type, &v_num, &v_r);
216   CHECK(v_type == TILEDB_INT32);
217   CHECK(v_num == 1);
218   CHECK(*((const int32_t*)v_r) == 5);
219 
220   array.get_metadata("bb", &v_type, &v_num, &v_r);
221   CHECK(v_type == TILEDB_FLOAT32);
222   CHECK(v_num == 2);
223   CHECK(((const float*)v_r)[0] == 1.1f);
224   CHECK(((const float*)v_r)[1] == 1.2f);
225 
226   array.get_metadata("zero_val", &v_type, &v_num, &v_r);
227   CHECK(v_type == TILEDB_FLOAT32);
228   CHECK(v_num == 1);
229   CHECK(v_r == nullptr);
230 
231   array.get_metadata("foo", &v_type, &v_num, &v_r);
232   CHECK(v_r == nullptr);
233 
234   uint64_t num = array.metadata_num();
235   CHECK(num == 3);
236 
237   std::string key;
238   CHECK_THROWS(array.get_metadata_from_index(10, &key, &v_type, &v_num, &v_r));
239 
240   array.get_metadata_from_index(1, &key, &v_type, &v_num, &v_r);
241   CHECK(v_type == TILEDB_FLOAT32);
242   CHECK(v_num == 2);
243   CHECK(((const float*)v_r)[0] == 1.1f);
244   CHECK(((const float*)v_r)[1] == 1.2f);
245   CHECK(key.size() == strlen("bb"));
246   CHECK(!strncmp(key.data(), "bb", strlen("bb")));
247 
248   // idx 2 is 'zero_val'
249   array.get_metadata_from_index(2, &key, &v_type, &v_num, &v_r);
250   CHECK(v_type == TILEDB_FLOAT32);
251   CHECK(v_num == 1);
252   CHECK(v_r == nullptr);
253 
254   // Check has_key
255   bool has_key;
256   v_type = (tiledb_datatype_t)std::numeric_limits<int32_t>::max();
257   has_key = array.has_metadata("bb", &v_type);
258   CHECK(has_key == true);
259   CHECK(v_type == TILEDB_FLOAT32);
260 
261   // Check not has_key
262   v_type = (tiledb_datatype_t)std::numeric_limits<int32_t>::max();
263   has_key = array.has_metadata("non-existent-key", &v_type);
264   CHECK(has_key == false);
265   CHECK(v_type == (tiledb_datatype_t)std::numeric_limits<int32_t>::max());
266 
267   // Close array
268   array.close();
269 }
270 
271 TEST_CASE_METHOD(
272     CPPMetadataFx, "C++ API: Metadata, UTF-8", "[cppapi][metadata][utf-8]") {
273   // Create default array
274   create_default_array_1d();
275 
276   // Open array in write mode
277   Context ctx;
278   Array array(ctx, std::string(array_name_), TILEDB_WRITE);
279 
280   // Write UTF-8 (≥ holds 3 bytes)
281   int32_t v = 5;
282   array.put_metadata("≥", TILEDB_INT32, 1, &v);
283 
284   // Close array
285   array.close();
286 
287   // Open the array in read mode
288   array.open(TILEDB_READ);
289 
290   // Read
291   const void* v_r;
292   tiledb_datatype_t v_type;
293   uint32_t v_num;
294   array.get_metadata("≥", &v_type, &v_num, &v_r);
295   CHECK(v_type == TILEDB_INT32);
296   CHECK(v_num == 1);
297   CHECK(*((const int32_t*)v_r) == 5);
298 
299   std::string key;
300   array.get_metadata_from_index(0, &key, &v_type, &v_num, &v_r);
301   CHECK(v_type == TILEDB_INT32);
302   CHECK(v_num == 1);
303   CHECK(*((const int32_t*)v_r) == 5);
304   CHECK(key.size() == strlen("≥"));
305   CHECK(!strncmp(key.data(), "≥", strlen("≥")));
306 
307   // Close array
308   array.close();
309 }
310 
311 TEST_CASE_METHOD(
312     CPPMetadataFx, "C++ API: Metadata, delete", "[cppapi][metadata][delete]") {
313   // Create default array
314   create_default_array_1d();
315 
316   // Create and open array in write mode
317   Context ctx;
318   Array array(ctx, std::string(array_name_), TILEDB_WRITE, 1);
319 
320   // Write items
321   int32_t v = 5;
322   array.put_metadata("aaa", TILEDB_INT32, 1, &v);
323   float f[] = {1.1f, 1.2f};
324   array.put_metadata("bb", TILEDB_FLOAT32, 2, f);
325 
326   // Close array
327   array.close();
328 
329   // Delete an item that exists and one that does not exist
330   array.open(TILEDB_WRITE, 2);
331   array.delete_metadata("aaa");
332   array.delete_metadata("foo");
333   array.close();
334 
335   // Open the array in read mode
336   array.open(TILEDB_READ);
337 
338   // Read
339   const void* v_r;
340   tiledb_datatype_t v_type;
341   uint32_t v_num;
342   array.get_metadata("aaa", &v_type, &v_num, &v_r);
343   CHECK(v_r == nullptr);
344   array.get_metadata("bb", &v_type, &v_num, &v_r);
345   CHECK(v_type == TILEDB_FLOAT32);
346   CHECK(v_num == 2);
347   CHECK(((const float*)v_r)[0] == 1.1f);
348   CHECK(((const float*)v_r)[1] == 1.2f);
349 
350   array.get_metadata("foo", &v_type, &v_num, &v_r);
351   CHECK(v_r == nullptr);
352 
353   uint64_t num = array.metadata_num();
354   CHECK(num == 1);
355 
356   std::string key;
357   array.get_metadata_from_index(0, &key, &v_type, &v_num, &v_r);
358   CHECK(v_type == TILEDB_FLOAT32);
359   CHECK(v_num == 2);
360   CHECK(((const float*)v_r)[0] == 1.1f);
361   CHECK(((const float*)v_r)[1] == 1.2f);
362   CHECK(key.size() == strlen("bb"));
363   CHECK(!strncmp(key.data(), "bb", strlen("bb")));
364 
365   // Close array
366   array.close();
367 }
368 
369 TEST_CASE_METHOD(
370     CPPMetadataFx,
371     "C++ API: Metadata, multiple metadata and consolidate",
372     "[cppapi][metadata][multiple][consolidation]") {
373   // Create default array
374   create_default_array_1d();
375 
376   // Create and open array in write mode
377   Context ctx;
378   Array array(ctx, array_name_, TILEDB_WRITE);
379 
380   // Write items
381   int32_t v = 5;
382   array.put_metadata("aaa", TILEDB_INT32, 1, &v);
383   float f[] = {1.1f, 1.2f};
384   array.put_metadata("bb", TILEDB_FLOAT32, 2, f);
385 
386   // Close array
387   array.close();
388 
389   // Prevent array metadata filename/timestamp conflicts
390   std::this_thread::sleep_for(std::chrono::milliseconds(1));
391 
392   // Update
393   array.open(TILEDB_WRITE);
394   array.delete_metadata("aaa");
395   v = 10;
396   array.put_metadata("cccc", TILEDB_INT32, 1, &v);
397   array.close();
398 
399   // Open the array in read mode
400   array.open(TILEDB_READ);
401 
402   // Read
403   const void* v_r;
404   tiledb_datatype_t v_type;
405   uint32_t v_num;
406   array.get_metadata("aaa", &v_type, &v_num, &v_r);
407   CHECK(v_r == nullptr);
408 
409   array.get_metadata("bb", &v_type, &v_num, &v_r);
410   CHECK(v_type == TILEDB_FLOAT32);
411   CHECK(v_num == 2);
412   CHECK(((const float*)v_r)[0] == 1.1f);
413   CHECK(((const float*)v_r)[1] == 1.2f);
414 
415   array.get_metadata("cccc", &v_type, &v_num, &v_r);
416   CHECK(v_type == TILEDB_INT32);
417   CHECK(v_num == 1);
418   CHECK(*((const int32_t*)v_r) == 10);
419 
420   uint64_t num = array.metadata_num();
421   CHECK(num == 2);
422 
423   std::string key;
424   array.get_metadata_from_index(0, &key, &v_type, &v_num, &v_r);
425   CHECK(v_type == TILEDB_FLOAT32);
426   CHECK(v_num == 2);
427   CHECK(((const float*)v_r)[0] == 1.1f);
428   CHECK(((const float*)v_r)[1] == 1.2f);
429   CHECK(key.size() == strlen("bb"));
430   CHECK(!strncmp(key.data(), "bb", strlen("bb")));
431 
432   // Close array
433   array.close();
434 
435   // Consolidate
436   Config consolidation_cfg;
437   consolidation_cfg["sm.consolidation.mode"] = "array_meta";
438   Array::consolidate(ctx, array_name_, &consolidation_cfg);
439 
440   // Open the array in read mode
441   array.open(TILEDB_READ);
442 
443   num = array.metadata_num();
444   CHECK(num == 2);
445 
446   // Close array
447   array.close();
448 
449   // Write once more
450   array.open(TILEDB_WRITE);
451 
452   // Write items
453   v = 50;
454   array.put_metadata("d", TILEDB_INT32, 1, &v);
455 
456   // Close array
457   array.close();
458 
459   // Consolidate again
460   Array::consolidate(ctx, array_name_, &consolidation_cfg);
461 
462   // Open the array in read mode
463   array.open(TILEDB_READ);
464 
465   num = array.metadata_num();
466   CHECK(num == 3);
467 
468   array.get_metadata("cccc", &v_type, &v_num, &v_r);
469   CHECK(v_type == TILEDB_INT32);
470   CHECK(v_num == 1);
471   CHECK(*((const int32_t*)v_r) == 10);
472 
473   array.get_metadata("d", &v_type, &v_num, &v_r);
474   CHECK(v_type == TILEDB_INT32);
475   CHECK(v_num == 1);
476   CHECK(*((const int32_t*)v_r) == 50);
477 
478   // Close array
479   array.close();
480 }
481 
482 TEST_CASE_METHOD(
483     CPPMetadataFx, "C++ Metadata, open at", "[cppapi][metadata][open-at]") {
484   // Create default array
485   create_default_array_1d();
486 
487   // Create and open array in write mode
488   Context ctx;
489   Array array(ctx, array_name_, TILEDB_WRITE);
490 
491   // Write items
492   int32_t v = 5;
493   array.put_metadata("aaa", TILEDB_INT32, 1, &v);
494   float f[] = {1.1f, 1.2f};
495   array.put_metadata("bb", TILEDB_FLOAT32, 2, f);
496 
497   // Close array
498   array.close();
499 
500   // Prevent array metadata filename/timestamp conflicts
501   auto timestamp = tiledb::sm::utils::time::timestamp_now_ms();
502   std::this_thread::sleep_for(std::chrono::milliseconds(1));
503 
504   // Update
505   array.open(TILEDB_WRITE);
506   array.delete_metadata("aaa");
507   array.close();
508 
509   // Open the array in read mode at a timestamp
510   array.set_open_timestamp_end(timestamp);
511   array.open(TILEDB_READ);
512 
513   // Read
514   const void* v_r;
515   tiledb_datatype_t v_type;
516   uint32_t v_num;
517   array.get_metadata("aaa", &v_type, &v_num, &v_r);
518   CHECK(v_type == TILEDB_INT32);
519   CHECK(v_num == 1);
520   CHECK(*((const int32_t*)v_r) == 5);
521 
522   uint64_t num = array.metadata_num();
523   CHECK(num == 2);
524 
525   // Close array
526   array.close();
527 }
528 
529 TEST_CASE_METHOD(
530     CPPMetadataFx, "C++ Metadata, reopen", "[cppapi][metadata][reopen]") {
531   // Create default array
532   create_default_array_1d();
533 
534   // Open array in write mode
535   Context ctx;
536   Array array(ctx, array_name_, TILEDB_WRITE);
537 
538   // Write items
539   int32_t v = 5;
540   array.put_metadata("aaa", TILEDB_INT32, 1, &v);
541   float f[] = {1.1f, 1.2f};
542   array.put_metadata("bb", TILEDB_FLOAT32, 2, f);
543 
544   // Close array
545   array.close();
546 
547   // Prevent array metadata filename/timestamp conflicts
548   auto timestamp = tiledb::sm::utils::time::timestamp_now_ms();
549   std::this_thread::sleep_for(std::chrono::milliseconds(1));
550 
551   // Update
552   array.open(TILEDB_WRITE);
553   array.delete_metadata("aaa");
554   array.close();
555 
556   // Open the array in read mode at a timestamp
557   array.set_open_timestamp_end(timestamp);
558   array.open(TILEDB_READ);
559 
560   // Read
561   const void* v_r;
562   tiledb_datatype_t v_type;
563   uint32_t v_num;
564   array.get_metadata("aaa", &v_type, &v_num, &v_r);
565   CHECK(v_type == TILEDB_INT32);
566   CHECK(v_num == 1);
567   CHECK(*((const int32_t*)v_r) == 5);
568 
569   uint64_t num = array.metadata_num();
570   CHECK(num == 2);
571 
572   // Reopen
573   array.reopen();
574 
575   // Read
576   array.get_metadata("aaa", &v_type, &v_num, &v_r);
577   CHECK(v_r == nullptr);
578 
579   num = array.metadata_num();
580   CHECK(num == 1);
581 
582   // Close array
583   array.close();
584 }
585 
586 TEST_CASE_METHOD(
587     CPPMetadataFx,
588     "C++ Metadata, encryption",
589     "[cppapi][metadata][encryption]") {
590   // Create default array
591   create_default_array_1d_with_key();
592 
593   // Create and open array in write mode
594   tiledb::Config cfg;
595   std::string enc_type_str =
596       encryption_type_str((tiledb::sm::EncryptionType)enc_type_);
597   cfg["sm.encryption_type"] = enc_type_str.c_str();
598   cfg["sm.encryption_key"] = key_;
599   Context ctx(cfg);
600   Array array(ctx, array_name_, TILEDB_WRITE);
601 
602   // Write items
603   int32_t v = 5;
604   array.put_metadata("aaa", TILEDB_INT32, 1, &v);
605   float f[] = {1.1f, 1.2f};
606   array.put_metadata("bb", TILEDB_FLOAT32, 2, f);
607 
608   // Close array
609   array.close();
610 
611   // Prevent array metadata filename/timestamp conflicts
612   std::this_thread::sleep_for(std::chrono::milliseconds(1));
613 
614   // Update
615   array.open(TILEDB_WRITE);
616   array.delete_metadata("aaa");
617   v = 10;
618   array.put_metadata("cccc", TILEDB_INT32, 1, &v);
619   array.close();
620 
621   // Open the array in read mode
622   array.open(TILEDB_READ);
623 
624   // Read
625   const void* v_r;
626   tiledb_datatype_t v_type;
627   uint32_t v_num;
628   array.get_metadata("aaa", &v_type, &v_num, &v_r);
629   CHECK(v_r == nullptr);
630 
631   array.get_metadata("bb", &v_type, &v_num, &v_r);
632   CHECK(v_type == TILEDB_FLOAT32);
633   CHECK(v_num == 2);
634   CHECK(((const float*)v_r)[0] == 1.1f);
635   CHECK(((const float*)v_r)[1] == 1.2f);
636 
637   array.get_metadata("cccc", &v_type, &v_num, &v_r);
638   CHECK(v_type == TILEDB_INT32);
639   CHECK(v_num == 1);
640   CHECK(*((const int32_t*)v_r) == 10);
641 
642   uint64_t num = array.metadata_num();
643   CHECK(num == 2);
644 
645   std::string key;
646   array.get_metadata_from_index(0, &key, &v_type, &v_num, &v_r);
647   CHECK(v_type == TILEDB_FLOAT32);
648   CHECK(v_num == 2);
649   CHECK(((const float*)v_r)[0] == 1.1f);
650   CHECK(((const float*)v_r)[1] == 1.2f);
651   CHECK(key.size() == strlen("bb"));
652   CHECK(!strncmp(key.data(), "bb", strlen("bb")));
653 
654   // Close array
655   array.close();
656 
657   // Consolidate without key - error
658   Config consolidate_without_key;
659   Context ctx_without_key(consolidate_without_key);
660   CHECK_THROWS(Array::consolidate(
661       ctx_without_key, array_name_, &consolidate_without_key));
662 
663   // Consolidate with key - ok
664   Config consolidation_cfg;
665   consolidation_cfg["sm.consolidation.mode"] = "array_meta";
666   Array::consolidate(ctx, array_name_, &consolidation_cfg);
667 
668   // Open the array in read mode
669   array.open(TILEDB_READ);
670 
671   num = array.metadata_num();
672   CHECK(num == 2);
673 
674   // Close array
675   array.close();
676 
677   // Write once more
678   array.open(TILEDB_WRITE);
679 
680   // Write items
681   v = 50;
682   array.put_metadata("d", TILEDB_INT32, 1, &v);
683 
684   // Close array
685   array.close();
686 
687   // Consolidate again
688   Array::consolidate_metadata(ctx, array_name_, &consolidation_cfg);
689 
690   // Open the array in read mode
691   array.open(TILEDB_READ);
692 
693   num = array.metadata_num();
694   CHECK(num == 3);
695 
696   array.get_metadata("cccc", &v_type, &v_num, &v_r);
697   CHECK(v_type == TILEDB_INT32);
698   CHECK(v_num == 1);
699   CHECK(*((const int32_t*)v_r) == 10);
700 
701   array.get_metadata("d", &v_type, &v_num, &v_r);
702   CHECK(v_type == TILEDB_INT32);
703   CHECK(v_num == 1);
704   CHECK(*((const int32_t*)v_r) == 50);
705 
706   // Close array
707   array.close();
708 }
709