Merge trunk for LP
[m6w6/libmemcached] / libmemcached / auto.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached library
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * Copyright (C) 2006-2009 Brian Aker All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38 #include <libmemcached/common.h>
39
40 static memcached_return_t text_incr_decr(memcached_st *ptr,
41 const char *verb,
42 const char *group_key, size_t group_key_length,
43 const char *key, size_t key_length,
44 uint64_t offset,
45 uint64_t *value)
46 {
47 char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
48 uint32_t server_key;
49 memcached_server_write_instance_st instance;
50 bool no_reply= ptr->flags.no_reply;
51
52 if (memcached_failed(memcached_key_test(*ptr, (const char **)&key, &key_length, 1)))
53 {
54 return memcached_set_error(*ptr, MEMCACHED_BAD_KEY_PROVIDED, MEMCACHED_AT);
55 }
56
57 server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length);
58 instance= memcached_server_instance_fetch(ptr, server_key);
59
60 int send_length;
61 send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE,
62 "%s %.*s%.*s %" PRIu64 "%s\r\n", verb,
63 memcached_print_array(ptr->_namespace),
64 (int)key_length, key,
65 offset, no_reply ? " noreply" : "");
66 if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE || send_length < 0)
67 {
68 return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT,
69 memcached_literal_param("snprintf(MEMCACHED_DEFAULT_COMMAND_SIZE)"));
70 }
71
72 memcached_return_t rc= memcached_do(instance, buffer, (size_t)send_length, true);
73 if (no_reply or memcached_failed(rc))
74 return rc;
75
76 rc= memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL);
77
78 /*
79 So why recheck responce? Because the protocol is brain dead :)
80 The number returned might end up equaling one of the string
81 values. Less chance of a mistake with strncmp() so we will
82 use it. We still called memcached_response() though since it
83 worked its magic for non-blocking IO.
84 */
85 if (not strncmp(buffer, memcached_literal_param("ERROR\r\n")))
86 {
87 *value= 0;
88 rc= MEMCACHED_PROTOCOL_ERROR;
89 }
90 else if (not strncmp(buffer, memcached_literal_param("CLIENT_ERROR\r\n")))
91 {
92 *value= 0;
93 rc= MEMCACHED_PROTOCOL_ERROR;
94 }
95 else if (not strncmp(buffer, memcached_literal_param("NOT_FOUND\r\n")))
96 {
97 *value= 0;
98 rc= MEMCACHED_NOTFOUND;
99 }
100 else
101 {
102 *value= strtoull(buffer, (char **)NULL, 10);
103 rc= MEMCACHED_SUCCESS;
104 }
105
106 return memcached_set_error(*instance, rc, MEMCACHED_AT);
107 }
108
109 static memcached_return_t binary_incr_decr(memcached_st *ptr, uint8_t cmd,
110 const char *group_key, size_t group_key_length,
111 const char *key, size_t key_length,
112 uint64_t offset, uint64_t initial,
113 uint32_t expiration,
114 uint64_t *value)
115 {
116 bool no_reply= ptr->flags.no_reply;
117
118 if (memcached_server_count(ptr) == 0)
119 return memcached_set_error(*ptr, MEMCACHED_NO_SERVERS, MEMCACHED_AT);
120
121 uint32_t server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length);
122 memcached_server_write_instance_st instance= memcached_server_instance_fetch(ptr, server_key);
123
124 if (no_reply)
125 {
126 if(cmd == PROTOCOL_BINARY_CMD_DECREMENT)
127 cmd= PROTOCOL_BINARY_CMD_DECREMENTQ;
128
129 if(cmd == PROTOCOL_BINARY_CMD_INCREMENT)
130 cmd= PROTOCOL_BINARY_CMD_INCREMENTQ;
131 }
132 protocol_binary_request_incr request= {}; // = {.bytes= {0}};
133
134 request.message.header.request.magic= PROTOCOL_BINARY_REQ;
135 request.message.header.request.opcode= cmd;
136 request.message.header.request.keylen= htons((uint16_t)(key_length + memcached_array_size(ptr->_namespace)));
137 request.message.header.request.extlen= 20;
138 request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
139 request.message.header.request.bodylen= htonl((uint32_t)(key_length + memcached_array_size(ptr->_namespace) +request.message.header.request.extlen));
140 request.message.body.delta= memcached_htonll(offset);
141 request.message.body.initial= memcached_htonll(initial);
142 request.message.body.expiration= htonl((uint32_t) expiration);
143
144 struct libmemcached_io_vector_st vector[]=
145 {
146 { sizeof(request.bytes), request.bytes },
147 { memcached_array_size(ptr->_namespace), memcached_array_string(ptr->_namespace) },
148 { key_length, key }
149 };
150
151 memcached_return_t rc;
152 if (memcached_failed(rc= memcached_vdo(instance, vector, 3, true)))
153 {
154 memcached_io_reset(instance);
155 return (rc == MEMCACHED_SUCCESS) ? MEMCACHED_WRITE_FAILURE : rc;
156 }
157
158 if (no_reply)
159 return MEMCACHED_SUCCESS;
160
161 return memcached_response(instance, (char*)value, sizeof(*value), NULL);
162 }
163
164 memcached_return_t memcached_increment(memcached_st *ptr,
165 const char *key, size_t key_length,
166 uint32_t offset,
167 uint64_t *value)
168 {
169 return memcached_increment_by_key(ptr, key, key_length, key, key_length, offset, value);
170 }
171
172 memcached_return_t memcached_decrement(memcached_st *ptr,
173 const char *key, size_t key_length,
174 uint32_t offset,
175 uint64_t *value)
176 {
177 return memcached_decrement_by_key(ptr, key, key_length, key, key_length, offset, value);
178 }
179
180 memcached_return_t memcached_increment_by_key(memcached_st *ptr,
181 const char *group_key, size_t group_key_length,
182 const char *key, size_t key_length,
183 uint64_t offset,
184 uint64_t *value)
185 {
186 memcached_return_t rc;
187 uint64_t local_value;
188 if (not value)
189 value= &local_value;
190
191 if (memcached_failed(rc= initialize_query(ptr)))
192 {
193 return rc;
194 }
195
196 if (memcached_failed(rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol)))
197 {
198 return rc;
199 }
200
201 LIBMEMCACHED_MEMCACHED_INCREMENT_START();
202 if (ptr->flags.binary_protocol)
203 {
204 rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_INCREMENT,
205 group_key, group_key_length, key, key_length,
206 (uint64_t)offset, 0, MEMCACHED_EXPIRATION_NOT_ADD,
207 value);
208 }
209 else
210 {
211 rc= text_incr_decr(ptr, "incr", group_key, group_key_length, key, key_length, offset, value);
212 }
213
214 LIBMEMCACHED_MEMCACHED_INCREMENT_END();
215
216 return rc;
217 }
218
219 memcached_return_t memcached_decrement_by_key(memcached_st *ptr,
220 const char *group_key, size_t group_key_length,
221 const char *key, size_t key_length,
222 uint64_t offset,
223 uint64_t *value)
224 {
225 uint64_t local_value;
226 if (not value)
227 value= &local_value;
228
229 memcached_return_t rc;
230 if (memcached_failed(rc= initialize_query(ptr)))
231 {
232 return rc;
233 }
234
235 if (memcached_failed(rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol)))
236 {
237 return rc;
238 }
239
240
241 LIBMEMCACHED_MEMCACHED_DECREMENT_START();
242 if (ptr->flags.binary_protocol)
243 {
244 rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_DECREMENT,
245 group_key, group_key_length, key, key_length,
246 (uint64_t)offset, 0, MEMCACHED_EXPIRATION_NOT_ADD,
247 value);
248 }
249 else
250 {
251 rc= text_incr_decr(ptr, "decr", group_key, group_key_length, key, key_length, offset, value);
252 }
253
254 LIBMEMCACHED_MEMCACHED_DECREMENT_END();
255
256 return rc;
257 }
258
259 memcached_return_t memcached_increment_with_initial(memcached_st *ptr,
260 const char *key,
261 size_t key_length,
262 uint64_t offset,
263 uint64_t initial,
264 time_t expiration,
265 uint64_t *value)
266 {
267 uint64_t local_value;
268 if (! value)
269 value= &local_value;
270
271 return memcached_increment_with_initial_by_key(ptr, key, key_length,
272 key, key_length,
273 offset, initial, expiration, value);
274 }
275
276 memcached_return_t memcached_increment_with_initial_by_key(memcached_st *ptr,
277 const char *group_key,
278 size_t group_key_length,
279 const char *key,
280 size_t key_length,
281 uint64_t offset,
282 uint64_t initial,
283 time_t expiration,
284 uint64_t *value)
285 {
286 uint64_t local_value;
287 if (not value)
288 value= &local_value;
289
290 memcached_return_t rc;
291 if (memcached_failed(rc= initialize_query(ptr)))
292 {
293 return rc;
294 }
295
296 if (memcached_failed(rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol)))
297 {
298 return rc;
299 }
300
301 LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_START();
302 if (ptr->flags.binary_protocol)
303 rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_INCREMENT,
304 group_key, group_key_length, key, key_length,
305 offset, initial, (uint32_t)expiration,
306 value);
307 else
308 rc= MEMCACHED_PROTOCOL_ERROR;
309
310 LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_END();
311
312 return rc;
313 }
314
315 memcached_return_t memcached_decrement_with_initial(memcached_st *ptr,
316 const char *key,
317 size_t key_length,
318 uint64_t offset,
319 uint64_t initial,
320 time_t expiration,
321 uint64_t *value)
322 {
323 uint64_t local_value;
324 if (! value)
325 value= &local_value;
326
327 return memcached_decrement_with_initial_by_key(ptr, key, key_length,
328 key, key_length,
329 offset, initial, expiration, value);
330 }
331
332 memcached_return_t memcached_decrement_with_initial_by_key(memcached_st *ptr,
333 const char *group_key,
334 size_t group_key_length,
335 const char *key,
336 size_t key_length,
337 uint64_t offset,
338 uint64_t initial,
339 time_t expiration,
340 uint64_t *value)
341 {
342 uint64_t local_value;
343 if (not value)
344 value= &local_value;
345
346 memcached_return_t rc;
347 if (memcached_failed(rc= memcached_validate_key_length(key_length, ptr->flags.binary_protocol)))
348 {
349 return rc;
350 }
351
352 if (memcached_failed(rc= initialize_query(ptr)))
353 {
354 return rc;
355 }
356
357
358 LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_START();
359 if (ptr->flags.binary_protocol)
360 {
361 rc= binary_incr_decr(ptr, PROTOCOL_BINARY_CMD_DECREMENT,
362 group_key, group_key_length, key, key_length,
363 offset, initial, (uint32_t)expiration,
364 value);
365 }
366 else
367 {
368 rc= MEMCACHED_PROTOCOL_ERROR;
369 }
370
371 LIBMEMCACHED_MEMCACHED_INCREMENT_WITH_INITIAL_END();
372
373 return rc;
374 }
375