void nvup_redis_cmd_2outpack(nvup_redis_cmd_t* cmd, navi_buf_chain_t* out_pack) { size_t bulk_cnt = 1; buf_merge_t merge; merge.p = merge.pbuf = (char*) malloc(1024); merge.end = merge.p + 1024; int pt, pt2, i, j; navi_array_part_t* part, *part2; nvup_redis_cmd_key_t* ka; char* arg; switch (cmd->cmd_st) { case NVUP_REDIS_CMDST_1KEY: { switch (cmd->s_key->arg_st) { case NVUP_REDIS_KEY_0ARG: bulk_cnt += 1; break; case NVUP_REDIS_KEY_1ARG: bulk_cnt += 2; break; case NVUP_REDIS_KEY_2ARG: bulk_cnt += 3; break; case NVUP_REDIS_KEY_MARG: bulk_cnt += cmd->s_key->margs->count + 1; break; } } break; case NVUP_REDIS_CMDST_MKEYS: { int pt, i; for (pt = 0; pt < cmd->m_keys->part_size; pt++) { part = cmd->m_keys->parts[pt]; if (part == NULL) break; ka = (nvup_redis_cmd_key_t*) part->allocs; for (i = 0; i < part->used; i++, ka++) { switch (ka->arg_st) { case NVUP_REDIS_KEY_0ARG: bulk_cnt += 1; break; case NVUP_REDIS_KEY_1ARG: bulk_cnt += 2; break; case NVUP_REDIS_KEY_2ARG: bulk_cnt += 3; break; case NVUP_REDIS_KEY_MARG: bulk_cnt += ka->margs->count + 1; break; } } } } break; case NVUP_REDIS_CMDST_PUR_1ARG: bulk_cnt += 1; break; case NVUP_REDIS_CMDST_PUR_MARGS: bulk_cnt += cmd->m_args->count; break; default: return; } merge_output(&merge, out_pack, MBULK_FMT BULK_FMT, bulk_cnt, strlen(cmd->cmd), cmd->cmd); switch (cmd->cmd_st) { case NVUP_REDIS_CMDST_1KEY: { switch (cmd->s_key->arg_st) { case NVUP_REDIS_KEY_0ARG: merge_output(&merge, out_pack, BULK_FMT, strlen(cmd->s_key->key), cmd->s_key->key); break; case NVUP_REDIS_KEY_1ARG: merge_output(&merge, out_pack, BULK_FMT BULK_FMT, strlen(cmd->s_key->key), cmd->s_key->key, strlen(cmd->s_key->arg1), cmd->s_key->arg1); break; case NVUP_REDIS_KEY_2ARG: merge_output(&merge, out_pack, BULK_FMT BULK_FMT BULK_FMT, strlen(cmd->s_key->key), cmd->s_key->key, strlen(cmd->s_key->arg1), cmd->s_key->arg1, strlen(cmd->s_key->arg2), cmd->s_key->arg2); break; case NVUP_REDIS_KEY_MARG: merge_output(&merge, out_pack, BULK_FMT, strlen(cmd->s_key->key), cmd->s_key->key); for (pt = 0; pt < cmd->s_key->margs->part_size; pt++) { part = cmd->s_key->margs->parts[pt]; if (!part) break; char** pa = (char**) part->allocs; for (i = 0; i < part->used; i++, pa++) { merge_output(&merge, out_pack, BULK_FMT, strlen(*pa), *pa); } } break; } merge_output(&merge, out_pack, NULL); return; } case NVUP_REDIS_CMDST_MKEYS: { int pt, i; for (pt = 0; pt < cmd->m_keys->part_size; pt++) { part = cmd->m_keys->parts[pt]; if (part == NULL) break; ka = (nvup_redis_cmd_key_t*) part->allocs; for (i = 0; i < part->used; i++, ka++) { merge_output(&merge, out_pack, BULK_FMT, strlen(ka->key), ka->key); switch (ka->arg_st) { case NVUP_REDIS_KEY_1ARG: merge_output(&merge, out_pack, BULK_FMT, strlen(ka->arg1), ka->arg1); break; case NVUP_REDIS_KEY_2ARG: merge_output(&merge, out_pack, BULK_FMT BULK_FMT, strlen(ka->arg1), ka->arg1, strlen(ka->arg2), ka->arg2); break; case NVUP_REDIS_KEY_MARG: for (pt2 = 0; pt2 < ka->margs->part_size; pt2++) { part2 = ka->margs->parts[pt2]; if (!part2) break; char** pa = (char**) part2->allocs; for (j = 0; j < part2->used; j++, pa++) { merge_output(&merge, out_pack, BULK_FMT, strlen(*pa), *pa); } } break; } } } merge_output(&merge, out_pack, NULL); return; } case NVUP_REDIS_CMDST_PUR_1ARG: merge_output(&merge, out_pack, BULK_FMT, strlen(cmd->s_arg), cmd->s_arg); merge_output(&merge, out_pack, NULL); return; case NVUP_REDIS_CMDST_PUR_MARGS: { for (pt = 0; pt < cmd->m_args->part_size; pt++) { part = cmd->m_args->parts[pt]; if (!part) break; char** pa = (char**) part->allocs; for (i = 0; i < part->used; i++, pa++) { merge_output(&merge, out_pack, BULK_FMT, strlen(*pa), *pa); } } merge_output(&merge, out_pack, NULL); return; } default: return; } //end out swith }
/* It's hard to explain "the rules" for bucket_merge, in large part because * any automatic conflict-resolution scheme is going to be incorrect for * some endcases of *some* app. The scheme here is pretty conservative, * and should be OK for most apps. It's easier to explain what the code * allows than what it forbids: * * Leaving things alone: it's OK if both s2 and s3 leave a piece of s1 * alone (don't delete the key, and don't change the value). * * Key deletion: a transaction (s2 or s3) can delete a key (from s1), but * only if the other transaction (of s2 and s3) doesn't delete the same key. * However, it's not OK for s2 and s3 to, between them, end up deleting all * the keys. This is a higher-level constraint, due to that the caller of * bucket_merge() doesn't have enough info to unlink the resulting empty * bucket from its BTree correctly. It's also not OK if s2 or s3 are empty, * because the transaction that emptied the bucket unlinked the bucket from * the tree, and nothing we do here can get it linked back in again. * * Key insertion: s2 or s3 can add a new key, provided the other transaction * doesn't insert the same key. It's not OK even if they insert the same * <key, value> pair. * * Mapping value modification: s2 or s3 can modify the value associated * with a key in s1, provided the other transaction doesn't make a * modification of the same key to a different value. It's OK if s2 and s3 * both give the same new value to the key while it's hard to be precise about * why, this doesn't seem consistent with that it's *not* OK for both to add * a new key mapping to the same value). */ static PyObject * bucket_merge(Bucket *s1, Bucket *s2, Bucket *s3) { Bucket *r=0; PyObject *s; SetIteration i1 = {0,0,0}, i2 = {0,0,0}, i3 = {0,0,0}; int cmp12, cmp13, cmp23, mapping, set; /* If either "after" bucket is empty, punt. */ if (s2->len == 0 || s3->len == 0) { merge_error(-1, -1, -1, 12); goto err; } if (initSetIteration(&i1, OBJECT(s1), 1) < 0) goto err; if (initSetIteration(&i2, OBJECT(s2), 1) < 0) goto err; if (initSetIteration(&i3, OBJECT(s3), 1) < 0) goto err; mapping = i1.usesValue | i2.usesValue | i3.usesValue; set = !mapping; if (mapping) r = (Bucket *)PyObject_CallObject((PyObject *)&BucketType, NULL); else r = (Bucket *)PyObject_CallObject((PyObject *)&SetType, NULL); if (r == NULL) goto err; if (i1.next(&i1) < 0) goto err; if (i2.next(&i2) < 0) goto err; if (i3.next(&i3) < 0) goto err; /* Consult zodb/btrees/interfaces.py for the meaning of the last * argument passed to merge_error(). */ /* TODO: This isn't passing on errors raised by value comparisons. */ while (i1.position >= 0 && i2.position >= 0 && i3.position >= 0) { TEST_KEY_SET_OR(cmp12, i1.key, i2.key) goto err; TEST_KEY_SET_OR(cmp13, i1.key, i3.key) goto err; if (cmp12==0) { if (cmp13==0) { if (set || (TEST_VALUE(i1.value, i2.value) == 0)) { /* change in i3 value or all same */ if (merge_output(r, &i3, mapping) < 0) goto err; } else if (set || (TEST_VALUE(i1.value, i3.value) == 0)) { /* change in i2 value */ if (merge_output(r, &i2, mapping) < 0) goto err; } else { /* conflicting value changes in i2 and i3 */ merge_error(i1.position, i2.position, i3.position, 1); goto err; } if (i1.next(&i1) < 0) goto err; if (i2.next(&i2) < 0) goto err; if (i3.next(&i3) < 0) goto err; } else if (cmp13 > 0) { /* insert i3 */ if (merge_output(r, &i3, mapping) < 0) goto err; if (i3.next(&i3) < 0) goto err; } else if (set || (TEST_VALUE(i1.value, i2.value) == 0)) { /* deleted in i3 */ if (i3.position == 1) { /* Deleted the first item. This will modify the parent node, so we don't know if merging will be safe */ merge_error(i1.position, i2.position, i3.position, 13); goto err; } if (i1.next(&i1) < 0) goto err; if (i2.next(&i2) < 0) goto err; } else { /* conflicting del in i3 and change in i2 */ merge_error(i1.position, i2.position, i3.position, 2); goto err; } } else if (cmp13 == 0) { if (cmp12 > 0) { /* insert i2 */ if (merge_output(r, &i2, mapping) < 0) goto err; if (i2.next(&i2) < 0) goto err; } else if (set || (TEST_VALUE(i1.value, i3.value) == 0)) { /* deleted in i2 */ if (i2.position == 1) { /* Deleted the first item. This will modify the parent node, so we don't know if merging will be safe */ merge_error(i1.position, i2.position, i3.position, 13); goto err; } if (i1.next(&i1) < 0) goto err; if (i3.next(&i3) < 0) goto err; } else { /* conflicting del in i2 and change in i3 */ merge_error(i1.position, i2.position, i3.position, 3); goto err; } } else { /* Both keys changed */ TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err; if (cmp23==0) { /* dueling inserts or deletes */ merge_error(i1.position, i2.position, i3.position, 4); goto err; } if (cmp12 > 0) { /* insert i2 */ if (cmp23 > 0) { /* insert i3 first */ if (merge_output(r, &i3, mapping) < 0) goto err; if (i3.next(&i3) < 0) goto err; } else { /* insert i2 first */ if (merge_output(r, &i2, mapping) < 0) goto err; if (i2.next(&i2) < 0) goto err; } } else if (cmp13 > 0) { /* Insert i3 */ if (merge_output(r, &i3, mapping) < 0) goto err; if (i3.next(&i3) < 0) goto err; } else { /* 1<2 and 1<3: both deleted 1.key */ merge_error(i1.position, i2.position, i3.position, 5); goto err; } } } while (i2.position >= 0 && i3.position >= 0) { /* New inserts */ TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err; if (cmp23==0) { /* dueling inserts */ merge_error(i1.position, i2.position, i3.position, 6); goto err; } if (cmp23 > 0) { /* insert i3 */ if (merge_output(r, &i3, mapping) < 0) goto err; if (i3.next(&i3) < 0) goto err; } else { /* insert i2 */ if (merge_output(r, &i2, mapping) < 0) goto err; if (i2.next(&i2) < 0) goto err; } } while (i1.position >= 0 && i2.position >= 0) { /* remainder of i1 deleted in i3 */ TEST_KEY_SET_OR(cmp12, i1.key, i2.key) goto err; if (cmp12 > 0) { /* insert i2 */ if (merge_output(r, &i2, mapping) < 0) goto err; if (i2.next(&i2) < 0) goto err; } else if (cmp12==0 && (set || (TEST_VALUE(i1.value, i2.value) == 0))) { /* delete i3 */ if (i1.next(&i1) < 0) goto err; if (i2.next(&i2) < 0) goto err; } else { /* Dueling deletes or delete and change */ merge_error(i1.position, i2.position, i3.position, 7); goto err; } } while (i1.position >= 0 && i3.position >= 0) { /* remainder of i1 deleted in i2 */ TEST_KEY_SET_OR(cmp13, i1.key, i3.key) goto err; if (cmp13 > 0) { /* insert i3 */ if (merge_output(r, &i3, mapping) < 0) goto err; if (i3.next(&i3) < 0) goto err; } else if (cmp13==0 && (set || (TEST_VALUE(i1.value, i3.value) == 0))) { /* delete i2 */ if (i1.next(&i1) < 0) goto err; if (i3.next(&i3) < 0) goto err; } else { /* Dueling deletes or delete and change */ merge_error(i1.position, i2.position, i3.position, 8); goto err; } } if (i1.position >= 0) { /* Dueling deletes */ merge_error(i1.position, i2.position, i3.position, 9); goto err; } while (i2.position >= 0) { /* Inserting i2 at end */ if (merge_output(r, &i2, mapping) < 0) goto err; if (i2.next(&i2) < 0) goto err; } while (i3.position >= 0) { /* Inserting i3 at end */ if (merge_output(r, &i3, mapping) < 0) goto err; if (i3.next(&i3) < 0) goto err; } /* If the output bucket is empty, conflict resolution doesn't have * enough info to unlink it from its containing BTree correctly. */ if (r->len == 0) { merge_error(-1, -1, -1, 10); goto err; } finiSetIteration(&i1); finiSetIteration(&i2); finiSetIteration(&i3); if (s1->next) { Py_INCREF(s1->next); r->next = s1->next; } s = bucket_getstate(r); Py_DECREF(r); return s; err: finiSetIteration(&i1); finiSetIteration(&i2); finiSetIteration(&i3); Py_XDECREF(r); return NULL; }