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