//Fill in doc from reading file. static couchstore_error_t bp_to_doc(Doc **pDoc, Db *db, cs_off_t bp, couchstore_open_options options) { couchstore_error_t errcode = COUCHSTORE_SUCCESS; int bodylen = 0; char *docbody = NULL; fatbuf *docbuf = NULL; if (options & DECOMPRESS_DOC_BODIES) { bodylen = pread_compressed(&db->file, bp, &docbody); } else { bodylen = pread_bin(&db->file, bp, &docbody); } error_unless(bodylen >= 0, bodylen); // if bodylen is negative it's an error code error_unless(docbody || bodylen == 0, COUCHSTORE_ERROR_READ); error_unless(docbuf = fatbuf_alloc(sizeof(Doc) + bodylen), COUCHSTORE_ERROR_ALLOC_FAIL); *pDoc = (Doc *) fatbuf_get(docbuf, sizeof(Doc)); if (bodylen == 0) { //Empty doc (*pDoc)->data.buf = NULL; (*pDoc)->data.size = 0; return 0; } (*pDoc)->data.buf = (char *) fatbuf_get(docbuf, bodylen); (*pDoc)->data.size = bodylen; memcpy((*pDoc)->data.buf, docbody, bodylen); cleanup: free(docbody); if (errcode < 0) { fatbuf_free(docbuf); } return errcode; }
int modify_node(couchfile_modify_request *rq, couchfile_pointer_info *nptr, int start, int end, couchfile_modify_result *dst) { eterm_buf current_node; int curnode_pos = 0; int read_size = 0; int list_start_pos = 0; int node_len = 0; int node_bound = 0; int errcode = 0; int kpos = 0; char node_type[MAXATOMLEN + 1]; node_type[0] = 0; DBG("Enter modify_node. %d - %d\r\n", start, end); if(start == end) { return 0; } if(nptr == NULL) { current_node = empty_root; } else { if((read_size = pread_bin(rq->fd, nptr->pointer, ¤t_node.buf)) < 0) { return ERROR_READ_FILE; } current_node.size = read_size; DBG("... read node from %d\r\n", nptr->pointer); curnode_pos++; //Skip over 131. } couchfile_modify_result *local_result = make_modres(rq); ei_decode_tuple_header(current_node.buf, &curnode_pos, NULL); if(ei_decode_atom(current_node.buf, &curnode_pos, node_type) < 0) { errcode = ERROR_PARSE; goto cleanup; } list_start_pos = curnode_pos; if(ei_decode_list_header(current_node.buf, &curnode_pos, &node_len) < 0) { errcode = ERROR_PARSE; goto cleanup; } if(strcmp("kv_node", node_type) == 0) { local_result->node_type = KV_NODE; while(start < end) { DBG("act on kvnode item\r\n"); if(node_bound >= node_len) { //We're at the end of a leaf node. DBG(" ... exec action at end!\r\n"); switch(rq->actions[start].type) { case ACTION_INSERT: local_result->modified = 1; mr_push_action(&rq->actions[start], local_result); break; case ACTION_REMOVE: local_result->modified = 1; break; case ACTION_FETCH: if(rq->fetch_callback) { //not found (*rq->fetch_callback)(rq, rq->actions[start].key, NULL); } break; } start++; } else { kpos = find_first_gteq(current_node.buf, list_start_pos, rq->actions[start].cmp_key, &rq->cmp, node_bound); if(kpos < 0) { errcode = ERROR_PARSE; goto cleanup; } //Add items from node_bound up to but not including the current mr_push_kv_range(current_node.buf, list_start_pos, node_bound, rq->cmp.list_pos, local_result); if(rq->cmp.last_cmp_val > 0) // Node key > action key { DBG(" Inserting action before\r\n"); switch(rq->actions[start].type) { case ACTION_INSERT: local_result->modified = 1; mr_push_action(&rq->actions[start], local_result); break; case ACTION_REMOVE: local_result->modified = 1; break; case ACTION_FETCH: if(rq->fetch_callback) { //not found (*rq->fetch_callback)(rq, rq->actions[start].key, NULL); } break; } start++; node_bound = rq->cmp.list_pos; } else if(rq->cmp.last_cmp_val < 0) // Node key < action key { DBG(" -- Continue with this action\r\n"); node_bound = rq->cmp.list_pos + 1; mr_push_kv_range(current_node.buf, list_start_pos, node_bound - 1, node_bound, local_result); } else //Node key == action key { DBG(" Replacing value with action\r\n"); switch(rq->actions[start].type) { case ACTION_INSERT: local_result->modified = 1; mr_push_action(&rq->actions[start], local_result); node_bound = rq->cmp.list_pos + 1; break; case ACTION_REMOVE: local_result->modified = 1; node_bound = rq->cmp.list_pos + 1; break; case ACTION_FETCH: if(rq->fetch_callback) { eterm_buf cb_tmp; int cb_vpos = kpos; ei_decode_tuple_header(current_node.buf, &cb_vpos, NULL); ei_skip_term(current_node.buf, &cb_vpos); cb_tmp.buf = current_node.buf + cb_vpos; cb_tmp.size = cb_vpos; ei_skip_term(current_node.buf, &cb_vpos); cb_tmp.size = cb_vpos - cb_tmp.size; (*rq->fetch_callback)(rq, rq->actions[start].key, &cb_tmp); } node_bound = rq->cmp.list_pos; break; } start++; } } } //Push any items past the end of what we dealt with onto result. if(node_bound < node_len) { mr_push_kv_range(current_node.buf, list_start_pos, node_bound, node_len, local_result); } } else if(strcmp("kp_node", node_type) == 0) { local_result->node_type = KP_NODE; while(start < end) { kpos = find_first_gteq(current_node.buf, list_start_pos, rq->actions[start].cmp_key, &rq->cmp, node_bound); if(kpos < 0) { errcode = ERROR_PARSE; goto cleanup; } if(rq->cmp.list_pos == (node_len - 1)) //got last item in kp_node { //Push all items in node onto mr mr_push_kp_range(current_node.buf, list_start_pos, node_bound, rq->cmp.list_pos, local_result); DBG(" ...descending into final item of kpnode\r\n"); couchfile_pointer_info *desc = read_pointer(current_node.buf, kpos); errcode = modify_node(rq, desc, start, end, local_result); if(local_result->values_end->value.pointer != desc) { free(desc); } if(errcode < 0) { goto cleanup; } node_bound = node_len; break; } else { //Get all actions with key <= the key of the current item in the //kp_node //Push items in node up to but not including current onto mr mr_push_kp_range(current_node.buf, list_start_pos, node_bound, rq->cmp.list_pos - 1, local_result); int range_end = start; while(range_end < end && ((*rq->cmp.compare)(rq->actions[range_end].cmp_key, rq->cmp.last_cmp_key) <= 0)) { range_end++; } DBG(" ...descending into item %d of kpnode\r\n", rq->cmp.list_pos); node_bound = rq->cmp.list_pos + 1; couchfile_pointer_info *desc = read_pointer(current_node.buf, kpos); errcode = modify_node(rq, desc, start, range_end, local_result); if(local_result->values_end->value.pointer != desc) { free(desc); } if(errcode < 0) { goto cleanup; } start = range_end; } } DBG(".. Finished kp node, up to %d\r\n", node_bound); if(node_bound < node_len) { //Processed all the actions but haven't exhausted this kpnode. //push the rest of it onto the mr. mr_push_kp_range(current_node.buf, list_start_pos, node_bound, node_len, local_result); } } else { errcode = ERROR_PARSE; goto cleanup; } //If we've done modifications, write out the last leaf node. errcode = flush_mr(local_result); if(errcode == 0) { if(!local_result->modified && nptr != NULL) { //If we didn't do anything, give back the pointer to the original mr_push_pointerinfo(nptr, dst); } else { //Otherwise, give back the pointers to the nodes we've created. dst->modified = 1; errcode = mr_move_pointers(local_result, dst); } } cleanup: free_modres(local_result); if(current_node.buf != empty_root.buf) { free(current_node.buf); } return errcode; }