1 /**
2 * Copyright 2016-2017 Couchbase, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "couchbase.h"
18
19 typedef struct {
20 opcookie_res header;
21 char *key;
22 int key_len;
23 } opcookie_durability_res;
24
durability_callback(lcb_t instance,const void * cookie,lcb_error_t error,const lcb_durability_resp_t * resp)25 void durability_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_durability_resp_t *resp)
26 {
27 opcookie_durability_res *result = ecalloc(1, sizeof(opcookie_durability_res));
28 TSRMLS_FETCH();
29
30 result->header.err = error;
31 if (resp->v.v0.key) {
32 result->key = estrndup(resp->v.v0.key, resp->v.v0.nkey);
33 }
34
35 opcookie_push((opcookie *)cookie, &result->header);
36 }
37
proc_durability_results(pcbc_bucket_t * bucket,zval * return_value,opcookie * cookie,int is_mapped TSRMLS_DC)38 static lcb_error_t proc_durability_results(pcbc_bucket_t *bucket, zval *return_value, opcookie *cookie,
39 int is_mapped TSRMLS_DC)
40 {
41 opcookie_durability_res *res;
42 lcb_error_t err = LCB_SUCCESS;
43
44 // If we are not mapped, we need to throw any op errors
45 if (is_mapped == 0) {
46 err = opcookie_get_first_error(cookie);
47 }
48
49 if (err == LCB_SUCCESS) {
50 FOREACH_OPCOOKIE_RES(opcookie_durability_res, res, cookie)
51 {
52 zval *doc = bop_get_return_doc(return_value, res->key, res->key_len, is_mapped TSRMLS_CC);
53
54 if (res->header.err == LCB_SUCCESS) {
55 pcbc_document_init(doc, bucket, NULL, 0, 0, 0, NULL TSRMLS_CC);
56 } else {
57 pcbc_document_init_error(doc, &res->header TSRMLS_CC);
58 }
59 }
60 }
61
62 FOREACH_OPCOOKIE_RES(opcookie_durability_res, res, cookie)
63 {
64 if (res->key) {
65 efree(res->key);
66 }
67 }
68
69 return err;
70 }
71
PHP_METHOD(Bucket,durability)72 PHP_METHOD(Bucket, durability)
73 {
74 pcbc_bucket_t *obj = Z_BUCKET_OBJ_P(getThis());
75 lcb_durability_cmd_t *cmd = NULL;
76 lcb_durability_opts_t opts = {0};
77 lcb_durability_cmd_t **cmds = NULL;
78 int ii, num_cmds;
79 pcbc_pp_state pp_state;
80 pcbc_pp_id id;
81 zval *zcas, *zgroupid, *zpersist, *zreplica;
82 opcookie *cookie;
83 lcb_error_t err;
84
85 if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id||cas,groupid,persist_to,replicate_to", &id, &zcas,
86 &zgroupid, &zpersist, &zreplica) != SUCCESS) {
87 throw_pcbc_exception("Invalid arguments.", LCB_EINVAL);
88 RETURN_NULL();
89 }
90
91 num_cmds = pcbc_pp_keycount(&pp_state);
92 cmd = emalloc(sizeof(lcb_durability_cmd_t) * num_cmds);
93 cmds = emalloc(sizeof(lcb_durability_cmd_t *) * num_cmds);
94 memset(cmd, 0, sizeof(lcb_durability_cmd_t) * num_cmds);
95
96 for (ii = 0; pcbc_pp_next(&pp_state); ++ii) {
97 PCBC_CHECK_ZVAL_STRING(zcas, "cas must be a string");
98 PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string");
99 PCBC_CHECK_ZVAL_LONG(zpersist, "persist_to must be an integer");
100 PCBC_CHECK_ZVAL_LONG(zreplica, "replicate_to must be an integer");
101
102 cmd[ii].version = 0;
103 cmd[ii].v.v0.key = id.str;
104 cmd[ii].v.v0.nkey = id.len;
105 if (zcas) {
106 cmd[ii].v.v0.cas = pcbc_cas_decode(zcas TSRMLS_CC);
107 }
108 if (zgroupid) {
109 cmd[ii].v.v0.hashkey = Z_STRVAL_P(zgroupid);
110 cmd[ii].v.v0.nhashkey = Z_STRLEN_P(zgroupid);
111 }
112
113 // These are written through each iteration, but only used once.
114 if (zpersist) {
115 opts.v.v0.persist_to = (lcb_U16)Z_LVAL_P(zpersist);
116 }
117 if (zreplica) {
118 opts.v.v0.replicate_to = (lcb_U16)Z_LVAL_P(zreplica);
119 }
120
121 cmds[ii] = &cmd[ii];
122 }
123
124 cookie = opcookie_init();
125
126 err = lcb_durability_poll(obj->conn->lcb, cookie, &opts, num_cmds, (const lcb_durability_cmd_t *const *)cmds);
127
128 if (err == LCB_SUCCESS) {
129 lcb_wait(obj->conn->lcb);
130
131 err = proc_durability_results(obj, return_value, cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC);
132 }
133
134 opcookie_destroy(cookie);
135 efree(cmds);
136 efree(cmd);
137
138 if (err != LCB_SUCCESS) {
139 throw_lcb_exception(err);
140 }
141 }
142