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