1 |
dpavlin |
2 |
/* |
2 |
|
|
* PostgreSQL functions to interface with memcache. |
3 |
|
|
* |
4 |
|
|
* $PostgreSQL: pgmemcache/pgmemcache.c,v 1.6 2004/12/29 18:20:57 seanc Exp $ |
5 |
|
|
* |
6 |
|
|
* Copyright (c) 2004 Sean Chittenden <sean@chittenden.org> |
7 |
|
|
* |
8 |
|
|
* Permission is hereby granted, free of charge, to any person |
9 |
|
|
* obtaining a copy of this software and associated documentation |
10 |
|
|
* files (the "Software"), to deal in the Software without |
11 |
|
|
* restriction, including without limitation the rights to use, copy, |
12 |
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies |
13 |
|
|
* of the Software, and to permit persons to whom the Software is |
14 |
|
|
* furnished to do so, subject to the following conditions: |
15 |
|
|
* |
16 |
|
|
* The above copyright notice and this permission notice shall be |
17 |
|
|
* included in all copies or substantial portions of the Software. |
18 |
|
|
* |
19 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
20 |
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
21 |
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
22 |
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
23 |
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
24 |
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
25 |
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
26 |
|
|
* SOFTWARE. |
27 |
|
|
* |
28 |
|
|
* http://people.FreeBSD.org/~seanc/pgmemcache/ |
29 |
|
|
*/ |
30 |
|
|
|
31 |
|
|
#include "postgres.h" |
32 |
|
|
#include "fmgr.h" |
33 |
|
|
#include "executor/spi.h" |
34 |
|
|
#include "lib/stringinfo.h" |
35 |
|
|
#include "utils/datetime.h" |
36 |
|
|
#include "utils/palloc.h" |
37 |
dpavlin |
5 |
#include "utils/memutils.h" |
38 |
dpavlin |
2 |
|
39 |
|
|
#include "pgmemcache.h" |
40 |
|
|
|
41 |
|
|
/* Global memcache instance. */ |
42 |
|
|
static struct memcache *mc = NULL; |
43 |
|
|
static struct memcache_ctxt *ctxt = NULL; |
44 |
|
|
|
45 |
|
|
/* Add a small work around function to compensate for PostgreSQLs lack |
46 |
|
|
* of actual memory functions: PostgreSQL supplies macros instead. */ |
47 |
|
|
inline static void mcm_pfree(void *ptr); |
48 |
|
|
inline static void *mcm_palloc(const size_t size); |
49 |
|
|
inline static void *mcm_repalloc(void *ptr, const size_t size); |
50 |
|
|
static bool _memcache_init(void); |
51 |
|
|
static Datum memcache_atomic_op(int type, PG_FUNCTION_ARGS); |
52 |
|
|
static text *memcache_gen_host(const struct memcache_server *ms); |
53 |
|
|
static Datum memcache_set_cmd(int type, PG_FUNCTION_ARGS); |
54 |
|
|
|
55 |
|
|
#define MCM_SET_CMD_TYPE_ADD 0x0001 |
56 |
|
|
#define MCM_SET_CMD_TYPE_REPLACE 0x0002 |
57 |
|
|
#define MCM_SET_CMD_TYPE_SET 0x0004 |
58 |
|
|
|
59 |
|
|
#define MCM_DATE_TYPE_INTERVAL 0x0010 |
60 |
|
|
#define MCM_DATE_TYPE_TIMESTAMP 0x0020 |
61 |
|
|
|
62 |
|
|
#define MCM_ATOMIC_TYPE_DECR 0x0040 |
63 |
|
|
#define MCM_ATOMIC_TYPE_INCR 0x0080 |
64 |
|
|
|
65 |
|
|
/* Don't add a server until a caller has called memcache_init(). This |
66 |
|
|
* is probably an unnecessary degree of explicitness, but it forces |
67 |
|
|
* good calling conventions for pgmemcache. */ |
68 |
|
|
#define MCM_CHECK(_ret) do { \ |
69 |
|
|
if (mc == NULL || ctxt == NULL) { \ |
70 |
|
|
elog(ERROR, "%s(): mc is NULL, call memcache_init()", __FUNCTION__); \ |
71 |
|
|
_ret; \ |
72 |
|
|
} \ |
73 |
|
|
} while(0) |
74 |
|
|
|
75 |
|
|
/* Add a small work around function to compensate for PostgreSQLs lack |
76 |
|
|
* of a palloc(3), pfree(3), and prealloc(3) functions. They exist, |
77 |
|
|
* but are macros, which is useless with regards to function pointers |
78 |
|
|
* if the macro doesn't have the same signature. Create that |
79 |
|
|
* signature here. */ |
80 |
|
|
inline static void |
81 |
|
|
mcm_pfree(void *ptr) { |
82 |
|
|
return pfree(ptr); |
83 |
|
|
} |
84 |
|
|
|
85 |
|
|
|
86 |
|
|
inline static void * |
87 |
|
|
mcm_palloc(const size_t size) { |
88 |
|
|
return palloc(size); |
89 |
|
|
} |
90 |
|
|
|
91 |
|
|
|
92 |
|
|
inline static void * |
93 |
|
|
mcm_repalloc(void *ptr, const size_t size) { |
94 |
|
|
return repalloc(ptr, size); |
95 |
|
|
} |
96 |
|
|
|
97 |
|
|
|
98 |
|
|
Datum |
99 |
|
|
memcache_add(PG_FUNCTION_ARGS) { |
100 |
|
|
return memcache_set_cmd(MCM_SET_CMD_TYPE_ADD | MCM_DATE_TYPE_INTERVAL, fcinfo); |
101 |
|
|
} |
102 |
|
|
|
103 |
|
|
|
104 |
|
|
Datum |
105 |
|
|
memcache_add_absexpire(PG_FUNCTION_ARGS) { |
106 |
|
|
return memcache_set_cmd(MCM_SET_CMD_TYPE_ADD | MCM_DATE_TYPE_TIMESTAMP, fcinfo); |
107 |
|
|
} |
108 |
|
|
|
109 |
|
|
|
110 |
|
|
static Datum |
111 |
|
|
memcache_atomic_op(int type, PG_FUNCTION_ARGS) { |
112 |
|
|
text *key; |
113 |
|
|
size_t key_len; |
114 |
|
|
u_int32_t val, |
115 |
|
|
ret = 0; |
116 |
|
|
|
117 |
|
|
MCM_CHECK(PG_RETURN_NULL()); |
118 |
|
|
|
119 |
|
|
SPI_connect(); |
120 |
|
|
|
121 |
|
|
key = PG_GETARG_TEXT_P(0); |
122 |
|
|
key_len = VARSIZE(key) - VARHDRSZ; |
123 |
|
|
if (key_len < 1) |
124 |
|
|
elog(ERROR, "Unable to have a zero length key"); |
125 |
|
|
|
126 |
|
|
val = 1; |
127 |
|
|
if (fcinfo->arg[1] != NULL && !PG_ARGISNULL(1)) { |
128 |
|
|
val = PG_GETARG_UINT32(1); |
129 |
|
|
} |
130 |
|
|
|
131 |
|
|
if (type & MCM_ATOMIC_TYPE_DECR) |
132 |
|
|
ret = mcm_decr(ctxt, mc, VARDATA(key), key_len, val); |
133 |
|
|
else if (type & MCM_ATOMIC_TYPE_INCR) |
134 |
|
|
ret = mcm_incr(ctxt, mc, VARDATA(key), key_len, val); |
135 |
|
|
else |
136 |
|
|
elog(ERROR, "%s():%s:%u\tunknown atomic type 0x%x", __FUNCTION__, __FILE__, __LINE__, type); |
137 |
|
|
|
138 |
|
|
SPI_finish(); |
139 |
|
|
|
140 |
|
|
PG_RETURN_UINT32(ret); |
141 |
|
|
} |
142 |
|
|
|
143 |
|
|
|
144 |
|
|
Datum |
145 |
|
|
memcache_decr(PG_FUNCTION_ARGS) { |
146 |
|
|
return memcache_atomic_op(MCM_ATOMIC_TYPE_DECR, fcinfo); |
147 |
|
|
} |
148 |
|
|
|
149 |
|
|
|
150 |
|
|
Datum |
151 |
|
|
memcache_delete(PG_FUNCTION_ARGS) { |
152 |
|
|
text *key; |
153 |
|
|
size_t key_len; |
154 |
|
|
Interval *span; |
155 |
|
|
float8 hold; |
156 |
|
|
int ret; |
157 |
|
|
|
158 |
|
|
MCM_CHECK(PG_RETURN_NULL()); |
159 |
|
|
|
160 |
|
|
SPI_connect(); |
161 |
|
|
|
162 |
|
|
key = PG_GETARG_TEXT_P(0); |
163 |
|
|
key_len = VARSIZE(key) - VARHDRSZ; |
164 |
|
|
if (key_len < 1) |
165 |
|
|
elog(ERROR, "Unable to have a zero length key"); |
166 |
|
|
|
167 |
|
|
hold = 0.0; |
168 |
|
|
if (fcinfo->arg[1] != NULL && !PG_ARGISNULL(1)) { |
169 |
|
|
span = PG_GETARG_INTERVAL_P(1); |
170 |
|
|
|
171 |
|
|
#ifdef HAVE_INT64_TIMESTAMP |
172 |
|
|
hold = (span->time / 1000000e0); |
173 |
|
|
#else |
174 |
|
|
hold = span->time; |
175 |
|
|
#endif |
176 |
|
|
if (span->month != 0) { |
177 |
|
|
hold += ((365.25 * 86400) * (span->month / 12)); |
178 |
|
|
hold += ((30.0 * 86400) * (span->month % 12)); |
179 |
|
|
} |
180 |
|
|
} |
181 |
|
|
|
182 |
|
|
ret = mcm_delete(ctxt, mc, VARDATA(key), key_len, (time_t)hold); |
183 |
|
|
|
184 |
|
|
SPI_finish(); |
185 |
|
|
|
186 |
|
|
if (ret == 0) |
187 |
|
|
PG_RETURN_BOOL(true); |
188 |
|
|
else if (ret > 0) |
189 |
|
|
PG_RETURN_BOOL(false); |
190 |
|
|
else |
191 |
|
|
elog(ERROR, "Internal libmemcache(3) error"); |
192 |
|
|
|
193 |
|
|
PG_RETURN_BOOL(ret); |
194 |
|
|
} |
195 |
|
|
|
196 |
|
|
|
197 |
|
|
/* Depreciated: use memcache_flush() */ |
198 |
|
|
Datum |
199 |
|
|
memcache_flush_all(PG_FUNCTION_ARGS) { |
200 |
|
|
return memcache_flush(fcinfo); |
201 |
|
|
} |
202 |
|
|
|
203 |
|
|
|
204 |
|
|
Datum |
205 |
|
|
memcache_flush_all0(PG_FUNCTION_ARGS) { |
206 |
|
|
text *key; |
207 |
|
|
size_t key_len; |
208 |
|
|
int ret; |
209 |
|
|
|
210 |
|
|
MCM_CHECK(PG_RETURN_NULL()); |
211 |
|
|
|
212 |
|
|
SPI_connect(); |
213 |
|
|
|
214 |
|
|
key = PG_GETARG_TEXT_P(0); |
215 |
|
|
key_len = VARSIZE(key) - VARHDRSZ; |
216 |
|
|
if (key_len < 1) |
217 |
|
|
elog(ERROR, "Unable to have a zero length key"); |
218 |
|
|
|
219 |
|
|
ret = mcm_flush_all(ctxt, mc); |
220 |
|
|
|
221 |
|
|
SPI_finish(); |
222 |
|
|
|
223 |
|
|
if (ret == 0) |
224 |
|
|
PG_RETURN_BOOL(true); |
225 |
|
|
else if (ret > 0) |
226 |
|
|
PG_RETURN_BOOL(false); |
227 |
|
|
else |
228 |
|
|
elog(ERROR, "Internal libmemcache(3) error"); |
229 |
|
|
|
230 |
|
|
PG_RETURN_BOOL(ret); |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
|
234 |
|
|
Datum |
235 |
|
|
memcache_flush(PG_FUNCTION_ARGS) { |
236 |
|
|
struct memcache_server *ms; |
237 |
|
|
text *key; |
238 |
|
|
size_t key_len; |
239 |
|
|
int ret; |
240 |
|
|
u_int32_t hash; |
241 |
|
|
|
242 |
|
|
MCM_CHECK(PG_RETURN_NULL()); |
243 |
|
|
|
244 |
|
|
SPI_connect(); |
245 |
|
|
|
246 |
|
|
key = PG_GETARG_TEXT_P(0); |
247 |
|
|
key_len = VARSIZE(key) - VARHDRSZ; |
248 |
|
|
if (key_len < 1) |
249 |
|
|
elog(ERROR, "Unable to have a zero length key"); |
250 |
|
|
|
251 |
|
|
hash = mcm_hash_key(ctxt, VARDATA(key), key_len); |
252 |
|
|
ms = mcm_server_find(ctxt, mc, hash); |
253 |
|
|
|
254 |
|
|
ret = mcm_flush(ctxt, mc, ms); |
255 |
|
|
|
256 |
|
|
SPI_finish(); |
257 |
|
|
|
258 |
|
|
if (ret == 0) |
259 |
|
|
PG_RETURN_BOOL(true); |
260 |
|
|
else if (ret > 0) |
261 |
|
|
PG_RETURN_BOOL(false); |
262 |
|
|
else |
263 |
|
|
elog(ERROR, "Internal libmemcache(3) error"); |
264 |
|
|
|
265 |
|
|
PG_RETURN_BOOL(ret); |
266 |
|
|
} |
267 |
|
|
|
268 |
|
|
|
269 |
|
|
Datum |
270 |
|
|
memcache_free(PG_FUNCTION_ARGS) { |
271 |
|
|
MCM_CHECK(PG_RETURN_BOOL(false)); |
272 |
|
|
|
273 |
|
|
SPI_connect(); |
274 |
|
|
|
275 |
|
|
mcm_free(ctxt, mc); |
276 |
|
|
mc = NULL; |
277 |
|
|
|
278 |
|
|
mcMemFreeCtxt(ctxt); |
279 |
|
|
ctxt = NULL; |
280 |
|
|
|
281 |
|
|
SPI_finish(); |
282 |
|
|
|
283 |
|
|
PG_RETURN_BOOL(true); |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
|
287 |
|
|
Datum |
288 |
|
|
memcache_get(PG_FUNCTION_ARGS) { |
289 |
|
|
text *key, |
290 |
|
|
*ret; |
291 |
|
|
size_t key_len; |
292 |
|
|
struct memcache_req *req; |
293 |
|
|
struct memcache_res *res; |
294 |
|
|
|
295 |
|
|
MCM_CHECK(PG_RETURN_NULL()); |
296 |
|
|
|
297 |
|
|
SPI_connect(); |
298 |
|
|
|
299 |
|
|
key = PG_GETARG_TEXT_P(0); |
300 |
|
|
key_len = VARSIZE(key) - VARHDRSZ; |
301 |
|
|
if (key_len < 1) |
302 |
|
|
elog(ERROR, "Unable to have a zero length key"); |
303 |
|
|
|
304 |
|
|
req = mcm_req_new(ctxt); |
305 |
|
|
if (req == NULL) |
306 |
|
|
elog(ERROR, "Unable to create a new memcache request structure"); |
307 |
|
|
|
308 |
|
|
res = mcm_req_add(ctxt, req, VARDATA(key), key_len); |
309 |
|
|
if (res == NULL) |
310 |
|
|
elog(ERROR, "Unable to create a new memcache responose structure"); |
311 |
|
|
|
312 |
|
|
mcm_get(ctxt, mc, req); |
313 |
|
|
|
314 |
|
|
if (!(res->_flags & MCM_RES_FOUND)) { |
315 |
|
|
SPI_finish(); |
316 |
|
|
PG_RETURN_NULL(); |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
ret = (text *)SPI_palloc(res->bytes + VARHDRSZ); |
320 |
|
|
VARATT_SIZEP(ret) = res->bytes + VARHDRSZ; |
321 |
|
|
memcpy(VARDATA(ret), res->val, res->bytes); |
322 |
|
|
|
323 |
|
|
SPI_finish(); |
324 |
|
|
|
325 |
|
|
PG_RETURN_TEXT_P(ret); |
326 |
|
|
} |
327 |
|
|
|
328 |
|
|
|
329 |
|
|
Datum |
330 |
|
|
memcache_hash(PG_FUNCTION_ARGS) { |
331 |
|
|
text *key; |
332 |
|
|
size_t key_len; |
333 |
|
|
u_int32_t hash; |
334 |
|
|
|
335 |
|
|
SPI_connect(); |
336 |
|
|
|
337 |
|
|
key = PG_GETARG_TEXT_P(0); |
338 |
|
|
key_len = VARSIZE(key) - VARHDRSZ; |
339 |
|
|
if (key_len < 1) |
340 |
|
|
elog(ERROR, "Unable to have a zero length key"); |
341 |
|
|
|
342 |
|
|
hash = mcm_hash_key(ctxt, VARDATA(key), key_len); |
343 |
|
|
|
344 |
|
|
SPI_finish(); |
345 |
|
|
|
346 |
|
|
PG_RETURN_UINT32(hash); |
347 |
|
|
} |
348 |
|
|
|
349 |
|
|
|
350 |
|
|
Datum |
351 |
|
|
memcache_incr(PG_FUNCTION_ARGS) { |
352 |
|
|
return memcache_atomic_op(MCM_ATOMIC_TYPE_INCR, fcinfo); |
353 |
|
|
} |
354 |
|
|
|
355 |
|
|
|
356 |
|
|
Datum |
357 |
|
|
memcache_init(PG_FUNCTION_ARGS) { |
358 |
|
|
bool ret; |
359 |
|
|
|
360 |
|
|
SPI_connect(); |
361 |
|
|
ret = _memcache_init(); |
362 |
|
|
SPI_finish(); |
363 |
|
|
PG_RETURN_BOOL(ret); |
364 |
|
|
} |
365 |
|
|
|
366 |
|
|
|
367 |
|
|
/* Caller of _memcache_init() must call SPI_connect() before calling |
368 |
|
|
* _memcache_init(). */ |
369 |
|
|
static bool |
370 |
|
|
_memcache_init(void) { |
371 |
|
|
MemoryContext oc; |
372 |
|
|
|
373 |
|
|
/* Return FALSE that way callers can conditionally add servers to |
374 |
|
|
* the instance. */ |
375 |
|
|
if (ctxt != NULL) |
376 |
|
|
return false; |
377 |
|
|
|
378 |
|
|
/* Make sure mc is allocated in the top memory context */ |
379 |
|
|
oc = MemoryContextSwitchTo(TopMemoryContext); |
380 |
|
|
|
381 |
|
|
/* Initialize libmemcache's memory functions */ |
382 |
|
|
ctxt = mcMemNewCtxt(mcm_pfree, mcm_palloc, NULL, mcm_repalloc); |
383 |
|
|
if (ctxt == NULL) |
384 |
|
|
elog(ERROR, "memcache_init: unable to create a memcache(3) memory context"); |
385 |
|
|
|
386 |
|
|
mc = mcm_new(ctxt); |
387 |
|
|
if (mc == NULL) |
388 |
|
|
elog(ERROR, "memcache_init: unable to allocate memory"); |
389 |
|
|
|
390 |
|
|
/* Return to old memory context */ |
391 |
|
|
MemoryContextSwitchTo(oc); |
392 |
|
|
|
393 |
|
|
return true; |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
|
397 |
|
|
Datum |
398 |
|
|
memcache_replace(PG_FUNCTION_ARGS) { |
399 |
|
|
return memcache_set_cmd(MCM_SET_CMD_TYPE_REPLACE | MCM_DATE_TYPE_INTERVAL, fcinfo); |
400 |
|
|
} |
401 |
|
|
|
402 |
|
|
|
403 |
|
|
Datum |
404 |
|
|
memcache_replace_absexpire(PG_FUNCTION_ARGS) { |
405 |
|
|
return memcache_set_cmd(MCM_SET_CMD_TYPE_REPLACE | MCM_DATE_TYPE_TIMESTAMP, fcinfo); |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
|
409 |
|
|
Datum |
410 |
|
|
memcache_set(PG_FUNCTION_ARGS) { |
411 |
|
|
return memcache_set_cmd(MCM_SET_CMD_TYPE_SET | MCM_DATE_TYPE_INTERVAL, fcinfo); |
412 |
|
|
} |
413 |
|
|
|
414 |
|
|
|
415 |
|
|
Datum |
416 |
|
|
memcache_set_absexpire(PG_FUNCTION_ARGS) { |
417 |
|
|
return memcache_set_cmd(MCM_SET_CMD_TYPE_SET | MCM_DATE_TYPE_TIMESTAMP, fcinfo); |
418 |
|
|
} |
419 |
|
|
|
420 |
|
|
|
421 |
|
|
static Datum |
422 |
|
|
memcache_set_cmd(int type, PG_FUNCTION_ARGS) { |
423 |
|
|
text *key, |
424 |
|
|
*val_t; |
425 |
|
|
char *val; |
426 |
|
|
size_t key_len, val_len; |
427 |
|
|
Interval *span; |
428 |
|
|
float8 expire; |
429 |
|
|
TimestampTz timestamptz; |
430 |
|
|
struct pg_tm tm; |
431 |
|
|
fsec_t fsec; |
432 |
|
|
u_int16_t flags; |
433 |
|
|
int ret = 0; |
434 |
|
|
|
435 |
|
|
MCM_CHECK(PG_RETURN_BOOL(false)); |
436 |
|
|
|
437 |
|
|
SPI_connect(); |
438 |
|
|
|
439 |
|
|
if (PG_ARGISNULL(0)) { |
440 |
|
|
elog(ERROR, "Unable to have a NULL key"); |
441 |
|
|
} |
442 |
|
|
key = PG_GETARG_TEXT_P(0); |
443 |
|
|
key_len = VARSIZE(key) - VARHDRSZ; |
444 |
|
|
if (key_len < 1) |
445 |
|
|
elog(ERROR, "Unable to have a zero length key"); |
446 |
|
|
|
447 |
|
|
if (fcinfo->arg[1] == NULL || PG_ARGISNULL(1)) { |
448 |
|
|
val_t = NULL; |
449 |
|
|
val = NULL; |
450 |
|
|
val_len = 0; |
451 |
|
|
} else { |
452 |
|
|
val_t = PG_GETARG_TEXT_P(1); |
453 |
|
|
val = (char *)VARDATA(val_t); |
454 |
|
|
val_len = VARSIZE(val_t) - VARHDRSZ; |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
expire = 0.0; |
458 |
|
|
if (fcinfo->arg[2] != NULL && !PG_ARGISNULL(2)) { |
459 |
|
|
if (type & MCM_DATE_TYPE_INTERVAL) { |
460 |
|
|
span = PG_GETARG_INTERVAL_P(2); |
461 |
|
|
#ifdef HAVE_INT64_TIMESTAMP |
462 |
|
|
expire = (span->time / 1000000e0); |
463 |
|
|
#else |
464 |
|
|
expire = span->time; |
465 |
|
|
#endif |
466 |
|
|
if (span->month != 0) { |
467 |
|
|
expire += ((365.25 * 86400) * (span->month / 12)); |
468 |
|
|
expire += ((30.0 * 86400) * (span->month % 12)); |
469 |
|
|
} |
470 |
|
|
} else if (type & MCM_DATE_TYPE_TIMESTAMP) { |
471 |
|
|
timestamptz = PG_GETARG_TIMESTAMPTZ(2); |
472 |
|
|
|
473 |
|
|
/* convert to timestamptz to produce consistent results */ |
474 |
dpavlin |
5 |
if (timestamp2tm(timestamptz, NULL, &tm, &fsec, NULL, NULL) !=0) |
475 |
dpavlin |
2 |
ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
476 |
|
|
errmsg("timestamp out of range"))); |
477 |
|
|
|
478 |
|
|
#ifdef HAVE_INT64_TIMESTAMP |
479 |
|
|
expire = ((timestamptz - SetEpochTimestamp()) / 1000000e0); |
480 |
|
|
#else |
481 |
|
|
expire = timestamptz - SetEpochTimestamp(); |
482 |
|
|
#endif |
483 |
|
|
} else { |
484 |
|
|
elog(ERROR, "%s():%s:%u: invalid date type", __FUNCTION__, __FILE__, __LINE__); |
485 |
|
|
} |
486 |
|
|
} |
487 |
|
|
|
488 |
|
|
flags = 0; |
489 |
|
|
if (fcinfo->arg[3] != NULL && !PG_ARGISNULL(3)) |
490 |
|
|
flags = PG_GETARG_INT16(3); |
491 |
|
|
|
492 |
|
|
if (type & MCM_SET_CMD_TYPE_ADD) |
493 |
|
|
ret = mcm_add(ctxt, mc, VARDATA(key), key_len, val, val_len, (time_t)expire, flags); |
494 |
|
|
else if (type & MCM_SET_CMD_TYPE_REPLACE) |
495 |
|
|
ret = mcm_add(ctxt, mc, VARDATA(key), key_len, val, val_len, (time_t)expire, flags); |
496 |
|
|
else if (type & MCM_SET_CMD_TYPE_SET) |
497 |
|
|
ret = mcm_add(ctxt, mc, VARDATA(key), key_len, val, val_len, (time_t)expire, flags); |
498 |
|
|
else |
499 |
|
|
elog(ERROR, "%s():%s:%u\tunknown set type 0x%x", __FUNCTION__, __FILE__, __LINE__, type); |
500 |
|
|
|
501 |
|
|
SPI_finish(); |
502 |
|
|
if (ret == 0) |
503 |
|
|
PG_RETURN_BOOL(true); |
504 |
|
|
else if (ret > 0) |
505 |
|
|
PG_RETURN_BOOL(false); |
506 |
|
|
else |
507 |
|
|
elog(ERROR, "Internal libmemcache(3) error"); |
508 |
|
|
|
509 |
|
|
/* Not reached */ |
510 |
|
|
abort(); |
511 |
|
|
} |
512 |
|
|
|
513 |
|
|
|
514 |
|
|
Datum |
515 |
|
|
memcache_server_add(PG_FUNCTION_ARGS) { |
516 |
|
|
text *server, *port; |
517 |
|
|
int ret; |
518 |
|
|
MemoryContext oc; |
519 |
|
|
|
520 |
|
|
MCM_CHECK(PG_RETURN_BOOL(false)); |
521 |
|
|
|
522 |
|
|
SPI_connect(); |
523 |
|
|
|
524 |
|
|
/* Make sure new servers are in the top memory context */ |
525 |
|
|
oc = MemoryContextSwitchTo(TopMemoryContext); |
526 |
|
|
|
527 |
|
|
server = PG_GETARG_TEXT_P(0); |
528 |
|
|
port = PG_GETARG_TEXT_P(1); |
529 |
|
|
|
530 |
|
|
elog(DEBUG1, "%s(): host=\"%.*s\" port=\"%.*s\"", __FUNCTION__, VARSIZE(server) - VARHDRSZ, VARDATA(server), VARSIZE(port) - VARHDRSZ, VARDATA(port)); |
531 |
|
|
|
532 |
|
|
/* Add the server and port */ |
533 |
|
|
ret = mcm_server_add2(ctxt, mc, VARDATA(server), VARSIZE(server) - VARHDRSZ, VARDATA(port), VARSIZE(port) - VARHDRSZ); |
534 |
|
|
if (ret < 0) { |
535 |
|
|
elog(NOTICE, "%s(): libmemcache unable to add server: %d", __FUNCTION__, ret); |
536 |
|
|
MemoryContextSwitchTo(oc); |
537 |
|
|
SPI_finish(); |
538 |
|
|
PG_RETURN_BOOL(false); |
539 |
|
|
} |
540 |
|
|
|
541 |
|
|
/* Return to current context */ |
542 |
|
|
MemoryContextSwitchTo(oc); |
543 |
|
|
|
544 |
|
|
SPI_finish(); |
545 |
|
|
|
546 |
|
|
PG_RETURN_BOOL(true); |
547 |
|
|
} |
548 |
|
|
|
549 |
|
|
|
550 |
|
|
static text * |
551 |
|
|
memcache_gen_host(const struct memcache_server *ms) { |
552 |
|
|
text *ret; |
553 |
|
|
char *cp; |
554 |
|
|
|
555 |
|
|
ret = (text *)SPI_palloc(strlen(ms->hostname) + strlen(ms->port) + 1); /* + 1 is for the colon */ |
556 |
|
|
VARATT_SIZEP(ret) = strlen(ms->hostname) + strlen(ms->port) + 1 + VARHDRSZ; |
557 |
|
|
cp = VARDATA(ret); |
558 |
|
|
|
559 |
|
|
/* Copy the hostname */ |
560 |
|
|
if (ms->hostname != NULL) |
561 |
|
|
memcpy(cp, ms->hostname, strlen(ms->hostname)); |
562 |
|
|
|
563 |
|
|
/* Advance the pointer */ |
564 |
|
|
cp += strlen(ms->hostname); |
565 |
|
|
|
566 |
|
|
/* Add a colon and advance the pointer */ |
567 |
|
|
cp[0] = ':'; |
568 |
|
|
cp++; |
569 |
|
|
|
570 |
|
|
/* Copy the port */ |
571 |
|
|
if (ms->port != NULL) |
572 |
|
|
memcpy(cp, ms->port, strlen(ms->port)); |
573 |
|
|
|
574 |
|
|
return ret; |
575 |
|
|
} |
576 |
|
|
|
577 |
|
|
|
578 |
|
|
Datum |
579 |
|
|
memcache_server_find(PG_FUNCTION_ARGS) { |
580 |
|
|
struct memcache_server *ms; |
581 |
|
|
text *key, *ret; |
582 |
|
|
u_int32_t hash; |
583 |
|
|
size_t key_len; |
584 |
|
|
|
585 |
|
|
MCM_CHECK(PG_RETURN_BOOL(false)); |
586 |
|
|
|
587 |
|
|
SPI_connect(); |
588 |
|
|
|
589 |
|
|
key = PG_GETARG_TEXT_P(0); |
590 |
|
|
key_len = VARSIZE(key) - VARHDRSZ; |
591 |
|
|
if (key_len < 1) |
592 |
|
|
elog(ERROR, "Unable to have a zero length key"); |
593 |
|
|
|
594 |
|
|
hash = mcm_hash_key(ctxt, VARDATA(key), key_len); |
595 |
|
|
ms = mcm_server_find(ctxt, mc, hash); |
596 |
|
|
ret = memcache_gen_host(ms); |
597 |
|
|
|
598 |
|
|
SPI_finish(); |
599 |
|
|
|
600 |
|
|
PG_RETURN_TEXT_P(ret); |
601 |
|
|
} |
602 |
|
|
|
603 |
|
|
|
604 |
|
|
Datum |
605 |
|
|
memcache_server_find_hash(PG_FUNCTION_ARGS) { |
606 |
|
|
struct memcache_server *ms; |
607 |
|
|
text *ret; |
608 |
|
|
|
609 |
|
|
MCM_CHECK(PG_RETURN_BOOL(false)); |
610 |
|
|
|
611 |
|
|
SPI_connect(); |
612 |
|
|
|
613 |
|
|
ms = mcm_server_find(ctxt, mc, PG_GETARG_UINT32(0)); |
614 |
|
|
ret = memcache_gen_host(ms); |
615 |
|
|
|
616 |
|
|
SPI_finish(); |
617 |
|
|
|
618 |
|
|
PG_RETURN_TEXT_P(ret); |
619 |
|
|
} |
620 |
|
|
|
621 |
|
|
|
622 |
|
|
Datum |
623 |
|
|
memcache_stats(PG_FUNCTION_ARGS) { |
624 |
|
|
struct memcache_server_stats *stats; |
625 |
|
|
text *ret; |
626 |
|
|
StringInfo str; |
627 |
|
|
|
628 |
|
|
MCM_CHECK(PG_RETURN_NULL()); |
629 |
|
|
|
630 |
|
|
SPI_connect(); |
631 |
|
|
|
632 |
|
|
stats = mcm_stats(ctxt, mc); |
633 |
|
|
if (stats == NULL) |
634 |
|
|
elog(ERROR, "Unable to create a new memcache stats structure"); |
635 |
|
|
|
636 |
|
|
str = makeStringInfo(); |
637 |
|
|
if (str == NULL) |
638 |
|
|
elog(ERROR, "Unable to create a new string object for stats"); |
639 |
|
|
|
640 |
|
|
appendStringInfo(str, "pid: %u\n", stats->pid); |
641 |
|
|
appendStringInfo(str, "uptime: %llu\n", (uint64)stats->uptime); |
642 |
|
|
appendStringInfo(str, "time: %llu\n", (uint64)stats->time); |
643 |
|
|
appendStringInfo(str, "version: %s\n", stats->version); |
644 |
dpavlin |
5 |
appendStringInfo(str, "rusage_user: %u.%us\n", (uint)stats->rusage_user.tv_sec, (uint)stats->rusage_user.tv_usec); |
645 |
|
|
appendStringInfo(str, "rusage_system: %u.%us\n", (uint)stats->rusage_system.tv_sec, (uint)stats->rusage_system.tv_usec); |
646 |
dpavlin |
2 |
appendStringInfo(str, "curr_items: %u\n", stats->curr_items); |
647 |
|
|
appendStringInfo(str, "total_items: %llu\n", stats->total_items); |
648 |
|
|
appendStringInfo(str, "bytes: %llu\n", stats->bytes); |
649 |
|
|
appendStringInfo(str, "curr_connections: %u\n", stats->curr_connections); |
650 |
|
|
appendStringInfo(str, "total_connections: %llu\n", stats->total_connections); |
651 |
|
|
appendStringInfo(str, "connection_structures: %u\n", stats->connection_structures); |
652 |
|
|
appendStringInfo(str, "cmd_get: %llu\n", stats->cmd_get); |
653 |
|
|
appendStringInfo(str, "cmd_set: %llu\n", stats->cmd_set); |
654 |
|
|
appendStringInfo(str, "get_hits: %llu\n", stats->get_hits); |
655 |
|
|
appendStringInfo(str, "get_misses: %llu\n", stats->get_misses); |
656 |
|
|
appendStringInfo(str, "bytes_read: %llu\n", stats->bytes_read); |
657 |
|
|
appendStringInfo(str, "bytes_written: %llu\n", stats->bytes_written); |
658 |
|
|
appendStringInfo(str, "limit_maxbytes: %llu\n", stats->limit_maxbytes); |
659 |
|
|
|
660 |
|
|
ret = (text *)SPI_palloc(str->len + VARHDRSZ); |
661 |
|
|
VARATT_SIZEP(ret) = str->len + VARHDRSZ; |
662 |
|
|
memcpy(VARDATA(ret), str->data, str->len); |
663 |
|
|
|
664 |
|
|
SPI_finish(); |
665 |
|
|
|
666 |
|
|
PG_RETURN_TEXT_P(ret); |
667 |
|
|
} |
668 |
|
|
|
669 |
|
|
|
670 |
|
|
Datum |
671 |
|
|
memcache_stat(PG_FUNCTION_ARGS) { |
672 |
|
|
struct memcache_server_stats *stats; |
673 |
|
|
text *ret, *stat; |
674 |
|
|
size_t stat_len; |
675 |
|
|
StringInfo str; |
676 |
|
|
|
677 |
|
|
MCM_CHECK(PG_RETURN_NULL()); |
678 |
|
|
|
679 |
|
|
SPI_connect(); |
680 |
|
|
|
681 |
|
|
stat = PG_GETARG_TEXT_P(0); |
682 |
|
|
stat_len = VARSIZE(stat) - VARHDRSZ; |
683 |
|
|
if (stat_len < 1) |
684 |
|
|
elog(ERROR, "Unable to have a zero length stat"); |
685 |
|
|
|
686 |
|
|
stats = mcm_stats(ctxt, mc); |
687 |
|
|
if (stats == NULL) |
688 |
|
|
elog(ERROR, "Unable to create a new memcache stats structure"); |
689 |
|
|
|
690 |
|
|
str = makeStringInfo(); |
691 |
|
|
if (str == NULL) |
692 |
|
|
elog(ERROR, "Unable to create a new string object for stats"); |
693 |
|
|
|
694 |
|
|
if (pg_strncasecmp("pid", VARDATA(stat), stat_len) == 0) |
695 |
|
|
appendStringInfo(str, "%u", stats->pid); |
696 |
|
|
else if (pg_strncasecmp("uptime", VARDATA(stat), stat_len) == 0) |
697 |
|
|
appendStringInfo(str, "%llu", (uint64)stats->uptime); |
698 |
|
|
else if (pg_strncasecmp("time", VARDATA(stat), stat_len) == 0) |
699 |
|
|
appendStringInfo(str, "%llu", (uint64)stats->time); |
700 |
|
|
else if (pg_strncasecmp("version", VARDATA(stat), stat_len) == 0) |
701 |
|
|
appendStringInfo(str, "%s", stats->version); |
702 |
|
|
else if (pg_strncasecmp("rusage_user", VARDATA(stat), stat_len) == 0) |
703 |
dpavlin |
5 |
appendStringInfo(str, "%u.%u", (uint)stats->rusage_user.tv_sec, (uint)stats->rusage_user.tv_usec); |
704 |
dpavlin |
2 |
else if (pg_strncasecmp("rusage_system", VARDATA(stat), stat_len) == 0) |
705 |
dpavlin |
5 |
appendStringInfo(str, "%u.%u", (uint)stats->rusage_system.tv_sec, (uint)stats->rusage_system.tv_usec); |
706 |
dpavlin |
2 |
else if (pg_strncasecmp("curr_items", VARDATA(stat), stat_len) == 0) |
707 |
|
|
appendStringInfo(str, "%u", stats->curr_items); |
708 |
|
|
else if (pg_strncasecmp("total_itmes", VARDATA(stat), stat_len) == 0) |
709 |
|
|
appendStringInfo(str, "%llu", stats->total_items); |
710 |
|
|
else if (pg_strncasecmp("bytes", VARDATA(stat), stat_len) == 0) |
711 |
|
|
appendStringInfo(str, "%llu", stats->bytes); |
712 |
|
|
else if (pg_strncasecmp("curr_connections", VARDATA(stat), stat_len) == 0) |
713 |
|
|
appendStringInfo(str, "%u", stats->curr_connections); |
714 |
|
|
else if (pg_strncasecmp("total_connections", VARDATA(stat), stat_len) == 0) |
715 |
|
|
appendStringInfo(str, "%llu", stats->total_connections); |
716 |
|
|
else if (pg_strncasecmp("connection_structures", VARDATA(stat), stat_len) == 0) |
717 |
|
|
appendStringInfo(str, "%u", stats->connection_structures); |
718 |
|
|
else if (pg_strncasecmp("cmd_get", VARDATA(stat), stat_len) == 0) |
719 |
|
|
appendStringInfo(str, "%llu", stats->cmd_get); |
720 |
|
|
else if (pg_strncasecmp("cmd_set", VARDATA(stat), stat_len) == 0) |
721 |
|
|
appendStringInfo(str, "%llu", stats->cmd_set); |
722 |
|
|
else if (pg_strncasecmp("get_hits", VARDATA(stat), stat_len) == 0) |
723 |
|
|
appendStringInfo(str, "%llu", stats->get_hits); |
724 |
|
|
else if (pg_strncasecmp("get_misses", VARDATA(stat), stat_len) == 0) |
725 |
|
|
appendStringInfo(str, "%llu", stats->get_misses); |
726 |
|
|
else if (pg_strncasecmp("bytes_read", VARDATA(stat), stat_len) == 0) |
727 |
|
|
appendStringInfo(str, "%llu", stats->bytes_read); |
728 |
|
|
else if (pg_strncasecmp("bytes_written", VARDATA(stat), stat_len) == 0) |
729 |
|
|
appendStringInfo(str, "%llu", stats->bytes_written); |
730 |
|
|
else if (pg_strncasecmp("limit_maxbytes", VARDATA(stat), stat_len) == 0) |
731 |
|
|
appendStringInfo(str, "%llu", stats->limit_maxbytes); |
732 |
|
|
else |
733 |
|
|
elog(ERROR, "Unknown memcache statistic \"%.*s\"", (int)stat_len, VARDATA(stat)); |
734 |
|
|
|
735 |
|
|
ret = (text *)SPI_palloc(str->len + VARHDRSZ); |
736 |
|
|
VARATT_SIZEP(ret) = str->len + VARHDRSZ; |
737 |
|
|
memcpy(VARDATA(ret), str->data, str->len); |
738 |
|
|
|
739 |
|
|
SPI_finish(); |
740 |
|
|
|
741 |
|
|
PG_RETURN_TEXT_P(ret); |
742 |
|
|
} |