1 /**
2 * @file unit-sparse-unordered-with-dups-reader.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 for the sparse unordered with duplicates reader.
31 */
32
33 #include "test/src/helpers.h"
34 #include "tiledb/sm/c_api/tiledb.h"
35
36 #ifdef _WIN32
37 #include "tiledb/sm/filesystem/win.h"
38 #else
39 #include "tiledb/sm/filesystem/posix.h"
40 #endif
41
42 #include <catch.hpp>
43
44 using namespace tiledb::sm;
45 using namespace tiledb::test;
46
47 /* ********************************* */
48 /* STRUCT DEFINITION */
49 /* ********************************* */
50
51 struct CSparseUnorderedWithDupsFx {
52 tiledb_ctx_t* ctx_ = nullptr;
53 tiledb_vfs_t* vfs_ = nullptr;
54 std::string temp_dir_;
55 std::string array_name_;
56 const char* ARRAY_NAME = "test_sparse_unordered_with_dups";
57 tiledb_array_t* array_ = nullptr;
58 std::string total_budget_;
59 std::string ratio_tile_ranges_;
60 std::string ratio_array_data_;
61 std::string ratio_result_tiles_;
62 std::string ratio_coords_;
63 std::string ratio_rcs_;
64 std::string ratio_query_condition_;
65
66 void create_default_array_1d();
67 void write_1d_fragment(
68 int* coords, uint64_t* coords_size, int* data, uint64_t* data_size);
69 int32_t read(
70 bool set_subarray,
71 bool set_qc,
72 int* coords,
73 uint64_t* coords_size,
74 int* data,
75 uint64_t* data_size,
76 tiledb_query_t** query = nullptr,
77 tiledb_array_t** array_ret = nullptr);
78 void reset_config();
79 void update_config();
80
81 CSparseUnorderedWithDupsFx();
82 ~CSparseUnorderedWithDupsFx();
83 };
84
CSparseUnorderedWithDupsFx()85 CSparseUnorderedWithDupsFx::CSparseUnorderedWithDupsFx() {
86 reset_config();
87
88 // Create temporary directory based on the supported filesystem.
89 #ifdef _WIN32
90 temp_dir_ = tiledb::sm::Win::current_dir() + "\\tiledb_test\\";
91 #else
92 temp_dir_ = "file://" + tiledb::sm::Posix::current_dir() + "/tiledb_test/";
93 #endif
94 create_dir(temp_dir_, ctx_, vfs_);
95 array_name_ = temp_dir_ + ARRAY_NAME;
96 }
97
~CSparseUnorderedWithDupsFx()98 CSparseUnorderedWithDupsFx::~CSparseUnorderedWithDupsFx() {
99 tiledb_array_free(&array_);
100 remove_dir(temp_dir_, ctx_, vfs_);
101 tiledb_ctx_free(&ctx_);
102 tiledb_vfs_free(&vfs_);
103 }
104
reset_config()105 void CSparseUnorderedWithDupsFx::reset_config() {
106 total_budget_ = "1048576";
107 ratio_tile_ranges_ = "0.1";
108 ratio_array_data_ = "0.1";
109 ratio_result_tiles_ = "0.05";
110 ratio_coords_ = "0.5";
111 ratio_rcs_ = "0.05";
112 ratio_query_condition_ = "0.25";
113 update_config();
114 }
115
update_config()116 void CSparseUnorderedWithDupsFx::update_config() {
117 if (ctx_ != nullptr)
118 tiledb_ctx_free(&ctx_);
119
120 if (vfs_ != nullptr)
121 tiledb_vfs_free(&vfs_);
122
123 tiledb_config_t* config;
124 tiledb_error_t* error = nullptr;
125 REQUIRE(tiledb_config_alloc(&config, &error) == TILEDB_OK);
126 REQUIRE(error == nullptr);
127
128 REQUIRE(
129 tiledb_config_set(
130 config,
131 "sm.query.sparse_unordered_with_dups.reader",
132 "refactored",
133 &error) == TILEDB_OK);
134 REQUIRE(error == nullptr);
135
136 REQUIRE(
137 tiledb_config_set(
138 config, "sm.mem.total_budget", total_budget_.c_str(), &error) ==
139 TILEDB_OK);
140 REQUIRE(error == nullptr);
141
142 REQUIRE(
143 tiledb_config_set(
144 config,
145 "sm.mem.reader.sparse_unordered_with_dups.ratio_tile_ranges",
146 ratio_tile_ranges_.c_str(),
147 &error) == TILEDB_OK);
148 REQUIRE(error == nullptr);
149
150 REQUIRE(
151 tiledb_config_set(
152 config,
153 "sm.mem.reader.sparse_unordered_with_dups.ratio_array_data",
154 ratio_array_data_.c_str(),
155 &error) == TILEDB_OK);
156 REQUIRE(error == nullptr);
157
158 REQUIRE(
159 tiledb_config_set(
160 config,
161 "sm.mem.reader.sparse_unordered_with_dups.ratio_result_tiles",
162 ratio_result_tiles_.c_str(),
163 &error) == TILEDB_OK);
164 REQUIRE(error == nullptr);
165
166 REQUIRE(
167 tiledb_config_set(
168 config,
169 "sm.mem.reader.sparse_unordered_with_dups.ratio_coords",
170 ratio_coords_.c_str(),
171 &error) == TILEDB_OK);
172 REQUIRE(error == nullptr);
173
174 REQUIRE(
175 tiledb_config_set(
176 config,
177 "sm.mem.reader.sparse_unordered_with_dups.ratio_rcs",
178 ratio_rcs_.c_str(),
179 &error) == TILEDB_OK);
180 REQUIRE(error == nullptr);
181
182 REQUIRE(
183 tiledb_config_set(
184 config,
185 "sm.mem.reader.sparse_unordered_with_dups.ratio_query_condition",
186 ratio_query_condition_.c_str(),
187 &error) == TILEDB_OK);
188 REQUIRE(error == nullptr);
189
190 REQUIRE(tiledb_ctx_alloc(config, &ctx_) == TILEDB_OK);
191 REQUIRE(error == nullptr);
192 REQUIRE(tiledb_vfs_alloc(ctx_, config, &vfs_) == TILEDB_OK);
193 tiledb_config_free(&config);
194 }
195
create_default_array_1d()196 void CSparseUnorderedWithDupsFx::create_default_array_1d() {
197 int domain[] = {1, 10};
198 int tile_extent = 2;
199 create_array(
200 ctx_,
201 array_name_,
202 TILEDB_SPARSE,
203 {"d"},
204 {TILEDB_INT32},
205 {domain},
206 {&tile_extent},
207 {"a"},
208 {TILEDB_INT32},
209 {1},
210 {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
211 TILEDB_ROW_MAJOR,
212 TILEDB_ROW_MAJOR,
213 2,
214 true); // allows dups.
215 }
216
write_1d_fragment(int * coords,uint64_t * coords_size,int * data,uint64_t * data_size)217 void CSparseUnorderedWithDupsFx::write_1d_fragment(
218 int* coords, uint64_t* coords_size, int* data, uint64_t* data_size) {
219 // Open array for writing.
220 tiledb_array_t* array;
221 auto rc = tiledb_array_alloc(ctx_, array_name_.c_str(), &array);
222 REQUIRE(rc == TILEDB_OK);
223 rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
224 REQUIRE(rc == TILEDB_OK);
225
226 // Create the query.
227 tiledb_query_t* query;
228 rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
229 REQUIRE(rc == TILEDB_OK);
230 rc = tiledb_query_set_layout(ctx_, query, TILEDB_UNORDERED);
231 REQUIRE(rc == TILEDB_OK);
232 rc = tiledb_query_set_data_buffer(ctx_, query, "a", data, data_size);
233 REQUIRE(rc == TILEDB_OK);
234 rc = tiledb_query_set_data_buffer(ctx_, query, "d", coords, coords_size);
235 REQUIRE(rc == TILEDB_OK);
236
237 // Submit query.
238 rc = tiledb_query_submit(ctx_, query);
239 REQUIRE(rc == TILEDB_OK);
240
241 // Close array.
242 rc = tiledb_array_close(ctx_, array);
243 REQUIRE(rc == TILEDB_OK);
244
245 // Clean up.
246 tiledb_array_free(&array);
247 tiledb_query_free(&query);
248 }
249
read(bool set_subarray,bool set_qc,int * coords,uint64_t * coords_size,int * data,uint64_t * data_size,tiledb_query_t ** query_ret,tiledb_array_t ** array_ret)250 int32_t CSparseUnorderedWithDupsFx::read(
251 bool set_subarray,
252 bool set_qc,
253 int* coords,
254 uint64_t* coords_size,
255 int* data,
256 uint64_t* data_size,
257 tiledb_query_t** query_ret,
258 tiledb_array_t** array_ret) {
259 // Open array for reading.
260 tiledb_array_t* array;
261 auto rc = tiledb_array_alloc(ctx_, array_name_.c_str(), &array);
262 CHECK(rc == TILEDB_OK);
263 rc = tiledb_array_open(ctx_, array, TILEDB_READ);
264 CHECK(rc == TILEDB_OK);
265
266 // Create query.
267 tiledb_query_t* query;
268 rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
269 CHECK(rc == TILEDB_OK);
270
271 if (set_subarray) {
272 // Set subarray.
273 int subarray[] = {1, 10};
274 rc = tiledb_query_set_subarray(ctx_, query, subarray);
275 CHECK(rc == TILEDB_OK);
276 }
277
278 if (set_qc) {
279 tiledb_query_condition_t* query_condition = nullptr;
280 rc = tiledb_query_condition_alloc(ctx_, &query_condition);
281 CHECK(rc == TILEDB_OK);
282 int32_t val = 10;
283 rc = tiledb_query_condition_init(
284 ctx_, query_condition, "a", &val, sizeof(int32_t), TILEDB_LT);
285 CHECK(rc == TILEDB_OK);
286
287 rc = tiledb_query_set_condition(ctx_, query, query_condition);
288 CHECK(rc == TILEDB_OK);
289
290 tiledb_query_condition_free(&query_condition);
291 }
292
293 rc = tiledb_query_set_layout(ctx_, query, TILEDB_UNORDERED);
294 CHECK(rc == TILEDB_OK);
295 rc = tiledb_query_set_data_buffer(ctx_, query, "a", data, data_size);
296 CHECK(rc == TILEDB_OK);
297 rc = tiledb_query_set_data_buffer(
298 ctx_, query, TILEDB_COORDS, coords, coords_size);
299 CHECK(rc == TILEDB_OK);
300
301 // Submit query.
302 auto ret = tiledb_query_submit(ctx_, query);
303
304 if (query_ret == nullptr || array_ret == nullptr) {
305 // Clean up.
306 rc = tiledb_array_close(ctx_, array);
307 CHECK(rc == TILEDB_OK);
308 tiledb_array_free(&array);
309 tiledb_query_free(&query);
310 } else {
311 *query_ret = query;
312 *array_ret = array;
313 }
314
315 return ret;
316 }
317
318 /* ********************************* */
319 /* TESTS */
320 /* ********************************* */
321
322 TEST_CASE_METHOD(
323 CSparseUnorderedWithDupsFx,
324 "Sparse unordered with dups reader: Tile ranges budget exceeded",
325 "[sparse-unordered-with-dups][tile-ranges][budget-exceeded]") {
326 // Create default array.
327 reset_config();
328 create_default_array_1d();
329
330 // Write a fragment.
331 int coords[] = {1, 2, 3, 4, 5};
332 uint64_t coords_size = sizeof(coords);
333 int data[] = {1, 2, 3, 4, 5};
334 uint64_t data_size = sizeof(data);
335 write_1d_fragment(coords, &coords_size, data, &data_size);
336
337 // We should have one tile range (size 16) which will be bigger than budget
338 // (10).
339 total_budget_ = "1000";
340 ratio_tile_ranges_ = "0.01";
341 update_config();
342
343 // Try to read.
344 int coords_r[5];
345 int data_r[5];
346 uint64_t coords_r_size = sizeof(coords_r);
347 uint64_t data_r_size = sizeof(data_r);
348 auto rc = read(true, false, coords_r, &coords_r_size, data_r, &data_r_size);
349 CHECK(rc == TILEDB_ERR);
350
351 // Check we hit the correct error.
352 tiledb_error_t* error = NULL;
353 rc = tiledb_ctx_get_last_error(ctx_, &error);
354 CHECK(rc == TILEDB_OK);
355
356 const char* msg;
357 rc = tiledb_error_message(error, &msg);
358 CHECK(rc == TILEDB_OK);
359
360 std::string error_str(msg);
361 CHECK(
362 error_str.find("Exceeded memory budget for result tile ranges") !=
363 std::string::npos);
364 }
365
366 TEST_CASE_METHOD(
367 CSparseUnorderedWithDupsFx,
368 "Sparse unordered with dups reader: tile offsets budget exceeded",
369 "[sparse-unordered-with-dups][tile-offsets][budget-exceeded]") {
370 // Create default array.
371 reset_config();
372 create_default_array_1d();
373
374 // Write a fragment.
375 int coords[] = {1, 2, 3, 4, 5};
376 uint64_t coords_size = sizeof(coords);
377 int data[] = {1, 2, 3, 4, 5};
378 uint64_t data_size = sizeof(data);
379 write_1d_fragment(coords, &coords_size, data, &data_size);
380
381 // We should have 3 tiles (tile offset size 24) which will be bigger than
382 // budget (10).
383 total_budget_ = "1000";
384 ratio_array_data_ = "0.01";
385 update_config();
386
387 // Try to read.
388 int coords_r[5];
389 int data_r[5];
390 uint64_t coords_r_size = sizeof(coords_r);
391 uint64_t data_r_size = sizeof(data_r);
392 auto rc = read(true, false, coords_r, &coords_r_size, data_r, &data_r_size);
393 CHECK(rc == TILEDB_ERR);
394
395 // Check we hit the correct error.
396 tiledb_error_t* error = NULL;
397 rc = tiledb_ctx_get_last_error(ctx_, &error);
398 CHECK(rc == TILEDB_OK);
399
400 const char* msg;
401 rc = tiledb_error_message(error, &msg);
402 CHECK(rc == TILEDB_OK);
403
404 std::string error_str(msg);
405 CHECK(error_str.find("Cannot load tile offsets") != std::string::npos);
406 }
407
408 TEST_CASE_METHOD(
409 CSparseUnorderedWithDupsFx,
410 "Sparse unordered with dups reader: result tiles budget forcing one tile "
411 "at a time",
412 "[sparse-unordered-with-dups][small-rt-budget]") {
413 // Create default array.
414 reset_config();
415 create_default_array_1d();
416
417 bool use_subarray = false;
418 SECTION("- No subarray") {
419 use_subarray = false;
420 }
421 SECTION("- Subarray") {
422 use_subarray = true;
423 }
424
425 // Write a fragment.
426 int coords[] = {1, 2, 3, 4, 5};
427 uint64_t coords_size = sizeof(coords);
428 int data[] = {1, 2, 3, 4, 5};
429 uint64_t data_size = sizeof(data);
430 write_1d_fragment(coords, &coords_size, data, &data_size);
431
432 // Two result tile (~880) will be bigger than the budget (500).
433 total_budget_ = "10000";
434 ratio_result_tiles_ = "0.05";
435 update_config();
436
437 tiledb_array_t* array = nullptr;
438 tiledb_query_t* query = nullptr;
439
440 // Try to read.
441 int coords_r[5];
442 int data_r[5];
443 uint64_t coords_r_size = sizeof(coords_r);
444 uint64_t data_r_size = sizeof(data_r);
445 auto rc = read(
446 use_subarray,
447 false,
448 coords_r,
449 &coords_r_size,
450 data_r,
451 &data_r_size,
452 &query,
453 &array);
454 CHECK(rc == TILEDB_OK);
455
456 // Check incomplete query status.
457 tiledb_query_status_t status;
458 tiledb_query_get_status(ctx_, query, &status);
459 CHECK(status == TILEDB_INCOMPLETE);
460
461 // Should only read one tile (2 values).
462 CHECK(8 == data_r_size);
463 CHECK(8 == coords_r_size);
464
465 int coords_c[] = {1, 2};
466 int data_c[] = {1, 2};
467 CHECK(!std::memcmp(coords_c, coords_r, coords_r_size));
468 CHECK(!std::memcmp(data_c, data_r, data_r_size));
469
470 // Read again.
471 rc = tiledb_query_submit(ctx_, query);
472 CHECK(rc == TILEDB_OK);
473
474 // Check incomplete query status.
475 tiledb_query_get_status(ctx_, query, &status);
476 CHECK(status == TILEDB_INCOMPLETE);
477
478 // Should only read one more tile (2 values).
479 CHECK(8 == data_r_size);
480 CHECK(8 == coords_r_size);
481
482 int coords_c_2[] = {3, 4};
483 int data_c_2[] = {3, 4};
484 CHECK(!std::memcmp(coords_c_2, coords_r, coords_r_size));
485 CHECK(!std::memcmp(data_c_2, data_r, data_r_size));
486
487 // Read again.
488 rc = tiledb_query_submit(ctx_, query);
489 CHECK(rc == TILEDB_OK);
490
491 // Check complete query status.
492 tiledb_query_get_status(ctx_, query, &status);
493 CHECK(status == TILEDB_COMPLETED);
494
495 // Should read last tile (1 value).
496 CHECK(4 == data_r_size);
497 CHECK(4 == coords_r_size);
498
499 int coords_c_3[] = {5};
500 int data_c_3[] = {5};
501 CHECK(!std::memcmp(coords_c_3, coords_r, coords_r_size));
502 CHECK(!std::memcmp(data_c_3, data_r, data_r_size));
503
504 // Clean up.
505 rc = tiledb_array_close(ctx_, array);
506 CHECK(rc == TILEDB_OK);
507 tiledb_array_free(&array);
508 tiledb_query_free(&query);
509 }
510
511 TEST_CASE_METHOD(
512 CSparseUnorderedWithDupsFx,
513 "Sparse unordered with dups reader: result tiles budget too small",
514 "[sparse-unordered-with-dups][rt-budget][too-small]") {
515 // Create default array.
516 reset_config();
517 create_default_array_1d();
518
519 bool use_subarray = false;
520 SECTION("- No subarray") {
521 use_subarray = false;
522 }
523 SECTION("- Subarray") {
524 use_subarray = true;
525 }
526
527 // Write a fragment.
528 int coords[] = {1, 2, 3, 4, 5};
529 uint64_t coords_size = sizeof(coords);
530 int data[] = {1, 2, 3, 4, 5};
531 uint64_t data_size = sizeof(data);
532 write_1d_fragment(coords, &coords_size, data, &data_size);
533
534 // One result tile will be bigger than the budget (10).
535 total_budget_ = "10000";
536 ratio_result_tiles_ = "0.001";
537 update_config();
538
539 // Try to read.
540 int coords_r[5];
541 int data_r[5];
542 uint64_t coords_r_size = sizeof(coords_r);
543 uint64_t data_r_size = sizeof(data_r);
544 auto rc =
545 read(use_subarray, false, coords_r, &coords_r_size, data_r, &data_r_size);
546 CHECK(rc == TILEDB_ERR);
547
548 // Check we hit the correct error.
549 tiledb_error_t* error = NULL;
550 rc = tiledb_ctx_get_last_error(ctx_, &error);
551 CHECK(rc == TILEDB_OK);
552
553 const char* msg;
554 rc = tiledb_error_message(error, &msg);
555 CHECK(rc == TILEDB_OK);
556
557 std::string error_str(msg);
558 CHECK(error_str.find("Cannot load a single tile") != std::string::npos);
559 }
560
561 TEST_CASE_METHOD(
562 CSparseUnorderedWithDupsFx,
563 "Sparse unordered with dups reader: coords budget forcing one tile at a "
564 "time",
565 "[sparse-unordered-with-dups][small-coords-budget]") {
566 // Create default array.
567 reset_config();
568 create_default_array_1d();
569
570 bool use_subarray = false;
571 SECTION("- No subarray") {
572 use_subarray = false;
573 }
574 SECTION("- Subarray") {
575 use_subarray = true;
576 }
577
578 // Write a fragment.
579 int coords[] = {1, 2, 3, 4, 5};
580 uint64_t coords_size = sizeof(coords);
581 int data[] = {1, 2, 3, 4, 5};
582 uint64_t data_size = sizeof(data);
583 write_1d_fragment(coords, &coords_size, data, &data_size);
584
585 // Two result tile (2 * 8) will be bigger than the budget (10).
586 total_budget_ = "10000";
587 ratio_coords_ = "0.001";
588 update_config();
589
590 tiledb_array_t* array = nullptr;
591 tiledb_query_t* query = nullptr;
592
593 // Try to read.
594 int coords_r[5];
595 int data_r[5];
596 uint64_t coords_r_size = sizeof(coords_r);
597 uint64_t data_r_size = sizeof(data_r);
598 auto rc = read(
599 use_subarray,
600 false,
601 coords_r,
602 &coords_r_size,
603 data_r,
604 &data_r_size,
605 &query,
606 &array);
607 CHECK(rc == TILEDB_OK);
608
609 // Check incomplete query status.
610 tiledb_query_status_t status;
611 tiledb_query_get_status(ctx_, query, &status);
612 CHECK(status == TILEDB_INCOMPLETE);
613
614 // Should only read one tile (2 values).
615 CHECK(8 == data_r_size);
616 CHECK(8 == coords_r_size);
617
618 int coords_c[] = {1, 2};
619 int data_c[] = {1, 2};
620 CHECK(!std::memcmp(coords_c, coords_r, coords_r_size));
621 CHECK(!std::memcmp(data_c, data_r, data_r_size));
622
623 // Read again.
624 rc = tiledb_query_submit(ctx_, query);
625 CHECK(rc == TILEDB_OK);
626
627 // Check incomplete query status.
628 tiledb_query_get_status(ctx_, query, &status);
629 CHECK(status == TILEDB_INCOMPLETE);
630
631 // Should only read one more tile (2 values).
632 CHECK(8 == data_r_size);
633 CHECK(8 == coords_r_size);
634
635 int coords_c_2[] = {3, 4};
636 int data_c_2[] = {3, 4};
637 CHECK(!std::memcmp(coords_c_2, coords_r, coords_r_size));
638 CHECK(!std::memcmp(data_c_2, data_r, data_r_size));
639
640 // Read again.
641 rc = tiledb_query_submit(ctx_, query);
642 CHECK(rc == TILEDB_OK);
643
644 // Check complete query status.
645 tiledb_query_get_status(ctx_, query, &status);
646 CHECK(status == TILEDB_COMPLETED);
647
648 // Should read last tile (1 value).
649 CHECK(4 == data_r_size);
650 CHECK(4 == coords_r_size);
651
652 int coords_c_3[] = {5};
653 int data_c_3[] = {5};
654 CHECK(!std::memcmp(coords_c_3, coords_r, coords_r_size));
655 CHECK(!std::memcmp(data_c_3, data_r, data_r_size));
656
657 // Clean up.
658 rc = tiledb_array_close(ctx_, array);
659 CHECK(rc == TILEDB_OK);
660 tiledb_array_free(&array);
661 tiledb_query_free(&query);
662 }
663
664 TEST_CASE_METHOD(
665 CSparseUnorderedWithDupsFx,
666 "Sparse unordered with dups reader: coords budget too small",
667 "[sparse-unordered-with-dups][coords-budget][too-small]") {
668 // Create default array.
669 reset_config();
670 create_default_array_1d();
671
672 bool use_subarray = false;
673 SECTION("- No subarray") {
674 use_subarray = false;
675 }
676 SECTION("- Subarray") {
677 use_subarray = true;
678 }
679
680 // Write a fragment.
681 int coords[] = {1, 2, 3, 4, 5};
682 uint64_t coords_size = sizeof(coords);
683 int data[] = {1, 2, 3, 4, 5};
684 uint64_t data_size = sizeof(data);
685 write_1d_fragment(coords, &coords_size, data, &data_size);
686
687 // One result tile (8) will be bigger than the budget (5).
688 total_budget_ = "10000";
689 ratio_coords_ = "0.0005";
690 update_config();
691
692 // Try to read.
693 int coords_r[5];
694 int data_r[5];
695 uint64_t coords_r_size = sizeof(coords_r);
696 uint64_t data_r_size = sizeof(data_r);
697 auto rc =
698 read(use_subarray, false, coords_r, &coords_r_size, data_r, &data_r_size);
699 CHECK(rc == TILEDB_ERR);
700
701 // Check we hit the correct error.
702 tiledb_error_t* error = NULL;
703 rc = tiledb_ctx_get_last_error(ctx_, &error);
704 CHECK(rc == TILEDB_OK);
705
706 const char* msg;
707 rc = tiledb_error_message(error, &msg);
708 CHECK(rc == TILEDB_OK);
709
710 std::string error_str(msg);
711 CHECK(error_str.find("Cannot load a single tile") != std::string::npos);
712 }
713
714 TEST_CASE_METHOD(
715 CSparseUnorderedWithDupsFx,
716 "Sparse unordered with dups reader: rcs budget forcing one tile at a time",
717 "[sparse-unordered-with-dups][small-rcs-budget]") {
718 // Create default array.
719 reset_config();
720 create_default_array_1d();
721
722 bool use_subarray = false;
723 SECTION("- No subarray") {
724 use_subarray = false;
725 }
726 SECTION("- Subarray") {
727 use_subarray = true;
728 }
729
730 // Write a fragment.
731 int coords[] = {1, 2, 3, 4, 5};
732 uint64_t coords_size = sizeof(coords);
733 int data[] = {1, 2, 3, 4, 5};
734 uint64_t data_size = sizeof(data);
735 write_1d_fragment(coords, &coords_size, data, &data_size);
736
737 // One cell slab (24) will be bigger than the budget (10).
738 total_budget_ = "10000";
739 ratio_rcs_ = "0.001";
740 update_config();
741
742 tiledb_array_t* array = nullptr;
743 tiledb_query_t* query = nullptr;
744
745 // Try to read.
746 int coords_r[5];
747 int data_r[5];
748 uint64_t coords_r_size = sizeof(coords_r);
749 uint64_t data_r_size = sizeof(data_r);
750 auto rc = read(
751 use_subarray,
752 false,
753 coords_r,
754 &coords_r_size,
755 data_r,
756 &data_r_size,
757 &query,
758 &array);
759 CHECK(rc == TILEDB_OK);
760
761 // Check incomplete query status.
762 tiledb_query_status_t status;
763 tiledb_query_get_status(ctx_, query, &status);
764 CHECK(status == TILEDB_INCOMPLETE);
765
766 // Should only read one tile (2 values).
767 CHECK(8 == data_r_size);
768 CHECK(8 == coords_r_size);
769
770 int coords_c[] = {1, 2};
771 int data_c[] = {1, 2};
772 CHECK(!std::memcmp(coords_c, coords_r, coords_r_size));
773 CHECK(!std::memcmp(data_c, data_r, data_r_size));
774
775 // Read again.
776 rc = tiledb_query_submit(ctx_, query);
777 CHECK(rc == TILEDB_OK);
778
779 // Check incomplete query status.
780 tiledb_query_get_status(ctx_, query, &status);
781 CHECK(status == TILEDB_INCOMPLETE);
782
783 // Should only read one more tile (2 values).
784 CHECK(8 == data_r_size);
785 CHECK(8 == coords_r_size);
786
787 int coords_c_2[] = {3, 4};
788 int data_c_2[] = {3, 4};
789 CHECK(!std::memcmp(coords_c_2, coords_r, coords_r_size));
790 CHECK(!std::memcmp(data_c_2, data_r, data_r_size));
791
792 // Read again.
793 rc = tiledb_query_submit(ctx_, query);
794 CHECK(rc == TILEDB_OK);
795
796 // Check complete query status.
797 tiledb_query_get_status(ctx_, query, &status);
798 CHECK(status == TILEDB_COMPLETED);
799
800 // Should read last tile (1 value).
801 CHECK(4 == data_r_size);
802 CHECK(4 == coords_r_size);
803
804 int coords_c_3[] = {5};
805 int data_c_3[] = {5};
806 CHECK(!std::memcmp(coords_c_3, coords_r, coords_r_size));
807 CHECK(!std::memcmp(data_c_3, data_r, data_r_size));
808
809 // Clean up.
810 rc = tiledb_array_close(ctx_, array);
811 CHECK(rc == TILEDB_OK);
812 tiledb_array_free(&array);
813 tiledb_query_free(&query);
814 }
815
816 TEST_CASE_METHOD(
817 CSparseUnorderedWithDupsFx,
818 "Sparse unordered with dups reader: qc budget forcing one tile at a time",
819 "[sparse-unordered-with-dups][small-qc-budget]") {
820 // Create default array.
821 reset_config();
822 create_default_array_1d();
823
824 bool use_subarray = false;
825 SECTION("- No subarray") {
826 use_subarray = false;
827 }
828 SECTION("- Subarray") {
829 use_subarray = true;
830 }
831
832 // Write a fragment.
833 int coords[] = {1, 2, 3, 4, 5};
834 uint64_t coords_size = sizeof(coords);
835 int data[] = {1, 2, 3, 4, 5};
836 uint64_t data_size = sizeof(data);
837 write_1d_fragment(coords, &coords_size, data, &data_size);
838
839 // Two qc tile (16) will be bigger than the budget (10).
840 total_budget_ = "10000";
841 ratio_query_condition_ = "0.001";
842 update_config();
843
844 tiledb_array_t* array = nullptr;
845 tiledb_query_t* query = nullptr;
846
847 // Try to read.
848 int coords_r[5];
849 int data_r[5];
850 uint64_t coords_r_size = sizeof(coords_r);
851 uint64_t data_r_size = sizeof(data_r);
852 auto rc = read(
853 use_subarray,
854 true,
855 coords_r,
856 &coords_r_size,
857 data_r,
858 &data_r_size,
859 &query,
860 &array);
861 CHECK(rc == TILEDB_OK);
862
863 // Check incomplete query status.
864 tiledb_query_status_t status;
865 tiledb_query_get_status(ctx_, query, &status);
866 CHECK(status == TILEDB_INCOMPLETE);
867
868 // Should only read one tile (2 values).
869 CHECK(8 == data_r_size);
870 CHECK(8 == coords_r_size);
871
872 int coords_c[] = {1, 2};
873 int data_c[] = {1, 2};
874 CHECK(!std::memcmp(coords_c, coords_r, coords_r_size));
875 CHECK(!std::memcmp(data_c, data_r, data_r_size));
876
877 // Read again.
878 rc = tiledb_query_submit(ctx_, query);
879 CHECK(rc == TILEDB_OK);
880
881 // Check incomplete query status.
882 tiledb_query_get_status(ctx_, query, &status);
883 CHECK(status == TILEDB_INCOMPLETE);
884
885 // Should only read one more tile (2 values).
886 CHECK(8 == data_r_size);
887 CHECK(8 == coords_r_size);
888
889 int coords_c_2[] = {3, 4};
890 int data_c_2[] = {3, 4};
891 CHECK(!std::memcmp(coords_c_2, coords_r, coords_r_size));
892 CHECK(!std::memcmp(data_c_2, data_r, data_r_size));
893
894 // Read again.
895 rc = tiledb_query_submit(ctx_, query);
896 CHECK(rc == TILEDB_OK);
897
898 // Check complete query status.
899 tiledb_query_get_status(ctx_, query, &status);
900 CHECK(status == TILEDB_COMPLETED);
901
902 // Should read last tile (1 value).
903 CHECK(4 == data_r_size);
904 CHECK(4 == coords_r_size);
905
906 int coords_c_3[] = {5};
907 int data_c_3[] = {5};
908 CHECK(!std::memcmp(coords_c_3, coords_r, coords_r_size));
909 CHECK(!std::memcmp(data_c_3, data_r, data_r_size));
910
911 // Clean up.
912 rc = tiledb_array_close(ctx_, array);
913 CHECK(rc == TILEDB_OK);
914 tiledb_array_free(&array);
915 tiledb_query_free(&query);
916 }
917
918 TEST_CASE_METHOD(
919 CSparseUnorderedWithDupsFx,
920 "Sparse unordered with dups reader: qc budget too small",
921 "[sparse-unordered-with-dups][qc-budget][too-small]") {
922 // Create default array.
923 reset_config();
924 create_default_array_1d();
925
926 bool use_subarray = false;
927 SECTION("- No subarray") {
928 use_subarray = false;
929 }
930 SECTION("- Subarray") {
931 use_subarray = true;
932 }
933
934 // Write a fragment.
935 int coords[] = {1, 2, 3, 4, 5};
936 uint64_t coords_size = sizeof(coords);
937 int data[] = {1, 2, 3, 4, 5};
938 uint64_t data_size = sizeof(data);
939 write_1d_fragment(coords, &coords_size, data, &data_size);
940
941 // One qc tile (8) will be bigger than the budget (5).
942 total_budget_ = "10000";
943 ratio_query_condition_ = "0.0005";
944 update_config();
945
946 // Try to read.
947 int coords_r[5];
948 int data_r[5];
949 uint64_t coords_r_size = sizeof(coords_r);
950 uint64_t data_r_size = sizeof(data_r);
951 auto rc =
952 read(use_subarray, true, coords_r, &coords_r_size, data_r, &data_r_size);
953 CHECK(rc == TILEDB_ERR);
954
955 // Check we hit the correct error.
956 tiledb_error_t* error = NULL;
957 rc = tiledb_ctx_get_last_error(ctx_, &error);
958 CHECK(rc == TILEDB_OK);
959
960 const char* msg;
961 rc = tiledb_error_message(error, &msg);
962 CHECK(rc == TILEDB_OK);
963
964 std::string error_str(msg);
965 CHECK(error_str.find("Cannot load a single tile") != std::string::npos);
966 }