1 |
/* |
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 |
#include "utils/memutils.h" |
38 |
|
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 |
if (timestamp2tm(timestamptz, NULL, &tm, &fsec, NULL, NULL) !=0) |
475 |
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 |
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 |
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 |
appendStringInfo(str, "%u.%u", (uint)stats->rusage_user.tv_sec, (uint)stats->rusage_user.tv_usec); |
704 |
else if (pg_strncasecmp("rusage_system", VARDATA(stat), stat_len) == 0) |
705 |
appendStringInfo(str, "%u.%u", (uint)stats->rusage_system.tv_sec, (uint)stats->rusage_system.tv_usec); |
706 |
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 |
} |