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