1 /**
2  * @file   unit-capi-sparse_real.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 real domains.
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/misc/utils.h"
43 
44 #include <iostream>
45 #include <sstream>
46 #include <thread>
47 
48 using namespace tiledb::test;
49 
50 struct SparseRealFx2 {
51   // TileDB context
52   tiledb_ctx_t* ctx_;
53   tiledb_vfs_t* vfs_;
54 
55   // Vector of supported filsystems
56   const std::vector<std::unique_ptr<SupportedFs>> fs_vec_;
57 
58   // Functions
59   SparseRealFx2();
60   ~SparseRealFx2();
61   void create_temp_dir(const std::string& path);
62   void remove_temp_dir(const std::string& path);
63   void create_sparse_array(const std::string& path);
64   void write_sparse_array(const std::string& path);
65   void write_sparse_array_next_partition_bug(const std::string& path);
66   void read_sparse_array(const std::string& path);
67   void read_sparse_array_next_partition_bug(const std::string& path);
68   static std::string random_name(const std::string& prefix);
69 };
70 
SparseRealFx2()71 SparseRealFx2::SparseRealFx2()
72     : fs_vec_(vfs_test_get_fs_vec()) {
73   // Initialize vfs test
74   REQUIRE(vfs_test_init(fs_vec_, &ctx_, &vfs_).ok());
75 }
76 
~SparseRealFx2()77 SparseRealFx2::~SparseRealFx2() {
78   // Close vfs test
79   REQUIRE(vfs_test_close(fs_vec_, ctx_, vfs_).ok());
80   tiledb_vfs_free(&vfs_);
81   tiledb_ctx_free(&ctx_);
82 }
83 
create_temp_dir(const std::string & path)84 void SparseRealFx2::create_temp_dir(const std::string& path) {
85   remove_temp_dir(path);
86   REQUIRE(tiledb_vfs_create_dir(ctx_, vfs_, path.c_str()) == TILEDB_OK);
87 }
88 
remove_temp_dir(const std::string & path)89 void SparseRealFx2::remove_temp_dir(const std::string& path) {
90   int is_dir = 0;
91   REQUIRE(tiledb_vfs_is_dir(ctx_, vfs_, path.c_str(), &is_dir) == TILEDB_OK);
92   if (is_dir)
93     REQUIRE(tiledb_vfs_remove_dir(ctx_, vfs_, path.c_str()) == TILEDB_OK);
94 }
95 
random_name(const std::string & prefix)96 std::string SparseRealFx2::random_name(const std::string& prefix) {
97   std::stringstream ss;
98   ss << prefix << "-" << std::this_thread::get_id() << "-"
99      << TILEDB_TIMESTAMP_NOW_MS;
100   return ss.str();
101 }
102 
create_sparse_array(const std::string & path)103 void SparseRealFx2::create_sparse_array(const std::string& path) {
104   // Create dimensions
105   float dim_domain[] = {-180.0f, 180.0f, -90.0f, 90.0f};
106   float tile_extents[] = {10.1f, 10.1f};
107   tiledb_dimension_t* d1;
108   int rc = tiledb_dimension_alloc(
109       ctx_, "d1", TILEDB_FLOAT32, &dim_domain[0], &tile_extents[0], &d1);
110   CHECK(rc == TILEDB_OK);
111   tiledb_dimension_t* d2;
112   rc = tiledb_dimension_alloc(
113       ctx_, "d2", TILEDB_FLOAT32, &dim_domain[2], &tile_extents[1], &d2);
114   CHECK(rc == TILEDB_OK);
115 
116   // Create domain
117   tiledb_domain_t* domain;
118   rc = tiledb_domain_alloc(ctx_, &domain);
119   CHECK(rc == TILEDB_OK);
120   rc = tiledb_domain_add_dimension(ctx_, domain, d1);
121   CHECK(rc == TILEDB_OK);
122   rc = tiledb_domain_add_dimension(ctx_, domain, d2);
123   CHECK(rc == TILEDB_OK);
124 
125   // Create attributes
126   tiledb_attribute_t* a;
127   rc = tiledb_attribute_alloc(ctx_, "a", TILEDB_INT32, &a);
128   CHECK(rc == TILEDB_OK);
129   tiledb_filter_t* filter;
130   tiledb_filter_list_t* list;
131   rc = tiledb_filter_alloc(ctx_, TILEDB_FILTER_LZ4, &filter);
132   CHECK(rc == TILEDB_OK);
133   rc = tiledb_filter_list_alloc(ctx_, &list);
134   CHECK(rc == TILEDB_OK);
135   rc = tiledb_filter_list_add_filter(ctx_, list, filter);
136   CHECK(rc == TILEDB_OK);
137   rc = tiledb_attribute_set_filter_list(ctx_, a, list);
138   CHECK(rc == TILEDB_OK);
139 
140   // Create array schema
141   tiledb_array_schema_t* array_schema;
142   rc = tiledb_array_schema_alloc(ctx_, TILEDB_SPARSE, &array_schema);
143   CHECK(rc == TILEDB_OK);
144   rc = tiledb_array_schema_set_cell_order(ctx_, array_schema, TILEDB_ROW_MAJOR);
145   CHECK(rc == TILEDB_OK);
146   rc = tiledb_array_schema_set_tile_order(ctx_, array_schema, TILEDB_ROW_MAJOR);
147   CHECK(rc == TILEDB_OK);
148   rc = tiledb_array_schema_set_domain(ctx_, array_schema, domain);
149   CHECK(rc == TILEDB_OK);
150   rc = tiledb_array_schema_add_attribute(ctx_, array_schema, a);
151   CHECK(rc == TILEDB_OK);
152 
153   // Check array schema
154   rc = tiledb_array_schema_check(ctx_, array_schema);
155   CHECK(rc == TILEDB_OK);
156 
157   // Create array
158   rc = tiledb_array_create(ctx_, path.c_str(), array_schema);
159   CHECK(rc == TILEDB_OK);
160 
161   // Clean up
162   tiledb_filter_free(&filter);
163   tiledb_filter_list_free(&list);
164   tiledb_attribute_free(&a);
165   tiledb_dimension_free(&d1);
166   tiledb_dimension_free(&d2);
167   tiledb_domain_free(&domain);
168   tiledb_array_schema_free(&array_schema);
169 }
170 
write_sparse_array(const std::string & path)171 void SparseRealFx2::write_sparse_array(const std::string& path) {
172   // Open array
173   tiledb_array_t* array;
174   int rc = tiledb_array_alloc(ctx_, path.c_str(), &array);
175   CHECK(rc == TILEDB_OK);
176   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
177   CHECK(rc == TILEDB_OK);
178 
179   int a[] = {1, 2, 3, 4, 5};
180   uint64_t a_size = sizeof(a);
181   float coords_dim1[] = {-23.5f, 43.56f, 66.2f, -160.1f, 1.0f};
182   float coords_dim2[] = {-20.0f, 80.0f, -0.3f, 89.1f, 1.0f};
183   uint64_t coords_size = sizeof(coords_dim1);
184   tiledb_query_t* query;
185   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
186   REQUIRE(rc == TILEDB_OK);
187   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a, &a_size);
188   REQUIRE(rc == TILEDB_OK);
189   rc = tiledb_query_set_data_buffer(
190       ctx_, query, "d1", coords_dim1, &coords_size);
191   REQUIRE(rc == TILEDB_OK);
192   rc = tiledb_query_set_data_buffer(
193       ctx_, query, "d2", coords_dim2, &coords_size);
194   REQUIRE(rc == TILEDB_OK);
195   rc = tiledb_query_set_layout(ctx_, query, TILEDB_UNORDERED);
196   REQUIRE(rc == TILEDB_OK);
197   rc = tiledb_query_submit(ctx_, query);
198   REQUIRE(rc == TILEDB_OK);
199   rc = tiledb_query_finalize(ctx_, query);
200   REQUIRE(rc == TILEDB_OK);
201 
202   // Close array
203   rc = tiledb_array_close(ctx_, array);
204   CHECK(rc == TILEDB_OK);
205 
206   // Clean up
207   tiledb_array_free(&array);
208   tiledb_query_free(&query);
209 }
210 
write_sparse_array_next_partition_bug(const std::string & path)211 void SparseRealFx2::write_sparse_array_next_partition_bug(
212     const std::string& path) {
213   // Open array
214   tiledb_array_t* array;
215   int rc = tiledb_array_alloc(ctx_, path.c_str(), &array);
216   CHECK(rc == TILEDB_OK);
217   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
218   CHECK(rc == TILEDB_OK);
219 
220   int a[] = {1, 2};
221   uint64_t a_size = sizeof(a);
222   float coords_dim1[] = {-180.0f, -180.0f};
223   float coords_dim2[] = {1.0f, 2.0f};
224   uint64_t coords_size = sizeof(coords_dim1);
225   tiledb_query_t* query;
226   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
227   REQUIRE(rc == TILEDB_OK);
228   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a, &a_size);
229   REQUIRE(rc == TILEDB_OK);
230   rc = tiledb_query_set_data_buffer(
231       ctx_, query, "d1", coords_dim1, &coords_size);
232   REQUIRE(rc == TILEDB_OK);
233   rc = tiledb_query_set_data_buffer(
234       ctx_, query, "d2", coords_dim2, &coords_size);
235   REQUIRE(rc == TILEDB_OK);
236   rc = tiledb_query_set_layout(ctx_, query, TILEDB_UNORDERED);
237   REQUIRE(rc == TILEDB_OK);
238   rc = tiledb_query_submit(ctx_, query);
239   REQUIRE(rc == TILEDB_OK);
240   rc = tiledb_query_finalize(ctx_, query);
241   REQUIRE(rc == TILEDB_OK);
242 
243   // Close array
244   rc = tiledb_array_close(ctx_, array);
245   CHECK(rc == TILEDB_OK);
246 
247   // Clean up
248   tiledb_array_free(&array);
249   tiledb_query_free(&query);
250 }
251 
read_sparse_array(const std::string & path)252 void SparseRealFx2::read_sparse_array(const std::string& path) {
253   // Open array
254   tiledb_array_t* array;
255   int rc = tiledb_array_alloc(ctx_, path.c_str(), &array);
256   CHECK(rc == TILEDB_OK);
257   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
258   CHECK(rc == TILEDB_OK);
259 
260   int a[16];
261   uint64_t a_size = sizeof(a);
262   float coords_dim1[16];
263   float coords_dim2[16];
264   uint64_t coords_size = sizeof(coords_dim1);
265   tiledb_query_t* query;
266   rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
267   REQUIRE(rc == TILEDB_OK);
268 
269   // Create some subarray
270   float s0[] = {-180.0f, 180.0f};
271   float s1[] = {-90.0f, 90.0f};
272   rc = tiledb_query_set_layout(ctx_, query, TILEDB_ROW_MAJOR);
273   REQUIRE(rc == TILEDB_OK);
274   rc = tiledb_query_add_range(ctx_, query, 0, &s0[0], &s0[1], nullptr);
275   REQUIRE(rc == TILEDB_OK);
276   rc = tiledb_query_add_range(ctx_, query, 1, &s1[0], &s1[1], nullptr);
277   REQUIRE(rc == TILEDB_OK);
278 
279   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a, &a_size);
280   REQUIRE(rc == TILEDB_OK);
281   rc = tiledb_query_set_data_buffer(
282       ctx_, query, "d1", coords_dim1, &coords_size);
283   REQUIRE(rc == TILEDB_OK);
284   rc = tiledb_query_set_data_buffer(
285       ctx_, query, "d2", coords_dim2, &coords_size);
286   REQUIRE(rc == TILEDB_OK);
287   rc = tiledb_query_submit(ctx_, query);
288   REQUIRE(rc == TILEDB_OK);
289   rc = tiledb_query_finalize(ctx_, query);
290   REQUIRE(rc == TILEDB_OK);
291 
292   int a_c[] = {4, 1, 5, 2, 3};
293   float coords_c_dim1[] = {-160.1f, -23.5f, 1.0f, 43.56f, 66.2f};
294   float coords_c_dim2[] = {89.1f, -20.0f, 1.0f, 80.0f, -0.3f};
295   CHECK(a_size == sizeof(a_c));
296   CHECK(!memcmp(a, a_c, sizeof(a_c)));
297   CHECK(coords_size == sizeof(coords_c_dim1));
298   CHECK(!memcmp(coords_dim1, coords_c_dim1, sizeof(coords_c_dim1)));
299   CHECK(!memcmp(coords_dim2, coords_c_dim2, sizeof(coords_c_dim2)));
300 
301   // Close array
302   rc = tiledb_array_close(ctx_, array);
303   CHECK(rc == TILEDB_OK);
304 
305   // Clean up
306   tiledb_array_free(&array);
307   tiledb_query_free(&query);
308 }
309 
read_sparse_array_next_partition_bug(const std::string & path)310 void SparseRealFx2::read_sparse_array_next_partition_bug(
311     const std::string& path) {
312   // Open array
313   tiledb_array_t* array;
314   int rc = tiledb_array_alloc(ctx_, path.c_str(), &array);
315   CHECK(rc == TILEDB_OK);
316   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
317   CHECK(rc == TILEDB_OK);
318 
319   int a[1];
320   uint64_t a_size = sizeof(a);
321   float coords_dim1[4];
322   float coords_dim2[4];
323   uint64_t coords_size = sizeof(coords_dim1);
324   tiledb_query_t* query;
325   rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
326   REQUIRE(rc == TILEDB_OK);
327 
328   // Set some subarray
329   float s0[] = {-180.0f, 180.0f};
330   float s1[] = {-90.0f, 90.0f};
331   rc = tiledb_query_set_layout(ctx_, query, TILEDB_ROW_MAJOR);
332   REQUIRE(rc == TILEDB_OK);
333   rc = tiledb_query_add_range(ctx_, query, 0, &s0[0], &s0[1], nullptr);
334   REQUIRE(rc == TILEDB_OK);
335   rc = tiledb_query_add_range(ctx_, query, 1, &s1[0], &s1[1], nullptr);
336   REQUIRE(rc == TILEDB_OK);
337 
338   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a, &a_size);
339   REQUIRE(rc == TILEDB_OK);
340   rc = tiledb_query_set_data_buffer(
341       ctx_, query, "d1", coords_dim1, &coords_size);
342   REQUIRE(rc == TILEDB_OK);
343   rc = tiledb_query_set_data_buffer(
344       ctx_, query, "d2", coords_dim2, &coords_size);
345   REQUIRE(rc == TILEDB_OK);
346   rc = tiledb_query_submit(ctx_, query);
347   REQUIRE(rc == TILEDB_OK);
348 
349   CHECK(a_size == sizeof(int));
350   CHECK(a[0] == 1);
351   CHECK(coords_dim1[0] == -180.0f);
352   CHECK(coords_dim2[0] == 1.0f);
353 
354   // Close array
355   rc = tiledb_array_close(ctx_, array);
356   CHECK(rc == TILEDB_OK);
357 
358   // Clean up
359   tiledb_array_free(&array);
360   tiledb_query_free(&query);
361 }
362 
363 TEST_CASE_METHOD(
364     SparseRealFx2,
365     "C API: Test 2d sparse array with real domain 2",
366     "[capi][sparse-real-2]") {
367   SupportedFsLocal local_fs;
368   std::string vector_name =
369       local_fs.file_prefix() + local_fs.temp_dir() + "sparse_real";
370   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
371 
372   create_sparse_array(vector_name);
373   write_sparse_array(vector_name);
374   read_sparse_array(vector_name);
375 
376   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
377 }
378 
379 TEST_CASE_METHOD(
380     SparseRealFx2,
381     "C API: Test 2d sparse array with real domain, next subarray partition bug "
382     "2",
383     "[capi][sparse-real-2][sparse-real-next-partition-bug-2]") {
384   SupportedFsLocal local_fs;
385   std::string array_name = local_fs.file_prefix() + local_fs.temp_dir() +
386                            "sparse_real_next_partition_bug";
387   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
388 
389   create_sparse_array(array_name);
390   write_sparse_array_next_partition_bug(array_name);
391   read_sparse_array_next_partition_bug(array_name);
392 
393   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
394 }
395 
396 TEST_CASE_METHOD(
397     SparseRealFx2,
398     "C API: Test 2d sparse array with real domain, NaN in subarray 2",
399     "[capi][sparse-real-2][sparse-real-nan-subarray-2]") {
400   SupportedFsLocal local_fs;
401   std::string array_name =
402       local_fs.file_prefix() + local_fs.temp_dir() + "sparse_real_nan_subarray";
403   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
404 
405   create_sparse_array(array_name);
406   write_sparse_array(array_name);
407 
408   // Open array
409   tiledb_array_t* array;
410   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
411   CHECK(rc == TILEDB_OK);
412   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
413   CHECK(rc == TILEDB_OK);
414 
415   tiledb_query_t* query;
416   rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
417   REQUIRE(rc == TILEDB_OK);
418 
419   // Set config for `sm.read_range_oob` = `error`
420   tiledb_config_t* config = nullptr;
421   tiledb_error_t* error = nullptr;
422   REQUIRE(tiledb_config_alloc(&config, &error) == TILEDB_OK);
423   REQUIRE(error == nullptr);
424   rc = tiledb_config_set(config, "sm.read_range_oob", "error", &error);
425   REQUIRE(rc == TILEDB_OK);
426   REQUIRE(error == nullptr);
427   rc = tiledb_query_set_config(ctx_, query, config);
428   REQUIRE(rc == TILEDB_OK);
429 
430   // Create some subarray
431   float s0[] = {-180.0f, std::numeric_limits<float>::quiet_NaN()};
432   float s1[] = {-90.0f, std::numeric_limits<float>::infinity()};
433   rc = tiledb_query_add_range(ctx_, query, 0, &s0[0], &s0[1], nullptr);
434   REQUIRE(rc == TILEDB_ERR);
435   rc = tiledb_query_add_range(ctx_, query, 1, &s1[0], &s1[1], nullptr);
436   REQUIRE(rc == TILEDB_ERR);
437 
438   // Clean up
439   rc = tiledb_array_close(ctx_, array);
440   CHECK(rc == TILEDB_OK);
441   tiledb_array_free(&array);
442   tiledb_config_free(&config);
443   tiledb_query_free(&query);
444 
445   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
446 }
447 
448 TEST_CASE_METHOD(
449     SparseRealFx2,
450     "C API: Test 2d sparse array with real domain 2, unary range",
451     "[capi][sparse-real-2]") {
452   SupportedFsLocal local_fs;
453   std::string array_name =
454       local_fs.file_prefix() + local_fs.temp_dir() + "sparse_real_unary";
455   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
456   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
457   create_sparse_array(array_name);
458 
459   // Write twice (2 fragments)
460   write_sparse_array(array_name);
461   write_sparse_array(array_name);
462 
463   // Open array
464   tiledb_array_t* array;
465   int rc = tiledb_array_alloc(ctx_, array_name.c_str(), &array);
466   CHECK(rc == TILEDB_OK);
467   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
468   CHECK(rc == TILEDB_OK);
469 
470   int a[1];
471   uint64_t a_size = sizeof(a);
472   float coords_dim1[1];
473   float coords_dim2[1];
474   uint64_t coords_size = sizeof(coords_dim1);
475   tiledb_query_t* query;
476   rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
477   REQUIRE(rc == TILEDB_OK);
478 
479   // Set some subarray
480   float s0[] = {-23.5f, -23.5f};
481   float s1[] = {-20.0f, -20.0f};
482   rc = tiledb_query_set_layout(ctx_, query, TILEDB_ROW_MAJOR);
483   REQUIRE(rc == TILEDB_OK);
484   rc = tiledb_query_add_range(ctx_, query, 0, &s0[0], &s0[1], nullptr);
485   REQUIRE(rc == TILEDB_OK);
486   rc = tiledb_query_add_range(ctx_, query, 1, &s1[0], &s1[1], nullptr);
487   REQUIRE(rc == TILEDB_OK);
488 
489   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a, &a_size);
490   REQUIRE(rc == TILEDB_OK);
491   rc = tiledb_query_set_data_buffer(
492       ctx_, query, "d1", coords_dim1, &coords_size);
493   REQUIRE(rc == TILEDB_OK);
494   rc = tiledb_query_set_data_buffer(
495       ctx_, query, "d2", coords_dim2, &coords_size);
496   REQUIRE(rc == TILEDB_OK);
497   rc = tiledb_query_submit(ctx_, query);
498   REQUIRE(rc == TILEDB_OK);
499   rc = tiledb_query_finalize(ctx_, query);
500   REQUIRE(rc == TILEDB_OK);
501 
502   int a_c[] = {1};
503   float coords_c_dim1[] = {-23.5f};
504   float coords_c_dim2[] = {-20.0f};
505   CHECK(a_size == sizeof(a_c));
506   CHECK(!memcmp(a, a_c, sizeof(a_c)));
507   CHECK(coords_size == sizeof(coords_c_dim1));
508   CHECK(!memcmp(coords_dim1, coords_c_dim1, sizeof(coords_c_dim1)));
509   CHECK(!memcmp(coords_dim2, coords_c_dim2, sizeof(coords_c_dim2)));
510 
511   // Close array
512   rc = tiledb_array_close(ctx_, array);
513   CHECK(rc == TILEDB_OK);
514 
515   // Clean up
516   tiledb_array_free(&array);
517   tiledb_query_free(&query);
518 
519   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
520 }
521