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