fffa42e8e7135cdf0425f9b89b88c5af68529fa6
[awesomized/libmemcached] / src / libmemcached / delete.cc
1 /*
2 +--------------------------------------------------------------------+
3 | libmemcached-awesome - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020-2021 Michael Wallner https://awesome.co/ |
13 +--------------------------------------------------------------------+
14 */
15
16 #include "libmemcached/common.h"
17
18 memcached_return_t memcached_delete(memcached_st *shell, const char *key, size_t key_length,
19 time_t expiration) {
20 return memcached_delete_by_key(shell, key, key_length, key, key_length, expiration);
21 }
22
23 static inline memcached_return_t meta_delete(memcached_instance_st *instance,
24 const char *key, size_t key_length,
25 time_t expiration) {
26
27 char ex_buf[32] = " I T";
28 size_t io_num = 0, ex_len = strlen(ex_buf);
29 libmemcached_io_vector_st io_vec[6] = {};
30 io_vec[io_num++] = {memcached_literal_param("md ")};
31 io_vec[io_num++] = {memcached_array_string(instance->root->_namespace),
32 memcached_array_size(instance->root->_namespace)};
33 io_vec[io_num++] = {key, key_length};
34 if (!memcached_is_replying(instance->root)) {
35 io_vec[io_num++] = {" q", 2};
36 }
37 if (expiration) {
38 ex_len += snprintf(ex_buf + ex_len, sizeof(ex_buf) - ex_len, "%llu", (unsigned long long) expiration);
39 io_vec[io_num++] = {ex_buf, ex_len};
40 }
41 io_vec[io_num++] = {memcached_literal_param("\r\n")};
42
43 /* Send command header, only flush if we are NOT buffering */
44 return memcached_vdo(instance, io_vec, io_num, !memcached_is_buffering(instance->root));
45 }
46
47 static inline memcached_return_t ascii_delete(memcached_instance_st *instance, uint32_t,
48 const char *key, const size_t key_length,
49 const bool reply, const bool is_buffering) {
50 libmemcached_io_vector_st vector[] = {
51 {NULL, 0},
52 {memcached_literal_param("delete ")},
53 {memcached_array_string(instance->root->_namespace),
54 memcached_array_size(instance->root->_namespace)},
55 {key, key_length},
56 {" noreply", reply ? 0 : memcached_literal_param_size(" noreply")},
57 {memcached_literal_param("\r\n")}};
58
59 /* Send command header, only flush if we are NOT buffering */
60 return memcached_vdo(instance, vector, 6, is_buffering ? false : true);
61 }
62
63 static inline memcached_return_t binary_delete(memcached_instance_st *instance, uint32_t server_key,
64 const char *key, const size_t key_length,
65 const bool reply, const bool is_buffering) {
66 protocol_binary_request_delete request = {};
67
68 bool should_flush = is_buffering ? false : true;
69
70 initialize_binary_request(instance, request.message.header);
71
72 if (reply) {
73 request.message.header.request.opcode = PROTOCOL_BINARY_CMD_DELETE;
74 } else {
75 request.message.header.request.opcode = PROTOCOL_BINARY_CMD_DELETEQ;
76 }
77 request.message.header.request.keylen =
78 htons(uint16_t(key_length + memcached_array_size(instance->root->_namespace)));
79 request.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES;
80 request.message.header.request.bodylen =
81 htonl(uint32_t(key_length + memcached_array_size(instance->root->_namespace)));
82
83 libmemcached_io_vector_st vector[] = {{NULL, 0},
84 {request.bytes, sizeof(request.bytes)},
85 {memcached_array_string(instance->root->_namespace),
86 memcached_array_size(instance->root->_namespace)},
87 {key, key_length}};
88
89 memcached_return_t rc = memcached_vdo(instance, vector, 4, should_flush);
90
91 if (memcached_has_replicas(instance)) {
92 request.message.header.request.opcode = PROTOCOL_BINARY_CMD_DELETEQ;
93
94 for (uint32_t x = 0; x < memcached_has_replicas(instance); ++x) {
95 ++server_key;
96
97 if (server_key == memcached_server_count(instance->root)) {
98 server_key = 0;
99 }
100
101 memcached_instance_st *replica = memcached_instance_fetch(instance->root, server_key);
102
103 if (memcached_success(memcached_vdo(replica, vector, 4, should_flush))) {
104 memcached_server_response_decrement(replica);
105 }
106 }
107 }
108
109 return rc;
110 }
111
112 memcached_return_t memcached_delete_by_key(memcached_st *memc, const char *group_key,
113 size_t group_key_length, const char *key,
114 size_t key_length, time_t expiration) {
115 LIBMEMCACHED_MEMCACHED_DELETE_START();
116
117 memcached_return_t rc;
118 if (memcached_fatal(rc = initialize_query(memc, true))) {
119 return rc;
120 }
121
122 if (memcached_fatal(rc = memcached_key_test(*memc, (const char **) &key, &key_length, 1))) {
123 return memcached_last_error(memc);
124 }
125
126 if (expiration && !memcached_is_meta(memc)) {
127 return memcached_set_error(
128 *memc, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT,
129 memcached_literal_param(
130 "Memcached server version does not allow expiration of deleted items"));
131 }
132
133 auto server_key = memcached_generate_hash_with_redistribution(memc, group_key, group_key_length);
134 auto *instance = memcached_instance_fetch(memc, server_key);
135
136 bool is_buffering = memcached_is_buffering(instance->root);
137 bool is_replying = memcached_is_replying(instance->root);
138
139 // If a delete trigger exists, we need a response, so no buffering/noreply
140 if (memc->delete_trigger) {
141 if (is_buffering) {
142 return memcached_set_error(
143 *memc, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT,
144 memcached_literal_param("Delete triggers cannot be used if buffering is enabled"));
145 }
146
147 if (is_replying == false) {
148 return memcached_set_error(
149 *memc, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT,
150 memcached_literal_param(
151 "Delete triggers cannot be used if MEMCACHED_BEHAVIOR_NOREPLY is set"));
152 }
153 }
154
155 if (memcached_is_binary(memc)) {
156 rc = binary_delete(instance, server_key, key, key_length, is_replying, is_buffering);
157 } else if (memcached_is_meta(memc)) {
158 rc = meta_delete(instance, key, key_length, expiration);
159 } else {
160 rc = ascii_delete(instance, server_key, key, key_length, is_replying, is_buffering);
161 }
162
163 if (rc == MEMCACHED_SUCCESS) {
164 if (is_buffering == true) {
165 rc = MEMCACHED_BUFFERED;
166 } else if (is_replying == false) {
167 rc = MEMCACHED_SUCCESS;
168 } else {
169 char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
170 rc = memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL);
171 if (rc == MEMCACHED_DELETED) {
172 rc = MEMCACHED_SUCCESS;
173 if (memc->delete_trigger) {
174 memc->delete_trigger(memc, key, key_length);
175 }
176 }
177 }
178 }
179
180 LIBMEMCACHED_MEMCACHED_DELETE_END();
181 return rc;
182 }