1 /*-------------------------------------------------------------------------
2 *
3 * pgpool_adm.c
4 *
5 *
6 * Copyright (c) 2002-2021, PostgreSQL Global Development Group
7 *
8 * Author: Jehan-Guillaume (ioguix) de Rorthais <jgdr@dalibo.com>
9 *
10 * IDENTIFICATION
11 * contrib/pgpool_adm/pgpool_adm.c
12 *
13 *-------------------------------------------------------------------------
14 */
15 #include "postgres.h"
16 #include "miscadmin.h"
17 #include "utils/builtins.h"
18 #include "foreign/foreign.h"
19 #include "nodes/pg_list.h"
20 #include "utils/timestamp.h"
21
22 /*
23 * PostgreSQL 9.3 or later requires htup_details.h to get the definition of
24 * heap_form_tuple
25 */
26 #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 90300)
27 #include "access/htup_details.h"
28 #endif
29
30 #include <unistd.h>
31 #include <time.h>
32
33 #include "catalog/pg_type.h"
34 #include "funcapi.h"
35 #include "libpcp_ext.h"
36 #include "pgpool_adm.h"
37
38
39 static PCPConnInfo * connect_to_server(char *host, int port, char *user, char *pass);
40 static PCPConnInfo * connect_to_server_from_foreign_server(char *name);
41 static Timestamp str2timestamp(char *str);
42
43 /**
44 * Wrapper around pcp_connect
45 * pcp_conninfo: pcpConninfo structure having pcp connection properties
46 */
47 static PCPConnInfo *
connect_to_server(char * host,int port,char * user,char * pass)48 connect_to_server(char *host, int port, char *user, char *pass)
49 {
50 PCPConnInfo *pcpConnInfo;
51
52 pcpConnInfo = pcp_connect(host, port, user, pass, NULL);
53 if (PCPConnectionStatus(pcpConnInfo) != PCP_CONNECTION_OK)
54 ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),
55 errmsg("connection to PCP server failed."),
56 errdetail("%s\n", pcp_get_last_error(pcpConnInfo) ? pcp_get_last_error(pcpConnInfo) : "unknown reason")));
57
58 return pcpConnInfo;
59 }
60
61 /**
62 * Returns a pcpConninfo structure filled from a foreign server
63 * name: the name of the foreign server
64 */
65 static PCPConnInfo *
connect_to_server_from_foreign_server(char * name)66 connect_to_server_from_foreign_server(char *name)
67 {
68 Oid userid = GetUserId();
69 char *user = NULL;
70 char *host = NULL;
71 int port = 9898;
72 char *pass = NULL;
73
74 /* raise an error if given foreign server doesn't exists */
75 ForeignServer *foreign_server = GetForeignServerByName(name, false);
76 UserMapping *user_mapping;
77 ListCell *cell;
78
79 /*
80 * raise an error if the current user isn't mapped with the given foreign
81 * server
82 */
83 user_mapping = GetUserMapping(userid, foreign_server->serverid);
84
85 foreach(cell, foreign_server->options)
86 {
87 DefElem *def = lfirst(cell);
88
89 if (strcmp(def->defname, "host") == 0)
90 {
91 host = pstrdup(strVal(def->arg));
92 }
93 else if (strcmp(def->defname, "port") == 0)
94 {
95 port = atoi(strVal(def->arg));
96 }
97 }
98
99 foreach(cell, user_mapping->options)
100 {
101 DefElem *def = lfirst(cell);
102
103 if (strcmp(def->defname, "user") == 0)
104 {
105 user = pstrdup(strVal(def->arg));
106 }
107 else if (strcmp(def->defname, "password") == 0)
108 {
109 pass = pstrdup(strVal(def->arg));
110 }
111 }
112
113 return connect_to_server(host, port, user, pass);
114 }
115
116 /**
117 * nodeID: the node id to get info from
118 * host_or_srv: server name or ip address of the pgpool server
119 * port: pcp port number
120 * user: user to connect with
121 * pass: password
122 **/
123 Datum
_pcp_node_info(PG_FUNCTION_ARGS)124 _pcp_node_info(PG_FUNCTION_ARGS)
125 {
126 int16 nodeID = PG_GETARG_INT16(0);
127 char *host_or_srv = text_to_cstring(PG_GETARG_TEXT_PP(1));
128
129 PCPConnInfo *pcpConnInfo;
130 PCPResultInfo *pcpResInfo;
131
132 BackendInfo *backend_info = NULL;
133 Datum values[11]; /* values to build the returned tuple from */
134 bool nulls[] = {false, false, false, false, false, false, false, false, false, false, false};
135 TupleDesc tupledesc;
136 HeapTuple tuple;
137 struct tm tm;
138 char datebuf[20];
139 int i;
140
141 if (nodeID < 0 || nodeID >= MAX_NUM_BACKENDS)
142 ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("NodeID is out of range.")));
143
144 if (PG_NARGS() == 5)
145 {
146 char *user,
147 *pass;
148 int port;
149
150 port = PG_GETARG_INT16(2);
151 user = text_to_cstring(PG_GETARG_TEXT_PP(3));
152 pass = text_to_cstring(PG_GETARG_TEXT_PP(4));
153 pcpConnInfo = connect_to_server(host_or_srv, port, user, pass);
154 }
155 else if (PG_NARGS() == 2)
156 {
157 pcpConnInfo = connect_to_server_from_foreign_server(host_or_srv);
158 }
159 else
160 {
161 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Wrong number of argument.")));
162 }
163
164 pcpResInfo = pcp_node_info(pcpConnInfo, nodeID);
165 if (pcpResInfo == NULL || PCPResultStatus(pcpResInfo) != PCP_RES_COMMAND_OK)
166 {
167 char *error = pcp_get_last_error(pcpConnInfo) ? pstrdup(pcp_get_last_error(pcpConnInfo)) : NULL;
168
169 pcp_disconnect(pcpConnInfo);
170 pcp_free_connection(pcpConnInfo);
171 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
172 errmsg("failed to get node information"),
173 errdetail("%s\n", error ? error : "unknown reason")));
174 }
175
176 /**
177 * Construct a tuple descriptor for the result rows.
178 **/
179 #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 120000)
180 tupledesc = CreateTemplateTupleDesc(11);
181 #else
182 tupledesc = CreateTemplateTupleDesc(11, false);
183 #endif
184 TupleDescInitEntry(tupledesc, (AttrNumber) 1, "hostname", TEXTOID, -1, 0);
185 TupleDescInitEntry(tupledesc, (AttrNumber) 2, "port", INT4OID, -1, 0);
186 TupleDescInitEntry(tupledesc, (AttrNumber) 3, "status", TEXTOID, -1, 0);
187 TupleDescInitEntry(tupledesc, (AttrNumber) 4, "pg_status", TEXTOID, -1, 0);
188 TupleDescInitEntry(tupledesc, (AttrNumber) 5, "weight", FLOAT4OID, -1, 0);
189 TupleDescInitEntry(tupledesc, (AttrNumber) 6, "role", TEXTOID, -1, 0);
190 TupleDescInitEntry(tupledesc, (AttrNumber) 7, "pg_role", TEXTOID, -1, 0);
191 TupleDescInitEntry(tupledesc, (AttrNumber) 8, "replication_delay", INT8OID, -1, 0);
192 TupleDescInitEntry(tupledesc, (AttrNumber) 9, "replication_state", TEXTOID, -1, 0);
193 TupleDescInitEntry(tupledesc, (AttrNumber) 10, "replication_sync_state", TEXTOID, -1, 0);
194 TupleDescInitEntry(tupledesc, (AttrNumber) 11, "last_status_change", TIMESTAMPOID, -1, 0);
195 tupledesc = BlessTupleDesc(tupledesc);
196
197 backend_info = (BackendInfo *) pcp_get_binary_data(pcpResInfo, 0);
198
199 /* set values */
200 i = 0;
201 values[i] = CStringGetTextDatum(backend_info->backend_hostname);
202 nulls[i] = false;
203 i++;
204 values[i] = Int16GetDatum(backend_info->backend_port);
205 nulls[i] = false;
206
207 i++;
208 switch (backend_info->backend_status)
209 {
210 case CON_UNUSED:
211 values[i] = CStringGetTextDatum("Connection unused");
212 break;
213 case CON_CONNECT_WAIT:
214 values[i] = CStringGetTextDatum("Waiting for connection to start");
215 break;
216 case CON_UP:
217 values[i] = CStringGetTextDatum("Connection in use");
218 break;
219 case CON_DOWN:
220 values[i] = CStringGetTextDatum("Disconnected");
221 break;
222 }
223 nulls[i] = false;
224
225 i++;
226 nulls[i] = false;
227 values[i] = CStringGetTextDatum(backend_info->pg_backend_status);
228
229 i++;
230 values[i] = Float4GetDatum(backend_info->backend_weight / RAND_MAX);
231 nulls[i] = false;
232
233 i++;
234 nulls[i] = false;
235 values[i] = backend_info->role == ROLE_PRIMARY ? CStringGetTextDatum("Primary") : CStringGetTextDatum("Standby");
236
237 i++;
238 nulls[i] = false;
239 values[i] = CStringGetTextDatum(backend_info->pg_role);
240
241 i++;
242 nulls[i] = false;
243 values[i] = Int64GetDatum(backend_info->standby_delay);
244
245 i++;
246 nulls[i] = false;
247 values[i] = CStringGetTextDatum(backend_info->replication_state);
248
249 i++;
250 nulls[i] = false;
251 values[i] = CStringGetTextDatum(backend_info->replication_sync_state);
252
253 i++;
254 nulls[i] = false;
255 localtime_r(&backend_info->status_changed_time, &tm);
256 strftime(datebuf, sizeof(datebuf), "%F %T", &tm);
257 values[i] = DatumGetTimestamp(DirectFunctionCall3(timestamp_in,
258 CStringGetDatum(datebuf),
259 ObjectIdGetDatum(InvalidOid),
260 Int32GetDatum(-1)));
261
262 pcp_disconnect(pcpConnInfo);
263 pcp_free_connection(pcpConnInfo);
264
265 /* build and return the tuple */
266 tuple = heap_form_tuple(tupledesc, values, nulls);
267
268 ReleaseTupleDesc(tupledesc);
269
270 PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
271 }
272
273 /**
274 * host_or_srv: server name or ip address of the pgpool server
275 * port: pcp port number
276 * user: user to connect with
277 * pass: password
278 **/
279 Datum
_pcp_pool_status(PG_FUNCTION_ARGS)280 _pcp_pool_status(PG_FUNCTION_ARGS)
281 {
282 MemoryContext oldcontext;
283 FuncCallContext *funcctx;
284 int32 nrows;
285 int32 call_cntr;
286 int32 max_calls;
287 AttInMetadata *attinmeta;
288 PCPConnInfo *pcpConnInfo;
289 PCPResultInfo *pcpResInfo;
290
291 /* stuff done only on the first call of the function */
292 if (SRF_IS_FIRSTCALL())
293 {
294 TupleDesc tupdesc;
295 char *host_or_srv = text_to_cstring(PG_GETARG_TEXT_PP(0));
296
297 /* create a function context for cross-call persistence */
298 funcctx = SRF_FIRSTCALL_INIT();
299
300 /* switch to memory context appropriate for multiple function calls */
301 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
302
303 if (PG_NARGS() == 4)
304 {
305 char *user,
306 *pass;
307 int port;
308
309 port = PG_GETARG_INT16(1);
310 user = text_to_cstring(PG_GETARG_TEXT_PP(2));
311 pass = text_to_cstring(PG_GETARG_TEXT_PP(3));
312 pcpConnInfo = connect_to_server(host_or_srv, port, user, pass);
313
314 }
315 else if (PG_NARGS() == 1)
316 {
317 pcpConnInfo = connect_to_server_from_foreign_server(host_or_srv);
318 }
319 else
320 {
321 MemoryContextSwitchTo(oldcontext);
322 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Wrong number of argument.")));
323 }
324
325 pcpResInfo = pcp_pool_status(pcpConnInfo);
326 if (pcpResInfo == NULL || PCPResultStatus(pcpResInfo) != PCP_RES_COMMAND_OK)
327 {
328 char *error = pcp_get_last_error(pcpConnInfo) ? pstrdup(pcp_get_last_error(pcpConnInfo)) : NULL;
329
330 pcp_disconnect(pcpConnInfo);
331 pcp_free_connection(pcpConnInfo);
332
333 MemoryContextSwitchTo(oldcontext);
334 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
335 errmsg("failed to get pool status"),
336 errdetail("%s\n", error ? error : "unknown reason")));
337 }
338
339 nrows = pcp_result_slot_count(pcpResInfo);
340 pcp_disconnect(pcpConnInfo);
341 /* Construct a tuple descriptor for the result rows */
342 #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 120000)
343 tupdesc = CreateTemplateTupleDesc(3);
344 #else
345 tupdesc = CreateTemplateTupleDesc(3, false);
346 #endif
347 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "item", TEXTOID, -1, 0);
348 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "value", TEXTOID, -1, 0);
349 TupleDescInitEntry(tupdesc, (AttrNumber) 3, "description", TEXTOID, -1, 0);
350
351 /*
352 * Generate attribute metadata needed later to produce tuples from raw
353 * C strings
354 */
355 attinmeta = TupleDescGetAttInMetadata(tupdesc);
356 funcctx->attinmeta = attinmeta;
357
358 if (nrows > 0)
359 {
360 funcctx->max_calls = nrows;
361
362 /* got results, keep track of them */
363 funcctx->user_fctx = pcpConnInfo;
364 }
365 else
366 {
367 /* fast track when no results */
368 MemoryContextSwitchTo(oldcontext);
369 SRF_RETURN_DONE(funcctx);
370 }
371
372 MemoryContextSwitchTo(oldcontext);
373 }
374
375 /* stuff done on every call of the function */
376 funcctx = SRF_PERCALL_SETUP();
377
378 /* initialize per-call variables */
379 call_cntr = funcctx->call_cntr;
380 max_calls = funcctx->max_calls;
381
382 pcpConnInfo = (PCPConnInfo *) funcctx->user_fctx;
383 pcpResInfo = (PCPResultInfo *) pcpConnInfo->pcpResInfo;
384 attinmeta = funcctx->attinmeta;
385
386 if (call_cntr < max_calls) /* executed while there is more left to send */
387 {
388 char *values[3];
389 HeapTuple tuple;
390 Datum result;
391 POOL_REPORT_CONFIG *status = (POOL_REPORT_CONFIG *) pcp_get_binary_data(pcpResInfo, call_cntr);
392
393 values[0] = pstrdup(status->name);
394 values[1] = pstrdup(status->value);
395 values[2] = pstrdup(status->desc);
396
397 /* build the tuple */
398 tuple = BuildTupleFromCStrings(attinmeta, values);
399
400 /* make the tuple into a datum */
401 result = HeapTupleGetDatum(tuple);
402
403 SRF_RETURN_NEXT(funcctx, result);
404 }
405 else
406 {
407 /* do when there is no more left */
408 pcp_free_connection(pcpConnInfo);
409 SRF_RETURN_DONE(funcctx);
410 }
411 }
412
413 /**
414 * nodeID: the node id to get info from
415 * host_or_srv: server name or ip address of the pgpool server
416 * port: pcp port number
417 * user: user to connect with
418 * pass: password
419 **/
420 Datum
_pcp_node_count(PG_FUNCTION_ARGS)421 _pcp_node_count(PG_FUNCTION_ARGS)
422 {
423 char *host_or_srv = text_to_cstring(PG_GETARG_TEXT_PP(0));
424 int16 node_count = 0;
425
426 PCPConnInfo *pcpConnInfo;
427 PCPResultInfo *pcpResInfo;
428
429 if (PG_NARGS() == 4)
430 {
431 char *user,
432 *pass;
433 int port;
434
435 port = PG_GETARG_INT16(1);
436 user = text_to_cstring(PG_GETARG_TEXT_PP(2));
437 pass = text_to_cstring(PG_GETARG_TEXT_PP(3));
438 pcpConnInfo = connect_to_server(host_or_srv, port, user, pass);
439 }
440 else if (PG_NARGS() == 1)
441 {
442 pcpConnInfo = connect_to_server_from_foreign_server(host_or_srv);
443 }
444 else
445 {
446 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Wrong number of argument.")));
447 }
448
449 pcpResInfo = pcp_node_count(pcpConnInfo);
450
451 if (pcpResInfo == NULL || PCPResultStatus(pcpResInfo) != PCP_RES_COMMAND_OK)
452 {
453 char *error = pcp_get_last_error(pcpConnInfo) ? pstrdup(pcp_get_last_error(pcpConnInfo)) : NULL;
454
455 pcp_disconnect(pcpConnInfo);
456 pcp_free_connection(pcpConnInfo);
457 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
458 errmsg("failed to get node count"),
459 errdetail("%s\n", error ? error : "unknown reason")));
460 }
461
462 node_count = pcp_get_int_data(pcpResInfo, 0);
463
464 pcp_disconnect(pcpConnInfo);
465 pcp_free_connection(pcpConnInfo);
466
467 PG_RETURN_INT16(node_count);
468 }
469
470 /**
471 * nodeID: the node id to get info from
472 * host_or_srv: server name or ip address of the pgpool server
473 * port: pcp port number
474 * user: user to connect with
475 * pass: password
476 **/
477 Datum
_pcp_attach_node(PG_FUNCTION_ARGS)478 _pcp_attach_node(PG_FUNCTION_ARGS)
479 {
480 int16 nodeID = PG_GETARG_INT16(0);
481 char *host_or_srv = text_to_cstring(PG_GETARG_TEXT_PP(1));
482
483 PCPConnInfo *pcpConnInfo;
484 PCPResultInfo *pcpResInfo;
485
486 if (nodeID < 0 || nodeID >= MAX_NUM_BACKENDS)
487 ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("NodeID is out of range.")));
488
489 if (PG_NARGS() == 5)
490 {
491 char *user,
492 *pass;
493 int port;
494
495 port = PG_GETARG_INT16(2);
496 user = text_to_cstring(PG_GETARG_TEXT_PP(3));
497 pass = text_to_cstring(PG_GETARG_TEXT_PP(4));
498 pcpConnInfo = connect_to_server(host_or_srv, port, user, pass);
499 }
500 else if (PG_NARGS() == 2)
501 {
502 pcpConnInfo = connect_to_server_from_foreign_server(host_or_srv);
503 }
504 else
505 {
506 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Wrong number of argument.")));
507 }
508
509 pcpResInfo = pcp_attach_node(pcpConnInfo, nodeID);
510
511 if (pcpResInfo == NULL || PCPResultStatus(pcpResInfo) != PCP_RES_COMMAND_OK)
512 {
513 char *error = pcp_get_last_error(pcpConnInfo) ? pstrdup(pcp_get_last_error(pcpConnInfo)) : NULL;
514
515 pcp_disconnect(pcpConnInfo);
516 pcp_free_connection(pcpConnInfo);
517 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
518 errmsg("failed to attach node"),
519 errdetail("%s\n", error ? error : "unknown reason")));
520 }
521
522 pcp_disconnect(pcpConnInfo);
523 pcp_free_connection(pcpConnInfo);
524
525
526 PG_RETURN_BOOL(true);
527 }
528
529 /**
530 * nodeID: the node id to get info from
531 * gracefully: detach node gracefully if true
532 * host_or_srv: server name or ip address of the pgpool server
533 * port: pcp port number
534 * user: user to connect with
535 * pass: password
536 **/
537 Datum
_pcp_detach_node(PG_FUNCTION_ARGS)538 _pcp_detach_node(PG_FUNCTION_ARGS)
539 {
540 int16 nodeID = PG_GETARG_INT16(0);
541 bool gracefully = PG_GETARG_BOOL(1);
542 char *host_or_srv = text_to_cstring(PG_GETARG_TEXT_PP(2));
543
544 PCPConnInfo *pcpConnInfo;
545 PCPResultInfo *pcpResInfo;
546
547 if (nodeID < 0 || nodeID >= MAX_NUM_BACKENDS)
548 ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("NodeID is out of range.")));
549
550 if (PG_NARGS() == 6)
551 {
552 char *user,
553 *pass;
554 int port;
555
556 port = PG_GETARG_INT16(3);
557 user = text_to_cstring(PG_GETARG_TEXT_PP(4));
558 pass = text_to_cstring(PG_GETARG_TEXT_PP(5));
559 pcpConnInfo = connect_to_server(host_or_srv, port, user, pass);
560 }
561 else if (PG_NARGS() == 3)
562 {
563 pcpConnInfo = connect_to_server_from_foreign_server(host_or_srv);
564 }
565 else
566 {
567 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Wrong number of argument.")));
568 }
569
570 if (gracefully)
571 {
572 pcpResInfo = pcp_detach_node_gracefully(pcpConnInfo, nodeID);
573 }
574 else
575 {
576 pcpResInfo = pcp_detach_node(pcpConnInfo, nodeID);
577 }
578
579 if (pcpResInfo == NULL || PCPResultStatus(pcpResInfo) != PCP_RES_COMMAND_OK)
580 {
581 char *error = pcp_get_last_error(pcpConnInfo) ? pstrdup(pcp_get_last_error(pcpConnInfo)) : NULL;
582
583 pcp_disconnect(pcpConnInfo);
584 pcp_free_connection(pcpConnInfo);
585 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
586 errmsg("failed to detach node"),
587 errdetail("%s\n", error ? error : "unknown reason")));
588 }
589
590 pcp_disconnect(pcpConnInfo);
591 pcp_free_connection(pcpConnInfo);
592
593 PG_RETURN_BOOL(true);
594 }
595
596 /**
597 * nodeID: the node id to get info from
598 * host_or_srv: server name or ip address of the pgpool server
599 * port: pcp port number
600 * user: user to connect with
601 * pass: password
602 **/
603 Datum
_pcp_health_check_stats(PG_FUNCTION_ARGS)604 _pcp_health_check_stats(PG_FUNCTION_ARGS)
605 {
606 int16 nodeID = PG_GETARG_INT16(0);
607 char *host_or_srv = text_to_cstring(PG_GETARG_TEXT_PP(1));
608
609 PCPConnInfo *pcpConnInfo;
610 PCPResultInfo *pcpResInfo;
611
612 POOL_HEALTH_CHECK_STATS *stats;
613 Datum values[20]; /* values to build the returned tuple from */
614 bool nulls[] = {false, false, false, false, false, false, false, false, false, false,
615 false, false, false, false, false, false, false, false, false, false};
616 TupleDesc tupledesc;
617 HeapTuple tuple;
618 AttrNumber an;
619 int i;
620
621 if (nodeID < 0 || nodeID >= MAX_NUM_BACKENDS)
622 ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("NodeID is out of range.")));
623
624 if (PG_NARGS() == 5)
625 {
626 char *user,
627 *pass;
628 int port;
629
630 port = PG_GETARG_INT16(2);
631 user = text_to_cstring(PG_GETARG_TEXT_PP(3));
632 pass = text_to_cstring(PG_GETARG_TEXT_PP(4));
633 pcpConnInfo = connect_to_server(host_or_srv, port, user, pass);
634 }
635 else if (PG_NARGS() == 2)
636 {
637 pcpConnInfo = connect_to_server_from_foreign_server(host_or_srv);
638 }
639 else
640 {
641 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Wrong number of argument.")));
642 }
643
644 pcpResInfo = pcp_health_check_stats(pcpConnInfo, nodeID);
645 if (pcpResInfo == NULL || PCPResultStatus(pcpResInfo) != PCP_RES_COMMAND_OK)
646 {
647 char *error = pcp_get_last_error(pcpConnInfo) ? pstrdup(pcp_get_last_error(pcpConnInfo)) : NULL;
648
649 pcp_disconnect(pcpConnInfo);
650 pcp_free_connection(pcpConnInfo);
651 ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
652 errmsg("failed to get node information"),
653 errdetail("%s\n", error ? error : "unknown reason")));
654 }
655
656 /**
657 * Construct a tuple descriptor for the result rows.
658 **/
659 #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 120000)
660 tupledesc = CreateTemplateTupleDesc(20);
661 #else
662 tupledesc = CreateTemplateTupleDesc(20, false);
663 #endif
664 an = 1;
665 TupleDescInitEntry(tupledesc, an++, "node_id", INT4OID, -1, 0);
666 TupleDescInitEntry(tupledesc, an++, "hostname", TEXTOID, -1, 0);
667 TupleDescInitEntry(tupledesc, an++, "port", INT4OID, -1, 0);
668 TupleDescInitEntry(tupledesc, an++, "status", TEXTOID, -1, 0);
669 TupleDescInitEntry(tupledesc, an++, "role", TEXTOID, -1, 0);
670 TupleDescInitEntry(tupledesc, an++, "last_status_change", TIMESTAMPOID, -1, 0);
671 TupleDescInitEntry(tupledesc, an++, "total_count", INT8OID, -1, 0);
672 TupleDescInitEntry(tupledesc, an++, "success_count", INT8OID, -1, 0);
673 TupleDescInitEntry(tupledesc, an++, "fail_count", INT8OID, -1, 0);
674 TupleDescInitEntry(tupledesc, an++, "skip_count", INT8OID, -1, 0);
675 TupleDescInitEntry(tupledesc, an++, "retry_count", INT8OID, -1, 0);
676 TupleDescInitEntry(tupledesc, an++, "average_retry_count", FLOAT4OID, -1, 0);
677 TupleDescInitEntry(tupledesc, an++, "max_retry_count", INT8OID, -1, 0);
678 TupleDescInitEntry(tupledesc, an++, "max_health_check_duration", INT8OID, -1, 0);
679 TupleDescInitEntry(tupledesc, an++, "min_health_check_duration", INT8OID, -1, 0);
680 TupleDescInitEntry(tupledesc, an++, "average_health_check_duration", FLOAT4OID, -1, 0);
681 TupleDescInitEntry(tupledesc, an++, "last_health_check", TIMESTAMPOID, -1, 0);
682 TupleDescInitEntry(tupledesc, an++, "last_successful_health_check", TIMESTAMPOID, -1, 0);
683 TupleDescInitEntry(tupledesc, an++, "last_skip_health_check", TIMESTAMPOID, -1, 0);
684 TupleDescInitEntry(tupledesc, an++, "last_failed_health_check", TIMESTAMPOID, -1, 0);
685
686 tupledesc = BlessTupleDesc(tupledesc);
687
688 stats = (POOL_HEALTH_CHECK_STATS *) pcp_get_binary_data(pcpResInfo, 0);
689
690 /* set values */
691 i = 0;
692 values[i++] = Int32GetDatum(nodeID);
693 values[i++] = CStringGetTextDatum(stats->hostname);
694 values[i++] = Int32GetDatum(atoi(stats->port));
695 values[i++] = CStringGetTextDatum(stats->status);
696 values[i++] = CStringGetTextDatum(stats->role);
697
698 if (*stats->last_status_change == '\0')
699 nulls[i++] = true;
700 else
701 values[i++] = str2timestamp(stats->last_status_change);
702
703 values[i++] = Int64GetDatum(atol(stats->total_count));
704 values[i++] = Int64GetDatum(atol(stats->success_count));
705 values[i++] = Int64GetDatum(atol(stats->fail_count));
706 values[i++] = Int64GetDatum(atol(stats->skip_count));
707 values[i++] = Int64GetDatum(atol(stats->retry_count));
708 values[i++] = Float4GetDatum(atof(stats->average_retry_count));
709 values[i++] = Int64GetDatum(atol(stats->max_retry_count));
710 values[i++] = Int64GetDatum(atol(stats->max_health_check_duration));
711 values[i++] = Int64GetDatum(atol(stats->min_health_check_duration));
712 values[i++] = Float4GetDatum(atof(stats->average_health_check_duration));
713
714 if (*stats->last_health_check =='\0' )
715 nulls[i++] = true;
716 else
717 values[i++] = str2timestamp(stats->last_health_check);
718
719 if (*stats->last_successful_health_check == '\0')
720 nulls[i++] = true;
721 else
722 values[i++] = str2timestamp(stats->last_successful_health_check);
723
724 if (*stats->last_skip_health_check == '\0')
725 nulls[i++] = true;
726 else
727 values[i++] = str2timestamp(stats->last_skip_health_check);
728
729 if (*stats->last_failed_health_check == '\0')
730 nulls[i++] = true;
731 else
732 values[i++] = str2timestamp(stats->last_failed_health_check);
733
734 pcp_disconnect(pcpConnInfo);
735 pcp_free_connection(pcpConnInfo);
736
737 /* build and return the tuple */
738 tuple = heap_form_tuple(tupledesc, values, nulls);
739
740 ReleaseTupleDesc(tupledesc);
741
742 PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
743 }
744
745 static
str2timestamp(char * str)746 Timestamp str2timestamp(char *str)
747 {
748 return (DatumGetTimestamp(DirectFunctionCall3(timestamp_in,
749 CStringGetDatum(str),
750 ObjectIdGetDatum(InvalidOid),
751 Int32GetDatum(-1))));
752 }
753
754