/*- * Public Domain 2014-present MongoDB, Inc. * Public Domain 2008-2014 WiredTiger, Inc. * * This is free and unencumbered software released into the public domain. * * Anyone is free to copy, modify, publish, use, compile, sell, or * distribute this software, either in source code form or as a compiled * binary, for any purpose, commercial or non-commercial, and by any * means. * * In jurisdictions that recognize copyright laws, the author or authors * of this software dedicate any and all copyright interest in the * software to the public domain. We make this dedication for the benefit * of the public at large and to the detriment of our heirs and * successors. We intend this dedication to be an overt act of * relinquishment in perpetuity of all present and future rights to this * software under copyright law. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include "test_util.h" /* * JIRA ticket reference: WT-3135 Test case description: Each set of data is ordered and contains * five elements (0-4). We insert elements 1 and 3, and then do search_near and search for each * element. For each set of data, we perform these tests first using a custom collator, and second * using a custom collator and extractor. In each case there are index keys having variable length. * Failure mode: In the reported test case, the custom compare routine is given a truncated key to * compare, and the unpack functions return errors because the truncation appeared in the middle of * a key. */ #define TEST_ENTRY_COUNT 5 typedef const char *TEST_SET[TEST_ENTRY_COUNT]; static TEST_SET test_sets[] = {{"0", "01", "012", "0123", "01234"}, {"A", "B", "C", "D", "E"}, {"5", "54", "543", "5432", "54321"}, {"54321", "5433", "544", "55", "6"}}; #define TEST_SET_COUNT (sizeof(test_sets) / sizeof(test_sets[0])) static bool item_str_equal(WT_ITEM *item, const char *str) { return (item->size == strlen(str) + 1 && strncmp((char *)item->data, str, item->size) == 0); } static int compare_int(int64_t a, int64_t b) { return (a < b ? -1 : (a > b ? 1 : 0)); } static int index_compare_primary(WT_PACK_STREAM *s1, WT_PACK_STREAM *s2, int *cmp) { int64_t pkey1, pkey2; int rc1, rc2; rc1 = wiredtiger_unpack_int(s1, &pkey1); rc2 = wiredtiger_unpack_int(s2, &pkey2); if (rc1 == 0 && rc2 == 0) *cmp = compare_int(pkey1, pkey2); else if (rc1 != 0 && rc2 != 0) *cmp = 0; else if (rc1 != 0) *cmp = -1; else *cmp = 1; return (0); } static int index_compare_S( WT_COLLATOR *collator, WT_SESSION *session, const WT_ITEM *key1, const WT_ITEM *key2, int *cmp) { WT_PACK_STREAM *s1, *s2; const char *skey1, *skey2; (void)collator; testutil_check(wiredtiger_unpack_start(session, "Si", key1->data, key1->size, &s1)); testutil_check(wiredtiger_unpack_start(session, "Si", key2->data, key2->size, &s2)); testutil_check(wiredtiger_unpack_str(s1, &skey1)); testutil_check(wiredtiger_unpack_str(s2, &skey2)); if ((*cmp = strcmp(skey1, skey2)) == 0) testutil_check(index_compare_primary(s1, s2, cmp)); testutil_check(wiredtiger_pack_close(s1, NULL)); testutil_check(wiredtiger_pack_close(s2, NULL)); return (0); } static int index_compare_u( WT_COLLATOR *collator, WT_SESSION *session, const WT_ITEM *key1, const WT_ITEM *key2, int *cmp) { WT_ITEM skey1, skey2; WT_PACK_STREAM *s1, *s2; (void)collator; testutil_check(wiredtiger_unpack_start(session, "ui", key1->data, key1->size, &s1)); testutil_check(wiredtiger_unpack_start(session, "ui", key2->data, key2->size, &s2)); testutil_check(wiredtiger_unpack_item(s1, &skey1)); testutil_check(wiredtiger_unpack_item(s2, &skey2)); if ((*cmp = strcmp(skey1.data, skey2.data)) == 0) testutil_check(index_compare_primary(s1, s2, cmp)); testutil_check(wiredtiger_pack_close(s1, NULL)); testutil_check(wiredtiger_pack_close(s2, NULL)); return (0); } static int index_extractor_u(WT_EXTRACTOR *extractor, WT_SESSION *session, const WT_ITEM *key, const WT_ITEM *value, WT_CURSOR *result_cursor) { (void)extractor; (void)session; (void)key; result_cursor->set_key(result_cursor, value); return result_cursor->insert(result_cursor); } static WT_COLLATOR collator_S = {index_compare_S, NULL, NULL}; static WT_COLLATOR collator_u = {index_compare_u, NULL, NULL}; static WT_EXTRACTOR extractor_u = {index_extractor_u, NULL, NULL}; /* * Check search() and search_near() using the test string indicated by test_index. */ static void search_using_str(WT_CURSOR *cursor, TEST_SET test_set, int test_index) { int exact, ret; const char *result; const char *str_01, *str_0123, *test_str; testutil_assert(test_index >= 0 && test_index <= 4); str_01 = test_set[1]; str_0123 = test_set[3]; test_str = test_set[test_index]; cursor->set_key(cursor, test_str); testutil_check(cursor->search_near(cursor, &exact)); testutil_check(cursor->get_key(cursor, &result)); if (test_index == 0) testutil_assert(strcmp(result, str_01) == 0 && exact > 0); else if (test_index == 1) testutil_assert(strcmp(result, str_01) == 0 && exact == 0); else if (test_index == 2) testutil_assert((strcmp(result, str_0123) == 0 && exact > 0) || (strcmp(result, str_01) == 0 && exact < 0)); else if (test_index == 3) testutil_assert(strcmp(result, str_0123) == 0 && exact == 0); else if (test_index == 4) testutil_assert(strcmp(result, str_0123) == 0 && exact < 0); cursor->set_key(cursor, test_str); ret = cursor->search(cursor); if (test_index == 0 || test_index == 2 || test_index == 4) testutil_assert(ret == WT_NOTFOUND); else if (test_index == 1 || test_index == 3) testutil_assert(ret == 0); } /* * Check search() and search_near() using the test string indicated by test_index against a table * containing a variable sized item. */ static void search_using_item(WT_CURSOR *cursor, TEST_SET test_set, int test_index) { WT_ITEM item; size_t testlen; int exact, ret; const char *str_01, *str_0123, *test_str; testutil_assert(test_index >= 0 && test_index <= 4); str_01 = test_set[1]; str_0123 = test_set[3]; test_str = test_set[test_index]; testlen = strlen(test_str) + 1; item.data = test_str; item.size = testlen; cursor->set_key(cursor, &item); testutil_check(cursor->search_near(cursor, &exact)); testutil_check(cursor->get_key(cursor, &item)); if (test_index == 0) testutil_assert(item_str_equal(&item, str_01) && exact > 0); else if (test_index == 1) testutil_assert(item_str_equal(&item, str_01) && exact == 0); else if (test_index == 2) testutil_assert((item_str_equal(&item, str_0123) && exact > 0) || (item_str_equal(&item, str_01) && exact < 0)); else if (test_index == 3) testutil_assert(item_str_equal(&item, str_0123) && exact == 0); else if (test_index == 4) testutil_assert(item_str_equal(&item, str_0123) && exact < 0); item.data = test_str; item.size = testlen; cursor->set_key(cursor, &item); ret = cursor->search(cursor); if (test_index == 0 || test_index == 2 || test_index == 4) testutil_assert(ret == WT_NOTFOUND); else if (test_index == 1 || test_index == 3) testutil_assert(ret == 0); } /* * For each set of data, perform tests. */ static void test_one_set(WT_SESSION *session, TEST_SET set) { WT_CURSOR *cursor; WT_ITEM item; int32_t i; /* * Part 1: Using a custom collator, insert some elements and verify results from search_near. */ testutil_check( session->create(session, "table:main", "key_format=i,value_format=S,columns=(k,v)")); testutil_check(session->create(session, "index:main:def_collator", "columns=(v)")); testutil_check( session->create(session, "index:main:custom_collator", "columns=(v),collator=collator_S")); /* Insert only elements #1 and #3. */ testutil_check(session->open_cursor(session, "table:main", NULL, NULL, &cursor)); cursor->set_key(cursor, 0); cursor->set_value(cursor, set[1]); testutil_check(cursor->insert(cursor)); cursor->set_key(cursor, 1); cursor->set_value(cursor, set[3]); testutil_check(cursor->insert(cursor)); testutil_check(cursor->close(cursor)); /* Check all elements in def_collator index. */ testutil_check(session->open_cursor(session, "index:main:def_collator", NULL, NULL, &cursor)); for (i = 0; i < (int32_t)TEST_ENTRY_COUNT; i++) search_using_str(cursor, set, i); testutil_check(cursor->close(cursor)); /* Check all elements in custom_collator index */ testutil_check( session->open_cursor(session, "index:main:custom_collator", NULL, NULL, &cursor)); for (i = 0; i < (int32_t)TEST_ENTRY_COUNT; i++) search_using_str(cursor, set, i); testutil_check(cursor->close(cursor)); /* * Part 2: perform the same checks using a custom collator and extractor. */ testutil_check( session->create(session, "table:main2", "key_format=i,value_format=u,columns=(k,v)")); testutil_check(session->create( session, "index:main2:idx_w_coll", "key_format=u,collator=collator_u,extractor=extractor_u")); testutil_check(session->open_cursor(session, "table:main2", NULL, NULL, &cursor)); memset(&item, 0, sizeof(item)); item.size = strlen(set[1]) + 1; item.data = set[1]; cursor->set_key(cursor, 1); cursor->set_value(cursor, &item); testutil_check(cursor->insert(cursor)); item.size = strlen(set[3]) + 1; item.data = set[3]; cursor->set_key(cursor, 3); cursor->set_value(cursor, &item); testutil_check(cursor->insert(cursor)); testutil_check(cursor->close(cursor)); testutil_check(session->open_cursor(session, "index:main2:idx_w_coll", NULL, NULL, &cursor)); for (i = 0; i < (int32_t)TEST_ENTRY_COUNT; i++) search_using_item(cursor, set, i); testutil_check(cursor->close(cursor)); testutil_check(session->drop(session, "table:main", NULL)); testutil_check(session->drop(session, "table:main2", NULL)); } int main(int argc, char *argv[]) { TEST_OPTS *opts, _opts; WT_SESSION *session; size_t i; opts = &_opts; memset(opts, 0, sizeof(*opts)); testutil_check(testutil_parse_opts(argc, argv, opts)); testutil_make_work_dir(opts->home); testutil_check(wiredtiger_open(opts->home, NULL, "create", &opts->conn)); testutil_check(opts->conn->open_session(opts->conn, NULL, NULL, &session)); /* Add any collators and extractors used by tests */ testutil_check(opts->conn->add_collator(opts->conn, "collator_S", &collator_S, NULL)); testutil_check(opts->conn->add_collator(opts->conn, "collator_u", &collator_u, NULL)); testutil_check(opts->conn->add_extractor(opts->conn, "extractor_u", &extractor_u, NULL)); for (i = 0; i < TEST_SET_COUNT; i++) { printf("test set %" WT_SIZET_FMT "\n", i); test_one_set(session, test_sets[i]); } testutil_check(session->close(session, NULL)); testutil_cleanup(opts); return (EXIT_SUCCESS); }