diff --git a/src/box/index.cc b/src/box/index.cc index 4d3074d50b07f13d07dc6d3096dc507c2e4a4fe1..209d92aa73d62bd90b2765a0cc983677231b36f4 100644 --- a/src/box/index.cc +++ b/src/box/index.cc @@ -74,16 +74,28 @@ key_validate(struct key_def *key_def, enum iterator_type type, const char *key, } if (key_def->type == RTREE) { - if (part_count != 1 && part_count != 2 && part_count != 4) { + if (part_count != 1 && part_count != 2 && part_count != 4) tnt_raise(ClientError, ER_KEY_PART_COUNT, 4, part_count); - } -#if 0 - for (uint32_t part = 0; part < part_count; part++) { + if (part_count == 1) { enum mp_type mp_type = mp_typeof(*key); - mp_next(&key); - key_mp_type_validate(NUM, mp_type, ER_KEY_PART_TYPE, part); - } -#endif + key_mp_type_validate(ARRAY, mp_type, ER_KEY_PART_TYPE, 0); + uint32_t arr_size = mp_decode_array(&key); + if (arr_size != 2 && arr_size != 4) + tnt_raise(ClientError, ER_UNSUPPORTED, + "R-Tree key", "Key should contain 2 (point) " + "or 4 (rectangle) numeric coordinates"); + for (uint32_t part = 0; part < arr_size; part++) { + enum mp_type mp_type = mp_typeof(*key); + mp_next(&key); + key_mp_type_validate(NUMBER, mp_type, ER_KEY_PART_TYPE, 0); + } + } else { + for (uint32_t part = 0; part < part_count; part++) { + enum mp_type mp_type = mp_typeof(*key); + mp_next(&key); + key_mp_type_validate(NUMBER, mp_type, ER_KEY_PART_TYPE, part); + } + } } else { if (part_count > key_def->part_count) tnt_raise(ClientError, ER_KEY_PART_COUNT, diff --git a/src/box/index.h b/src/box/index.h index 0b22c3fb9e4cdbb3511c806c1edf8b1a875c527b..a86fd2f1c7f5e6110dc620671d396727921467a4 100644 --- a/src/box/index.h +++ b/src/box/index.h @@ -105,7 +105,7 @@ iterator_close(struct iterator *it) { * * @param key_def key definition * @param type iterator type (see enum iterator_type) - * @param key BER-encoded key + * @param key msgpack-encoded key * @param part_count number of parts in \a key */ void diff --git a/src/box/key_def.cc b/src/box/key_def.cc index 463d19ada18869f0c452e34f96e9ad07c684c9b2..7a071d490e7d15a0cbfc22193b8cf4883d56fba2 100644 --- a/src/box/key_def.cc +++ b/src/box/key_def.cc @@ -33,7 +33,7 @@ #include <stdio.h> #include "exception.h" -const char *field_type_strs[] = {"UNKNOWN", "NUM", "STR", "ARRAY", "\0"}; +const char *field_type_strs[] = {"UNKNOWN", "NUM", "STR", "ARRAY", "NUMBER", ""}; STRS(index_type, ENUM_INDEX_TYPE); const uint32_t key_mp_type[] = { @@ -41,6 +41,7 @@ const uint32_t key_mp_type[] = { /* [NUM] = */ 1U << MP_UINT, /* [STR] = */ 1U << MP_STR, /* [ARRAY] = */ 1U << MP_ARRAY, + /* [NUMBER] = */ (1U << MP_UINT) | (1U << MP_INT) | (1U << MP_FLOAT) | (1U << MP_DOUBLE), }; enum schema_object_type diff --git a/src/box/key_def.h b/src/box/key_def.h index fb7a3a4b7059256a6f590adebbd42e721b85303e..008db160fb2052361ddf93939e0596d33f30fe8b 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -75,7 +75,7 @@ schema_object_type(const char *name); * since there is a mismatch between enum name (STRING) and type * name literal ("STR"). STR is already used as Objective C type. */ -enum field_type { UNKNOWN = 0, NUM, STRING, ARRAY, field_type_MAX }; +enum field_type { UNKNOWN = 0, NUM, STRING, ARRAY, NUMBER, field_type_MAX }; extern const char *field_type_strs[]; static inline uint32_t diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index 212946343b35073452109f2a83a90c8b8ee6161b..ce473137025c19635628fb7603051f1de6b508e1 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -533,8 +533,8 @@ function box.schema.space.bless(space) index_mt.__index = index_mt -- min and max index_mt.min = function(index, key) - if index.type == 'HASH' then - box.error(box.error.UNSUPPORTED, 'HASH', 'min()') + if index.type ~= 'TREE' then + box.error(box.error.UNSUPPORTED, index.type, 'min()') end local lst = index:select(key, { iterator = 'GE', limit = 1 })[1] if lst ~= nil then @@ -544,8 +544,8 @@ function box.schema.space.bless(space) end end index_mt.max = function(index, key) - if index.type == 'HASH' then - box.error(box.error.UNSUPPORTED, 'HASH', 'max()') + if index.type ~= 'TREE' then + box.error(box.error.UNSUPPORTED, index.type, 'max()') end local lst = index:select(key, { iterator = 'LE', limit = 1 })[1] if lst ~= nil then diff --git a/src/box/rtree_index.cc b/src/box/rtree_index.cc index f8ddddf7e216f5a60ce0be2fadd95921191002b8..5869d24d00d239786f7a8d62aa21b84577452a4d 100644 --- a/src/box/rtree_index.cc +++ b/src/box/rtree_index.cc @@ -44,9 +44,9 @@ static int rtree_page_pool_initialized = 0; inline void extract_rectangle(struct rtree_rect *rect, const struct tuple *tuple, struct key_def *kd) { + assert(kd->part_count == 1); const char *elems = tuple_field(tuple, kd->parts[0].fieldno); uint32_t size = mp_decode_array(&elems); - assert (kd->part_count == 1); switch (size) { case 1: // array { @@ -197,9 +197,7 @@ RTreeIndex::findByKey(const char *key, uint32_t part_count) const rect.upper_point.coords[1] = mp_decode_num(&key, 3); break; default: - tnt_raise(ClientError, ER_UNSUPPORTED, - "R-Tree key", "Key should be array of 2 (point) " - "or 4 (rectangle) numeric coordinates"); + assert(false); } break; } @@ -218,9 +216,7 @@ RTreeIndex::findByKey(const char *key, uint32_t part_count) const rect.upper_point.coords[1] = mp_decode_num(&key, 3); break; default: - tnt_raise(ClientError, ER_UNSUPPORTED, - "R-Tree key", "Key should contain 2 (point) " - "or 4 (rectangle) numeric coordinates"); + assert(false); } struct tuple *result = NULL; if (rtree_search(&tree, &rect, SOP_OVERLAPS, &iterator)) diff --git a/src/box/tuple.cc b/src/box/tuple.cc index de65e1af7b86c997256824d9bd0fc7e2844ac9ea..d00728e2c8134a197c5242071fbcf628b359545c 100644 --- a/src/box/tuple.cc +++ b/src/box/tuple.cc @@ -36,15 +36,6 @@ #include <exception.h> #include <stdio.h> -#ifndef DBL_MIN -#define DBL_MIN 4.94065645841246544e-324 -#define FLT_MIN ((float)1.40129846432481707e-45) -#endif -#ifndef DBL_MAX -#define DBL_MAX 1.79769313486231470e+308 -#define FLT_MAX ((float)3.40282346638528860e+38) -#endif - /** Global table of tuple formats */ struct tuple_format **tuple_formats; struct tuple_format *tuple_format_ber; diff --git a/src/lib/salad/rtree.c b/src/lib/salad/rtree.c index 1ad73cd886e99468e18ff89aeec660f94fcc124a..a14f89134ee1ff5555b177baadab3cb0a7e1fcc9 100644 --- a/src/lib/salad/rtree.c +++ b/src/lib/salad/rtree.c @@ -83,7 +83,6 @@ struct rtree_reinsert_list { /* R-tree rectangle methods */ /*------------------------------------------------------------------------- */ - void rtree_rect_normalize(struct rtree_rect *rect) { @@ -284,8 +283,8 @@ rtree_page_cover(const struct rtree_page *page) /* Create root page by first inserting record */ static void -rtree_page_init_record(struct rtree_page *page, - struct rtree_rect *rect, record_t obj) +rtree_page_init_with_record(struct rtree_page *page, + struct rtree_rect *rect, record_t obj) { page->n = 1; page->b[0].rect = *rect; @@ -294,8 +293,8 @@ rtree_page_init_record(struct rtree_page *page, /* Create root page by branch */ static void -rtree_page_init_branch(struct rtree_page *page, - const struct rtree_page_branch *br) +rtree_page_init_with_branch(struct rtree_page *page, + const struct rtree_page_branch *br) { page->n = 1; page->b[0] = *br; @@ -303,9 +302,9 @@ rtree_page_init_branch(struct rtree_page *page, /* Create new root page (root splitting) */ static void -rtree_page_init_page(struct rtree_page *page, - struct rtree_page *page1, - struct rtree_page *page2) +rtree_page_init_with_pages(struct rtree_page *page, + struct rtree_page *page1, + struct rtree_page *page2) { page->n = 2; page->b[0].rect = rtree_page_cover(page1); @@ -366,10 +365,10 @@ rtree_split_page(struct rtree *tree, struct rtree_page *page, if (seed[0] == 0) { group_rect[0] = br->rect; - rtree_page_init_branch(p, br); + rtree_page_init_with_branch(p, br); } else { group_rect[0] = page->b[seed[0] - 1].rect; - rtree_page_init_branch(p, &page->b[seed[0] - 1]); + rtree_page_init_with_branch(p, &page->b[seed[0] - 1]); page->b[seed[0] - 1] = *br; } area_t group_area[2] = {rect_area[seed[0]], rect_area[seed[1]]}; @@ -788,7 +787,7 @@ rtree_insert(struct rtree *tree, struct rtree_rect *rect, record_t obj) { if (tree->root == NULL) { tree->root = rtree_alloc_page(tree); - rtree_page_init_record(tree->root, rect, obj); + rtree_page_init_with_record(tree->root, rect, obj); tree->height = 1; tree->n_pages++; } else { @@ -797,7 +796,7 @@ rtree_insert(struct rtree *tree, struct rtree_rect *rect, record_t obj) if (p != NULL) { /* root splitted */ struct rtree_page *new_root = rtree_alloc_page(tree); - rtree_page_init_page(new_root, tree->root, p); + rtree_page_init_with_pages(new_root, tree->root, p); tree->root = new_root; tree->height++; tree->n_pages++; @@ -830,7 +829,8 @@ rtree_remove(struct rtree *tree, const struct rtree_rect *rect, record_t obj) /* root splitted */ struct rtree_page *new_root = rtree_alloc_page(tree); - rtree_page_init_page(new_root, tree->root, p); + rtree_page_init_with_pages(new_root, + tree->root, p); tree->root = new_root; tree->height++; tree->n_pages++; diff --git a/src/lib/salad/rtree.h b/src/lib/salad/rtree.h index 6eb8a3eb1fc8262afc5cd2d2baa1e35c9fb95649..f3e62085009023b2ee4aeb4af23375898295baf8 100644 --- a/src/lib/salad/rtree.h +++ b/src/lib/salad/rtree.h @@ -60,15 +60,30 @@ enum { RTREE_PAGE_SIZE = 1024 }; +/** + * Rtree search operations. Used for searching and iterations. + * All operations except SOP_ALL reqires a rectangle to be set, + * and treat it in different ways + */ enum spatial_search_op { + /* Find and itearate all records */ SOP_ALL, + /* Find and itearate records with the same rectangle */ SOP_EQUALS, + /* Find and itearate records that contain given rectangle */ SOP_CONTAINS, + /* Find and itearate records that strictly contain given rectangle */ SOP_STRICT_CONTAINS, + /* Find and itearate records that overlaps with given rectangle */ SOP_OVERLAPS, + /* Find and itearate records that belongs to given rectangle */ SOP_BELONGS, + /* Find and itearate records that strictly belongs to given rectangle */ SOP_STRICT_BELONGS, + /* Find and itearate nearest records from a given point (the point is + * acluattly lowest_point of given rectangle). Records are iterated in + * order of distance to given point. Yes, it is KNN iterator */ SOP_NEIGHBOR }; @@ -143,63 +158,147 @@ struct rtree_iterator /* Position of ready-to-use list entry in allocated page */ int page_pos; + /* Comparators for comparison rectagnle of the iterator with + * rectangles of tree nodes. If the comparator returns true, + * the node is accepted; if false - skipped. + */ + /* Comparator for interanal (not leaf) nodes of the tree */ rtree_comparator_t intr_cmp; + /* Comparator for leaf nodes of the tree */ rtree_comparator_t leaf_cmp; + /* Current path of search in tree */ struct { struct rtree_page *page; int pos; } stack[RTREE_MAX_HEIGHT]; }; +/** + * @brief Rectangle normalization. Makes lower_point member to be vertex + * with minimal coordinates, and upper_point - with maximal coordinates. + * Useful when the rectangle is initialized with two diagonal vertexes that + * could be not lowest and highest correspondingly. + * @param rect - pointer to a rectangle + */ void rtree_rect_normalize(struct rtree_rect *rect); +/** + * @brief Set up 2D rectangle by 4 coordinates + * @param rect - pointer to a rectangle + * @params left, bottom, right, top - corresponding coordinates + */ void rtree_set2d(struct rtree_rect *rect, coord_t left, coord_t bottom, coord_t right, coord_t top); +/** + * @brief Initialize a tree + * @param tree - pointer to a tree + * @param page_alloc - page allocation function + * @param page_free - page deallocation function + */ void -rtree_init(struct rtree *tree, rtree_page_alloc_t page_alloc, rtree_page_free_t page_free); +rtree_init(struct rtree *tree, + rtree_page_alloc_t page_alloc, rtree_page_free_t page_free); +/** + * @brief Destroy a tree + * @param tree - pointer to a tree + */ void rtree_destroy(struct rtree *tree); +/** + * @brief Delete all data from a tree, i.e. make it empty + * @param tree - pointer to a tree + */ void rtree_purge(struct rtree *tree); +/** + * @brief Find a record in a tree + * @return true if at least one record found (false otherwise) + * @param tree - pointer to a tree + * @param rect - rectangle to find (the meaning depends on op argument) + * @param op - type of search, see enum spatial_search_op for details + * @param itr - pointer to iterator (must be initialized earlier), + * iterator itr should be used for accessing found record + */ bool rtree_search(const struct rtree *tree, const struct rtree_rect *rect, enum spatial_search_op op, struct rtree_iterator *itr); +/** + * @brief Insert a record to the tree + * @param tree - pointer to a tree + * @param rect - rectangle to insert + * @param obj - record to insert + */ void rtree_insert(struct rtree *tree, struct rtree_rect *rect, record_t obj); +/** + * @brief Remove the record from a tree + * @return true if the record deleted (false otherwise) + * @param tree - pointer to a tree + * @param rect - rectangle of the record to delete + * @param obj - record to delete + */ bool rtree_remove(struct rtree *tree, const struct rtree_rect *rect, record_t obj); +/** + * @brief Size of memory used by tree + * @param tree - pointer to a tree + **/ size_t rtree_used_size(const struct rtree *tree); +/** + * @brief Number of records in the tree + * @param tree - pointer to a tree + **/ unsigned rtree_number_of_records(const struct rtree *tree); #if 0 +/** + * @brief Print a tree to stdout. Debug function, thus disabled. + * Needs <stdio.h> to be included before + * @param tree - pointer to a tree + **/ void rtree_debug_print(const struct rtree *tree); #endif +/** + * @brief Initialize an iterator for rtree + * Every iterator must be initialized before any usage + * @param itr - pointer to a iterator + **/ void rtree_iterator_init(struct rtree_iterator *itr); +/** + * @brief Destroy an iterator + * Every iterator must be destroyed + * @param itr - pointer to a iterator + **/ void rtree_iterator_destroy(struct rtree_iterator *itr); +/** + * @brief Retrieve a record from the iterator and iterate it to the next record + * @return a record or NULL if no more records + * @param itr - pointer to a iterator + **/ record_t rtree_iterator_next(struct rtree_iterator *itr); #if defined(__cplusplus) -} +} /* extern "C" { */ #endif /* defined(__cplusplus) */ #endif /* #ifndef INCLUDES_TARANTOOL_SALAD_RTREE_H */ diff --git a/test/box/rtree_misc.result b/test/box/rtree_misc.result new file mode 100644 index 0000000000000000000000000000000000000000..9e073f9cedbb7fa6a2286157a1e1d7b96bc74d8e --- /dev/null +++ b/test/box/rtree_misc.result @@ -0,0 +1,288 @@ +s = box.schema.create_space('spatial') +--- +... +-- rtree index as primary key must be forbidden (unique) +i = s:create_index('spatial', { type = 'rtree', unique = true, parts = {1, 'array'}}) +--- +- error: 'Can''t create or modify index 0 in space 512: RTREE index can not be unique' +... +-- any non-unique index as primary key must be forbidden +i = s:create_index('spatial', { type = 'hash', unique = false, parts = {1, 'num'}}) +--- +- error: 'Can''t create or modify index 0 in space 512: primary key must be unique' +... +i = s:create_index('spatial', { type = 'tree', unique = false, parts = {1, 'num'}}) +--- +- error: 'Can''t create or modify index 0 in space 512: primary key must be unique' +... +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {1, 'array'}}) +--- +- error: 'Can''t create or modify index 0 in space 512: primary key must be unique' +... +-- tree and hash indexes over array field is not possible +i = s:create_index('primary', { type = 'tree', parts = {1, 'array'}}) +--- +- error: 'Can''t create or modify index 0 in space 512: ARRAY field type is not supported' +... +i = s:create_index('primary', { type = 'hash', parts = {1, 'array'}}) +--- +- error: 'Can''t create or modify index 0 in space 512: ARRAY field type is not supported' +... +-- normal indexes +i = s:create_index('primary', { type = 'tree', parts = {1, 'num'}}) +--- +... +i = s:create_index('secondary', { type = 'hash', parts = {2, 'num'}}) +--- +... +-- adding a tuple with array instead of num will fail +i = s:insert{{1, 2, 3}, 4} +--- +- error: 'Tuple field 0 type does not match one required by operation: expected NUM' +... +i = s:insert{1, {2, 3, 4}} +--- +- error: 'Tuple field 1 type does not match one required by operation: expected NUM' +... +-- rtree index must be one-part +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {1, 'array', 2, 'array'}}) +--- +- error: 'Can''t create or modify index 2 in space 512: RTREE index key can not be + multipart' +... +-- unique rtree index is not possible +i = s:create_index('spatial', { type = 'rtree', unique = true, parts = {3, 'array'}}) +--- +- error: 'Can''t create or modify index 2 in space 512: RTREE index can not be unique' +... +-- num rtree index is not possible +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {3, 'num'}}) +--- +- error: 'Can''t create or modify index 2 in space 512: RTREE index field type must + be ARRAY' +... +-- str rtree index is not possible +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {3, 'str'}}) +--- +- error: 'Can''t create or modify index 2 in space 512: RTREE index field type must + be ARRAY' +... +-- normal rtree index +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {3, 'array'}}) +--- +... +-- inserting wrong values (should fail) +s:insert{1, 2, 3} +--- +- error: 'Tuple field 2 type does not match one required by operation: expected ARRAY' +... +s:insert{1, 2, "3"} +--- +- error: 'Tuple field 2 type does not match one required by operation: expected ARRAY' +... +s:insert{1, 2, nil, 3} +--- +- error: 'Tuple field 2 type does not match one required by operation: expected ARRAY' +... +s:insert{1, 2, {}} +--- +- error: R-Tree index does not support Key should contain 2 (point) or 4 (rectangle) + coordinates +... +s:insert{1, 2, {"3", "4", "5", "6"}} +--- +- error: 'Tuple field 0 type does not match one required by operation: expected NUM' +... +s:insert{1, 2, {nil, 4, 5, 6}} +--- +- error: 'Tuple field 0 type does not match one required by operation: expected NUM' +... +s:insert{1, 2, {3, {4}, 5, 6}} +--- +- error: 'Tuple field 1 type does not match one required by operation: expected NUM' +... +s:insert{1, 2, {3, 4, {}, 6}} +--- +- error: 'Tuple field 2 type does not match one required by operation: expected NUM' +... +s:insert{1, 2, {3, 4, 5, "6"}} +--- +- error: 'Tuple field 3 type does not match one required by operation: expected NUM' +... +s:insert{1, 2, {3}} +--- +- error: R-Tree index does not support Field should be array with size 2 (point) or + 4 (rectangle) +... +s:insert{1, 2, {3, 4, 5}} +--- +- error: R-Tree index does not support Key should contain 2 (point) or 4 (rectangle) + coordinates +... +-- inserting good value +s:insert{1, 2, {3, 4, 5, 6}} +--- +- [1, 2, [3, 4, 5, 6]] +... +-- invalid alters +s.index.spatial:alter({unique = true}) +--- +- error: 'Can''t create or modify index 2 in space 512: RTREE index can not be unique' +... +s.index.spatial:alter({type = 'tree'}) +--- +- error: 'Can''t create or modify index 2 in space 512: ARRAY field type is not supported' +... +box.space[box.schema.SPACE_ID]:update({s.id}, {{"=", 4, 'sophia'}}) +--- +- error: 'Can''t modify space 512: can not change space engine' +... +-- chech that truncate works +s.index.spatial:select({0, 0, 10, 10}, {iterator = 'le'}) +--- +- - [1, 2, [3, 4, 5, 6]] +... +s:truncate() +--- +... +s.index.spatial:select({0, 0, 10, 10}, {iterator = 'le'}) +--- +- [] +... +-- inserting lots of equvalent records +for i = 1,500 do s:insert{i, i, {3, 4, 5, 6}} end +--- +... +-- and some records for chaos +for i = 1,10 do for j = 1,10 do s:insert{500+i+j*20, 500+i*20+j, {i, j, i, j}} end end +--- +... +s.index.spatial:count() +--- +- 600 +... +#s.index.spatial:select({3, 4, 5, 6}) +--- +- 500 +... +for i = 1,500,2 do s:delete{i} end +--- +... +s.index.spatial:count() +--- +- 350 +... +#s.index.spatial:select({3, 4, 5, 6}) +--- +- 250 +... +s.index.spatial:min() +--- +- error: RTREE does not support min() +... +s.index.spatial:max() +--- +- error: RTREE does not support max() +... +s:drop() +--- +... +s = box.schema.create_space('sophia', {engine = 'sophia'}) +--- +... +-- rtree indexes are not enabled in sophia +i = s:create_index('spatial', { type = 'rtree', unique = true, parts = {3, 'array'}}) +--- +- error: Unsupported index type supplied for index 0 in space 512 +... +i = s:create_index('primary', { type = 'tree', parts = {1, 'num'}}) +--- +... +-- ... even secondary +i = s:create_index('spatial', { type = 'rtree', unique = true, parts = {3, 'array'}}) +--- +- error: Unsupported index type supplied for index 1 in space 512 +... +s:drop() +--- +... +-- rtree in temp space must work fine +s = box.schema.create_space('spatial', {temporary = true}) +--- +... +i = s:create_index('primary', { type = 'tree', parts = {1, 'num'}}) +--- +... +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {3, 'array'}}) +--- +... +s:insert{1, 2, {3, 4, 5, 6}} +--- +- [1, 2, [3, 4, 5, 6]] +... +s.index.spatial:select({0, 0, 10, 10}, {iterator = 'le'}) +--- +- - [1, 2, [3, 4, 5, 6]] +... +s:drop() +--- +... +-- snapshot test +s = box.schema.create_space('spatial') +--- +... +i = s:create_index('primary', { type = 'tree', parts = {1, 'num'}}) +--- +... +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {3, 'array'}}) +--- +... +for i = 1,10 do s:insert{i, i, {i, i, i + 1, i + 1}} end +--- +... +box.snapshot() +--- +- ok +... +for i = 11,20 do s:insert{i, i, i + 1, i + 1} +--- +- error: '[string "for i = 11,20 do s:insert{i, i, i + 1, i + 1} "]:1: ''end'' expected + near ''<eof>''' +... +i:select({0, 0}, {iterator = 'neighbor'}) +--- +- - [1, 1, [1, 1, 2, 2]] + - [2, 2, [2, 2, 3, 3]] + - [3, 3, [3, 3, 4, 4]] + - [4, 4, [4, 4, 5, 5]] + - [5, 5, [5, 5, 6, 6]] + - [6, 6, [6, 6, 7, 7]] + - [7, 7, [7, 7, 8, 8]] + - [8, 8, [8, 8, 9, 9]] + - [9, 9, [9, 9, 10, 10]] + - [10, 10, [10, 10, 11, 11]] +... +--# stop server default +--# start server default +s = box.space.spatial +--- +... +i = s.index.spatial +--- +... +i:select({0, 0}, {iterator = 'neighbor'}) +--- +- - [1, 1, [1, 1, 2, 2]] + - [2, 2, [2, 2, 3, 3]] + - [3, 3, [3, 3, 4, 4]] + - [4, 4, [4, 4, 5, 5]] + - [5, 5, [5, 5, 6, 6]] + - [6, 6, [6, 6, 7, 7]] + - [7, 7, [7, 7, 8, 8]] + - [8, 8, [8, 8, 9, 9]] + - [9, 9, [9, 9, 10, 10]] + - [10, 10, [10, 10, 11, 11]] +... +s:drop() +--- +... diff --git a/test/box/rtree_misc.test.lua b/test/box/rtree_misc.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..fa2680025a1e6377e2f41bb92e5b5dbf7d4b5f17 --- /dev/null +++ b/test/box/rtree_misc.test.lua @@ -0,0 +1,110 @@ +s = box.schema.create_space('spatial') + +-- rtree index as primary key must be forbidden (unique) +i = s:create_index('spatial', { type = 'rtree', unique = true, parts = {1, 'array'}}) + +-- any non-unique index as primary key must be forbidden +i = s:create_index('spatial', { type = 'hash', unique = false, parts = {1, 'num'}}) +i = s:create_index('spatial', { type = 'tree', unique = false, parts = {1, 'num'}}) +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {1, 'array'}}) + +-- tree and hash indexes over array field is not possible +i = s:create_index('primary', { type = 'tree', parts = {1, 'array'}}) +i = s:create_index('primary', { type = 'hash', parts = {1, 'array'}}) + +-- normal indexes +i = s:create_index('primary', { type = 'tree', parts = {1, 'num'}}) +i = s:create_index('secondary', { type = 'hash', parts = {2, 'num'}}) + +-- adding a tuple with array instead of num will fail +i = s:insert{{1, 2, 3}, 4} +i = s:insert{1, {2, 3, 4}} + +-- rtree index must be one-part +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {1, 'array', 2, 'array'}}) + +-- unique rtree index is not possible +i = s:create_index('spatial', { type = 'rtree', unique = true, parts = {3, 'array'}}) + +-- num rtree index is not possible +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {3, 'num'}}) + +-- str rtree index is not possible +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {3, 'str'}}) + + +-- normal rtree index +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {3, 'array'}}) + +-- inserting wrong values (should fail) +s:insert{1, 2, 3} +s:insert{1, 2, "3"} +s:insert{1, 2, nil, 3} +s:insert{1, 2, {}} +s:insert{1, 2, {"3", "4", "5", "6"}} +s:insert{1, 2, {nil, 4, 5, 6}} +s:insert{1, 2, {3, {4}, 5, 6}} +s:insert{1, 2, {3, 4, {}, 6}} +s:insert{1, 2, {3, 4, 5, "6"}} +s:insert{1, 2, {3}} +s:insert{1, 2, {3, 4, 5}} + +-- inserting good value +s:insert{1, 2, {3, 4, 5, 6}} + +-- invalid alters +s.index.spatial:alter({unique = true}) +s.index.spatial:alter({type = 'tree'}) +box.space[box.schema.SPACE_ID]:update({s.id}, {{"=", 4, 'sophia'}}) + +-- chech that truncate works +s.index.spatial:select({0, 0, 10, 10}, {iterator = 'le'}) +s:truncate() +s.index.spatial:select({0, 0, 10, 10}, {iterator = 'le'}) + +-- inserting lots of equvalent records +for i = 1,500 do s:insert{i, i, {3, 4, 5, 6}} end +-- and some records for chaos +for i = 1,10 do for j = 1,10 do s:insert{500+i+j*20, 500+i*20+j, {i, j, i, j}} end end +s.index.spatial:count() +#s.index.spatial:select({3, 4, 5, 6}) +for i = 1,500,2 do s:delete{i} end +s.index.spatial:count() +#s.index.spatial:select({3, 4, 5, 6}) + +s.index.spatial:min() +s.index.spatial:max() + +s:drop() + +s = box.schema.create_space('sophia', {engine = 'sophia'}) +-- rtree indexes are not enabled in sophia +i = s:create_index('spatial', { type = 'rtree', unique = true, parts = {3, 'array'}}) +i = s:create_index('primary', { type = 'tree', parts = {1, 'num'}}) +-- ... even secondary +i = s:create_index('spatial', { type = 'rtree', unique = true, parts = {3, 'array'}}) +s:drop() + +-- rtree in temp space must work fine +s = box.schema.create_space('spatial', {temporary = true}) +i = s:create_index('primary', { type = 'tree', parts = {1, 'num'}}) +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {3, 'array'}}) +s:insert{1, 2, {3, 4, 5, 6}} +s.index.spatial:select({0, 0, 10, 10}, {iterator = 'le'}) +s:drop() + +-- snapshot test +s = box.schema.create_space('spatial') +i = s:create_index('primary', { type = 'tree', parts = {1, 'num'}}) +i = s:create_index('spatial', { type = 'rtree', unique = false, parts = {3, 'array'}}) +for i = 1,10 do s:insert{i, i, {i, i, i + 1, i + 1}} end +box.snapshot() +for i = 11,20 do s:insert{i, i, i + 1, i + 1} +i:select({0, 0}, {iterator = 'neighbor'}) +--# stop server default +--# start server default +s = box.space.spatial +i = s.index.spatial +i:select({0, 0}, {iterator = 'neighbor'}) +s:drop() + diff --git a/test/box/session.storage.result b/test/box/session.storage.result index a292e1b2f287f2e476444ae66b9637ac7f928e8b..9893da2cc197e34bbbfd7b97e9461880e89f6071 100644 --- a/test/box/session.storage.result +++ b/test/box/session.storage.result @@ -31,7 +31,7 @@ all = getmetatable(session).aggregate_storage ... dump(all) --- -- '''[null,null,{"abc":"cde"}]''' +- '''[null,{"abc":"cde"}]''' ... --# create connection second to default --# set connection second