Kouhei Sutou
null+****@clear*****
Fri Sep 20 19:17:19 JST 2013
Kouhei Sutou 2013-09-20 19:17:19 +0900 (Fri, 20 Sep 2013) New Revision: d488768d3eca23aadd1a429528a4054a1dcc53e3 https://github.com/groonga/groonga/commit/d488768d3eca23aadd1a429528a4054a1dcc53e3 Message: Support cascade delete You can delete referenced records by this change. You got an error for the operation before this change. See the committed test about the detail. Note that an index must be created for this feature. If no index exists, the record is just deleted and some dangling references are appeared. Added files: test/command/suite/delete/reference/cross.expected test/command/suite/delete/reference/cross.test Modified files: lib/db.c Modified: lib/db.c (+131 -37) =================================================================== --- lib/db.c 2013-09-20 17:44:32 +0900 (5afe27e) +++ lib/db.c 2013-09-20 19:17:19 +0900 (e349640) @@ -1530,45 +1530,129 @@ clear_column_values(grn_ctx *ctx, grn_obj *table, grn_id rid) } } -static grn_bool -is_deletable(grn_ctx *ctx, grn_obj *table, grn_id id) -{ - grn_bool deletable = GRN_TRUE; - if (id) { - grn_hash *cols; - if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, - GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) { - if (grn_table_columns(ctx, table, "", 0, (grn_obj *)cols)) { - grn_id *key; - uint32_t esize; - GRN_HASH_EACH(ctx, cols, tid, &key, NULL, NULL, { - grn_obj *col = grn_ctx_at(ctx, *key); - if (col && col->header.type == GRN_COLUMN_INDEX && - (esize = grn_ii_estimate_size(ctx, (grn_ii *)col, id))) { - int len; - char table_name[GRN_TABLE_MAX_KEY_SIZE]; - char column_name[GRN_TABLE_MAX_KEY_SIZE]; - len = grn_obj_name(ctx, table, table_name, GRN_TABLE_MAX_KEY_SIZE); - table_name[len] = '\0'; - len = grn_column_name(ctx, col, column_name, GRN_TABLE_MAX_KEY_SIZE); - column_name[len] = '\0'; - GRN_LOG(ctx, GRN_WARN, - "undeletable record (%s:%d) has value (%s:%d)", - table_name, id, column_name, esize); - LOGTRACE(ctx, GRN_WARN); - ERR(GRN_OPERATION_NOT_PERMITTED, - "undeletable record (%s:%d) has value (%s:%d)", - table_name, id, column_name, esize); - deletable = GRN_FALSE; +static void +delete_reference_records_in_index(grn_ctx *ctx, grn_obj *table, grn_id id, + grn_obj *index) +{ + grn_ii *ii = (grn_ii *)index; + grn_ii_cursor *ii_cursor; + grn_ii_posting *posting; + grn_obj source_ids; + unsigned int i, n_ids; + grn_obj sources; + + GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR); + GRN_PTR_INIT(&sources, GRN_OBJ_VECTOR, 0); + + grn_obj_get_info(ctx, index, GRN_INFO_SOURCE, &source_ids); + n_ids = GRN_BULK_VSIZE(&source_ids) / sizeof(grn_id); + if (n_ids == 0) { + goto exit; + } + + for (i = 0; i < n_ids; i++) { + grn_id source_id; + grn_obj *source; + + source_id = GRN_UINT32_VALUE_AT(&source_ids, i); + source = grn_ctx_at(ctx, source_id); + GRN_PTR_PUT(ctx, &sources, source); + } + + ii_cursor = grn_ii_cursor_open(ctx, ii, id, GRN_ID_NIL, GRN_ID_MAX, + ii->n_elements, 0); + if (!ii_cursor) { + goto exit; + } + + while ((posting = grn_ii_cursor_next(ctx, ii_cursor))) { + grn_obj *source = GRN_PTR_VALUE_AT(&sources, posting->sid - 1); + switch (source->header.type) { + case GRN_COLUMN_VAR_SIZE : + switch (source->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) { + case GRN_OBJ_COLUMN_SCALAR : + grn_obj_clear_value(ctx, source, posting->rid); + break; + case GRN_OBJ_COLUMN_VECTOR : + { + grn_obj value; + GRN_TEXT_INIT(&value, 0); + grn_obj_get_value(ctx, source, posting->rid, &value); + if (value.header.type == GRN_UVECTOR) { + int i, n_ids; + grn_obj new_value; + GRN_RECORD_INIT(&new_value, GRN_OBJ_VECTOR, value.header.domain); + n_ids = GRN_BULK_VSIZE(&value) / sizeof(grn_id); + for (i = 0; i < n_ids; i++) { + grn_id reference_id = GRN_RECORD_VALUE_AT(&value, i); + if (reference_id == id) { + continue; + } + GRN_RECORD_PUT(ctx, &new_value, reference_id); + } + grn_obj_set_value(ctx, source, posting->rid, &new_value, + GRN_OBJ_SET); + GRN_OBJ_FIN(ctx, &new_value); + } else { + /* TODO */ } - }); + GRN_OBJ_FIN(ctx, &value); + } + break; } - grn_hash_close(ctx, cols); + break; + case GRN_COLUMN_FIX_SIZE : + grn_obj_clear_value(ctx, source, posting->rid); + break; } - } else { - deletable = GRN_FALSE; } - return deletable; + +exit: + if (ii_cursor) { + grn_ii_cursor_close(ctx, ii_cursor); + } + grn_obj_unlink(ctx, &source_ids); + { + int i, n_sources; + n_sources = GRN_BULK_VSIZE(&sources) / sizeof(grn_obj *); + for (i = 0; i < n_sources; i++) { + grn_obj *source = GRN_RECORD_VALUE_AT(&sources, i); + grn_obj_unlink(ctx, source); + } + grn_obj_unlink(ctx, &sources); + } +} + +static grn_rc +delete_reference_records(grn_ctx *ctx, grn_obj *table, grn_id id) +{ + grn_hash *cols; + grn_id *key; + + cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, + GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY); + if (!cols) { + return ctx->rc; + } + + if (!grn_table_columns(ctx, table, "", 0, (grn_obj *)cols)) { + grn_hash_close(ctx, cols); + return ctx->rc; + } + + GRN_HASH_EACH(ctx, cols, tid, &key, NULL, NULL, { + grn_obj *col = grn_ctx_at(ctx, *key); + if (col && col->header.type == GRN_COLUMN_INDEX) { + delete_reference_records_in_index(ctx, table, id, col); + if (ctx->rc != GRN_SUCCESS) { + break; + } + } + }); + + grn_hash_close(ctx, cols); + + return ctx->rc; } grn_rc @@ -1579,7 +1663,11 @@ grn_table_delete(grn_ctx *ctx, grn_obj *table, const void *key, unsigned int key GRN_API_ENTER; if (table) { if (key && key_size) { rid = grn_table_get(ctx, table, key, key_size); } - if (is_deletable(ctx, table, rid)) { + if (rid) { + rc = delete_reference_records(ctx, table, rid); + if (rc != GRN_SUCCESS) { + goto exit; + } call_delete_hook(ctx, table, rid, key, key_size); clear_column_values(ctx, table, rid); switch (table->header.type) { @@ -1629,6 +1717,7 @@ grn_table_delete(grn_ctx *ctx, grn_obj *table, const void *key, unsigned int key grn_obj_touch(ctx, table, NULL); } } +exit: GRN_API_RETURN(rc); } @@ -1640,7 +1729,11 @@ _grn_table_delete_by_id(grn_ctx *ctx, grn_obj *table, grn_id id, if (table) { const void *key; unsigned int key_size; - if (is_deletable(ctx, table, id)) { + if (id) { + rc = delete_reference_records(ctx, table, id); + if (rc != GRN_SUCCESS) { + goto exit; + } if ((key = _grn_table_key(ctx, table, id, &key_size))) { call_delete_hook(ctx, table, id, key, key_size); } @@ -1664,6 +1757,7 @@ _grn_table_delete_by_id(grn_ctx *ctx, grn_obj *table, grn_id id, } } } +exit: return rc; } Added: test/command/suite/delete/reference/cross.expected (+40 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/delete/reference/cross.expected 2013-09-20 19:17:19 +0900 (4acb7cd) @@ -0,0 +1,40 @@ +table_create Users TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +table_create URLs TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Users bookmarks COLUMN_VECTOR URLs +[[0,0.0,0.0],true] +column_create URLs author COLUMN_SCALAR Users +[[0,0.0,0.0],true] +column_create URLs bookmarks_index COLUMN_INDEX Users bookmarks +[[0,0.0,0.0],true] +load --table Users +[ +{"_key": "mori", "bookmarks": ["http://mroonga.org/", "http://groonga.org/", "http://ranguba.org/"]} +] +[[0,0.0,0.0],1] +load --table URLs +[ +{"_key": "http://groonga.org/", "author": "mori"} +] +[[0,0.0,0.0],1] +delete URLs --key "http://groonga.org/" +[[0,0.0,0.0],true] +dump +table_create Users TABLE_HASH_KEY ShortText +table_create URLs TABLE_HASH_KEY ShortText +column_create URLs bookmarks_index COLUMN_INDEX Users bookmarks +column_create URLs author COLUMN_SCALAR Users +column_create Users bookmarks COLUMN_VECTOR URLs +load --table Users +[ +["_key","bookmarks"], +["mori",["http://mroonga.org/","http://ranguba.org/"]] +] +load --table URLs +[ +["_key","author"], +["http://mroonga.org/",""], +["http://ranguba.org/",""] +] + Added: test/command/suite/delete/reference/cross.test (+21 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/delete/reference/cross.test 2013-09-20 19:17:19 +0900 (c254cf4) @@ -0,0 +1,21 @@ +table_create Users TABLE_HASH_KEY ShortText +table_create URLs TABLE_HASH_KEY ShortText + +column_create Users bookmarks COLUMN_VECTOR URLs +column_create URLs author COLUMN_SCALAR Users + +column_create URLs bookmarks_index COLUMN_INDEX Users bookmarks + +load --table Users +[ +{"_key": "mori", "bookmarks": ["http://mroonga.org/", "http://groonga.org/", "http://ranguba.org/"]} +] + +load --table URLs +[ +{"_key": "http://groonga.org/", "author": "mori"} +] + +delete URLs --key "http://groonga.org/" + +dump -------------- next part -------------- HTML����������������������������...Download