Susumu Yata
null+****@clear*****
Thu Dec 17 10:52:10 JST 2015
Susumu Yata 2015-12-17 10:52:10 +0900 (Thu, 17 Dec 2015) New Revision: 3428de2d1b774818c73a32293e4c17fb55de1038 https://github.com/groonga/groonga/commit/3428de2d1b774818c73a32293e4c17fb55de1038 Message: grn_array, hash: return an error when `truncate` is detected GitHub: #447 Modified files: lib/db.c lib/grn_hash.h lib/hash.c Modified: lib/db.c (+2 -2) =================================================================== --- lib/db.c 2015-12-16 11:50:20 +0900 (4530c9c) +++ lib/db.c 2015-12-17 10:52:10 +0900 (4b2d216) @@ -2165,10 +2165,10 @@ grn_table_size(grn_ctx *ctx, grn_obj *table) n = grn_dat_size(ctx, (grn_dat *)table); break; case GRN_TABLE_HASH_KEY : - n = GRN_HASH_SIZE((grn_hash *)table); + n = grn_hash_size(ctx, (grn_hash *)table); break; case GRN_TABLE_NO_KEY : - n = GRN_ARRAY_SIZE((grn_array *)table); + n = grn_array_size(ctx, (grn_array *)table); break; default : ERR(GRN_INVALID_ARGUMENT, "not supported"); Modified: lib/grn_hash.h (+13 -1) =================================================================== --- lib/grn_hash.h 2015-12-16 11:50:20 +0900 (76a71de) +++ lib/grn_hash.h 2015-12-17 10:52:10 +0900 (d72ae0f) @@ -156,7 +156,12 @@ struct _grn_array_cursor { int dir; }; -#define GRN_ARRAY_SIZE(array) (*((array)->n_entries)) +/* + * grn_array_size() returns the number of entries in an array. + * If the array was truncated by another process but `array` still refers to + * the old one, this function returns 0. + */ +uint32_t grn_array_size(grn_ctx *ctx, grn_array *array); grn_rc grn_array_truncate(grn_ctx *ctx, grn_array *array); grn_rc grn_array_copy_sort_key(grn_ctx *ctx, grn_array *array, @@ -324,6 +329,13 @@ grn_rc grn_hash_clear_lock(grn_ctx *ctx, grn_hash *hash); #define GRN_HASH_SIZE(hash) (*((hash)->n_entries)) +/* + * grn_hash_size() returns the number of entries in a hash. + * Note that grn_hash_size() detects `truncate` by another process and returns + * 0 in such a case. + */ +uint32_t grn_hash_size(grn_ctx *ctx, grn_hash *hash); + /* private */ typedef enum { grn_rec_document = 0, Modified: lib/hash.c (+101 -217) =================================================================== --- lib/hash.c 2015-12-16 11:50:20 +0900 (e25f0e6) +++ lib/hash.c 2015-12-17 10:52:10 +0900 (1105a20) @@ -610,54 +610,22 @@ grn_array_open(grn_ctx *ctx, const char *path) return NULL; } -/* grn_array_reopen() reopens a grn_io for a truncated grn_array. */ +/* + * grn_array_error_if_truncated() logs an error and returns its error code if + * an array is truncated by another process. + * Otherwise, this function returns GRN_SUCCESS. + * Note that `ctx` and `array` must be valid. + * + * FIXME: An array should be reopened if possible. + */ static grn_rc -grn_array_reopen(grn_ctx *ctx, grn_array *array) +grn_array_error_if_truncated(grn_ctx *ctx, grn_array *array) { - grn_io *new_io; - const char *path; - struct grn_array_header *new_header; - if (!ctx) { - return GRN_INVALID_ARGUMENT; - } - if (!array || !array->io) { - ERR(GRN_INVALID_ARGUMENT, "invalid argument"); - return ctx->rc; - } - path = grn_io_path(array->io); - if (!path || !*path) { - ERR(GRN_INVALID_ARGUMENT, "path not available"); - return ctx->rc; - } - new_io = grn_io_open(ctx, grn_io_path(array->io), grn_io_auto); - if (!new_io) { - if (ctx->rc == GRN_SUCCESS) { - ERR(GRN_UNKNOWN_ERROR, "grn_io_open failed"); - } - return ctx->rc; - } - if (grn_io_get_type(new_io) != GRN_TABLE_NO_KEY) { - grn_io_close(ctx, new_io); - ERR(GRN_INVALID_FORMAT, "file type unmatch"); - return ctx->rc; - } - new_header = grn_io_header(new_io); - if (new_header->flags & GRN_ARRAY_TINY) { - grn_io_close(ctx, new_io); - ERR(GRN_INVALID_FORMAT, "invalid array flags"); - return ctx->rc; + if (array->header && array->header->truncated) { + ERR(GRN_FILE_CORRUPT, + "array is truncated, please unmap or reopen the database"); + return GRN_FILE_CORRUPT; } - array->obj.header.flags = new_header->flags; - array->ctx = ctx; - array->value_size = new_header->value_size; - array->n_keys = 0; - array->keys = NULL; - array->n_garbages = &new_header->n_garbages; - array->n_entries = &new_header->n_entries; - grn_io_close(ctx, array->io); - array->io = new_io; - array->header = new_header; - array->lock = &new_header->lock; return GRN_SUCCESS; } @@ -685,19 +653,26 @@ grn_array_remove(grn_ctx *ctx, const char *path) return grn_io_remove(ctx, path); } +uint32_t +grn_array_size(grn_ctx *ctx, grn_array *array) +{ + if (grn_array_error_if_truncated(ctx, array) != GRN_SUCCESS) { + return 0; + } + return *array->n_entries; +} + grn_rc grn_array_truncate(grn_ctx *ctx, grn_array *array) { - grn_rc rc = GRN_SUCCESS; + grn_rc rc; char *path = NULL; uint32_t value_size, flags; if (!ctx || !array) { return GRN_INVALID_ARGUMENT; } - if (array->header && array->header->truncated) { - grn_rc rc = grn_array_reopen(ctx, array); - if (rc != GRN_SUCCESS) { - return rc; - } + rc = grn_array_error_if_truncated(ctx, array); + if (rc != GRN_SUCCESS) { + return rc; } if (grn_array_is_io_array(array)) { const char * const io_path = grn_io_path(array->io); @@ -744,11 +719,8 @@ grn_array_get_value_inline(grn_ctx *ctx, grn_array *array, grn_id id) if (!ctx || !array) { return NULL; } - if (array->header && array->header->truncated) { - grn_rc rc = grn_array_reopen(ctx, array); - if (rc != GRN_SUCCESS) { - return NULL; - } + if (grn_array_error_if_truncated(ctx, array) != GRN_SUCCESS) { + return NULL; } if (*array->n_garbages) { /* @@ -788,12 +760,6 @@ grn_array_set_value_inline(grn_ctx *ctx, grn_array *array, grn_id id, const void *value, int flags) { void *entry; - if (array->header && array->header->truncated) { - grn_rc rc = grn_array_reopen(ctx, array); - if (rc != GRN_SUCCESS) { - return rc; - } - } entry = grn_array_entry_at(ctx, array, id, 0); if (!entry) { return GRN_NO_MEMORY_AVAILABLE; @@ -840,6 +806,10 @@ grn_array_set_value(grn_ctx *ctx, grn_array *array, grn_id id, if (!ctx || !array || !value) { return GRN_INVALID_ARGUMENT; } + grn_rc rc = grn_array_error_if_truncated(ctx, array); + if (rc != GRN_SUCCESS) { + return rc; + } if (*array->n_garbages) { /* * grn_array_bitmap_at() is a time-consuming function, so it is called only @@ -858,21 +828,20 @@ grn_rc grn_array_delete_by_id(grn_ctx *ctx, grn_array *array, grn_id id, grn_table_delete_optarg *optarg) { + grn_rc rc; if (!ctx || !array) { return GRN_INVALID_ARGUMENT; } - if (array->header && array->header->truncated) { - grn_rc rc = grn_array_reopen(ctx, array); - if (rc != GRN_SUCCESS) { - return rc; - } + rc = grn_array_error_if_truncated(ctx, array); + if (rc != GRN_SUCCESS) { + return rc; } if (grn_array_bitmap_at(ctx, array, id) != 1) { return GRN_INVALID_ARGUMENT; } { - grn_rc rc = GRN_SUCCESS; + rc = GRN_SUCCESS; /* lock */ if (grn_array_is_io_array(array)) { if (array->value_size >= sizeof(grn_id)) { @@ -924,11 +893,8 @@ grn_array_delete_by_id(grn_ctx *ctx, grn_array *array, grn_id id, grn_id grn_array_at(grn_ctx *ctx, grn_array *array, grn_id id) { - if (array->header && array->header->truncated) { - grn_rc rc = grn_array_reopen(ctx, array); - if (rc != GRN_SUCCESS) { - return GRN_ID_NIL; - } + if (grn_array_error_if_truncated(ctx, array) != GRN_SUCCESS) { + return GRN_ID_NIL; } if (*array->n_garbages) { /* @@ -970,11 +936,8 @@ grn_array_cursor_open(grn_ctx *ctx, grn_array *array, grn_id min, grn_id max, { grn_array_cursor *cursor; if (!array || !ctx) { return NULL; } - if (array->header && array->header->truncated) { - grn_rc rc = grn_array_reopen(ctx, array); - if (rc != GRN_SUCCESS) { - return NULL; - } + if (grn_array_error_if_truncated(ctx, array) != GRN_SUCCESS) { + return NULL; } cursor = (grn_array_cursor *)GRN_MALLOCN(grn_array_cursor, 1); @@ -1054,11 +1017,8 @@ grn_id grn_array_next(grn_ctx *ctx, grn_array *array, grn_id id) { grn_id max_id; - if (array->header && array->header->truncated) { - grn_rc rc = grn_array_reopen(ctx, array); - if (rc != GRN_SUCCESS) { - return rc; - } + if (grn_array_error_if_truncated(ctx, array) != GRN_SUCCESS) { + return GRN_ID_NIL; } max_id = grn_array_get_max_id(array); while (++id <= max_id) { @@ -1142,11 +1102,8 @@ grn_array_add_to_io_array(grn_ctx *ctx, grn_array *array, void **value) grn_id id; void *entry; struct grn_array_header *header; - if (array->header->truncated) { - grn_rc rc = grn_array_reopen(ctx, array); - if (rc != GRN_SUCCESS) { - return GRN_ID_NIL; - } + if (grn_array_error_if_truncated(ctx, array) != GRN_SUCCESS) { + return GRN_ID_NIL; } header = array->header; id = header->garbages; @@ -1909,60 +1866,22 @@ grn_hash_open(grn_ctx *ctx, const char *path) return NULL; } -/* grn_hash_reopen() reopens a grn_io for a truncated grn_hash. */ +/* + * grn_hash_error_if_truncated() logs an error and returns its error code if + * a hash is truncated by another process. + * Otherwise, this function returns GRN_SUCCESS. + * Note that `ctx` and `hash` must be valid. + * + * FIXME: A hash should be reopened if possible. + */ static grn_rc -grn_hash_reopen(grn_ctx *ctx, grn_hash *hash) +grn_hash_error_if_truncated(grn_ctx *ctx, grn_hash *hash) { - grn_io *new_io; - const char *path; - grn_hash_header_common *new_header; - if (!ctx) { - return GRN_INVALID_ARGUMENT; - } - if (!hash || !hash->io) { - ERR(GRN_INVALID_ARGUMENT, "invalid argument"); - return ctx->rc; - } - path = grn_io_path(hash->io); - if (!path || !*path) { - ERR(GRN_INVALID_ARGUMENT, "path not available"); - return ctx->rc; - } - new_io = grn_io_open(ctx, grn_io_path(hash->io), grn_io_auto); - if (!new_io) { - if (ctx->rc == GRN_SUCCESS) { - ERR(GRN_UNKNOWN_ERROR, "grn_io_open failed"); - } - return ctx->rc; - } - if (grn_io_get_type(new_io) != GRN_TABLE_HASH_KEY) { - grn_io_close(ctx, new_io); - ERR(GRN_INVALID_FORMAT, "file type unmatch"); - return ctx->rc; - } - new_header = grn_io_header(new_io); - if (new_header->flags & GRN_HASH_TINY) { - grn_io_close(ctx, new_io); - ERR(GRN_INVALID_FORMAT, "invalid hash flags"); - return ctx->rc; + if (hash->header.common && hash->header.common->truncated) { + ERR(GRN_FILE_CORRUPT, + "hash is truncated, please unmap or reopen the database"); + return GRN_FILE_CORRUPT; } - - hash->ctx = ctx; - hash->key_size = new_header->key_size; - hash->encoding = new_header->encoding; - hash->value_size = new_header->value_size; - hash->entry_size = new_header->entry_size; - hash->n_garbages = &new_header->n_garbages; - hash->n_entries = &new_header->n_entries; - hash->max_offset = &new_header->max_offset; - grn_io_close(ctx, hash->io); - hash->io = new_io; - hash->header.common = new_header; - hash->lock = &new_header->lock; - hash->tokenizer = grn_ctx_at(ctx, new_header->tokenizer); - hash->normalizer = grn_ctx_at(ctx, new_header->normalizer); - GRN_PTR_INIT(&(hash->token_filters), GRN_OBJ_VECTOR, GRN_ID_NIL); - hash->obj.header.flags = new_header->flags; return GRN_SUCCESS; } @@ -2023,18 +1942,16 @@ grn_hash_remove(grn_ctx *ctx, const char *path) grn_rc grn_hash_truncate(grn_ctx *ctx, grn_hash *hash) { - grn_rc rc = GRN_SUCCESS; + grn_rc rc; char *path = NULL; uint32_t key_size, value_size, flags; if (!ctx || !hash) { return GRN_INVALID_ARGUMENT; } - if (hash->header.common && hash->header.common->truncated) { - rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return rc; - } + rc = grn_hash_error_if_truncated(ctx, hash); + if (rc != GRN_SUCCESS) { + return rc; } if (grn_hash_is_io_hash(hash)) { @@ -2233,6 +2150,15 @@ grn_hash_clear_lock(grn_ctx *ctx, grn_hash *hash) return GRN_SUCCESS; } +uint32_t +grn_hash_size(grn_ctx *ctx, grn_hash *hash) +{ + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return 0; + } + return *hash->n_entries; +} + inline static grn_id grn_io_hash_add(grn_ctx *ctx, grn_hash *hash, uint32_t hash_value, const void *key, unsigned int key_size, void **value) @@ -2322,11 +2248,8 @@ grn_hash_add(grn_ctx *ctx, grn_hash *hash, const void *key, unsigned int key_size, void **value, int *added) { uint32_t hash_value; - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return GRN_ID_NIL; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return GRN_ID_NIL; } if (!key || !key_size) { return GRN_ID_NIL; @@ -2420,11 +2343,8 @@ grn_hash_get(grn_ctx *ctx, grn_hash *hash, const void *key, unsigned int key_size, void **value) { uint32_t hash_value; - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return GRN_ID_NIL; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return GRN_ID_NIL; } if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) { if (key_size > hash->key_size) { @@ -2497,11 +2417,8 @@ grn_hash_get_key(grn_ctx *ctx, grn_hash *hash, grn_id id, void *keybuf, int bufs { int key_size; grn_hash_entry *entry; - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return 0; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return 0; } entry = grn_hash_get_entry(ctx, hash, id); if (!entry) { @@ -2520,11 +2437,8 @@ grn_hash_get_key2(grn_ctx *ctx, grn_hash *hash, grn_id id, grn_obj *bulk) int key_size; char *key; grn_hash_entry *entry; - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return 0; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return 0; } entry = grn_hash_get_entry(ctx, hash, id); if (!entry) { @@ -2546,11 +2460,8 @@ grn_hash_get_value(grn_ctx *ctx, grn_hash *hash, grn_id id, void *valuebuf) { void *value; grn_hash_entry *entry; - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return 0; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return 0; } entry = grn_hash_get_entry(ctx, hash, id); if (!entry) { @@ -2571,11 +2482,8 @@ grn_hash_get_value_(grn_ctx *ctx, grn_hash *hash, grn_id id, uint32_t *size) { const void *value; grn_hash_entry *entry; - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return NULL; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return NULL; } entry = grn_hash_get_entry(ctx, hash, id); if (!entry) { @@ -2596,11 +2504,8 @@ grn_hash_get_key_value(grn_ctx *ctx, grn_hash *hash, grn_id id, void *value; int key_size; grn_hash_entry *entry; - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return 0; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return 0; } entry = grn_hash_get_entry(ctx, hash, id); if (!entry) { @@ -2626,11 +2531,8 @@ _grn_hash_get_key_value(grn_ctx *ctx, grn_hash *hash, grn_id id, { int key_size; grn_hash_entry *entry; - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return 0; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return 0; } entry = grn_hash_get_entry(ctx, hash, id); if (!entry) { @@ -2648,11 +2550,8 @@ grn_hash_set_value(grn_ctx *ctx, grn_hash *hash, grn_id id, { void *entry_value; grn_hash_entry *entry; - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return rc; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return GRN_ID_NIL; } if (!value) { return GRN_INVALID_ARGUMENT; @@ -2734,11 +2633,9 @@ grn_hash_delete_by_id(grn_ctx *ctx, grn_hash *hash, grn_id id, entry_str *ee; grn_rc rc; if (!hash || !id) { return GRN_INVALID_ARGUMENT; } - if (hash->header.common && hash->header.common->truncated) { - rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return rc; - } + rc = grn_hash_error_if_truncated(ctx, hash); + if (rc != GRN_SUCCESS) { + return rc; } rc = GRN_INVALID_ARGUMENT; /* lock */ @@ -2765,12 +2662,9 @@ grn_hash_delete(grn_ctx *ctx, grn_hash *hash, const void *key, uint32_t key_size grn_table_delete_optarg *optarg) { uint32_t h, i, m, s; - grn_rc rc; - if (hash->header.common && hash->header.common->truncated) { - rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return rc; - } + grn_rc rc = grn_hash_error_if_truncated(ctx, hash); + if (rc != GRN_SUCCESS) { + return rc; } rc = GRN_INVALID_ARGUMENT; if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) { @@ -2834,11 +2728,8 @@ grn_hash_cursor_open(grn_ctx *ctx, grn_hash *hash, { grn_hash_cursor *c; if (!hash || !ctx) { return NULL; } - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return NULL; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return GRN_ID_NIL; } if (!(c = GRN_MALLOCN(grn_hash_cursor, 1))) { return NULL; } GRN_DB_OBJ_SET_TYPE(c, GRN_CURSOR_TABLE_HASH_KEY); @@ -3291,11 +3182,8 @@ grn_hash_sort(grn_ctx *ctx, grn_hash *hash, { entry **res; if (!result || !*hash->n_entries) { return 0; } - if (hash->header.common && hash->header.common->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return 0; - } + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return 0; } if (!(res = GRN_MALLOC(sizeof(entry *) * *hash->n_entries))) { GRN_LOG(ctx, GRN_LOG_ALERT, "allocation of entries failed on grn_hash_sort !"); @@ -3361,12 +3249,8 @@ grn_hash_check(grn_ctx *ctx, grn_hash *hash) { char buf[8]; grn_hash_header_common *h = hash->header.common; - if (h && h->truncated) { - grn_rc rc = grn_hash_reopen(ctx, hash); - if (rc != GRN_SUCCESS) { - return; - } - h = hash->header.common; + if (grn_hash_error_if_truncated(ctx, hash) != GRN_SUCCESS) { + return; } GRN_OUTPUT_ARRAY_OPEN("RESULT", 1); GRN_OUTPUT_MAP_OPEN("SUMMARY", 25); -------------- next part -------------- HTML����������������������������... Download