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 #define LOGARGS(instance, lvl) LCB_LOG_##lvl, instance, "pcbc/get", __FILE__, __LINE__
20
21 typedef struct {
22 opcookie_res header;
23 char *key;
24 int key_len;
25 char *bytes;
26 int bytes_len;
27 lcb_U32 flags;
28 lcb_datatype_t datatype;
29 lcb_cas_t cas;
30 } opcookie_get_res;
31
get_callback(lcb_t instance,int cbtype,const lcb_RESPBASE * rb)32 void get_callback(lcb_t instance, int cbtype, const lcb_RESPBASE *rb)
33 {
34 opcookie_get_res *result = ecalloc(1, sizeof(opcookie_get_res));
35 const lcb_RESPGET *resp = (const lcb_RESPGET *)rb;
36 TSRMLS_FETCH();
37
38 PCBC_RESP_ERR_COPY(result->header, cbtype, rb);
39 result->key_len = resp->nkey;
40 if (resp->nkey) {
41 result->key = estrndup(resp->key, resp->nkey);
42 }
43 result->bytes_len = resp->nvalue;
44 if (resp->nvalue) {
45 result->bytes = estrndup(resp->value, resp->nvalue);
46 }
47 result->flags = resp->itmflags;
48 result->datatype = resp->datatype;
49 result->cas = resp->cas;
50
51 opcookie_push((opcookie *)rb->cookie, &result->header);
52 }
53
proc_get_results(pcbc_bucket_t * bucket,zval * return_value,opcookie * cookie,int is_mapped TSRMLS_DC)54 static lcb_error_t proc_get_results(pcbc_bucket_t *bucket, zval *return_value, opcookie *cookie,
55 int is_mapped TSRMLS_DC)
56 {
57 opcookie_get_res *res;
58 lcb_error_t err = LCB_SUCCESS;
59 #ifdef LCB_TRACING
60 lcbtrace_SPAN *parent = cookie->span;
61 lcbtrace_TRACER *tracer = lcb_get_tracer(bucket->conn->lcb);
62 #endif
63
64 // If we are not mapped, we need to throw any op errors
65 if (is_mapped == 0) {
66 err = opcookie_get_first_error(cookie);
67 }
68
69 if (err == LCB_SUCCESS) {
70 FOREACH_OPCOOKIE_RES(opcookie_get_res, res, cookie)
71 {
72 zval *doc = bop_get_return_doc(return_value, res->key, res->key_len, is_mapped TSRMLS_CC);
73
74 if (res->header.err == LCB_SUCCESS) {
75 #ifdef LCB_TRACING
76 lcbtrace_SPAN *span = NULL;
77 if (parent) {
78 lcbtrace_REF ref;
79 ref.type = LCBTRACE_REF_CHILD_OF;
80 ref.span = parent;
81 span = lcbtrace_span_start(tracer, "php/" LCBTRACE_OP_RESPONSE_DECODING, LCBTRACE_NOW, &ref);
82 lcbtrace_span_add_tag_str(span, LCBTRACE_TAG_COMPONENT, pcbc_client_string);
83 lcbtrace_span_add_tag_str(span, LCBTRACE_TAG_SERVICE, LCBTRACE_TAG_SERVICE_KV);
84 }
85 #endif
86 pcbc_document_init_decode(doc, bucket, res->bytes, res->bytes_len, res->flags, res->datatype, res->cas,
87 NULL TSRMLS_CC);
88 #ifdef LCB_TRACING
89 if (span) {
90 lcbtrace_span_finish(span, LCBTRACE_NOW);
91 }
92 #endif
93 } else {
94 pcbc_document_init_error(doc, &res->header TSRMLS_CC);
95 }
96 }
97 }
98
99 FOREACH_OPCOOKIE_RES(opcookie_get_res, res, cookie)
100 {
101 if (res->key) {
102 efree(res->key);
103 }
104 if (res->bytes) {
105 efree(res->bytes);
106 }
107 PCBC_RESP_ERR_FREE(res->header);
108 }
109
110 return err;
111 }
112
pcbc_bucket_get(pcbc_bucket_t * obj,pcbc_pp_state * pp_state,pcbc_pp_id * id,zval ** lock,zval ** expiry,zval ** groupid,zval * return_value TSRMLS_DC)113 void pcbc_bucket_get(pcbc_bucket_t *obj, pcbc_pp_state *pp_state, pcbc_pp_id *id, zval **lock, zval **expiry,
114 zval **groupid, zval *return_value TSRMLS_DC)
115 {
116 int ii, ncmds, nscheduled;
117 opcookie *cookie;
118 lcb_error_t err = LCB_SUCCESS;
119 #ifdef LCB_TRACING
120 lcbtrace_TRACER *tracer = NULL;
121 #endif
122
123 ncmds = pcbc_pp_keycount(pp_state);
124 cookie = opcookie_init();
125
126 #ifdef LCB_TRACING
127 tracer = lcb_get_tracer(obj->conn->lcb);
128 if (tracer) {
129 cookie->span = lcbtrace_span_start(tracer, "php/" LCBTRACE_OP_GET, 0, NULL);
130 lcbtrace_span_add_tag_str(cookie->span, LCBTRACE_TAG_COMPONENT, pcbc_client_string);
131 lcbtrace_span_add_tag_str(cookie->span, LCBTRACE_TAG_SERVICE, LCBTRACE_TAG_SERVICE_KV);
132 }
133 #endif
134
135 nscheduled = 0;
136 for (ii = 0; pcbc_pp_next(pp_state); ++ii) {
137 lcb_CMDGET cmd = {0};
138
139 if (lock) {
140 PCBC_CHECK_ZVAL_LONG(*lock, "lockTime must be an integer");
141 }
142 if (expiry) {
143 PCBC_CHECK_ZVAL_LONG(*expiry, "expiry must be an integer");
144 }
145 if (groupid) {
146 PCBC_CHECK_ZVAL_STRING(*groupid, "groupid must be a string");
147 }
148
149 LCB_CMD_SET_KEY(&cmd, id->str, id->len);
150 #ifdef LCB_TRACING
151 if (cookie->span) {
152 LCB_CMD_SET_TRACESPAN(&cmd, cookie->span);
153 }
154 #endif
155 if (expiry && *expiry) {
156 cmd.lock = 0;
157 cmd.exptime = Z_LVAL_P(*expiry);
158 } else if (lock && *lock) {
159 cmd.lock = 1;
160 cmd.exptime = Z_LVAL_P(*lock);
161 }
162 if (groupid && *groupid) {
163 LCB_KREQ_SIMPLE(&cmd._hashkey, Z_STRVAL_P(*groupid), Z_STRLEN_P(*groupid));
164 }
165 err = lcb_get3(obj->conn->lcb, cookie, &cmd);
166 if (err != LCB_SUCCESS) {
167 break;
168 }
169
170 nscheduled++;
171 }
172 pcbc_assert_number_of_commands(obj->conn->lcb, "get", nscheduled, ncmds, err);
173
174 if (nscheduled) {
175 lcb_wait(obj->conn->lcb);
176 err = proc_get_results(obj, return_value, cookie, pcbc_pp_ismapped(pp_state) TSRMLS_CC);
177 }
178
179 #ifdef LCB_TRACING
180 if (cookie->span) {
181 lcbtrace_span_finish(cookie->span, LCBTRACE_NOW);
182 }
183 #endif
184 opcookie_destroy(cookie);
185
186 if (err != LCB_SUCCESS) {
187 throw_lcb_exception(err);
188 }
189 }
190
191 /* {{{ proto mixed Bucket::get(string $id, array $options) */
PHP_METHOD(Bucket,get)192 PHP_METHOD(Bucket, get)
193 {
194 pcbc_bucket_t *obj = Z_BUCKET_OBJ_P(getThis());
195 pcbc_pp_state pp_state;
196 pcbc_pp_id id;
197 zval *lock = NULL, *expiry = NULL, *groupid = NULL;
198
199 // Note that groupid is experimental here and should not be used.
200 if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id||lockTime,expiry,groupid", &id, &lock, &expiry,
201 &groupid) != SUCCESS) {
202 throw_pcbc_exception("Invalid arguments.", LCB_EINVAL);
203 RETURN_NULL();
204 }
205
206 pcbc_bucket_get(obj, &pp_state, &id, &lock, &expiry, &groupid, return_value TSRMLS_CC);
207 }
208
209 /* {{{ proto mixed Bucket::getAndLock(string $id, int $lockTime, array $options) */
PHP_METHOD(Bucket,getAndLock)210 PHP_METHOD(Bucket, getAndLock)
211 {
212 pcbc_bucket_t *obj = Z_BUCKET_OBJ_P(getThis());
213 pcbc_pp_state pp_state;
214 pcbc_pp_id id;
215 zval *lock = NULL, *groupid = NULL;
216
217 // Note that groupid is experimental here and should not be used.
218 if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id,lockTime||groupid", &id, &lock, &groupid) != SUCCESS) {
219 throw_pcbc_exception("Invalid arguments.", LCB_EINVAL);
220 RETURN_NULL();
221 }
222
223 pcbc_bucket_get(obj, &pp_state, &id, &lock, NULL, &groupid, return_value TSRMLS_CC);
224 }
225
226 /* {{{ proto mixed Bucket::getAndTouch(string $id, int $expiry, array $options) */
PHP_METHOD(Bucket,getAndTouch)227 PHP_METHOD(Bucket, getAndTouch)
228 {
229 pcbc_bucket_t *obj = Z_BUCKET_OBJ_P(getThis());
230 pcbc_pp_state pp_state;
231 pcbc_pp_id id;
232 zval *expiry = NULL, *groupid = NULL;
233
234 // Note that groupid is experimental here and should not be used.
235 if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id,expiry||groupid", &id, &expiry, &groupid) != SUCCESS) {
236 throw_pcbc_exception("Invalid arguments.", LCB_EINVAL);
237 RETURN_NULL();
238 }
239
240 pcbc_bucket_get(obj, &pp_state, &id, NULL, &expiry, &groupid, return_value TSRMLS_CC);
241 }
242
243 // get($id {, $lock, $groupid}) : MetaDoc
PHP_METHOD(Bucket,getFromReplica)244 PHP_METHOD(Bucket, getFromReplica)
245 {
246 pcbc_bucket_t *obj = Z_BUCKET_OBJ_P(getThis());
247 int ii, ncmds, nscheduled;
248 pcbc_pp_state pp_state;
249 pcbc_pp_id id;
250 zval *zindex, *zgroupid;
251 opcookie *cookie;
252 lcb_error_t err = LCB_SUCCESS;
253 #ifdef LCB_TRACING
254 lcbtrace_TRACER *tracer = NULL;
255 #endif
256
257 // Note that groupid is experimental here and should not be used.
258 if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id||index,groupid", &id, &zindex, &zgroupid) != SUCCESS) {
259 throw_pcbc_exception("Invalid arguments.", LCB_EINVAL);
260 RETURN_NULL();
261 }
262
263 ncmds = pcbc_pp_keycount(&pp_state);
264 cookie = opcookie_init();
265 #ifdef LCB_TRACING
266 tracer = lcb_get_tracer(obj->conn->lcb);
267 if (tracer) {
268 cookie->span = lcbtrace_span_start(tracer, "php/" LCBTRACE_OP_GET_FROM_REPLICA, 0, NULL);
269 lcbtrace_span_add_tag_str(cookie->span, LCBTRACE_TAG_COMPONENT, pcbc_client_string);
270 lcbtrace_span_add_tag_str(cookie->span, LCBTRACE_TAG_SERVICE, LCBTRACE_TAG_SERVICE_KV);
271 }
272 #endif
273
274 nscheduled = 0;
275 for (ii = 0; pcbc_pp_next(&pp_state); ++ii) {
276 lcb_CMDGETREPLICA cmd = {0};
277
278 PCBC_CHECK_ZVAL_LONG(zindex, "index must be an integer");
279 PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string");
280
281 LCB_CMD_SET_KEY(&cmd, id.str, id.len);
282 #ifdef LCB_TRACING
283 if (cookie->span) {
284 LCB_CMD_SET_TRACESPAN(&cmd, cookie->span);
285 }
286 #endif
287 if (zindex) {
288 cmd.index = Z_LVAL_P(zindex);
289 if (cmd.index >= 0) {
290 cmd.strategy = LCB_REPLICA_SELECT;
291 } else {
292 cmd.strategy = LCB_REPLICA_FIRST;
293 }
294 }
295 if (zgroupid) {
296 LCB_KREQ_SIMPLE(&cmd._hashkey, Z_STRVAL_P(zgroupid), Z_STRLEN_P(zgroupid));
297 }
298
299 err = lcb_rget3(obj->conn->lcb, cookie, &cmd);
300 if (err != LCB_SUCCESS) {
301 break;
302 }
303 nscheduled++;
304 }
305 pcbc_assert_number_of_commands(obj->conn->lcb, "get_from_replica", nscheduled, ncmds, err);
306
307 if (nscheduled) {
308 lcb_wait(obj->conn->lcb);
309
310 err = proc_get_results(obj, return_value, cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC);
311 }
312
313 #ifdef LCB_TRACING
314 if (cookie->span) {
315 lcbtrace_span_finish(cookie->span, LCBTRACE_NOW);
316 }
317 #endif
318 opcookie_destroy(cookie);
319
320 if (err != LCB_SUCCESS) {
321 throw_lcb_exception(err);
322 }
323 }
324