static void idfetch_update_cb(couchfile_modify_request *rq, sized_buf *k, sized_buf *v, void *arg) { (void)k; (void)rq; //v contains a seq we need to remove ( {Seq,_,_,_,_} ) uint64_t oldseq; sized_buf *delbuf = NULL; index_update_ctx *ctx = (index_update_ctx *) arg; if (v == NULL) { //Doc not found return; } const raw_id_index_value *raw = (raw_id_index_value*) v->buf; oldseq = decode_raw48(raw->db_seq); delbuf = (sized_buf *) fatbuf_get(ctx->deltermbuf, sizeof(sized_buf)); delbuf->buf = (char *) fatbuf_get(ctx->deltermbuf, 6); delbuf->size = 6; memset(delbuf->buf, 0, 6); *(raw_48*)delbuf->buf = encode_raw48(oldseq); ctx->seqacts[ctx->actpos].type = ACTION_REMOVE; ctx->seqacts[ctx->actpos].value.data = NULL; ctx->seqacts[ctx->actpos].key = delbuf; ctx->actpos++; }
static couchstore_error_t local_doc_fetch(couchfile_lookup_request *rq, void *k, sized_buf *v) { sized_buf *id = (sized_buf *) k; LocalDoc **lDoc = (LocalDoc **) rq->callback_ctx; LocalDoc *dp; if (!v) { *lDoc = NULL; return COUCHSTORE_SUCCESS; } fatbuf *ldbuf = fatbuf_alloc(sizeof(LocalDoc) + id->size + v->size); if (ldbuf == NULL) { return COUCHSTORE_ERROR_ALLOC_FAIL; } dp = *lDoc = (LocalDoc *) fatbuf_get(ldbuf, sizeof(LocalDoc)); dp->id.buf = (char *) fatbuf_get(ldbuf, id->size); dp->id.size = id->size; dp->json.buf = (char *) fatbuf_get(ldbuf, v->size); dp->json.size = v->size; dp->deleted = 0; memcpy(dp->id.buf, id->buf, id->size); memcpy(dp->json.buf, v->buf, v->size); return COUCHSTORE_SUCCESS; }
static couchstore_error_t add_doc_to_update_list(Db *db, const Doc *doc, const DocInfo *info, fatbuf *fb, sized_buf *seqterm, sized_buf *idterm, sized_buf *seqval, sized_buf *idval, uint64_t seq, couchstore_save_options options) { couchstore_error_t errcode = COUCHSTORE_SUCCESS; DocInfo updated = *info; updated.db_seq = seq; seqterm->buf = (char *) fatbuf_get(fb, 6); seqterm->size = 6; error_unless(seqterm->buf, COUCHSTORE_ERROR_ALLOC_FAIL); *(raw_48*)seqterm->buf = encode_raw48(seq); if (doc) { size_t disk_size; // Don't compress a doc unless the meta flag is set if (!(info->content_meta & COUCH_DOC_IS_COMPRESSED)) { options &= ~COMPRESS_DOC_BODIES; } errcode = write_doc(db, doc, &updated.bp, &disk_size, options); if (errcode != COUCHSTORE_SUCCESS) { return errcode; } updated.size = disk_size; } else { updated.deleted = 1; updated.bp = 0; updated.size = 0; } *idterm = updated.id; seqval->buf = (char *) fatbuf_get(fb, (44 + updated.id.size + updated.rev_meta.size)); error_unless(seqval->buf, COUCHSTORE_ERROR_ALLOC_FAIL); seqval->size = assemble_seq_index_value(&updated, seqval->buf); idval->buf = (char *) fatbuf_get(fb, (44 + 10 + updated.rev_meta.size)); error_unless(idval->buf, COUCHSTORE_ERROR_ALLOC_FAIL); idval->size = assemble_id_index_value(&updated, idval->buf); //We use 37 + id.size + 2 * rev_meta.size bytes cleanup: return errcode; }
static void docset_init(int numdocs) { testdocset.size = numdocs; testdocset.pos = 0; testdocset.datasize = 0; if (docsetbuf) { fatbuf_free(docsetbuf); docsetbuf = NULL; } docsetbuf = fatbuf_alloc(numdocs * (sizeof(Doc) + sizeof(DocInfo))); testdocset.docs = fatbuf_get(docsetbuf, numdocs * sizeof(Doc)); testdocset.infos = fatbuf_get(docsetbuf, numdocs * sizeof(DocInfo)); ZERO(testdocset.counters); }
//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; }
LIBCOUCHSTORE_API couchstore_error_t couchstore_save_documents(Db *db, Doc* const docs[], DocInfo *infos[], unsigned numdocs, couchstore_save_options options) { couchstore_error_t errcode = COUCHSTORE_SUCCESS; unsigned ii; sized_buf *seqklist, *idklist, *seqvlist, *idvlist; size_t term_meta_size = 0; const Doc *curdoc; uint64_t seq = db->header.update_seq; fatbuf *fb; for (ii = 0; ii < numdocs; ii++) { // Get additional size for terms to be inserted into indexes // IMPORTANT: This must match the sizes of the fatbuf_get calls in add_doc_to_update_list! term_meta_size += 6 + 44 + infos[ii]->id.size + infos[ii]->rev_meta.size + 44 + 10 + infos[ii]->rev_meta.size; } fb = fatbuf_alloc(term_meta_size + numdocs * (sizeof(sized_buf) * 4)); //seq/id key and value lists if (fb == NULL) { return COUCHSTORE_ERROR_ALLOC_FAIL; } seqklist = fatbuf_get(fb, numdocs * sizeof(sized_buf)); idklist = fatbuf_get(fb, numdocs * sizeof(sized_buf)); seqvlist = fatbuf_get(fb, numdocs * sizeof(sized_buf)); idvlist = fatbuf_get(fb, numdocs * sizeof(sized_buf)); for (ii = 0; ii < numdocs; ii++) { seq++; if (docs) { curdoc = docs[ii]; } else { curdoc = NULL; } errcode = add_doc_to_update_list(db, curdoc, infos[ii], fb, &seqklist[ii], &idklist[ii], &seqvlist[ii], &idvlist[ii], seq, options); if (errcode != COUCHSTORE_SUCCESS) { break; } } if (errcode == COUCHSTORE_SUCCESS) { errcode = update_indexes(db, seqklist, seqvlist, idklist, idvlist, numdocs); } fatbuf_free(fb); if (errcode == COUCHSTORE_SUCCESS) { // Fill in the assigned sequence numbers for caller's later use: seq = db->header.update_seq; for (ii = 0; ii < numdocs; ii++) { infos[ii]->db_seq = ++seq; } db->header.update_seq = seq; } return errcode; }
static couchstore_error_t update_indexes(Db *db, sized_buf *seqs, sized_buf *seqvals, sized_buf *ids, sized_buf *idvals, int numdocs) { couchfile_modify_action *idacts; couchfile_modify_action *seqacts; const sized_buf **sorted_ids = NULL; size_t size; fatbuf *actbuf; node_pointer *new_id_root; node_pointer *new_seq_root; couchstore_error_t errcode; couchfile_modify_request seqrq, idrq; sized_buf tmpsb; int ii; /* ** Two action list up to numdocs * 2 in size + Compare keys for ids, ** and compare keys for removed seqs found from id index + ** Max size of a int64 erlang term (for deleted seqs) */ size = 4 * sizeof(couchfile_modify_action) + 2 * sizeof(sized_buf) + 10; actbuf = fatbuf_alloc(numdocs * size); error_unless(actbuf, COUCHSTORE_ERROR_ALLOC_FAIL); idacts = fatbuf_get(actbuf, numdocs * sizeof(couchfile_modify_action) * 2); seqacts = fatbuf_get(actbuf, numdocs * sizeof(couchfile_modify_action) * 2); error_unless(idacts && seqacts, COUCHSTORE_ERROR_ALLOC_FAIL); index_update_ctx fetcharg = { seqacts, 0, &seqs, &seqvals, 0, actbuf }; // Sort the array indexes of ids[] by ascending id. Since we can't pass context info to qsort, // actually sort an array of pointers to the elements of ids[], rather than the array indexes. sorted_ids = malloc(numdocs * sizeof(sized_buf*)); error_unless(sorted_ids, COUCHSTORE_ERROR_ALLOC_FAIL); for (ii = 0; ii < numdocs; ++ii) { sorted_ids[ii] = &ids[ii]; } qsort(sorted_ids, numdocs, sizeof(sorted_ids[0]), &ebin_ptr_compare); // Assemble idacts[] array, in sorted order by id: for (ii = 0; ii < numdocs; ii++) { ptrdiff_t isorted = sorted_ids[ii] - ids; // recover index of ii'th id in sort order idacts[ii * 2].type = ACTION_FETCH; idacts[ii * 2].value.arg = &fetcharg; idacts[ii * 2 + 1].type = ACTION_INSERT; idacts[ii * 2 + 1].value.data = &idvals[isorted]; idacts[ii * 2].key = &ids[isorted]; idacts[ii * 2 + 1].key = &ids[isorted]; } idrq.cmp.compare = ebin_cmp; idrq.cmp.arg = &tmpsb; idrq.db = db; idrq.actions = idacts; idrq.num_actions = numdocs * 2; idrq.reduce = by_id_reduce; idrq.rereduce = by_id_rereduce; idrq.fetch_callback = idfetch_update_cb; idrq.db = db; idrq.compacting = 0; new_id_root = modify_btree(&idrq, db->header.by_id_root, &errcode); error_pass(errcode); while (fetcharg.valpos < numdocs) { seqacts[fetcharg.actpos].type = ACTION_INSERT; seqacts[fetcharg.actpos].value.data = &seqvals[fetcharg.valpos]; seqacts[fetcharg.actpos].key = &seqs[fetcharg.valpos]; fetcharg.valpos++; fetcharg.actpos++; } //printf("Total seq actions: %d\n", fetcharg.actpos); qsort(seqacts, fetcharg.actpos, sizeof(couchfile_modify_action), seq_action_compare); seqrq.cmp.compare = seq_cmp; seqrq.cmp.arg = &tmpsb; seqrq.actions = seqacts; seqrq.num_actions = fetcharg.actpos; seqrq.reduce = by_seq_reduce; seqrq.rereduce = by_seq_rereduce; seqrq.db = db; seqrq.compacting = 0; new_seq_root = modify_btree(&seqrq, db->header.by_seq_root, &errcode); if (errcode != COUCHSTORE_SUCCESS) { fatbuf_free(actbuf); return errcode; } if (db->header.by_id_root != new_id_root) { free(db->header.by_id_root); db->header.by_id_root = new_id_root; } if (db->header.by_seq_root != new_seq_root) { free(db->header.by_seq_root); db->header.by_seq_root = new_seq_root; } cleanup: free(sorted_ids); fatbuf_free(actbuf); return errcode; }