1 /**
2  * @file   unit-capi-string_dims.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 of C API for sparse arrays with string dimensions.
31  */
32 
33 #include "catch.hpp"
34 #include "test/src/helpers.h"
35 #include "test/src/vfs_helpers.h"
36 #ifdef _WIN32
37 #include "tiledb/sm/filesystem/win.h"
38 #else
39 #include "tiledb/sm/filesystem/posix.h"
40 #endif
41 #include "tiledb/sm/c_api/tiledb.h"
42 #include "tiledb/sm/c_api/tiledb_serialization.h"
43 #include "tiledb/sm/enums/serialization_type.h"
44 #include "tiledb/sm/misc/utils.h"
45 
46 #include <chrono>
47 #include <iostream>
48 #include <sstream>
49 #include <thread>
50 
51 using namespace tiledb::test;
52 
53 typedef std::pair<std::string, uint64_t> EstSize;
54 
55 struct StringDimsFx {
56   /**
57    * If true, array schema is serialized before submission, to test the
58    * serialization paths.
59    */
60   bool serialize_ = false;
61 
62   // TileDB context
63   tiledb_ctx_t* ctx_;
64   tiledb_vfs_t* vfs_;
65 
66   // Vector of supported filsystems
67   const std::vector<std::unique_ptr<SupportedFs>> fs_vec_;
68 
69   // Used to get the number of directories or files of another directory
70   struct get_num_struct {
71     tiledb_ctx_t* ctx;
72     tiledb_vfs_t* vfs;
73     int num;
74   };
75   static int get_dir_num(const char* path, void* data);
76 
77   // Functions
78   StringDimsFx();
79   ~StringDimsFx();
80   void create_temp_dir(const std::string& path);
81   void remove_temp_dir(const std::string& path);
82   std::string random_name(const std::string& prefix);
83   int array_create_wrapper(
84       const std::string& path, tiledb_array_schema_t* array_schema);
85   int array_schema_load_wrapper(
86       const std::string& path, tiledb_array_schema_t** array_schema);
87   void write_array_ascii(const std::string& array_name);
88   void write_array_1d(
89       tiledb_ctx_t* ctx,
90       const std::string& array_name,
91       tiledb_layout_t layout,
92       const std::vector<uint64_t>& d_off,
93       const std::string& d_val,
94       const std::vector<int32_t>& a);
95   void write_array_2d(
96       tiledb_ctx_t* ctx,
97       const std::string& array_name,
98       tiledb_layout_t layout,
99       const std::vector<uint64_t>& d1_off,
100       const std::string& d1_val,
101       const std::vector<int32_t>& d2,
102       const std::vector<int32_t>& a);
103   int tiledb_array_get_non_empty_domain_var_size_from_name_wrapper(
104       tiledb_ctx_t* ctx,
105       tiledb_array_t* array,
106       const char* name,
107       uint64_t* start_size,
108       uint64_t* end_size,
109       int32_t* is_empty);
110   int tiledb_array_get_non_empty_domain_var_from_name_wrapper(
111       tiledb_ctx_t* ctx,
112       tiledb_array_t* array,
113       const char* name,
114       void* start,
115       void* end,
116       int32_t* is_empty);
117   int tiledb_array_get_non_empty_domain_from_name_wrapper(
118       tiledb_ctx_t* ctx,
119       tiledb_array_t* array,
120       const char* name,
121       void* domain,
122       int32_t* is_empty);
123   int tiledb_query_submit_wrapper(
124       tiledb_ctx_t* ctx, tiledb_query_t* query, const std::string& array_uri);
125   void get_non_empty_domain(
126       const std::string& array_name,
127       const std::string& dim_name,
128       std::vector<int32_t>* dom,
129       int32_t* is_empty);
130   void get_non_empty_domain_var(
131       const std::string& array_name,
132       const std::string& dim_name,
133       std::string* start,
134       std::string* end,
135       int32_t* is_empty);
136   void get_est_result_size_var(
137       tiledb_array_t* array,
138       unsigned dim_idx,
139       const std::string& dim_name,
140       const std::string& start,
141       const std::string& end,
142       uint64_t* size_off,
143       uint64_t* size_val);
144   void read_array_1d(
145       tiledb_ctx_t* ctx,
146       tiledb_array_t* array,
147       tiledb_layout_t layout,
148       const std::string& start,
149       const std::string& end,
150       std::vector<uint64_t>* d_off,
151       std::string* d_val,
152       std::vector<int32_t>* a,
153       tiledb_query_status_t* status);
154   void read_array_2d(
155       tiledb_ctx_t* ctx,
156       tiledb_array_t* array,
157       tiledb_layout_t layout,
158       const std::string& d1_start,
159       const std::string& d1_end,
160       int32_t d2_start,
161       int32_t d2_end,
162       std::vector<uint64_t>* d1_off,
163       std::string* d1_val,
164       std::vector<int32_t>* d2,
165       std::vector<int32_t>* a,
166       tiledb_query_status_t* status);
167 };
168 
StringDimsFx()169 StringDimsFx::StringDimsFx()
170     : fs_vec_(vfs_test_get_fs_vec()) {
171   // Initialize vfs test
172   REQUIRE(vfs_test_init(fs_vec_, &ctx_, &vfs_).ok());
173 }
174 
~StringDimsFx()175 StringDimsFx::~StringDimsFx() {
176   // Close vfs test
177   REQUIRE(vfs_test_close(fs_vec_, ctx_, vfs_).ok());
178   tiledb_vfs_free(&vfs_);
179   tiledb_ctx_free(&ctx_);
180 }
181 
create_temp_dir(const std::string & path)182 void StringDimsFx::create_temp_dir(const std::string& path) {
183   remove_temp_dir(path);
184   REQUIRE(tiledb_vfs_create_dir(ctx_, vfs_, path.c_str()) == TILEDB_OK);
185 }
186 
remove_temp_dir(const std::string & path)187 void StringDimsFx::remove_temp_dir(const std::string& path) {
188   int is_dir = 0;
189   REQUIRE(tiledb_vfs_is_dir(ctx_, vfs_, path.c_str(), &is_dir) == TILEDB_OK);
190   if (is_dir)
191     REQUIRE(tiledb_vfs_remove_dir(ctx_, vfs_, path.c_str()) == TILEDB_OK);
192 }
193 
random_name(const std::string & prefix)194 std::string StringDimsFx::random_name(const std::string& prefix) {
195   std::stringstream ss;
196   ss << prefix << "-" << std::this_thread::get_id() << "-"
197      << TILEDB_TIMESTAMP_NOW_MS;
198   return ss.str();
199 }
200 
get_dir_num(const char * path,void * data)201 int StringDimsFx::get_dir_num(const char* path, void* data) {
202   auto data_struct = (StringDimsFx::get_num_struct*)data;
203   auto ctx = data_struct->ctx;
204   auto vfs = data_struct->vfs;
205   int is_dir;
206   int rc = tiledb_vfs_is_dir(ctx, vfs, path, &is_dir);
207   CHECK(rc == TILEDB_OK);
208   auto meta_dir =
209       std::string("/") + tiledb::sm::constants::array_metadata_folder_name;
210   auto schema_dir =
211       std::string("/") + tiledb::sm::constants::array_schema_folder_name;
212   if (!tiledb::sm::utils::parse::ends_with(path, meta_dir) &&
213       !tiledb::sm::utils::parse::ends_with(path, schema_dir)) {
214     // Ignoring the meta folder and the schema folder
215     data_struct->num += is_dir;
216   }
217 
218   return 1;
219 }
220 
array_schema_load_wrapper(const std::string & path,tiledb_array_schema_t ** array_schema)221 int StringDimsFx::array_schema_load_wrapper(
222     const std::string& path, tiledb_array_schema_t** array_schema) {
223 #ifndef TILEDB_SERIALIZATION
224   return tiledb_array_schema_load(ctx_, path.c_str(), array_schema);
225 #endif
226 
227   if (!serialize_) {
228     return tiledb_array_schema_load(ctx_, path.c_str(), array_schema);
229   }
230 
231   // Load array.
232   int rc = tiledb_array_schema_load(ctx_, path.c_str(), array_schema);
233   REQUIRE(rc == TILEDB_OK);
234 
235   // Serialize the array
236   tiledb_buffer_t* buff;
237   REQUIRE(
238       tiledb_serialize_array_schema(
239           ctx_,
240           *array_schema,
241           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
242           1,
243           &buff) == TILEDB_OK);
244 
245   // Load array schema from the rest server
246   tiledb_array_schema_t* new_array_schema = nullptr;
247   REQUIRE(
248       tiledb_deserialize_array_schema(
249           ctx_,
250           buff,
251           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
252           0,
253           &new_array_schema) == TILEDB_OK);
254 
255   // Serialize the new array schema and deserialize into the original array
256   // schema.
257   tiledb_array_schema_free(array_schema);
258   tiledb_buffer_t* buff2;
259   REQUIRE(
260       tiledb_serialize_array_schema(
261           ctx_,
262           new_array_schema,
263           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
264           0,
265           &buff2) == TILEDB_OK);
266   REQUIRE(
267       tiledb_deserialize_array_schema(
268           ctx_,
269           buff2,
270           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
271           1,
272           array_schema) == TILEDB_OK);
273 
274   // Clean up.
275   tiledb_array_schema_free(&new_array_schema);
276   tiledb_buffer_free(&buff);
277   tiledb_buffer_free(&buff2);
278 
279   return rc;
280 }
281 
array_create_wrapper(const std::string & path,tiledb_array_schema_t * array_schema)282 int StringDimsFx::array_create_wrapper(
283     const std::string& path, tiledb_array_schema_t* array_schema) {
284 #ifndef TILEDB_SERIALIZATION
285   return tiledb_array_create(ctx_, path.c_str(), array_schema);
286 #endif
287 
288   if (!serialize_) {
289     return tiledb_array_create(ctx_, path.c_str(), array_schema);
290   }
291 
292   // Serialize the array
293   tiledb_buffer_t* buff;
294   REQUIRE(
295       tiledb_serialize_array_schema(
296           ctx_,
297           array_schema,
298           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
299           1,
300           &buff) == TILEDB_OK);
301 
302   // Load array schema from the rest server
303   tiledb_array_schema_t* new_array_schema = nullptr;
304   REQUIRE(
305       tiledb_deserialize_array_schema(
306           ctx_,
307           buff,
308           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
309           0,
310           &new_array_schema) == TILEDB_OK);
311 
312   // Create array from new schema
313   int rc = tiledb_array_create(ctx_, path.c_str(), new_array_schema);
314 
315   // Serialize the new array schema and deserialize into the original array
316   // schema.
317   tiledb_buffer_t* buff2;
318   tiledb_array_schema_t* new_array_schema2 = nullptr;
319   REQUIRE(
320       tiledb_serialize_array_schema(
321           ctx_,
322           new_array_schema,
323           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
324           0,
325           &buff2) == TILEDB_OK);
326   REQUIRE(
327       tiledb_deserialize_array_schema(
328           ctx_,
329           buff2,
330           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
331           1,
332           &new_array_schema2) == TILEDB_OK);
333 
334   // Clean up.
335   tiledb_array_schema_free(&new_array_schema);
336   tiledb_array_schema_free(&new_array_schema2);
337   tiledb_buffer_free(&buff);
338   tiledb_buffer_free(&buff2);
339 
340   return rc;
341 }
342 
tiledb_query_submit_wrapper(tiledb_ctx_t * ctx,tiledb_query_t * query,const std::string & array_uri)343 int StringDimsFx::tiledb_query_submit_wrapper(
344     tiledb_ctx_t* ctx, tiledb_query_t* query, const std::string& array_uri) {
345 #ifndef TILEDB_SERIALIZATION
346   return tiledb_query_submit(ctx, query);
347 #endif
348 
349   if (!serialize_) {
350     return tiledb_query_submit(ctx, query);
351   }
352 
353   // Get the query type and layout
354   tiledb_query_type_t query_type;
355   tiledb_layout_t layout;
356   REQUIRE(tiledb_query_get_type(ctx, query, &query_type) == TILEDB_OK);
357   REQUIRE(tiledb_query_get_layout(ctx, query, &layout) == TILEDB_OK);
358 
359   // Serialize the query (client-side).
360   tiledb_buffer_list_t* buff_list1;
361   int rc = tiledb_serialize_query(ctx, query, TILEDB_CAPNP, 1, &buff_list1);
362 
363   // Global order writes are not (yet) supported for serialization. Just
364   // check that serialization is an error, and then execute the regular query.
365   if (layout == TILEDB_GLOBAL_ORDER && query_type == TILEDB_WRITE) {
366     REQUIRE(rc == TILEDB_ERR);
367     tiledb_buffer_list_free(&buff_list1);
368     return tiledb_query_submit(ctx, query);
369   } else {
370     REQUIRE(rc == TILEDB_OK);
371   }
372 
373   // Copy the data to a temporary memory region ("send over the network").
374   tiledb_buffer_t* buff1;
375   REQUIRE(tiledb_buffer_list_flatten(ctx, buff_list1, &buff1) == TILEDB_OK);
376   uint64_t buff1_size;
377   void* buff1_data;
378   REQUIRE(
379       tiledb_buffer_get_data(ctx, buff1, &buff1_data, &buff1_size) ==
380       TILEDB_OK);
381   void* buff1_copy = std::malloc(buff1_size);
382   REQUIRE(buff1_copy != nullptr);
383   std::memcpy(buff1_copy, buff1_data, buff1_size);
384   tiledb_buffer_free(&buff1);
385 
386   // Create a new buffer that wraps the data from the temporary buffer.
387   // This mimics what the REST server side would do.
388   tiledb_buffer_t* buff2;
389   REQUIRE(tiledb_buffer_alloc(ctx, &buff2) == TILEDB_OK);
390   REQUIRE(
391       tiledb_buffer_set_data(ctx, buff2, buff1_copy, buff1_size) == TILEDB_OK);
392 
393   // Open a new array instance.
394   tiledb_array_t* new_array = nullptr;
395   REQUIRE(tiledb_array_alloc(ctx, array_uri.c_str(), &new_array) == TILEDB_OK);
396   REQUIRE(tiledb_array_open(ctx, new_array, query_type) == TILEDB_OK);
397 
398   // Create a new query and deserialize from the buffer (server-side)
399   tiledb_query_t* new_query = nullptr;
400   REQUIRE(
401       tiledb_query_alloc(ctx, new_array, query_type, &new_query) == TILEDB_OK);
402   REQUIRE(
403       tiledb_deserialize_query(ctx, buff2, TILEDB_CAPNP, 0, new_query) ==
404       TILEDB_OK);
405 
406   // Next, for reads, allocate buffers for the new query.
407   std::vector<void*> to_free;
408   if (query_type == TILEDB_READ) {
409     tiledb_array_schema_t* schema;
410     REQUIRE(tiledb_array_get_schema(ctx, new_array, &schema) == TILEDB_OK);
411     uint32_t num_attributes;
412     REQUIRE(
413         tiledb_array_schema_get_attribute_num(ctx, schema, &num_attributes) ==
414         TILEDB_OK);
415     for (uint32_t i = 0; i < num_attributes; i++) {
416       tiledb_attribute_t* attr;
417       REQUIRE(
418           tiledb_array_schema_get_attribute_from_index(ctx, schema, i, &attr) ==
419           TILEDB_OK);
420       const char* name;
421       REQUIRE(tiledb_attribute_get_name(ctx, attr, &name) == TILEDB_OK);
422       uint32_t cell_num;
423       REQUIRE(
424           tiledb_attribute_get_cell_val_num(ctx, attr, &cell_num) == TILEDB_OK);
425       bool var_len = cell_num == TILEDB_VAR_NUM;
426 
427       if (var_len) {
428         void* buff;
429         uint64_t* buff_size;
430         uint64_t* offset_buff;
431         uint64_t* offset_buff_size;
432         REQUIRE(
433             tiledb_query_get_data_buffer(
434                 ctx, new_query, name, &buff, &buff_size) == TILEDB_OK);
435         REQUIRE(
436             tiledb_query_get_offsets_buffer(
437                 ctx, new_query, name, &offset_buff, &offset_buff_size) ==
438             TILEDB_OK);
439         // Buffers will always be null after deserialization on server side
440         REQUIRE(buff == nullptr);
441         REQUIRE(offset_buff == nullptr);
442         if (buff_size != nullptr) {
443           // Buffer size was set for the attribute; allocate one of the
444           // appropriate size.
445           buff = std::malloc(*buff_size);
446           offset_buff = (uint64_t*)std::malloc(*offset_buff_size);
447           to_free.push_back(buff);
448           to_free.push_back(offset_buff);
449 
450           REQUIRE(
451               tiledb_query_set_data_buffer(
452                   ctx, new_query, name, buff, buff_size) == TILEDB_OK);
453           REQUIRE(
454               tiledb_query_set_offsets_buffer(
455                   ctx, new_query, name, offset_buff, offset_buff_size) ==
456               TILEDB_OK);
457         }
458       } else {
459         void* buff;
460         uint64_t* buff_size;
461         REQUIRE(
462             tiledb_query_get_data_buffer(
463                 ctx, new_query, name, &buff, &buff_size) == TILEDB_OK);
464         // Buffers will always be null after deserialization on server side
465         REQUIRE(buff == nullptr);
466         if (buff_size != nullptr) {
467           // Buffer size was set for the attribute; allocate one of the
468           // appropriate size.
469           buff = std::malloc(*buff_size);
470           to_free.push_back(buff);
471           REQUIRE(
472               tiledb_query_set_data_buffer(
473                   ctx, new_query, name, buff, buff_size) == TILEDB_OK);
474         }
475       }
476 
477       tiledb_attribute_free(&attr);
478     }
479 
480     // Repeat for coords
481     void* buff;
482     uint64_t* buff_size;
483     REQUIRE(
484         tiledb_query_get_data_buffer(
485             ctx, new_query, TILEDB_COORDS, &buff, &buff_size) == TILEDB_OK);
486     if (buff_size != nullptr) {
487       buff = std::malloc(*buff_size);
488       to_free.push_back(buff);
489       REQUIRE(
490           tiledb_query_set_data_buffer(
491               ctx, new_query, TILEDB_COORDS, buff, buff_size) == TILEDB_OK);
492     }
493 
494     // Repeat for split dimensions, if they are set we will set the buffer
495     uint32_t num_dimension;
496     tiledb_domain_t* domain;
497     REQUIRE(tiledb_array_schema_get_domain(ctx, schema, &domain) == TILEDB_OK);
498     REQUIRE(tiledb_domain_get_ndim(ctx, domain, &num_dimension) == TILEDB_OK);
499 
500     for (uint32_t i = 0; i < num_dimension; i++) {
501       tiledb_dimension_t* dim;
502       REQUIRE(
503           tiledb_domain_get_dimension_from_index(ctx, domain, i, &dim) ==
504           TILEDB_OK);
505       const char* name;
506       REQUIRE(tiledb_dimension_get_name(ctx, dim, &name) == TILEDB_OK);
507 
508       void* buff = nullptr;
509       uint64_t* buff_size = nullptr;
510       uint64_t* offset_buff = nullptr;
511       uint64_t* offset_buff_size = nullptr;
512 
513       uint32_t cell_val_num = 0;
514       REQUIRE(
515           tiledb_dimension_get_cell_val_num(ctx, dim, &cell_val_num) ==
516           TILEDB_OK);
517 
518       if (cell_val_num == TILEDB_VAR_NUM) {
519         REQUIRE(
520             tiledb_query_get_data_buffer(
521                 ctx, new_query, name, &buff, &buff_size) == TILEDB_OK);
522         REQUIRE(
523             tiledb_query_get_offsets_buffer(
524                 ctx, new_query, name, &offset_buff, &offset_buff_size) ==
525             TILEDB_OK);
526       } else {
527         REQUIRE(
528             tiledb_query_get_data_buffer(
529                 ctx, new_query, name, &buff, &buff_size) == TILEDB_OK);
530       }
531       // Buffers will always be null after deserialization on server side
532       REQUIRE(buff == nullptr);
533       REQUIRE(offset_buff == nullptr);
534       if (offset_buff_size != nullptr) {
535         // Buffer size was set for the attribute; allocate one of the
536         // appropriate size.
537         offset_buff = static_cast<uint64_t*>(std::malloc(*offset_buff_size));
538         to_free.push_back(offset_buff);
539         buff = std::malloc(*buff_size);
540         to_free.push_back(buff);
541         REQUIRE(
542             tiledb_query_set_data_buffer(
543                 ctx, new_query, name, buff, buff_size) == TILEDB_OK);
544         REQUIRE(
545             tiledb_query_set_offsets_buffer(
546                 ctx, new_query, name, offset_buff, offset_buff_size) ==
547             TILEDB_OK);
548       } else if (buff_size != nullptr) {
549         // Buffer size was set for the attribute; allocate one of the
550         // appropriate size.
551         buff = std::malloc(*buff_size);
552         to_free.push_back(buff);
553         REQUIRE(
554             tiledb_query_set_data_buffer(
555                 ctx, new_query, name, buff, buff_size) == TILEDB_OK);
556       }
557       tiledb_dimension_free(&dim);
558     }
559 
560     tiledb_domain_free(&domain);
561     tiledb_array_schema_free(&schema);
562   }
563 
564   // Submit the new query ("on the server").
565   rc = tiledb_query_submit(ctx, new_query);
566 
567   // Serialize the new query and "send it over the network" (server-side)
568   tiledb_buffer_list_t* buff_list2;
569   REQUIRE(
570       tiledb_serialize_query(ctx, new_query, TILEDB_CAPNP, 0, &buff_list2) ==
571       TILEDB_OK);
572   tiledb_buffer_t* buff3;
573   REQUIRE(tiledb_buffer_list_flatten(ctx, buff_list2, &buff3) == TILEDB_OK);
574   uint64_t buff3_size;
575   void* buff3_data;
576   REQUIRE(
577       tiledb_buffer_get_data(ctx, buff3, &buff3_data, &buff3_size) ==
578       TILEDB_OK);
579   void* buff3_copy = std::malloc(buff3_size);
580   REQUIRE(buff3_copy != nullptr);
581   std::memcpy(buff3_copy, buff3_data, buff3_size);
582   tiledb_buffer_free(&buff2);
583   tiledb_buffer_free(&buff3);
584 
585   // Create a new buffer that wraps the data from the temporary buffer.
586   tiledb_buffer_t* buff4;
587   REQUIRE(tiledb_buffer_alloc(ctx, &buff4) == TILEDB_OK);
588   REQUIRE(
589       tiledb_buffer_set_data(ctx, buff4, buff3_copy, buff3_size) == TILEDB_OK);
590 
591   // Deserialize into the original query. Client-side
592   REQUIRE(
593       tiledb_deserialize_query(ctx, buff4, TILEDB_CAPNP, 1, query) ==
594       TILEDB_OK);
595 
596   // Clean up.
597   REQUIRE(tiledb_array_close(ctx, new_array) == TILEDB_OK);
598   tiledb_query_free(&new_query);
599   tiledb_array_free(&new_array);
600   tiledb_buffer_free(&buff4);
601   tiledb_buffer_list_free(&buff_list1);
602   tiledb_buffer_list_free(&buff_list2);
603   std::free(buff1_copy);
604   std::free(buff3_copy);
605   for (void* b : to_free)
606     std::free(b);
607 
608   return rc;
609 }
610 
write_array_ascii(const std::string & array_name)611 void StringDimsFx::write_array_ascii(const std::string& array_name) {
612   // Open array
613   tiledb_array_t* array;
614   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
615   CHECK(rc == TILEDB_OK);
616   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
617   CHECK(rc == TILEDB_OK);
618 
619   // Create and submit query
620   tiledb_query_t* query;
621   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
622   REQUIRE(rc == TILEDB_OK);
623 
624   char d_data[] = "aabbbcdddd";
625   uint64_t d_data_size = strlen(d_data);
626   uint64_t d_off[] = {0, 2, 5, 6};
627   uint64_t d_off_size = sizeof(d_off);
628   int32_t a_data[] = {1, 2, 3, 4};
629   uint64_t a_size = sizeof(a_data);
630   rc = tiledb_query_set_data_buffer(ctx_, query, "d", d_data, &d_data_size);
631   REQUIRE(rc == TILEDB_ERR);
632   rc = tiledb_query_set_data_buffer(ctx_, query, "d", d_data, &d_data_size);
633   REQUIRE(rc == TILEDB_OK);
634   rc = tiledb_query_set_offsets_buffer(ctx_, query, "d", d_off, &d_off_size);
635   REQUIRE(rc == TILEDB_OK);
636   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a_data, &a_size);
637   REQUIRE(rc == TILEDB_OK);
638   rc = tiledb_query_set_layout(ctx_, query, TILEDB_GLOBAL_ORDER);
639   REQUIRE(rc == TILEDB_OK);
640   rc = tiledb_query_submit_wrapper(ctx_, query, array_name);
641   REQUIRE(rc == TILEDB_OK);
642   rc = tiledb_query_finalize(ctx_, query);
643   REQUIRE(rc == TILEDB_OK);
644 
645   // Close array
646   rc = tiledb_array_close(ctx_, array);
647   CHECK(rc == TILEDB_OK);
648 
649   // Clean up
650   tiledb_array_free(&array);
651   tiledb_query_free(&query);
652 }
653 
write_array_1d(tiledb_ctx_t * ctx,const std::string & array_name,tiledb_layout_t layout,const std::vector<uint64_t> & d_off,const std::string & d_val,const std::vector<int32_t> & a)654 void StringDimsFx::write_array_1d(
655     tiledb_ctx_t* ctx,
656     const std::string& array_name,
657     tiledb_layout_t layout,
658     const std::vector<uint64_t>& d_off,
659     const std::string& d_val,
660     const std::vector<int32_t>& a) {
661   // Open array
662   tiledb_array_t* array;
663   int rc = tiledb_array_alloc(ctx, array_name.c_str(), &array);
664   CHECK(rc == TILEDB_OK);
665   rc = tiledb_array_open(ctx, array, TILEDB_WRITE);
666   CHECK(rc == TILEDB_OK);
667 
668   // Create and submit query
669   tiledb_query_t* query;
670   rc = tiledb_query_alloc(ctx, array, TILEDB_WRITE, &query);
671   REQUIRE(rc == TILEDB_OK);
672 
673   uint64_t d_off_size = d_off.size() * sizeof(uint64_t);
674   uint64_t d_val_size = d_val.size();
675   uint64_t a_size = a.size() * sizeof(int32_t);
676   rc = tiledb_query_set_data_buffer(
677       ctx, query, "d", (void*)&d_val[0], &d_val_size);
678   REQUIRE(rc == TILEDB_OK);
679   rc = tiledb_query_set_offsets_buffer(
680       ctx, query, "d", (uint64_t*)&d_off[0], &d_off_size);
681   REQUIRE(rc == TILEDB_OK);
682   rc = tiledb_query_set_data_buffer(ctx, query, "a", (void*)a.data(), &a_size);
683   REQUIRE(rc == TILEDB_OK);
684   rc = tiledb_query_set_layout(ctx, query, layout);
685   REQUIRE(rc == TILEDB_OK);
686   rc = tiledb_query_submit_wrapper(ctx, query, array_name);
687   REQUIRE(rc == TILEDB_OK);
688   rc = tiledb_query_finalize(ctx, query);
689   REQUIRE(rc == TILEDB_OK);
690 
691   // Close array
692   rc = tiledb_array_close(ctx, array);
693   CHECK(rc == TILEDB_OK);
694 
695   // Clean up
696   tiledb_array_free(&array);
697   tiledb_query_free(&query);
698 }
699 
write_array_2d(tiledb_ctx_t * ctx,const std::string & array_name,tiledb_layout_t layout,const std::vector<uint64_t> & d1_off,const std::string & d1_val,const std::vector<int32_t> & d2,const std::vector<int32_t> & a)700 void StringDimsFx::write_array_2d(
701     tiledb_ctx_t* ctx,
702     const std::string& array_name,
703     tiledb_layout_t layout,
704     const std::vector<uint64_t>& d1_off,
705     const std::string& d1_val,
706     const std::vector<int32_t>& d2,
707     const std::vector<int32_t>& a) {
708   // Open array
709   tiledb_array_t* array;
710   int rc = tiledb_array_alloc(ctx, array_name.c_str(), &array);
711   CHECK(rc == TILEDB_OK);
712   rc = tiledb_array_open(ctx, array, TILEDB_WRITE);
713   CHECK(rc == TILEDB_OK);
714 
715   // Create and submit query
716   tiledb_query_t* query;
717   rc = tiledb_query_alloc(ctx, array, TILEDB_WRITE, &query);
718   REQUIRE(rc == TILEDB_OK);
719 
720   uint64_t d1_off_size = d1_off.size() * sizeof(uint64_t);
721   uint64_t d1_val_size = d1_val.size();
722   uint64_t d2_size = d2.size() * sizeof(int32_t);
723   uint64_t a_size = a.size() * sizeof(int32_t);
724   rc = tiledb_query_set_data_buffer(
725       ctx, query, "d1", (void*)&d1_val[0], &d1_val_size);
726   REQUIRE(rc == TILEDB_OK);
727   rc = tiledb_query_set_offsets_buffer(
728       ctx, query, "d1", (uint64_t*)&d1_off[0], &d1_off_size);
729   REQUIRE(rc == TILEDB_OK);
730   rc = tiledb_query_set_data_buffer(
731       ctx, query, "d2", (void*)d2.data(), &d2_size);
732   REQUIRE(rc == TILEDB_OK);
733   rc = tiledb_query_set_data_buffer(ctx, query, "a", (void*)a.data(), &a_size);
734   REQUIRE(rc == TILEDB_OK);
735   rc = tiledb_query_set_layout(ctx, query, layout);
736   REQUIRE(rc == TILEDB_OK);
737   rc = tiledb_query_submit_wrapper(ctx, query, array_name);
738   REQUIRE(rc == TILEDB_OK);
739   rc = tiledb_query_finalize(ctx, query);
740   REQUIRE(rc == TILEDB_OK);
741 
742   // Close array
743   rc = tiledb_array_close(ctx, array);
744   CHECK(rc == TILEDB_OK);
745 
746   // Clean up
747   tiledb_array_free(&array);
748   tiledb_query_free(&query);
749 }
750 
tiledb_array_get_non_empty_domain_from_name_wrapper(tiledb_ctx_t * ctx,tiledb_array_t * array,const char * name,void * domain,int32_t * is_empty)751 int StringDimsFx::tiledb_array_get_non_empty_domain_from_name_wrapper(
752     tiledb_ctx_t* ctx,
753     tiledb_array_t* array,
754     const char* name,
755     void* domain,
756     int32_t* is_empty) {
757   int ret = tiledb_array_get_non_empty_domain_from_name(
758       ctx, array, name, domain, is_empty);
759 #ifndef TILEDB_SERIALIZATION
760   return ret;
761 #endif
762 
763   if (ret != TILEDB_OK || !serialize_)
764     return ret;
765 
766   // Serialize the non_empty_domain
767   tiledb_buffer_t* buff;
768   REQUIRE(
769       tiledb_serialize_array_non_empty_domain_all_dimensions(
770           ctx,
771           array,
772           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
773           0,
774           &buff) == TILEDB_OK);
775 
776   // Deserialize to validate we can round-trip
777   REQUIRE(
778       tiledb_deserialize_array_non_empty_domain_all_dimensions(
779           ctx,
780           array,
781           buff,
782           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
783           1) == TILEDB_OK);
784 
785   tiledb_buffer_free(&buff);
786 
787   return tiledb_array_get_non_empty_domain_from_name(
788       ctx, array, name, domain, is_empty);
789 }
790 
get_non_empty_domain(const std::string & array_name,const std::string & dim_name,std::vector<int32_t> * dom,int32_t * is_empty)791 void StringDimsFx::get_non_empty_domain(
792     const std::string& array_name,
793     const std::string& dim_name,
794     std::vector<int32_t>* dom,
795     int32_t* is_empty) {
796   dom->resize(2);
797 
798   // Open array
799   tiledb_array_t* array;
800   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
801   CHECK(rc == TILEDB_OK);
802   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
803   CHECK(rc == TILEDB_OK);
804 
805   // Get non-empty domain
806   rc = tiledb_array_get_non_empty_domain_from_name_wrapper(
807       ctx_, array, dim_name.c_str(), &(*dom)[0], is_empty);
808   CHECK(rc == TILEDB_OK);
809 
810   // Close array
811   rc = tiledb_array_close(ctx_, array);
812   CHECK(rc == TILEDB_OK);
813 
814   // Clean up
815   tiledb_array_free(&array);
816 }
817 
tiledb_array_get_non_empty_domain_var_size_from_name_wrapper(tiledb_ctx_t * ctx,tiledb_array_t * array,const char * name,uint64_t * start_size,uint64_t * end_size,int32_t * is_empty)818 int StringDimsFx::tiledb_array_get_non_empty_domain_var_size_from_name_wrapper(
819     tiledb_ctx_t* ctx,
820     tiledb_array_t* array,
821     const char* name,
822     uint64_t* start_size,
823     uint64_t* end_size,
824     int32_t* is_empty) {
825   int ret = tiledb_array_get_non_empty_domain_var_size_from_name(
826       ctx, array, name, start_size, end_size, is_empty);
827 #ifndef TILEDB_SERIALIZATION
828   return ret;
829 #endif
830 
831   if (ret != TILEDB_OK || !serialize_)
832     return ret;
833 
834   // Serialize the non_empty_domain
835   tiledb_buffer_t* buff;
836   REQUIRE(
837       tiledb_serialize_array_non_empty_domain_all_dimensions(
838           ctx,
839           array,
840           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
841           0,
842           &buff) == TILEDB_OK);
843 
844   // Deserialize to validate we can round-trip
845   REQUIRE(
846       tiledb_deserialize_array_non_empty_domain_all_dimensions(
847           ctx,
848           array,
849           buff,
850           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
851           1) == TILEDB_OK);
852 
853   tiledb_buffer_free(&buff);
854 
855   return tiledb_array_get_non_empty_domain_var_size_from_name(
856       ctx, array, name, start_size, end_size, is_empty);
857 }
858 
tiledb_array_get_non_empty_domain_var_from_name_wrapper(tiledb_ctx_t * ctx,tiledb_array_t * array,const char * name,void * start,void * end,int32_t * is_empty)859 int StringDimsFx::tiledb_array_get_non_empty_domain_var_from_name_wrapper(
860     tiledb_ctx_t* ctx,
861     tiledb_array_t* array,
862     const char* name,
863     void* start,
864     void* end,
865     int32_t* is_empty) {
866   int ret = tiledb_array_get_non_empty_domain_var_from_name(
867       ctx, array, name, start, end, is_empty);
868 #ifndef TILEDB_SERIALIZATION
869   return ret;
870 #endif
871 
872   if (ret != TILEDB_OK || !serialize_)
873     return ret;
874 
875   // Serialize the non_empty_domain
876   tiledb_buffer_t* buff;
877   REQUIRE(
878       tiledb_serialize_array_non_empty_domain_all_dimensions(
879           ctx,
880           array,
881           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
882           0,
883           &buff) == TILEDB_OK);
884 
885   // Deserialize to validate we can round-trip
886   REQUIRE(
887       tiledb_deserialize_array_non_empty_domain_all_dimensions(
888           ctx,
889           array,
890           buff,
891           (tiledb_serialization_type_t)tiledb::sm::SerializationType::CAPNP,
892           1) == TILEDB_OK);
893 
894   tiledb_buffer_free(&buff);
895 
896   return tiledb_array_get_non_empty_domain_var_from_name(
897       ctx, array, name, start, end, is_empty);
898 }
899 
get_non_empty_domain_var(const std::string & array_name,const std::string & dim_name,std::string * start,std::string * end,int32_t * is_empty)900 void StringDimsFx::get_non_empty_domain_var(
901     const std::string& array_name,
902     const std::string& dim_name,
903     std::string* start,
904     std::string* end,
905     int32_t* is_empty) {
906   // Open array
907   tiledb_array_t* array;
908   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
909   CHECK(rc == TILEDB_OK);
910   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
911   CHECK(rc == TILEDB_OK);
912 
913   // Get non-empty domain size
914   uint64_t start_size = 0, end_size = 0;
915   rc = tiledb_array_get_non_empty_domain_var_size_from_name_wrapper(
916       ctx_, array, dim_name.c_str(), &start_size, &end_size, is_empty);
917   CHECK(rc == TILEDB_OK);
918 
919   // Get non-empty domain
920   start->resize(start_size);
921   end->resize(end_size);
922   rc = tiledb_array_get_non_empty_domain_var_from_name_wrapper(
923       ctx_, array, dim_name.c_str(), &(*start)[0], &(*end)[0], is_empty);
924   CHECK(rc == TILEDB_OK);
925 
926   // Close array
927   rc = tiledb_array_close(ctx_, array);
928   CHECK(rc == TILEDB_OK);
929 
930   // Clean up
931   tiledb_array_free(&array);
932 }
933 
get_est_result_size_var(tiledb_array_t * array,unsigned dim_idx,const std::string & dim_name,const std::string & start,const std::string & end,uint64_t * size_off,uint64_t * size_val)934 void StringDimsFx::get_est_result_size_var(
935     tiledb_array_t* array,
936     unsigned dim_idx,
937     const std::string& dim_name,
938     const std::string& start,
939     const std::string& end,
940     uint64_t* size_off,
941     uint64_t* size_val) {
942   tiledb_query_t* query;
943   int rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
944   CHECK(rc == TILEDB_OK);
945   rc = tiledb_query_add_range_var(
946       ctx_, query, dim_idx, start.data(), start.size(), end.data(), end.size());
947   CHECK(rc == TILEDB_OK);
948   rc = tiledb_query_get_est_result_size_var(
949       ctx_, query, dim_name.c_str(), size_off, size_val);
950   CHECK(rc == TILEDB_OK);
951   tiledb_query_free(&query);
952 }
953 
read_array_1d(tiledb_ctx_t * ctx,tiledb_array_t * array,tiledb_layout_t layout,const std::string & start,const std::string & end,std::vector<uint64_t> * d_off,std::string * d_val,std::vector<int32_t> * a,tiledb_query_status_t * status)954 void StringDimsFx::read_array_1d(
955     tiledb_ctx_t* ctx,
956     tiledb_array_t* array,
957     tiledb_layout_t layout,
958     const std::string& start,
959     const std::string& end,
960     std::vector<uint64_t>* d_off,
961     std::string* d_val,
962     std::vector<int32_t>* a,
963     tiledb_query_status_t* status) {
964   // Create query
965   tiledb_query_t* query;
966   int rc = tiledb_query_alloc(ctx, array, TILEDB_READ, &query);
967   CHECK(rc == TILEDB_OK);
968   rc = tiledb_query_add_range_var(
969       ctx, query, 0, start.data(), start.size(), end.data(), end.size());
970   CHECK(rc == TILEDB_OK);
971 
972   // Check range num
973   uint64_t range_num;
974   rc = tiledb_query_get_range_num(ctx_, query, 0, &range_num);
975   CHECK(rc == TILEDB_OK);
976   CHECK(range_num == 1);
977 
978   // Check getting range from an invalid range index
979   uint64_t start_size = 0, end_size = 0;
980   rc = tiledb_query_get_range_var_size(
981       ctx_, query, 0, 2, &start_size, &end_size);
982   CHECK(rc == TILEDB_ERR);
983   std::vector<char> start_data(start_size);
984   std::vector<char> end_data(end_size);
985   rc = tiledb_query_get_range_var(
986       ctx_, query, 0, 2, start_data.data(), end_data.data());
987   CHECK(rc == TILEDB_ERR);
988 
989   // Check ranges
990   rc = tiledb_query_get_range_var_size(
991       ctx_, query, 0, 0, &start_size, &end_size);
992   CHECK(rc == TILEDB_OK);
993   start_data.resize(start_size);
994   end_data.resize(end_size);
995   rc = tiledb_query_get_range_var(
996       ctx_, query, 0, 0, start_data.data(), end_data.data());
997   CHECK(rc == TILEDB_OK);
998   CHECK(std::string(start_data.data(), start_data.size()) == start);
999   CHECK(std::string(end_data.data(), end_data.size()) == end);
1000 
1001   // Set query buffers
1002   uint64_t d_off_size = d_off->size() * sizeof(uint64_t);
1003   uint64_t d_val_size = d_val->size();
1004   uint64_t a_size = a->size() * sizeof(int32_t);
1005   rc = tiledb_query_set_data_buffer(
1006       ctx, query, "d", (void*)d_val->data(), &d_val_size);
1007   REQUIRE(rc == TILEDB_OK);
1008   rc = tiledb_query_set_offsets_buffer(
1009       ctx, query, "d", (uint64_t*)d_off->data(), &d_off_size);
1010   REQUIRE(rc == TILEDB_OK);
1011   rc = tiledb_query_set_data_buffer(ctx, query, "a", a->data(), &a_size);
1012   REQUIRE(rc == TILEDB_OK);
1013 
1014   // Set layout
1015   rc = tiledb_query_set_layout(ctx, query, layout);
1016   REQUIRE(rc == TILEDB_OK);
1017 
1018   // Submit query
1019   const char* array_uri;
1020   rc = tiledb_array_get_uri(ctx, array, &array_uri);
1021   CHECK(rc == TILEDB_OK);
1022   rc = tiledb_query_submit_wrapper(ctx, query, array_uri);
1023   CHECK(rc == TILEDB_OK);
1024 
1025   // Get status
1026   rc = tiledb_query_get_status(ctx, query, status);
1027   CHECK(rc == TILEDB_OK);
1028 
1029   // Resize the result buffers
1030   d_off->resize(d_off_size / sizeof(uint64_t));
1031   d_val->resize(d_val_size / sizeof(char));
1032   a->resize(a_size / sizeof(int32_t));
1033 
1034   // Clean up
1035   tiledb_query_free(&query);
1036 }
1037 
read_array_2d(tiledb_ctx_t * ctx,tiledb_array_t * array,tiledb_layout_t layout,const std::string & d1_start,const std::string & d1_end,int32_t d2_start,int32_t d2_end,std::vector<uint64_t> * d1_off,std::string * d1_val,std::vector<int32_t> * d2,std::vector<int32_t> * a,tiledb_query_status_t * status)1038 void StringDimsFx::read_array_2d(
1039     tiledb_ctx_t* ctx,
1040     tiledb_array_t* array,
1041     tiledb_layout_t layout,
1042     const std::string& d1_start,
1043     const std::string& d1_end,
1044     int32_t d2_start,
1045     int32_t d2_end,
1046     std::vector<uint64_t>* d1_off,
1047     std::string* d1_val,
1048     std::vector<int32_t>* d2,
1049     std::vector<int32_t>* a,
1050     tiledb_query_status_t* status) {
1051   // Create query
1052   tiledb_query_t* query;
1053   int rc = tiledb_query_alloc(ctx, array, TILEDB_READ, &query);
1054   CHECK(rc == TILEDB_OK);
1055   rc = tiledb_query_add_range_var(
1056       ctx,
1057       query,
1058       0,
1059       d1_start.data(),
1060       d1_start.size(),
1061       d1_end.data(),
1062       d1_end.size());
1063   CHECK(rc == TILEDB_OK);
1064   rc = tiledb_query_add_range(ctx, query, 1, &d2_start, &d2_end, nullptr);
1065   CHECK(rc == TILEDB_OK);
1066 
1067   // Check range num d1
1068   uint64_t range_num;
1069   rc = tiledb_query_get_range_num(ctx_, query, 0, &range_num);
1070   CHECK(rc == TILEDB_OK);
1071   CHECK(range_num == 1);
1072   // Check range num d2
1073   rc = tiledb_query_get_range_num(ctx_, query, 1, &range_num);
1074   CHECK(rc == TILEDB_OK);
1075   CHECK(range_num == 1);
1076 
1077   // Check getting range from an invalid range index
1078   uint64_t d1_start_size = 0, d1_end_size = 0;
1079   rc = tiledb_query_get_range_var_size(
1080       ctx_, query, 0, 2, &d1_start_size, &d1_end_size);
1081   CHECK(rc == TILEDB_ERR);
1082   std::vector<char> d1_start_data(d1_start_size);
1083   std::vector<char> d1_end_data(d1_end_size);
1084   rc = tiledb_query_get_range_var(
1085       ctx_, query, 0, 2, d1_start_data.data(), d1_end_data.data());
1086   CHECK(rc == TILEDB_ERR);
1087 
1088   // Check ranges
1089   rc = tiledb_query_get_range_var_size(
1090       ctx_, query, 0, 0, &d1_start_size, &d1_end_size);
1091   CHECK(rc == TILEDB_OK);
1092   d1_start_data.resize(d1_start_size);
1093   d1_end_data.resize(d1_end_size);
1094   rc = tiledb_query_get_range_var(
1095       ctx_, query, 0, 0, d1_start_data.data(), d1_end_data.data());
1096   CHECK(rc == TILEDB_OK);
1097   CHECK(std::string(d1_start_data.data(), d1_start_data.size()) == d1_start);
1098   CHECK(std::string(d1_end_data.data(), d1_end_data.size()) == d1_end);
1099 
1100   const void *d2_start_data, *d2_end_data, *stride;
1101   rc = tiledb_query_get_range(
1102       ctx_, query, 1, 0, &d2_start_data, &d2_end_data, &stride);
1103   CHECK(rc == TILEDB_OK);
1104   CHECK(*(int32_t*)d2_start_data == d2_start);
1105   CHECK(*(int32_t*)d2_end_data == d2_end);
1106   CHECK(stride == nullptr);
1107 
1108   // Set query buffers
1109   uint64_t d1_off_size = d1_off->size() * sizeof(uint64_t);
1110   uint64_t d1_val_size = d1_val->size();
1111   uint64_t d2_size = d2->size() * sizeof(int32_t);
1112   uint64_t a_size = a->size() * sizeof(int32_t);
1113   rc = tiledb_query_set_data_buffer(
1114       ctx, query, "d1", (void*)d1_val->data(), &d1_val_size);
1115   REQUIRE(rc == TILEDB_OK);
1116   rc = tiledb_query_set_offsets_buffer(
1117       ctx, query, "d1", (uint64_t*)d1_off->data(), &d1_off_size);
1118   REQUIRE(rc == TILEDB_OK);
1119   rc = tiledb_query_set_data_buffer(ctx, query, "d2", d2->data(), &d2_size);
1120   REQUIRE(rc == TILEDB_OK);
1121   rc = tiledb_query_set_data_buffer(ctx, query, "a", a->data(), &a_size);
1122   REQUIRE(rc == TILEDB_OK);
1123 
1124   // Set layout
1125   rc = tiledb_query_set_layout(ctx, query, layout);
1126   REQUIRE(rc == TILEDB_OK);
1127 
1128   // Submit query
1129   const char* array_uri;
1130   rc = tiledb_array_get_uri(ctx, array, &array_uri);
1131   CHECK(rc == TILEDB_OK);
1132   rc = tiledb_query_submit_wrapper(ctx, query, array_uri);
1133   CHECK(rc == TILEDB_OK);
1134 
1135   // Get status
1136   rc = tiledb_query_get_status(ctx, query, status);
1137   CHECK(rc == TILEDB_OK);
1138 
1139   // Resize the result buffers
1140   d1_off->resize(d1_off_size / sizeof(uint64_t));
1141   d1_val->resize(d1_val_size / sizeof(char));
1142   d2->resize(d2_size / sizeof(int32_t));
1143   a->resize(a_size / sizeof(int32_t));
1144 
1145   // Clean up
1146   tiledb_query_free(&query);
1147 }
1148 
1149 TEST_CASE_METHOD(
1150     StringDimsFx,
1151     "C API: Test sparse array with string dimensions, array schema",
1152     "[capi][sparse][string-dims][array-schema]") {
1153   SECTION("- No serialization") {
1154     serialize_ = false;
1155   }
1156   SECTION("- Serialization") {
1157     serialize_ = true;
1158   }
1159   SupportedFsLocal local_fs;
1160   std::string array_name =
1161       local_fs.file_prefix() + local_fs.temp_dir() + "string_dims";
1162   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1163 
1164   // Create dimension
1165   tiledb_domain_t* domain;
1166   tiledb_dimension_t* d;
1167   char tmp;
1168   int rc =
1169       tiledb_dimension_alloc(ctx_, "d", TILEDB_STRING_ASCII, &tmp, nullptr, &d);
1170   REQUIRE(rc == TILEDB_ERR);
1171   rc =
1172       tiledb_dimension_alloc(ctx_, "d", TILEDB_STRING_ASCII, nullptr, &tmp, &d);
1173   REQUIRE(rc == TILEDB_ERR);
1174   rc = tiledb_dimension_alloc(
1175       ctx_, "d", TILEDB_STRING_ASCII, nullptr, nullptr, &d);
1176   REQUIRE(rc == TILEDB_OK);
1177 
1178   // Setting cell val num to a TILEDB_STRING_ASCII dimension should error out
1179   rc = tiledb_dimension_set_cell_val_num(ctx_, d, 4);
1180   REQUIRE(rc == TILEDB_ERR);
1181   rc = tiledb_dimension_set_cell_val_num(ctx_, d, TILEDB_VAR_NUM);
1182   REQUIRE(rc == TILEDB_OK);
1183 
1184   // Create domain
1185   rc = tiledb_domain_alloc(ctx_, &domain);
1186   REQUIRE(rc == TILEDB_OK);
1187   rc = tiledb_domain_add_dimension(ctx_, domain, d);
1188   REQUIRE(rc == TILEDB_OK);
1189 
1190   // Setting a string dimension to a dense array should error out
1191   tiledb_array_schema_t* array_schema;
1192   rc = tiledb_array_schema_alloc(ctx_, TILEDB_DENSE, &array_schema);
1193   REQUIRE(rc == TILEDB_OK);
1194   rc = tiledb_array_schema_set_domain(ctx_, array_schema, domain);
1195   REQUIRE(rc == TILEDB_ERR);
1196   tiledb_array_schema_free(&array_schema);
1197 
1198   // Create sparse array schema
1199   rc = tiledb_array_schema_alloc(ctx_, TILEDB_SPARSE, &array_schema);
1200   REQUIRE(rc == TILEDB_OK);
1201 
1202   // Set domain to schema
1203   rc = tiledb_array_schema_set_domain(ctx_, array_schema, domain);
1204   REQUIRE(rc == TILEDB_OK);
1205 
1206   // Create attributes
1207   tiledb_attribute_t* a;
1208   rc = tiledb_attribute_alloc(ctx_, "a", TILEDB_INT32, &a);
1209   REQUIRE(rc == TILEDB_OK);
1210 
1211   // Check array schema
1212   rc = tiledb_array_schema_check(ctx_, array_schema);
1213   REQUIRE(rc == TILEDB_OK);
1214 
1215   // Create array
1216   rc = array_create_wrapper(array_name, array_schema);
1217   REQUIRE(rc == TILEDB_OK);
1218 
1219   // Clean up
1220   tiledb_array_schema_free(&array_schema);
1221   tiledb_dimension_free(&d);
1222   tiledb_domain_free(&domain);
1223   tiledb_attribute_free(&a);
1224 
1225   // Load array schema and domain
1226   rc = array_schema_load_wrapper(array_name, &array_schema);
1227   REQUIRE(rc == TILEDB_OK);
1228   rc = tiledb_array_schema_get_domain(ctx_, array_schema, &domain);
1229   REQUIRE(rc == TILEDB_OK);
1230 
1231   // Get dimension
1232   rc = tiledb_domain_get_dimension_from_index(ctx_, domain, 0, &d);
1233   REQUIRE(rc == TILEDB_OK);
1234 
1235   // Check dimension type, domain and tile extent
1236   tiledb_datatype_t type;
1237   rc = tiledb_dimension_get_type(ctx_, d, &type);
1238   REQUIRE(rc == TILEDB_OK);
1239   CHECK(type == TILEDB_STRING_ASCII);
1240   const void *dom, *extent;
1241   rc = tiledb_dimension_get_domain(ctx_, d, &dom);
1242   REQUIRE(rc == TILEDB_OK);
1243   CHECK(dom == nullptr);
1244   rc = tiledb_dimension_get_tile_extent(ctx_, d, &extent);
1245   REQUIRE(rc == TILEDB_OK);
1246   CHECK(extent == nullptr);
1247 
1248   // Clean up
1249   tiledb_array_schema_free(&array_schema);
1250   tiledb_domain_free(&domain);
1251   tiledb_dimension_free(&d);
1252 
1253   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1254 }
1255 
1256 TEST_CASE_METHOD(
1257     StringDimsFx,
1258     "C API: Test sparse array with string dimensions, check duplicates, global "
1259     "order",
1260     "[capi][sparse][string-dims][duplicates][global]") {
1261   SECTION("- No serialization") {
1262     serialize_ = false;
1263   }
1264   SECTION("- Serialization") {
1265     serialize_ = true;
1266   }
1267   SupportedFsLocal local_fs;
1268   std::string array_name =
1269       local_fs.file_prefix() + local_fs.temp_dir() + "string_dims";
1270   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1271 
1272   create_array(
1273       ctx_,
1274       array_name,
1275       TILEDB_SPARSE,
1276       {"d"},
1277       {TILEDB_STRING_ASCII},
1278       {nullptr},
1279       {nullptr},
1280       {"a"},
1281       {TILEDB_INT32},
1282       {1},
1283       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
1284       TILEDB_ROW_MAJOR,
1285       TILEDB_ROW_MAJOR,
1286       2,
1287       false,
1288       false);
1289 
1290   // Open array
1291   tiledb_array_t* array;
1292   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
1293   CHECK(rc == TILEDB_OK);
1294   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
1295   CHECK(rc == TILEDB_OK);
1296 
1297   // Create and submit query
1298   tiledb_query_t* query;
1299   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
1300   REQUIRE(rc == TILEDB_OK);
1301 
1302   char d_data[] = "aabbbbdddd";
1303   uint64_t d_data_size = sizeof(d_data) - 1;  // Ignore '\0'
1304   uint64_t d_off[] = {0, 2, 4, 6};
1305   uint64_t d_off_size = sizeof(d_off);
1306   int32_t a_data[] = {1, 2, 3, 4};
1307   uint64_t a_size = sizeof(a_data);
1308   rc = tiledb_query_set_data_buffer(ctx_, query, "d", d_data, &d_data_size);
1309   REQUIRE(rc == TILEDB_OK);
1310   rc = tiledb_query_set_offsets_buffer(ctx_, query, "d", d_off, &d_off_size);
1311   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a_data, &a_size);
1312   REQUIRE(rc == TILEDB_OK);
1313   rc = tiledb_query_set_layout(ctx_, query, TILEDB_GLOBAL_ORDER);
1314   REQUIRE(rc == TILEDB_OK);
1315   rc = tiledb_query_submit_wrapper(ctx_, query, array_name);
1316   REQUIRE(rc == TILEDB_ERR);
1317 
1318   // Close array
1319   rc = tiledb_array_close(ctx_, array);
1320   CHECK(rc == TILEDB_OK);
1321 
1322   // Clean up
1323   tiledb_array_free(&array);
1324   tiledb_query_free(&query);
1325 
1326   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1327 }
1328 
1329 TEST_CASE_METHOD(
1330     StringDimsFx,
1331     "C API: Test sparse array with string dimensions, check duplicates, "
1332     "unordered",
1333     "[capi][sparse][string-dims][duplicates][unordered]") {
1334   SECTION("- No serialization") {
1335     serialize_ = false;
1336   }
1337   SECTION("- Serialization") {
1338     serialize_ = true;
1339   }
1340   SupportedFsLocal local_fs;
1341   std::string array_name =
1342       local_fs.file_prefix() + local_fs.temp_dir() + "string_dims";
1343   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1344 
1345   create_array(
1346       ctx_,
1347       array_name,
1348       TILEDB_SPARSE,
1349       {"d"},
1350       {TILEDB_STRING_ASCII},
1351       {nullptr},
1352       {nullptr},
1353       {"a"},
1354       {TILEDB_INT32},
1355       {1},
1356       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
1357       TILEDB_ROW_MAJOR,
1358       TILEDB_ROW_MAJOR,
1359       2,
1360       false,
1361       false);
1362 
1363   // Open array
1364   tiledb_array_t* array;
1365   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
1366   CHECK(rc == TILEDB_OK);
1367   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
1368   CHECK(rc == TILEDB_OK);
1369 
1370   // Create and submit query
1371   tiledb_query_t* query;
1372   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
1373   REQUIRE(rc == TILEDB_OK);
1374 
1375   char d_data[] = "ddddbbaabb";
1376   uint64_t d_data_size = sizeof(d_data) - 1;  // Ignore '\0'
1377   uint64_t d_off[] = {0, 4, 6, 8};
1378   uint64_t d_off_size = sizeof(d_off);
1379   int32_t a_data[] = {1, 2, 3, 4};
1380   uint64_t a_size = sizeof(a_data);
1381   rc = tiledb_query_set_data_buffer(ctx_, query, "d", d_data, &d_data_size);
1382   REQUIRE(rc == TILEDB_OK);
1383   rc = tiledb_query_set_offsets_buffer(ctx_, query, "d", d_off, &d_off_size);
1384   REQUIRE(rc == TILEDB_OK);
1385   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a_data, &a_size);
1386   REQUIRE(rc == TILEDB_OK);
1387   rc = tiledb_query_set_layout(ctx_, query, TILEDB_UNORDERED);
1388   REQUIRE(rc == TILEDB_OK);
1389   rc = tiledb_query_submit_wrapper(ctx_, query, array_name);
1390   REQUIRE(rc == TILEDB_ERR);
1391 
1392   // Close array
1393   rc = tiledb_array_close(ctx_, array);
1394   CHECK(rc == TILEDB_OK);
1395 
1396   // Clean up
1397   tiledb_array_free(&array);
1398   tiledb_query_free(&query);
1399 
1400   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1401 }
1402 
1403 TEST_CASE_METHOD(
1404     StringDimsFx,
1405     "C API: Test sparse array with string dimensions, check global order "
1406     "violation",
1407     "[capi][sparse][string-dims][global-order][violation]") {
1408   SECTION("- No serialization") {
1409     serialize_ = false;
1410   }
1411   SECTION("- Serialization") {
1412     serialize_ = true;
1413   }
1414   SupportedFsLocal local_fs;
1415   std::string array_name =
1416       local_fs.file_prefix() + local_fs.temp_dir() + "string_dims";
1417   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1418 
1419   create_array(
1420       ctx_,
1421       array_name,
1422       TILEDB_SPARSE,
1423       {"d"},
1424       {TILEDB_STRING_ASCII},
1425       {nullptr},
1426       {nullptr},
1427       {"a"},
1428       {TILEDB_INT32},
1429       {1},
1430       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
1431       TILEDB_ROW_MAJOR,
1432       TILEDB_ROW_MAJOR,
1433       2,
1434       false,
1435       false);
1436 
1437   // Open array
1438   tiledb_array_t* array;
1439   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
1440   CHECK(rc == TILEDB_OK);
1441   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
1442   CHECK(rc == TILEDB_OK);
1443 
1444   // Create and submit query
1445   tiledb_query_t* query;
1446   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
1447   REQUIRE(rc == TILEDB_OK);
1448 
1449   char d_data[] = "ddddbbbcaa";
1450   uint64_t d_data_size = sizeof(d_data) - 1;  // Ignore '\0'
1451   uint64_t d_off[] = {0, 4, 6, 8};
1452   uint64_t d_off_size = sizeof(d_off);
1453   int32_t a_data[] = {1, 2, 3, 4};
1454   uint64_t a_size = sizeof(a_data);
1455   rc = tiledb_query_set_data_buffer(ctx_, query, "d", d_data, &d_data_size);
1456   REQUIRE(rc == TILEDB_OK);
1457   rc = tiledb_query_set_offsets_buffer(ctx_, query, "d", d_off, &d_off_size);
1458   REQUIRE(rc == TILEDB_OK);
1459   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a_data, &a_size);
1460   REQUIRE(rc == TILEDB_OK);
1461   rc = tiledb_query_set_layout(ctx_, query, TILEDB_GLOBAL_ORDER);
1462   REQUIRE(rc == TILEDB_OK);
1463   rc = tiledb_query_submit_wrapper(ctx_, query, array_name);
1464   REQUIRE(rc == TILEDB_ERR);
1465 
1466   // Close array
1467   rc = tiledb_array_close(ctx_, array);
1468   CHECK(rc == TILEDB_OK);
1469 
1470   // Clean up
1471   tiledb_array_free(&array);
1472   tiledb_query_free(&query);
1473 
1474   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1475 }
1476 
1477 TEST_CASE_METHOD(
1478     StringDimsFx,
1479     "C API: Test sparse array with string dimensions, errors",
1480     "[capi][sparse][string-dims][errors]") {
1481   SECTION("- No serialization") {
1482     serialize_ = false;
1483   }
1484   SECTION("- Serialization") {
1485     serialize_ = true;
1486   }
1487   SupportedFsLocal local_fs;
1488   std::string array_name =
1489       local_fs.file_prefix() + local_fs.temp_dir() + "string_dims";
1490   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1491 
1492   // Create array
1493   create_array(
1494       ctx_,
1495       array_name,
1496       TILEDB_SPARSE,
1497       {"d"},
1498       {TILEDB_STRING_ASCII},
1499       {nullptr},
1500       {nullptr},
1501       {"a"},
1502       {TILEDB_INT32},
1503       {1},
1504       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
1505       TILEDB_ROW_MAJOR,
1506       TILEDB_ROW_MAJOR,
1507       2,
1508       false,
1509       false);
1510 
1511   // ####### WRITE #######
1512 
1513   // Open array
1514   tiledb_array_t* array;
1515   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
1516   CHECK(rc == TILEDB_OK);
1517   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
1518   CHECK(rc == TILEDB_OK);
1519 
1520   // Create and submit query
1521   tiledb_query_t* query;
1522   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
1523   REQUIRE(rc == TILEDB_OK);
1524 
1525   char d_data[] = "ccbbddddaa";
1526   uint64_t d_data_size = sizeof(d_data) - 1;  // Ignore '\0'
1527   uint64_t d_off[] = {0, 2, 4, 8};
1528   uint64_t d_off_size = sizeof(d_off);
1529   int32_t a_data[] = {3, 2, 4, 1};
1530   uint64_t a_size = sizeof(a_data);
1531   rc = tiledb_query_set_data_buffer(ctx_, query, "d", d_data, &d_data_size);
1532   REQUIRE(rc == TILEDB_OK);
1533   rc = tiledb_query_set_offsets_buffer(ctx_, query, "d", d_off, &d_off_size);
1534   REQUIRE(rc == TILEDB_OK);
1535   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a_data, &a_size);
1536   REQUIRE(rc == TILEDB_OK);
1537   rc = tiledb_query_set_layout(ctx_, query, TILEDB_UNORDERED);
1538   REQUIRE(rc == TILEDB_OK);
1539   rc = tiledb_query_submit_wrapper(ctx_, query, array_name);
1540   REQUIRE(rc == TILEDB_OK);
1541 
1542   // Close array
1543   rc = tiledb_array_close(ctx_, array);
1544   CHECK(rc == TILEDB_OK);
1545 
1546   // Clean up
1547   tiledb_array_free(&array);
1548   tiledb_query_free(&query);
1549 
1550   // ####### CHECK ERRORS #######
1551   // Open array
1552   rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
1553   CHECK(rc == TILEDB_OK);
1554   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
1555   CHECK(rc == TILEDB_OK);
1556 
1557   // Get non-empty domain and array max buffer sizes
1558   int32_t dom[4];
1559   int32_t is_empty;
1560   uint64_t size = 1024;
1561   rc = tiledb_array_get_non_empty_domain(ctx_, array, dom, &is_empty);
1562   CHECK(rc == TILEDB_ERR);
1563 
1564   // Set subarray and buffer
1565   rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
1566   REQUIRE(rc == TILEDB_OK);
1567   rc = tiledb_query_set_subarray(ctx_, query, dom);
1568   REQUIRE(rc == TILEDB_ERR);
1569   int32_t buff[10];
1570   uint64_t buff_size = sizeof(buff);
1571   rc = tiledb_query_set_data_buffer(
1572       ctx_, query, TILEDB_COORDS, buff, &buff_size);
1573   REQUIRE(rc == TILEDB_ERR);
1574   int data[1];
1575   uint64_t data_size;
1576   rc = tiledb_query_set_data_buffer(ctx_, query, "d", data, &data_size);
1577   REQUIRE(rc == TILEDB_OK);
1578 
1579   // Get estimated buffer size
1580   rc = tiledb_query_get_est_result_size(ctx_, query, TILEDB_COORDS, &size);
1581   CHECK(rc == TILEDB_ERR);
1582   rc = tiledb_query_get_est_result_size(ctx_, query, "d", &size);
1583   CHECK(rc == TILEDB_ERR);
1584 
1585   // Close array
1586   rc = tiledb_array_close(ctx_, array);
1587   CHECK(rc == TILEDB_OK);
1588 
1589   // Clean up
1590   tiledb_array_free(&array);
1591   tiledb_query_free(&query);
1592 
1593   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1594 }
1595 
1596 TEST_CASE_METHOD(
1597     StringDimsFx,
1598     "C API: Test sparse array with string dimensions, 1d",
1599     "[capi][sparse][string-dims][1d][basic]") {
1600   SECTION("- No serialization") {
1601     serialize_ = false;
1602   }
1603   SECTION("- Serialization") {
1604     serialize_ = true;
1605   }
1606   SupportedFsLocal local_fs;
1607   std::string array_name =
1608       local_fs.file_prefix() + local_fs.temp_dir() + "string_dims";
1609   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1610 
1611   // Create array
1612   create_array(
1613       ctx_,
1614       array_name,
1615       TILEDB_SPARSE,
1616       {"d"},
1617       {TILEDB_STRING_ASCII},
1618       {nullptr},
1619       {nullptr},
1620       {"a"},
1621       {TILEDB_INT32},
1622       {1},
1623       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
1624       TILEDB_ROW_MAJOR,
1625       TILEDB_ROW_MAJOR,
1626       2,
1627       false,
1628       false);
1629 
1630   // Write
1631   std::vector<uint64_t> d_off = {0, 2, 4, 8};
1632   std::string d_val("ccbbddddaa");
1633   std::vector<int32_t> a = {3, 2, 4, 1};
1634   write_array_1d(ctx_, array_name, TILEDB_UNORDERED, d_off, d_val, a);
1635 
1636   // ####### READ #######
1637 
1638   // Open array
1639   tiledb_array_t* array;
1640   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
1641   CHECK(rc == TILEDB_OK);
1642   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
1643   CHECK(rc == TILEDB_OK);
1644 
1645   // Check proper errors for getting non-empty domain
1646   char dom[100];
1647   int32_t is_empty = false;
1648   uint64_t start_size, end_size;
1649   rc = tiledb_array_get_non_empty_domain_from_index(
1650       ctx_, array, 0, dom, &is_empty);
1651   CHECK(rc == TILEDB_ERR);
1652   rc = tiledb_array_get_non_empty_domain_from_name(
1653       ctx_, array, "d", dom, &is_empty);
1654   CHECK(rc == TILEDB_ERR);
1655   rc = tiledb_array_get_non_empty_domain_var_size_from_index(
1656       ctx_, array, 2, &start_size, &end_size, &is_empty);
1657   CHECK(rc == TILEDB_ERR);
1658   rc = tiledb_array_get_non_empty_domain_var_size_from_name(
1659       ctx_, array, "foo", &start_size, &end_size, &is_empty);
1660   CHECK(rc == TILEDB_ERR);
1661 
1662   std::string start, end;
1663   get_non_empty_domain_var(array_name, "d", &start, &end, &is_empty);
1664   CHECK(is_empty == 0);
1665   CHECK(start == "aa");
1666   CHECK(end == "dddd");
1667 
1668   // Create query
1669   tiledb_query_t* query;
1670   rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
1671   CHECK(rc == TILEDB_OK);
1672   char s1[] = "a";
1673   char s2[] = "ee";
1674 
1675   // Check we can add empty ranges
1676   rc = tiledb_query_add_range_var(ctx_, query, 0, s1, 0, s2, 2);
1677   CHECK(rc == TILEDB_OK);
1678   rc = tiledb_query_add_range_var(ctx_, query, 0, s1, 1, s2, 0);
1679   CHECK(rc == TILEDB_OK);
1680   rc = tiledb_query_add_range_var(ctx_, query, 0, nullptr, 0, s2, 2);
1681   CHECK(rc == TILEDB_OK);
1682   rc = tiledb_query_add_range_var(ctx_, query, 0, s1, 1, nullptr, 0);
1683   CHECK(rc == TILEDB_OK);
1684 
1685   // Clean query and re-alloc
1686   tiledb_query_free(&query);
1687   rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
1688   CHECK(rc == TILEDB_OK);
1689 
1690   // Check errors when adding range
1691   rc = tiledb_query_add_range(ctx_, query, 0, s1, s2, nullptr);
1692   CHECK(rc == TILEDB_ERR);
1693   rc = tiledb_query_add_range_var(ctx_, query, 1, s1, 1, s2, 2);
1694   CHECK(rc == TILEDB_ERR);
1695   rc = tiledb_query_add_range_var(ctx_, query, 0, nullptr, 1, s2, 2);
1696   CHECK(rc == TILEDB_ERR);
1697   rc = tiledb_query_add_range_var(ctx_, query, 0, s1, 1, nullptr, 2);
1698   CHECK(rc == TILEDB_ERR);
1699 
1700   // Add string range
1701   rc = tiledb_query_add_range_var(ctx_, query, 0, s1, 1, s2, 2);
1702   CHECK(rc == TILEDB_OK);
1703 
1704   // Check error on getting estimated result size
1705   uint64_t size_off = 0, size_val = 0;
1706   rc = tiledb_query_get_est_result_size(ctx_, query, "d", &size_off);
1707   CHECK(rc == TILEDB_ERR);
1708 
1709   // Get estimated result size
1710   rc = tiledb_query_get_est_result_size_var(
1711       ctx_, query, "d", &size_off, &size_val);
1712   CHECK(rc == TILEDB_OK);
1713   CHECK(size_off == 32);
1714   CHECK(size_val == 10);
1715 
1716   // Clean query
1717   tiledb_query_free(&query);
1718 
1719   // Set layout
1720   tiledb_layout_t layout = TILEDB_ROW_MAJOR;
1721   SECTION("Global order") {
1722     layout = TILEDB_GLOBAL_ORDER;
1723   }
1724   SECTION("Row-major") {
1725     layout = TILEDB_ROW_MAJOR;
1726   }
1727   SECTION("Col-major") {
1728     layout = TILEDB_COL_MAJOR;
1729   }
1730   SECTION("Unordered") {
1731     layout = TILEDB_UNORDERED;
1732   }
1733 
1734   // Read [a, ee]
1735   std::vector<uint64_t> r_d_off(10);
1736   std::string r_d_val;
1737   r_d_val.resize(20);
1738   std::vector<int32_t> r_a(10);
1739   tiledb_query_status_t status;
1740   read_array_1d(
1741       ctx_, array, layout, "a", "ee", &r_d_off, &r_d_val, &r_a, &status);
1742   CHECK(status == TILEDB_COMPLETED);
1743   CHECK(r_d_val == "aabbccdddd");
1744   std::vector<uint64_t> c_d_off = {0, 2, 4, 6};
1745   CHECK(r_d_off == c_d_off);
1746   std::vector<int32_t> c_a = {1, 2, 3, 4};
1747   CHECK(r_a == c_a);
1748 
1749   // Read [aab, cc]
1750   r_d_off.resize(10);
1751   r_d_val.resize(20);
1752   r_a.resize(10);
1753   read_array_1d(
1754       ctx_, array, layout, "aab", "cc", &r_d_off, &r_d_val, &r_a, &status);
1755   CHECK(status == TILEDB_COMPLETED);
1756   CHECK(r_d_val == "bbcc");
1757   c_d_off = {0, 2};
1758   CHECK(r_d_off == c_d_off);
1759   c_a = {2, 3};
1760   CHECK(r_a == c_a);
1761 
1762   // Read [aa, cc] - INCOMPLETE
1763   r_d_off.resize(2);
1764   r_d_val.resize(20);
1765   r_a.resize(10);
1766   read_array_1d(
1767       ctx_, array, layout, "aa", "cc", &r_d_off, &r_d_val, &r_a, &status);
1768   CHECK(status == TILEDB_INCOMPLETE);
1769   CHECK(r_d_val == "aabb");
1770   c_d_off = {0, 2};
1771   CHECK(r_d_off == c_d_off);
1772   c_a = {1, 2};
1773   CHECK(r_a == c_a);
1774 
1775   // Read [aa, cc] - INCOMPLETE, no result
1776   r_d_off.resize(1);
1777   r_d_val.resize(1);
1778   r_a.resize(10);
1779   read_array_1d(
1780       ctx_, array, layout, "aa", "bb", &r_d_off, &r_d_val, &r_a, &status);
1781   CHECK(status == TILEDB_INCOMPLETE);
1782   CHECK(r_d_val.size() == 0);
1783   CHECK(r_d_off.size() == 0);
1784   CHECK(r_a.size() == 0);
1785 
1786   // Close array
1787   rc = tiledb_array_close(ctx_, array);
1788   CHECK(rc == TILEDB_OK);
1789 
1790   // Clean up
1791   tiledb_array_free(&array);
1792 
1793   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1794 }
1795 
1796 TEST_CASE_METHOD(
1797     StringDimsFx,
1798     "C API: Test sparse array with string dimensions, 1d, consolidation",
1799     "[capi][sparse][string-dims][1d][consolidation]") {
1800   SECTION("- No serialization") {
1801     serialize_ = false;
1802   }
1803   SECTION("- Serialization") {
1804     serialize_ = true;
1805   }
1806   SupportedFsLocal local_fs;
1807   std::string array_name =
1808       local_fs.file_prefix() + local_fs.temp_dir() + "string_dims";
1809   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1810 
1811   // Create array
1812   create_array(
1813       ctx_,
1814       array_name,
1815       TILEDB_SPARSE,
1816       {"d"},
1817       {TILEDB_STRING_ASCII},
1818       {nullptr},
1819       {nullptr},
1820       {"a"},
1821       {TILEDB_INT32},
1822       {1},
1823       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
1824       TILEDB_ROW_MAJOR,
1825       TILEDB_ROW_MAJOR,
1826       2,
1827       false,
1828       false);
1829 
1830   // Write #1
1831   std::vector<uint64_t> d_off = {0, 2, 4, 8};
1832   std::string d_val("ccbbddddaa");
1833   std::vector<int32_t> a = {3, 2, 4, 1};
1834   write_array_1d(ctx_, array_name, TILEDB_UNORDERED, d_off, d_val, a);
1835 
1836   // Write #2
1837   d_off = {0, 1, 2};
1838   d_val = "abee";
1839   a = {5, 6, 7};
1840   write_array_1d(ctx_, array_name, TILEDB_GLOBAL_ORDER, d_off, d_val, a);
1841 
1842   // Open array
1843   tiledb_array_t* array;
1844   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
1845   CHECK(rc == TILEDB_OK);
1846   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
1847   CHECK(rc == TILEDB_OK);
1848 
1849   // Get non-empty domain
1850   std::string start, end;
1851   int32_t is_empty;
1852   get_non_empty_domain_var(array_name, "d", &start, &end, &is_empty);
1853   CHECK(is_empty == 0);
1854   CHECK(start == "a");
1855   CHECK(end == "ee");
1856 
1857   // Get estimated result size
1858   uint64_t size_off = 0, size_val = 0;
1859   get_est_result_size_var(array, 0, "d", "a", "ee", &size_off, &size_val);
1860   CHECK(size_off == 56);
1861   CHECK(size_val == 14);
1862 
1863   // Set layout
1864   tiledb_layout_t layout = TILEDB_ROW_MAJOR;
1865   SECTION("Global order") {
1866     layout = TILEDB_GLOBAL_ORDER;
1867   }
1868   SECTION("Row-major") {
1869     layout = TILEDB_ROW_MAJOR;
1870   }
1871   SECTION("Col-major") {
1872     layout = TILEDB_COL_MAJOR;
1873   }
1874   SECTION("Unordered") {
1875     layout = TILEDB_UNORDERED;
1876   }
1877 
1878   // Read [a, ee]
1879   std::vector<uint64_t> r_d_off(10);
1880   std::string r_d_val;
1881   r_d_val.resize(20);
1882   std::vector<int32_t> r_a(10);
1883   tiledb_query_status_t status;
1884   read_array_1d(
1885       ctx_, array, layout, "a", "ee", &r_d_off, &r_d_val, &r_a, &status);
1886   CHECK(status == TILEDB_COMPLETED);
1887   CHECK(r_d_val == "aaabbbccddddee");
1888   std::vector<uint64_t> c_d_off = {0, 1, 3, 4, 6, 8, 12};
1889   CHECK(r_d_off == c_d_off);
1890   std::vector<int32_t> c_a = {5, 1, 6, 2, 3, 4, 7};
1891   CHECK(r_a == c_a);
1892 
1893   // Close array
1894   rc = tiledb_array_close(ctx_, array);
1895   CHECK(rc == TILEDB_OK);
1896 
1897   // Check number of fragments
1898   get_num_struct dirs = {ctx_, vfs_, 0};
1899   rc = tiledb_vfs_ls(ctx_, vfs_, array_name.c_str(), &get_dir_num, &dirs);
1900   CHECK(rc == TILEDB_OK);
1901   CHECK(dirs.num == 2);
1902 
1903   // Consolidate
1904   rc = tiledb_array_consolidate(ctx_, array_name.c_str(), nullptr);
1905   CHECK(rc == TILEDB_OK);
1906   rc = tiledb_array_vacuum(ctx_, array_name.c_str(), nullptr);
1907 
1908   // Check number of fragments
1909   dirs = {ctx_, vfs_, 0};
1910   rc = tiledb_vfs_ls(ctx_, vfs_, array_name.c_str(), &get_dir_num, &dirs);
1911   CHECK(rc == TILEDB_OK);
1912   CHECK(dirs.num == 1);
1913 
1914   // Get non-empty domain
1915   start = "";
1916   end = "";
1917   get_non_empty_domain_var(array_name, "d", &start, &end, &is_empty);
1918   CHECK(is_empty == 0);
1919   CHECK(start == "a");
1920   CHECK(end == "ee");
1921 
1922   // Free array
1923   tiledb_array_free(&array);
1924 
1925   // Open array
1926   rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
1927   CHECK(rc == TILEDB_OK);
1928   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
1929   CHECK(rc == TILEDB_OK);
1930 
1931   // Read [a, ee]
1932   r_d_off.resize(10);
1933   r_d_val.resize(20);
1934   r_a.resize(10);
1935   read_array_1d(
1936       ctx_, array, layout, "a", "ee", &r_d_off, &r_d_val, &r_a, &status);
1937   CHECK(status == TILEDB_COMPLETED);
1938   CHECK(r_d_val == "aaabbbccddddee");
1939   CHECK(r_d_off == c_d_off);
1940   CHECK(r_a == c_a);
1941 
1942   // Close array
1943   rc = tiledb_array_close(ctx_, array);
1944   CHECK(rc == TILEDB_OK);
1945 
1946   // Clean up
1947   tiledb_array_free(&array);
1948 
1949   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1950 }
1951 
1952 TEST_CASE_METHOD(
1953     StringDimsFx,
1954     "C API: Test sparse array with string dimensions, 1d, allow duplicates",
1955     "[capi][sparse][string-dims][1d][allow-dups]") {
1956   SECTION("- No serialization") {
1957     serialize_ = false;
1958   }
1959   SECTION("- Serialization") {
1960     serialize_ = true;
1961   }
1962   SupportedFsLocal local_fs;
1963   std::string array_name =
1964       local_fs.file_prefix() + local_fs.temp_dir() + "string_dims";
1965   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
1966 
1967   // Create array
1968   create_array(
1969       ctx_,
1970       array_name,
1971       TILEDB_SPARSE,
1972       {"d"},
1973       {TILEDB_STRING_ASCII},
1974       {nullptr},
1975       {nullptr},
1976       {"a"},
1977       {TILEDB_INT32},
1978       {1},
1979       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
1980       TILEDB_ROW_MAJOR,
1981       TILEDB_ROW_MAJOR,
1982       2,
1983       true,
1984       false);
1985 
1986   // Write
1987   std::vector<uint64_t> d_off = {0, 2, 4, 8};
1988   std::string d_val("ccccddddaa");
1989   std::vector<int32_t> a = {2, 3, 4, 1};
1990   write_array_1d(ctx_, array_name, TILEDB_UNORDERED, d_off, d_val, a);
1991 
1992   // ####### READ #######
1993 
1994   // Open array
1995   tiledb_array_t* array;
1996   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
1997   CHECK(rc == TILEDB_OK);
1998   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
1999   CHECK(rc == TILEDB_OK);
2000 
2001   // Check non-empty domain
2002   std::string start, end;
2003   int32_t is_empty;
2004   get_non_empty_domain_var(array_name, "d", &start, &end, &is_empty);
2005   CHECK(is_empty == 0);
2006   CHECK(start == "aa");
2007   CHECK(end == "dddd");
2008 
2009   // Set layout
2010   tiledb_layout_t layout = TILEDB_ROW_MAJOR;
2011   SECTION("Global order") {
2012     layout = TILEDB_GLOBAL_ORDER;
2013   }
2014   SECTION("Row-major") {
2015     layout = TILEDB_ROW_MAJOR;
2016   }
2017   SECTION("Col-major") {
2018     layout = TILEDB_COL_MAJOR;
2019   }
2020   SECTION("Unordered") {
2021     layout = TILEDB_UNORDERED;
2022   }
2023 
2024   // Read [a, e]
2025   std::vector<uint64_t> r_d_off(10);
2026   std::string r_d_val;
2027   r_d_val.resize(20);
2028   std::vector<int32_t> r_a(10);
2029   tiledb_query_status_t status;
2030   read_array_1d(
2031       ctx_, array, layout, "a", "e", &r_d_off, &r_d_val, &r_a, &status);
2032   CHECK(status == TILEDB_COMPLETED);
2033   CHECK(r_d_val == "aaccccdddd");
2034   std::vector<uint64_t> c_d_off = {0, 2, 4, 6};
2035   CHECK(r_d_off == c_d_off);
2036   // The ordering of 'a' is undefined for duplicate dimension
2037   // elements. Check both for dimension element "c".
2038   std::vector<int32_t> c_a_1 = {1, 3, 2, 4};
2039   std::vector<int32_t> c_a_2 = {1, 2, 3, 4};
2040   const bool c_a_matches = r_a == c_a_1 || r_a == c_a_2;
2041   CHECK(c_a_matches);
2042 
2043   // Close array
2044   rc = tiledb_array_close(ctx_, array);
2045   CHECK(rc == TILEDB_OK);
2046 
2047   // Clean up
2048   tiledb_array_free(&array);
2049 
2050   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
2051 }
2052 
2053 TEST_CASE_METHOD(
2054     StringDimsFx,
2055     "C API: Test sparse array with string dimensions, 1d, dedup",
2056     "[capi][sparse][string-dims][1d][dedup]") {
2057   SECTION("- No serialization") {
2058     serialize_ = false;
2059   }
2060   SECTION("- Serialization") {
2061     serialize_ = true;
2062   }
2063   SupportedFsLocal local_fs;
2064   std::string array_name =
2065       local_fs.file_prefix() + local_fs.temp_dir() + "string_dims";
2066   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
2067 
2068   // Create array
2069   create_array(
2070       ctx_,
2071       array_name,
2072       TILEDB_SPARSE,
2073       {"d"},
2074       {TILEDB_STRING_ASCII},
2075       {nullptr},
2076       {nullptr},
2077       {"a"},
2078       {TILEDB_INT32},
2079       {1},
2080       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
2081       TILEDB_ROW_MAJOR,
2082       TILEDB_ROW_MAJOR,
2083       2,
2084       false,
2085       false);
2086 
2087   // Create config
2088   tiledb_config_t* config = nullptr;
2089   tiledb_error_t* error = nullptr;
2090   int rc = tiledb_config_alloc(&config, &error);
2091   CHECK(rc == TILEDB_OK);
2092   CHECK(error == nullptr);
2093   rc = tiledb_config_set(config, "sm.dedup_coords", "true", &error);
2094   CHECK(rc == TILEDB_OK);
2095 
2096   // Create context
2097   tiledb_ctx_t* ctx;
2098   rc = tiledb_ctx_alloc(config, &ctx);
2099   CHECK(rc == TILEDB_OK);
2100 
2101   // Write
2102   std::vector<uint64_t> d_off = {0, 2, 4, 8};
2103   std::string d_val("ccccddddaa");
2104   std::vector<int32_t> a = {2, 3, 4, 1};
2105   write_array_1d(ctx, array_name, TILEDB_UNORDERED, d_off, d_val, a);
2106 
2107   // Clean up
2108   tiledb_config_free(&config);
2109 
2110   // ####### READ #######
2111 
2112   // Open array
2113   tiledb_array_t* array;
2114   rc = tiledb_array_alloc(ctx, array_name.c_str(), &array);
2115   CHECK(rc == TILEDB_OK);
2116   rc = tiledb_array_open(ctx, array, TILEDB_READ);
2117   CHECK(rc == TILEDB_OK);
2118 
2119   // Check non-empty domain
2120   std::string start, end;
2121   int32_t is_empty;
2122   get_non_empty_domain_var(array_name, "d", &start, &end, &is_empty);
2123   CHECK(is_empty == 0);
2124   CHECK(start == "aa");
2125   CHECK(end == "dddd");
2126 
2127   // Set layout
2128   tiledb_layout_t layout = TILEDB_ROW_MAJOR;
2129   SECTION("Global order") {
2130     layout = TILEDB_GLOBAL_ORDER;
2131   }
2132   SECTION("Row-major") {
2133     layout = TILEDB_ROW_MAJOR;
2134   }
2135   SECTION("Col-major") {
2136     layout = TILEDB_COL_MAJOR;
2137   }
2138   SECTION("Unordered") {
2139     layout = TILEDB_UNORDERED;
2140   }
2141 
2142   // Read [a, e]
2143   std::vector<uint64_t> r_d_off(10);
2144   std::string r_d_val;
2145   r_d_val.resize(20);
2146   std::vector<int32_t> r_a(10);
2147   tiledb_query_status_t status;
2148   read_array_1d(
2149       ctx, array, layout, "a", "e", &r_d_off, &r_d_val, &r_a, &status);
2150   CHECK(status == TILEDB_COMPLETED);
2151   CHECK(r_d_val == "aaccdddd");
2152   std::vector<uint64_t> c_d_off = {0, 2, 4};
2153   CHECK(r_d_off == c_d_off);
2154   // Either value for dimension index 'cc' may be de-duped.
2155   std::vector<int32_t> c_a_1 = {1, 2, 4};
2156   std::vector<int32_t> c_a_2 = {1, 3, 4};
2157   const bool c_a_matches = r_a == c_a_1 || r_a == c_a_2;
2158   CHECK(c_a_matches);
2159 
2160   // Close array
2161   rc = tiledb_array_close(ctx, array);
2162   CHECK(rc == TILEDB_OK);
2163 
2164   // Clean up
2165   tiledb_array_free(&array);
2166   tiledb_ctx_free(&ctx);
2167 
2168   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
2169 }
2170 
2171 TEST_CASE_METHOD(
2172     StringDimsFx,
2173     "C API: Test sparse array with string dimensions, 2d",
2174     "[capi][sparse][string-dims][2d]") {
2175   SECTION("- No serialization") {
2176     serialize_ = false;
2177   }
2178   SECTION("- Serialization") {
2179     serialize_ = true;
2180   }
2181   SupportedFsLocal local_fs;
2182   std::string array_name =
2183       local_fs.file_prefix() + local_fs.temp_dir() + "string_dims";
2184   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
2185 
2186   // Create array
2187   int32_t dom[] = {1, 10};
2188   int32_t extent = 5;
2189   create_array(
2190       ctx_,
2191       array_name,
2192       TILEDB_SPARSE,
2193       {"d1", "d2"},
2194       {TILEDB_STRING_ASCII, TILEDB_INT32},
2195       {nullptr, dom},
2196       {nullptr, &extent},
2197       {"a"},
2198       {TILEDB_INT32},
2199       {1},
2200       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
2201       TILEDB_ROW_MAJOR,
2202       TILEDB_ROW_MAJOR,
2203       2,
2204       false,
2205       false);
2206 
2207   // Write
2208   tiledb_layout_t write_layout = TILEDB_UNORDERED;
2209   SECTION("Unordered write") {
2210     write_layout = TILEDB_UNORDERED;
2211   }
2212   SECTION("Global write") {
2213     write_layout = TILEDB_GLOBAL_ORDER;
2214   }
2215   std::vector<uint64_t> d1_off = {0, 2, 4, 6};
2216   std::string d1_val("aabbccdddd");
2217   std::vector<int32_t> d2 = {1, 2, 3, 4};
2218   std::vector<int32_t> a = {11, 12, 13, 14};
2219   write_array_2d(ctx_, array_name, write_layout, d1_off, d1_val, d2, a);
2220 
2221   // ####### READ #######
2222 
2223   // Check non-empty domain
2224   std::string start, end;
2225   int32_t is_empty;
2226   get_non_empty_domain_var(array_name, "d1", &start, &end, &is_empty);
2227   CHECK(is_empty == 0);
2228   CHECK(start == "aa");
2229   CHECK(end == "dddd");
2230   std::vector<int32_t> non_empty;
2231   get_non_empty_domain(array_name, "d2", &non_empty, &is_empty);
2232   CHECK(is_empty == 0);
2233   CHECK(non_empty[0] == 1);
2234   CHECK(non_empty[1] == 4);
2235 
2236   // Open array
2237   tiledb_array_t* array;
2238   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
2239   CHECK(rc == TILEDB_OK);
2240   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
2241   CHECK(rc == TILEDB_OK);
2242 
2243   // Set layout
2244   tiledb_layout_t layout = TILEDB_ROW_MAJOR;
2245 
2246   SECTION("Global order") {
2247     layout = TILEDB_GLOBAL_ORDER;
2248   }
2249   SECTION("Row-major read") {
2250     layout = TILEDB_ROW_MAJOR;
2251   }
2252   SECTION("Col-major") {
2253     layout = TILEDB_COL_MAJOR;
2254   }
2255   SECTION("Unordered") {
2256     layout = TILEDB_UNORDERED;
2257   }
2258 
2259   // Read [a, e], [1, 10]
2260   std::vector<uint64_t> r_d1_off(10);
2261   std::string r_d1_val;
2262   r_d1_val.resize(20);
2263   std::vector<int32_t> r_d2(10);
2264   std::vector<int32_t> r_a(10);
2265   tiledb_query_status_t status;
2266   read_array_2d(
2267       ctx_,
2268       array,
2269       layout,
2270       "a",
2271       "e",
2272       1,
2273       10,
2274       &r_d1_off,
2275       &r_d1_val,
2276       &r_d2,
2277       &r_a,
2278       &status);
2279   CHECK(status == TILEDB_COMPLETED);
2280   CHECK(r_d1_val == "aabbccdddd");
2281   std::vector<uint64_t> c_d1_off = {0, 2, 4, 6};
2282   CHECK(r_d1_off == c_d1_off);
2283   std::vector<int32_t> c_d2 = {1, 2, 3, 4};
2284   CHECK(r_d2 == c_d2);
2285   std::vector<int32_t> c_a = {11, 12, 13, 14};
2286   CHECK(r_a == c_a);
2287 
2288   // Read [a, cc], [2, 3]
2289   r_d1_off.resize(10);
2290   r_d1_val.resize(20);
2291   r_d2.resize(10);
2292   r_a.resize(10);
2293   read_array_2d(
2294       ctx_,
2295       array,
2296       layout,
2297       "a",
2298       "cc",
2299       2,
2300       3,
2301       &r_d1_off,
2302       &r_d1_val,
2303       &r_d2,
2304       &r_a,
2305       &status);
2306   CHECK(status == TILEDB_COMPLETED);
2307   CHECK(r_d1_val == "bbcc");
2308   c_d1_off = {0, 2};
2309   CHECK(r_d1_off == c_d1_off);
2310   c_d2 = {2, 3};
2311   CHECK(r_d2 == c_d2);
2312   c_a = {12, 13};
2313   CHECK(r_a == c_a);
2314 
2315   // Close array
2316   rc = tiledb_array_close(ctx_, array);
2317   CHECK(rc == TILEDB_OK);
2318 
2319   std::this_thread::sleep_for(std::chrono::milliseconds(1));
2320 
2321   // Write again
2322   d1_off = {0, 1, 2};
2323   d1_val = "abff";
2324   d2 = {2, 2, 3};
2325   a = {15, 16, 17};
2326   write_array_2d(ctx_, array_name, write_layout, d1_off, d1_val, d2, a);
2327 
2328   std::this_thread::sleep_for(std::chrono::milliseconds(1));
2329 
2330   // Create config
2331   tiledb_config_t* config = nullptr;
2332   tiledb_error_t* error = nullptr;
2333   rc = tiledb_config_alloc(&config, &error);
2334   CHECK(rc == TILEDB_OK);
2335   CHECK(error == nullptr);
2336   rc = tiledb_config_set(
2337       config, "sm.consolidation.mode", "fragment_meta", &error);
2338   CHECK(rc == TILEDB_OK);
2339 
2340   // Consolidate fragment metadata
2341   rc = tiledb_array_consolidate(ctx_, array_name.c_str(), config);
2342   CHECK(rc == TILEDB_OK);
2343   tiledb_array_free(&array);
2344 
2345   // Open array
2346   rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
2347   CHECK(rc == TILEDB_OK);
2348   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
2349   CHECK(rc == TILEDB_OK);
2350 
2351   // Read [a, ff], [1, 10]
2352   r_d1_off.resize(20);
2353   r_d1_val.resize(20);
2354   r_d2.resize(20);
2355   r_a.resize(20);
2356   read_array_2d(
2357       ctx_,
2358       array,
2359       TILEDB_GLOBAL_ORDER,
2360       "a",
2361       "ff",
2362       1,
2363       10,
2364       &r_d1_off,
2365       &r_d1_val,
2366       &r_d2,
2367       &r_a,
2368       &status);
2369   CHECK(status == TILEDB_COMPLETED);
2370   CHECK(r_d1_val == "aaabbbccddddff");
2371   c_d1_off = {0, 1, 3, 4, 6, 8, 12};
2372   CHECK(r_d1_off == c_d1_off);
2373   c_d2 = {2, 1, 2, 2, 3, 4, 3};
2374   CHECK(r_d2 == c_d2);
2375   c_a = {15, 11, 16, 12, 13, 14, 17};
2376   CHECK(r_a == c_a);
2377 
2378   // Close array
2379   rc = tiledb_array_close(ctx_, array);
2380   CHECK(rc == TILEDB_OK);
2381   tiledb_array_free(&array);
2382 
2383   // Consolidate
2384   rc = tiledb_array_consolidate(ctx_, array_name.c_str(), nullptr);
2385   CHECK(rc == TILEDB_OK);
2386   rc = tiledb_array_vacuum(ctx_, array_name.c_str(), nullptr);
2387   CHECK(rc == TILEDB_OK);
2388 
2389   // Open array
2390   rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
2391   CHECK(rc == TILEDB_OK);
2392   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
2393   CHECK(rc == TILEDB_OK);
2394 
2395   // Read [a, ff], [1, 10]
2396   r_d1_off.resize(20);
2397   r_d1_val.resize(20);
2398   r_d2.resize(20);
2399   r_a.resize(20);
2400   read_array_2d(
2401       ctx_,
2402       array,
2403       TILEDB_GLOBAL_ORDER,
2404       "a",
2405       "ff",
2406       1,
2407       10,
2408       &r_d1_off,
2409       &r_d1_val,
2410       &r_d2,
2411       &r_a,
2412       &status);
2413   CHECK(status == TILEDB_COMPLETED);
2414   CHECK(r_d1_val == "aaabbbccddddff");
2415   c_d1_off = {0, 1, 3, 4, 6, 8, 12};
2416   CHECK(r_d1_off == c_d1_off);
2417   c_d2 = {2, 1, 2, 2, 3, 4, 3};
2418   CHECK(r_d2 == c_d2);
2419   c_a = {15, 11, 16, 12, 13, 14, 17};
2420   CHECK(r_a == c_a);
2421 
2422   // Close array
2423   rc = tiledb_array_close(ctx_, array);
2424   CHECK(rc == TILEDB_OK);
2425 
2426   // Clean up
2427   tiledb_array_free(&array);
2428   tiledb_config_free(&config);
2429 
2430   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
2431 }