PHP_COUCHBASE_LOCAL void php_couchbase_store_multi_impl_oo(INTERNAL_FUNCTION_PARAMETERS) { lcb_error_t retval; php_couchbase_res *couchbase_res; php_couchbase_ctx *ctx; time_t exp = {0}; long expire = 0; zval *akeys; long persist_to = 0; long replicate_to = 0; struct observe_entry *entries; int numkeys; lcb_store_cmd_t *cmds; lcb_store_cmd_t **commands; int ii; PHP_COUCHBASE_GET_PARAMS(couchbase_res, PHP_COUCHBASE_ARG_F_OO, "a|lll", &akeys, &expire, &persist_to, &replicate_to); if (pcbc_check_expiry(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, expire, &exp) == -1) { /* Incorrect expiry time */ return; } if (validate_simple_observe_clause(couchbase_res->handle, persist_to, replicate_to TSRMLS_CC) == -1) { /* Exception already thrown */ return; } numkeys = zend_hash_num_elements(Z_ARRVAL_P(akeys)); if (numkeys == 0) { zend_throw_exception(cb_illegal_key_exception, "No items specified", 0 TSRMLS_CC); return ; } entries = ecalloc(numkeys, sizeof(struct observe_entry)); commands = ecalloc(numkeys, sizeof(lcb_store_cmd_t *)); cmds = ecalloc(numkeys, sizeof(lcb_store_cmd_t)); /* link the command pointers */ for (ii = 0; ii < numkeys; ++ii) { commands[ii] = cmds + ii; } ctx = ecalloc(1, sizeof(php_couchbase_ctx)); ctx->res = couchbase_res; ctx->rv = return_value; array_init(ctx->rv); for (ii = 0, zend_hash_internal_pointer_reset(Z_ARRVAL_P(akeys)); zend_hash_has_more_elements(Z_ARRVAL_P(akeys)) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(akeys)), ++ii) { char *key = NULL; uint klen; size_t payload_len = 0; char *payload; unsigned int flags = 0; zval **ppzval; if (zend_hash_get_current_key_type(Z_ARRVAL_P(akeys)) != HASH_KEY_IS_STRING) { int xx; for (xx = 0; xx < ii; ++xx) { efree((void *)cmds[xx].v.v0.bytes); } efree(commands); efree(cmds); efree(ctx); release_entry_array(entries, xx); zend_throw_exception(cb_illegal_key_exception, "Invalid key specified (not a string)", 0 TSRMLS_CC); return ; } zend_hash_get_current_key(Z_ARRVAL_P(akeys), &key, NULL, 0); if ((klen = strlen(key)) == 0) { int xx; for (xx = 0; xx < ii; ++xx) { efree((void *)cmds[xx].v.v0.bytes); } efree(commands); efree(cmds); efree(ctx); release_entry_array(entries, xx); zend_throw_exception(cb_illegal_key_exception, "Invalid key specified (empty string)", 0 TSRMLS_CC); return ; } if (zend_hash_get_current_data(Z_ARRVAL_P(akeys), (void **)&ppzval) == FAILURE) { int xx; for (xx = 0; xx < ii; ++xx) { efree((void *)cmds[xx].v.v0.bytes); } efree(commands); efree(cmds); efree(ctx); release_entry_array(entries, xx); zend_throw_exception(cb_exception, "Failed to get data for key", 0 TSRMLS_CC); return ; } payload = php_couchbase_zval_to_payload(*ppzval, &payload_len, &flags, couchbase_res->serializer, couchbase_res->compressor TSRMLS_CC); if (payload == NULL) { /* Shouldn't we call an exception? */ RETURN_FALSE; } if (couchbase_res->prefix_key_len) { char *new_key; klen = spprintf(&new_key, 0, "%s_%s", couchbase_res->prefix_key, key); key = new_key; } entries[ii].nkey = klen; entries[ii].key = emalloc(klen); memcpy(entries[ii].key, key, klen); cmds[ii].v.v0.operation = LCB_SET; cmds[ii].v.v0.key = entries[ii].key; cmds[ii].v.v0.nkey = klen; cmds[ii].v.v0.bytes = payload; cmds[ii].v.v0.nbytes = payload_len; cmds[ii].v.v0.flags = flags; cmds[ii].v.v0.exptime = exp; if (couchbase_res->prefix_key_len) { efree(key); } } retval = lcb_store(couchbase_res->handle, ctx, numkeys, (const lcb_store_cmd_t * const *)commands); couchbase_res->seqno += numkeys; pcbc_start_loop(couchbase_res); /* * Time to release the payloads... */ for (ii = 0; ii < numkeys; ++ii) { efree((void *)cmds[ii].v.v0.bytes); } efree(cmds); efree(commands); if (LCB_SUCCESS != retval) { efree(ctx); couchbase_report_error(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, cb_lcb_exception, "Failed to schedule set request: %s", lcb_strerror(couchbase_res->handle, retval)); release_entry_array(entries, numkeys); RETURN_FALSE; } /* * The item was stored successfully. Did the user want to wait until * it was persisted/replicated? */ if (persist_to != 0 || replicate_to != 0) { int ii = 0; for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(return_value)); zend_hash_has_more_elements(Z_ARRVAL_P(return_value)) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(return_value)), ++ii) { zval **curr_cas; zend_hash_get_current_data(Z_ARRVAL_P(return_value), (void **)&curr_cas); if (Z_STRLEN_PP(curr_cas)) { entries[ii].cas = strtoull(Z_STRVAL_PP(curr_cas), 0, 10); } else { /* @todo what to do here? */ fprintf(stderr, "wtf!\n"); } } retval = simple_observe(couchbase_res->handle, entries, numkeys, persist_to, replicate_to); couchbase_res->rc = retval; if (retval != LCB_SUCCESS) { if (retval == LCB_ETIMEDOUT) { zend_throw_exception(cb_timeout_exception, "Timed out waiting for the objects to persist", 0 TSRMLS_CC); } else { char errmsg[256]; snprintf(errmsg, sizeof(errmsg), "An error occured while waiting for the objects to persist: %s", lcb_strerror(couchbase_res->handle, retval)); zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); } } else { int currsize = 4096; char *errmsg = malloc(currsize); int offset = sprintf(errmsg, "The following documents was mutated:"); int errors = 0; for (ii = 0; ii < numkeys; ++ii) { if (entries[ii].mutated) { if ((offset + entries[ii].nkey + 3) > currsize) { char *p = realloc(errmsg, currsize * 2); if (p) { currsize *= 2; errmsg = p; } } if ((offset + entries[ii].nkey + 3) < currsize) { offset += sprintf(errmsg + offset, " \""); memcpy(errmsg + offset, entries[ii].key, entries[ii].nkey); offset += entries[ii].nkey; offset += sprintf(errmsg + offset, "\""); } errors = 1; } } if (errors) { zend_throw_exception(cb_key_mutated_exception, errmsg, 0 TSRMLS_CC); } free(errmsg); } } release_entry_array(entries, numkeys); efree(ctx); }
PHP_COUCHBASE_LOCAL void php_couchbase_remove_impl(INTERNAL_FUNCTION_PARAMETERS, int oo) { char *key; char *cas = NULL; long klen = 0; long cas_len = 0; long replicate_to = 0; long persist_to = 0; lcb_cas_t cas_v = 0; php_couchbase_res *couchbase_res; lcb_error_t retval; php_couchbase_ctx *ctx; char errmsg[256]; int arg = (oo) ? PHP_COUCHBASE_ARG_F_OO : PHP_COUCHBASE_ARG_F_FUNCTIONAL; PHP_COUCHBASE_GET_PARAMS(couchbase_res, arg, "s|sll", &key, &klen, &cas, &cas_len, &persist_to, &replicate_to); if (klen == 0) { couchbase_report_error(INTERNAL_FUNCTION_PARAM_PASSTHRU, oo, cb_illegal_key_exception, "No key specified: Empty key"); return ; } if (validate_simple_observe_clause(couchbase_res->handle, persist_to, replicate_to TSRMLS_CC) == -1) { /* Exception already thrown */ return; } if (cas_len > 0) { char *e; cas_v = (lcb_cas_t)strtoull(cas, &e, 10); if (*e != '\0') { couchbase_report_error(INTERNAL_FUNCTION_PARAM_PASSTHRU, oo, cb_illegal_key_exception, "Invalid CAS specified"); return; } } retval = do_remove(couchbase_res->handle, key, klen, &cas_v); couchbase_res->rc = retval; switch (retval) { case LCB_SUCCESS: Z_TYPE_P(return_value) = IS_STRING; Z_STRLEN_P(return_value) = spprintf(&(Z_STRVAL_P(return_value)), 0, "%llu", cas_v); break; case LCB_KEY_ENOENT: RETURN_FALSE; /* NOTREACHED */ case LCB_KEY_EEXISTS: if (oo) { couchbase_report_error(INTERNAL_FUNCTION_PARAM_PASSTHRU, oo, cb_key_mutated_exception, "Failed to remove the value from the server: %s", lcb_strerror(couchbase_res->handle, retval)); return ; } else { RETURN_FALSE; } default: couchbase_report_error(INTERNAL_FUNCTION_PARAM_PASSTHRU, oo, cb_lcb_exception, "Failed to remove the value from the server: %s", lcb_strerror(couchbase_res->handle, retval)); return ; } if (retval == LCB_SUCCESS && (persist_to > 0 || replicate_to > 0)) { /* * If we have a durability spec, after the commands have been * issued (and callbacks returned), try to fulfill that spec by * using polling observe internal (please note that this is * only possible from OO) */ struct observe_entry entry; memset(&entry, 0, sizeof(entry)); entry.key = key; entry.nkey = klen; entry.cas = cas_v; retval = simple_observe(couchbase_res->handle, &entry, 1, persist_to, replicate_to); couchbase_res->rc = retval; if (retval != LCB_SUCCESS) { if (retval == LCB_ETIMEDOUT) { zend_throw_exception(cb_timeout_exception, "Timed out waiting for the objects to persist", 0 TSRMLS_CC); } else { snprintf(errmsg, sizeof(errmsg), "observe failed for: %s", klen, key, lcb_strerror(couchbase_res->handle, retval)); zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); } } else { /* @todo checkfor timeout!!! */ if (entry.mutated) { zend_throw_exception(cb_key_mutated_exception, "The document was mutated", 0 TSRMLS_CC); } } } }
PHP_COUCHBASE_LOCAL void php_couchbase_store_impl_oo(INTERNAL_FUNCTION_PARAMETERS, lcb_storage_t op) { lcb_error_t retval; php_couchbase_res *couchbase_res; php_couchbase_ctx *ctx; time_t exp = {0}; unsigned int flags = 0; char *cas = NULL; char *payload; size_t payload_len = 0; unsigned long long cas_v = 0; long expire = 0; long replicate_to = 0; long persist_to = 0; long cas_len = 0; char *key = NULL; zval *value; long klen = 0; lcb_store_cmd_t cmd; const lcb_store_cmd_t *const commands[] = { &cmd }; if (op == LCB_ADD) { PHP_COUCHBASE_GET_PARAMS(couchbase_res, PHP_COUCHBASE_ARG_F_OO, "sz|lll", &key, &klen, &value, &expire, &persist_to, &replicate_to); } else { PHP_COUCHBASE_GET_PARAMS(couchbase_res, PHP_COUCHBASE_ARG_F_OO, "sz|lsll", &key, &klen, &value, &expire, &cas, &cas_len, &persist_to, &replicate_to); } if (pcbc_check_expiry(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, expire, &exp) == -1) { /* Incorrect expiry time */ return; } if (validate_simple_observe_clause(couchbase_res->handle, persist_to, replicate_to TSRMLS_CC) == -1) { /* Exception already thrown */ return; } if (!klen) { zend_throw_exception(cb_illegal_key_exception, "Failed to schedule set request: Empty key", 0 TSRMLS_CC); return ; } if (cas) { char *e; cas_v = (lcb_cas_t)strtoull(cas, &e, 10); if (*e != '\0') { couchbase_report_error(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, cb_illegal_key_exception, "Invalid CAS specified"); return; } } payload = php_couchbase_zval_to_payload(value, &payload_len, &flags, couchbase_res->serializer, couchbase_res->compressor TSRMLS_CC); if (payload == NULL) { /* ?? I guess we should throw an exception here? */ RETURN_FALSE; } if (couchbase_res->prefix_key_len) { klen = spprintf(&key, 0, "%s_%s", couchbase_res->prefix_key, key); } ctx = ecalloc(1, sizeof(php_couchbase_ctx)); ctx->res = couchbase_res; ctx->rv = return_value; couchbase_res->seqno += 1; memset(&cmd, 0, sizeof(cmd)); cmd.v.v0.operation = op; cmd.v.v0.key = key; cmd.v.v0.nkey = klen; cmd.v.v0.bytes = payload; cmd.v.v0.nbytes = payload_len; cmd.v.v0.flags = flags; cmd.v.v0.exptime = exp; cmd.v.v0.cas = (uint64_t)cas_v; retval = lcb_store(couchbase_res->handle, ctx, 1, commands); efree(payload); if (couchbase_res->prefix_key_len) { efree(key); } if (retval != LCB_SUCCESS) { char errmsg[256]; efree(ctx); sprintf(errmsg, "Failed to schedule set request: %s", lcb_strerror(couchbase_res->handle, retval)); zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); return ; } pcbc_start_loop(couchbase_res); if (LCB_SUCCESS != ctx->res->rc) { RETVAL_FALSE; switch (op) { case LCB_ADD: if (LCB_KEY_EEXISTS == ctx->res->rc) { break; } case LCB_APPEND: case LCB_PREPEND: if (LCB_NOT_STORED == ctx->res->rc) { break; } case LCB_REPLACE: case LCB_SET: if (LCB_KEY_ENOENT == ctx->res->rc) { break; } if (cas && LCB_KEY_EEXISTS == ctx->res->rc) { break; } default: { char errmsg[256]; sprintf(errmsg, "Failed to store value to server: %s", lcb_strerror(couchbase_res->handle, ctx->res->rc)); zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); } break; } } else { /* * The item was stored successfully. Did the user want to wait until * it was persisted/replicated? */ if (persist_to != 0 || replicate_to != 0) { struct observe_entry entry; memset(&entry, 0, sizeof(entry)); entry.key = key; entry.nkey = klen; entry.cas = strtoull(Z_STRVAL_P(return_value), 0, 10); retval = simple_observe(couchbase_res->handle, &entry, 1, persist_to, replicate_to); couchbase_res->rc = retval; if (retval != LCB_SUCCESS) { if (retval == LCB_ETIMEDOUT) { zend_throw_exception(cb_timeout_exception, "Timed out waiting for the objects to persist", 0 TSRMLS_CC); } else { char errmsg[512]; snprintf(errmsg, sizeof(errmsg), "observe failed for: %s", klen, key, lcb_strerror(couchbase_res->handle, retval)); zend_throw_exception(cb_lcb_exception, errmsg, 0 TSRMLS_CC); } } else { /* @todo checkfor timeout!!! */ if (entry.mutated) { zend_throw_exception(cb_key_mutated_exception, "The document was mutated", 0 TSRMLS_CC); } } } } efree(ctx); }