1 /* -*-pgsql-c-*- */
2 /*
3  *
4  * pgpool: a language independent connection pool server for PostgreSQL
5  * written by Tatsuo Ishii
6  *
7  * Copyright (c) 2003-2020	PgPool Global Development Group
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose and without fee is hereby
11  * granted, provided that the above copyright notice appear in all
12  * copies and that both that copyright notice and this permission
13  * notice appear in supporting documentation, and that the name of the
14  * author not be used in advertising or publicity pertaining to
15  * distribution of the software without specific, written prior
16  * permission. The author makes no representations about the
17  * suitability of this software for any purpose.  It is provided "as
18  * is" without express or implied warranty.
19  *
20  * pool_config_variables.c.
21  *
22  */
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <limits.h>
31 #include <math.h>
32 
33 #include "pool.h"
34 #include "pool_config.h"
35 #include "pool_config_variables.h"
36 #include "utils/regex_array.h"
37 
38 #ifndef POOL_PRIVATE
39 #include "utils/elog.h"
40 #include "parser/stringinfo.h"
41 #include "utils/pool_process_reporting.h"
42 #include "utils/pool_stream.h"
43 #include "utils/palloc.h"
44 #include "utils/memutils.h"
45 #include "watchdog/wd_utils.h"
46 
47 #else
48 #include "utils/fe_ports.h"
49 #endif
50 
51 #define default_reset_query_list	"ABORT;DISCARD ALL"
52 #define default_write_function_list "nextval,setval"
53 
54 #define EMPTY_CONFIG_GENERIC {NULL, 0, 0, NULL, 0, false, 0, 0, 0, 0, NULL, NULL}
55 #define EMPTY_CONFIG_BOOL {EMPTY_CONFIG_GENERIC, NULL, false, NULL, NULL, NULL, false}
56 #define EMPTY_CONFIG_INT {EMPTY_CONFIG_GENERIC, NULL, 0, 0, 0, NULL, NULL, NULL, 0}
57 #define EMPTY_CONFIG_DOUBLE {EMPTY_CONFIG_GENERIC, NULL, 0, 0, 0, NULL, NULL, NULL, 0}
58 #define EMPTY_CONFIG_LONG {EMPTY_CONFIG_GENERIC, NULL, 0, 0, 0, NULL, NULL, NULL, 0}
59 #define EMPTY_CONFIG_STRING {EMPTY_CONFIG_GENERIC, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
60 #define EMPTY_CONFIG_ENUM {EMPTY_CONFIG_GENERIC, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0}
61 #define EMPTY_CONFIG_INT_ARRAY {EMPTY_CONFIG_GENERIC, NULL, 0, 0, 0, EMPTY_CONFIG_INT, NULL, NULL, NULL, NULL, NULL}
62 #define EMPTY_CONFIG_DOUBLE_ARRAY {EMPTY_CONFIG_GENERIC, NULL, 0, 0, 0, EMPTY_CONFIG_DOUBLE, NULL, NULL, NULL, NULL, NULL}
63 #define EMPTY_CONFIG_STRING_ARRAY {EMPTY_CONFIG_GENERIC, NULL, NULL, EMPTY_CONFIG_STRING, NULL, NULL, NULL, NULL, NULL}
64 #define EMPTY_CONFIG_STRING_LIST {EMPTY_CONFIG_GENERIC, NULL, NULL, NULL, NULL, false, NULL, NULL, NULL, NULL, NULL}
65 #define EMPTY_CONFIG_GROUP_ARRAY {EMPTY_CONFIG_GENERIC, 0, NULL}
66 
67 extern POOL_CONFIG g_pool_config;
68 struct config_generic **all_parameters = NULL;
69 static int	num_all_parameters = 0;
70 
71 static void initialize_variables_with_default(struct config_generic *gconf);
72 static bool config_enum_lookup_by_name(struct config_enum *record, const char *value, int *retval);
73 
74 static void build_variable_groups(void);
75 static void build_config_variables(void);
76 static void initialize_config_gen(struct config_generic *gen);
77 
78 static struct config_generic *find_option(const char *name, int elevel);
79 
80 static bool config_post_processor(ConfigContext context, int elevel);
81 
82 static void sort_config_vars(void);
83 static bool setConfigOptionArrayVarWithConfigDefault(struct config_generic *record, const char *name,
84 										 const char *value, ConfigContext context, int elevel);
85 
86 static bool setConfigOption(const char *name, const char *value,
87 				ConfigContext context, GucSource source, int elevel);
88 static bool setConfigOptionVar(struct config_generic *record, const char *name, int index_val,
89 				   const char *value, ConfigContext context, GucSource source, int elevel);
90 
91 static bool get_index_in_var_name(struct config_generic *record,
92 					  const char *name, int *index, int elevel);
93 
94 
95 static bool MakeDBRedirectListRegex(char *newval, int elevel);
96 static bool MakeAppRedirectListRegex(char *newval, int elevel);
97 static bool MakeDMLAdaptiveObjectRelationList(char *newval, int elevel);
98 static char* getParsedToken(char *token, DBObjectTypes *object_type);
99 
100 static bool check_redirect_node_spec(char *node_spec);
101 static char **get_list_from_string(const char *str, const char *delimi, int *n);
102 static char **get_list_from_string_regex_delim(const char *str, const char *delimi, int *n);
103 
104 
105 /*show functions */
106 static const char *IntValueShowFunc(int value);
107 static const char *HBDestinationPortShowFunc(int index);
108 static const char *HBDeviceShowFunc(int index);
109 static const char *HBHostnameShowFunc(int index);
110 static const char *OtherWDPortShowFunc(int index);
111 static const char *OtherPPPortShowFunc(int index);
112 static const char *OtherPPHostShowFunc(int index);
113 static const char *BackendFlagsShowFunc(int index);
114 static const char *BackendDataDirShowFunc(int index);
115 static const char *BackendHostShowFunc(int index);
116 static const char *BackendPortShowFunc(int index);
117 static const char *BackendWeightShowFunc(int index);
118 static const char *BackendAppNameShowFunc(int index);
119 
120 static const char *HealthCheckPeriodShowFunc(int index);
121 static const char *HealthCheckTimeOutShowFunc(int index);
122 static const char *HealthCheckMaxRetriesShowFunc(int index);
123 static const char *HealthCheckRetryDelayShowFunc(int index);
124 static const char *HealthCheckConnectTimeOutShowFunc(int index);
125 static const char *HealthCheckUserShowFunc(int index);
126 static const char *HealthCheckPasswordShowFunc(int index);
127 static const char *HealthCheckDatabaseShowFunc(int index);
128 
129 
130 /* check empty slot functions */
131 static bool WdIFSlotEmptyCheckFunc(int index);
132 static bool WdSlotEmptyCheckFunc(int index);
133 static bool BackendSlotEmptyCheckFunc(int index);
134 
135 /*variable custom assign functions */
136 static bool FailOverOnBackendErrorAssignMessage(ConfigContext scontext, bool newval, int elevel);
137 static bool BackendPortAssignFunc(ConfigContext context, int newval, int index, int elevel);
138 static bool BackendHostAssignFunc(ConfigContext context, char *newval, int index, int elevel);
139 static bool BackendDataDirAssignFunc(ConfigContext context, char *newval, int index, int elevel);
140 static bool BackendFlagsAssignFunc(ConfigContext context, char *newval, int index, int elevel);
141 static bool BackendWeightAssignFunc(ConfigContext context, double newval, int index, int elevel);
142 static bool BackendAppNameAssignFunc(ConfigContext context, char *newval, int index, int elevel);
143 static bool HBDestinationPortAssignFunc(ConfigContext context, int newval, int index, int elevel);
144 static bool HBDeviceAssignFunc(ConfigContext context, char *newval, int index, int elevel);
145 static bool HBHostnameAssignFunc(ConfigContext context, char *newval, int index, int elevel);
146 static bool OtherWDPortAssignFunc(ConfigContext context, int newval, int index, int elevel);
147 static bool OtherPPPortAssignFunc(ConfigContext context, int newval, int index, int elevel);
148 static bool OtherPPHostAssignFunc(ConfigContext context, char *newval, int index, int elevel);
149 
150 static bool HealthCheckPeriodAssignFunc(ConfigContext context, int newval, int index, int elevel);
151 static bool HealthCheckTimeOutAssignFunc(ConfigContext context, int newval, int index, int elevel);
152 static bool HealthCheckMaxRetriesAssignFunc(ConfigContext context, int newval, int index, int elevel);
153 static bool HealthCheckRetryDelayAssignFunc(ConfigContext context, int newval, int index, int elevel);
154 static bool HealthCheckConnectTimeOutAssignFunc(ConfigContext context, int newval, int index, int elevel);
155 
156 static bool HealthCheckUserAssignFunc(ConfigContext context, char *newval, int index, int elevel);
157 static bool HealthCheckPasswordAssignFunc(ConfigContext context, char *newval, int index, int elevel);
158 static bool HealthCheckDatabaseAssignFunc(ConfigContext context, char *newval, int index, int elevel);
159 
160 static bool LogDestinationProcessFunc(char *newval, int elevel);
161 static bool SyslogIdentProcessFunc(char *newval, int elevel);
162 static bool SyslogFacilityProcessFunc(int newval, int elevel);
163 static bool SetHBDestIfFunc(int elevel);
164 static bool SetPgpoolNodeId(int elevel);
165 
166 static struct config_generic *get_index_free_record_if_any(struct config_generic *record);
167 
168 static bool parse_int(const char *value, int64 *result, int flags, const char **hintmsg, int64 MaxVal);
169 static bool convert_to_base_unit(double value, const char *unit,
170 								 int base_unit, double *base_value);
171 
172 #ifndef POOL_PRIVATE
173 
174 static void convert_int_from_base_unit(int64 base_value, int base_unit,
175 						   int64 *value, const char **unit);
176 
177 
178 /* These functions are used to provide Hints for enum type config parameters and
179  * to output the values of the parameters.
180  * These functuons are not available for tools since they use the stringInfo that is
181  * not present for tools.
182  */
183 
184 static const char *config_enum_lookup_by_value(struct config_enum *record, int val);
185 
186 static char *ShowOption(struct config_generic *record, int index, int elevel);
187 
188 static char *config_enum_get_options(struct config_enum *record, const char *prefix,
189 						const char *suffix, const char *separator);
190 static void send_row_description_for_detail_view(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend);
191 static int send_grouped_type_variable_to_frontend(struct config_grouped_array_var *grouped_record,
192 									   POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend);
193 static int send_array_type_variable_to_frontend(struct config_generic *record,
194 									 POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend);
195 
196 #endif
197 
198 
199 static const struct config_enum_entry log_error_verbosity_options[] = {
200 	{"terse", PGERROR_TERSE, false},
201 	{"default", PGERROR_DEFAULT, false},
202 	{"verbose", PGERROR_VERBOSE, false},
203 	{NULL, 0, false}
204 };
205 static const struct config_enum_entry server_message_level_options[] = {
206 	{"debug", DEBUG2, true},
207 	{"debug5", DEBUG5, false},
208 	{"debug4", DEBUG4, false},
209 	{"debug3", DEBUG3, false},
210 	{"debug2", DEBUG2, false},
211 	{"debug1", DEBUG1, false},
212 	{"info", INFO, false},
213 	{"notice", NOTICE, false},
214 	{"warning", WARNING, false},
215 	{"error", ERROR, false},
216 	{"log", LOG, false},
217 	{"fatal", FATAL, false},
218 	{"panic", PANIC, false},
219 	{NULL, 0, false}
220 };
221 
222 static const struct config_enum_entry backend_clustering_mode_options[] = {
223 	{"streaming_replication", CM_STREAMING_REPLICATION, false},
224 	{"native_replication", CM_NATIVE_REPLICATION, false},
225 	{"logical_replication", CM_LOGICAL_REPLICATION, false},
226 	{"slony", CM_SLONY, false},
227 	{"raw", CM_RAW, false},
228 	{"snapshot_isolation", CM_SNAPSHOT_ISOLATION, false},
229 	{NULL, 0, false}
230 };
231 
232 static const struct config_enum_entry log_standby_delay_options[] = {
233 	{"always", LSD_ALWAYS, false},
234 	{"if_over_threshold", LSD_OVER_THRESHOLD, false},
235 	{"none", LSD_NONE, false},
236 	{NULL, 0, false}
237 };
238 
239 static const struct config_enum_entry memqcache_method_options[] = {
240 	{"shmem", SHMEM_CACHE, false},
241 	{"memcached", MEMCACHED_CACHE, false},
242 	{NULL, 0, false}
243 };
244 
245 static const struct config_enum_entry wd_lifecheck_method_options[] = {
246 	{"query", LIFECHECK_BY_QUERY, false},
247 	{"heartbeat", LIFECHECK_BY_HB, false},
248 	{"external", LIFECHECK_BY_EXTERNAL, false},
249 	{NULL, 0, false}
250 };
251 
252 static const struct config_enum_entry syslog_facility_options[] = {
253 	{"LOCAL0", LOG_LOCAL0, false},
254 	{"LOCAL1", LOG_LOCAL1, false},
255 	{"LOCAL2", LOG_LOCAL2, false},
256 	{"LOCAL3", LOG_LOCAL3, false},
257 	{"LOCAL4", LOG_LOCAL4, false},
258 	{"LOCAL5", LOG_LOCAL5, false},
259 	{"LOCAL6", LOG_LOCAL6, false},
260 	{"LOCAL7", LOG_LOCAL7, false},
261 	{NULL, 0, false}
262 };
263 
264 static const struct config_enum_entry disable_load_balance_on_write_options[] = {
265 	{"off", DLBOW_OFF, false},
266 	{"transaction", DLBOW_TRANSACTION, false},
267 	{"trans_transaction", DLBOW_TRANS_TRANSACTION, false},
268 	{"always", DLBOW_ALWAYS, false},
269 	{"dml_adaptive", DLBOW_DML_ADAPTIVE, false},
270 	{NULL, 0, false}
271 };
272 
273 static const struct config_enum_entry relcache_query_target_options[] = {
274 	{"primary", RELQTARGET_PRIMARY, false},
275 	{"load_balance_node", RELQTARGET_LOAD_BALANCE_NODE, false},
276 	{NULL, 0, false}
277 };
278 
279 static const struct config_enum_entry check_temp_table_options[] = {
280 	{"catalog", CHECK_TEMP_CATALOG, false},	/* search system catalogs */
281 	{"trace", CHECK_TEMP_TRACE, false},		/* tracing temp tables */
282 	{"none", CHECK_TEMP_NONE, false},		/* do not check temp tables */
283 	{"on", CHECK_TEMP_ON, false},			/* same as CHECK_TEMP_CATALOG. Just for backward compatibilty. */
284 	{"off", CHECK_TEMP_OFF, false},			/* same as CHECK_TEMP_NONE. Just for backward compatibilty. */
285 	{NULL, 0, false}
286 };
287 
288 /* From PostgreSQL's guc.c */
289 /*
290  * Unit conversion tables.
291  *
292  * There are two tables, one for memory units, and another for time units.
293  * For each supported conversion from one unit to another, we have an entry
294  * in the table.
295  *
296  * To keep things simple, and to avoid possible roundoff error,
297  * conversions are never chained.  There needs to be a direct conversion
298  * between all units (of the same type).
299  *
300  * The conversions for each base unit must be kept in order from greatest to
301  * smallest human-friendly unit; convert_xxx_from_base_unit() rely on that.
302  * (The order of the base-unit groups does not matter.)
303  */
304 #define MAX_UNIT_LEN		3	/* length of longest recognized unit string */
305 
306 typedef struct
307 {
308 	char		unit[MAX_UNIT_LEN + 1]; /* unit, as a string, like "kB" or
309 										 * "min" */
310 	int			base_unit;		/* GUC_UNIT_XXX */
311 	double		multiplier;		/* Factor for converting unit -> base_unit */
312 } unit_conversion;
313 
314 static const char *memory_units_hint = "Valid units for this parameter are \"B\", \"kB\", \"MB\", \"GB\", and \"TB\".";
315 
316 static const unit_conversion memory_unit_conversion_table[] =
317 {
318 	{"TB", GUC_UNIT_BYTE, 1024.0 * 1024.0 * 1024.0 * 1024.0},
319 	{"GB", GUC_UNIT_BYTE, 1024.0 * 1024.0 * 1024.0},
320 	{"MB", GUC_UNIT_BYTE, 1024.0 * 1024.0},
321 	{"kB", GUC_UNIT_BYTE, 1024.0},
322 	{"B", GUC_UNIT_BYTE, 1.0},
323 
324 	{"TB", GUC_UNIT_KB, 1024.0 * 1024.0 * 1024.0},
325 	{"GB", GUC_UNIT_KB, 1024.0 * 1024.0},
326 	{"MB", GUC_UNIT_KB, 1024.0},
327 	{"kB", GUC_UNIT_KB, 1.0},
328 	{"B", GUC_UNIT_KB, 1.0 / 1024.0},
329 
330 	{"TB", GUC_UNIT_MB, 1024.0 * 1024.0},
331 	{"GB", GUC_UNIT_MB, 1024.0},
332 	{"MB", GUC_UNIT_MB, 1.0},
333 	{"kB", GUC_UNIT_MB, 1.0 / 1024.0},
334 	{"B", GUC_UNIT_MB, 1.0 / (1024.0 * 1024.0)},
335 
336 	{""}						/* end of table marker */
337 };
338 
339 static const char *time_units_hint = "Valid units for this parameter are \"us\", \"ms\", \"s\", \"min\", \"h\", and \"d\".";
340 
341 static const unit_conversion time_unit_conversion_table[] =
342 {
343 	{"d", GUC_UNIT_MS, 1000 * 60 * 60 * 24},
344 	{"h", GUC_UNIT_MS, 1000 * 60 * 60},
345 	{"min", GUC_UNIT_MS, 1000 * 60},
346 	{"s", GUC_UNIT_MS, 1000},
347 	{"ms", GUC_UNIT_MS, 1},
348 	{"us", GUC_UNIT_MS, 1.0 / 1000},
349 
350 	{"d", GUC_UNIT_S, 60 * 60 * 24},
351 	{"h", GUC_UNIT_S, 60 * 60},
352 	{"min", GUC_UNIT_S, 60},
353 	{"s", GUC_UNIT_S, 1},
354 	{"ms", GUC_UNIT_S, 1.0 / 1000},
355 	{"us", GUC_UNIT_S, 1.0 / (1000 * 1000)},
356 
357 	{"d", GUC_UNIT_MIN, 60 * 24},
358 	{"h", GUC_UNIT_MIN, 60},
359 	{"min", GUC_UNIT_MIN, 1},
360 	{"s", GUC_UNIT_MIN, 1.0 / 60},
361 	{"ms", GUC_UNIT_MIN, 1.0 / (1000 * 60)},
362 	{"us", GUC_UNIT_MIN, 1.0 / (1000 * 1000 * 60)},
363 
364 	{""}						/* end of table marker */
365 };
366 static struct config_bool ConfigureNamesBool[] =
367 {
368 	{
369 		{"serialize_accept", CFGCXT_INIT, CONNECTION_CONFIG,
370 			"whether to serialize accept() call to avoid thundering herd problem",
371 			CONFIG_VAR_TYPE_BOOL, false, 0
372 		},
373 		&g_pool_config.serialize_accept,	/* variable */
374 		false,					/* boot value */
375 		NULL,					/* assign func */
376 		NULL,					/* check func */
377 		NULL					/* show hook */
378 	},
379 	{
380 		{"failover_when_quorum_exists", CFGCXT_INIT, FAILOVER_CONFIG,
381 			"Do failover only when cluster has the quorum.",
382 			CONFIG_VAR_TYPE_BOOL, false, 0
383 		},
384 		&g_pool_config.failover_when_quorum_exists,
385 		false,
386 		NULL, NULL, NULL
387 	},
388 	{
389 		{"failover_require_consensus", CFGCXT_INIT, FAILOVER_CONFIG,
390 			"Only do failover when majority aggrees.",
391 			CONFIG_VAR_TYPE_BOOL, false, 0
392 		},
393 		&g_pool_config.failover_require_consensus,
394 		false,
395 		NULL, NULL, NULL
396 	},
397 	{
398 		{"allow_multiple_failover_requests_from_node", CFGCXT_INIT, FAILOVER_CONFIG,
399 			"A Pgpool-II node can send multiple failover requests to build consensus.",
400 			CONFIG_VAR_TYPE_BOOL, false, 0
401 		},
402 		&g_pool_config.allow_multiple_failover_requests_from_node,
403 		false,
404 		NULL, NULL, NULL
405 	},
406 	{
407 		{"enable_consensus_with_half_votes", CFGCXT_INIT, FAILOVER_CONFIG,
408 			"apply majority rule for consensus and quorum computation at 50% of votes in a cluster with an even number of nodes.",
409 			CONFIG_VAR_TYPE_BOOL, false, 0
410 		},
411 		&g_pool_config.enable_consensus_with_half_votes,
412 		false,
413 		NULL, NULL, NULL
414 	},
415 	{
416 		{"log_connections", CFGCXT_RELOAD, LOGING_CONFIG,
417 			"Logs each successful connection.",
418 			CONFIG_VAR_TYPE_BOOL, false, 0
419 		},
420 		&g_pool_config.log_connections,
421 		false,
422 		NULL, NULL, NULL
423 	},
424 
425 	{
426 		{"log_disconnections", CFGCXT_RELOAD, LOGING_CONFIG,
427 			"Logs end of a session.",
428 			CONFIG_VAR_TYPE_BOOL, false, 0
429 		},
430 		&g_pool_config.log_disconnections,
431 		false,
432 		NULL, NULL, NULL
433 	},
434 
435 	{
436 		{"log_hostname", CFGCXT_RELOAD, LOGING_CONFIG,
437 			"Logs the host name in the connection logs.",
438 			CONFIG_VAR_TYPE_BOOL, false, 0
439 		},
440 		&g_pool_config.log_hostname,
441 		false,
442 		NULL, NULL, NULL
443 	},
444 
445 	{
446 		{"enable_pool_hba", CFGCXT_RELOAD, CONNECTION_CONFIG,
447 			"Use pool_hba.conf for client authentication.",
448 			CONFIG_VAR_TYPE_BOOL, false, 0
449 		},
450 		&g_pool_config.enable_pool_hba,
451 		false,
452 		NULL, NULL, NULL
453 	},
454 
455 	{
456 		{"replication_mode", CFGCXT_INIT, LOGING_CONFIG,
457 			"Enables replication mode.",
458 			CONFIG_VAR_TYPE_BOOL, false, 0
459 		},
460 		&g_pool_config.replication_mode,
461 		false,
462 		NULL, NULL, NULL
463 	},
464 
465 	{
466 		{"load_balance_mode", CFGCXT_INIT, LOAD_BALANCE_CONFIG,
467 			"Enables load balancing of queries.",
468 			CONFIG_VAR_TYPE_BOOL, false, 0
469 		},
470 		&g_pool_config.load_balance_mode,
471 		false,
472 		NULL, NULL, NULL
473 	},
474 
475 	{
476 		{"replication_stop_on_mismatch", CFGCXT_RELOAD, REPLICATION_CONFIG,
477 			"Starts degeneration and stops replication, If there's a data mismatch between primary and secondary.",
478 			CONFIG_VAR_TYPE_BOOL, false, 0
479 		},
480 		&g_pool_config.replication_stop_on_mismatch,
481 		false,
482 		NULL, NULL, NULL
483 	},
484 
485 	{
486 		{"failover_if_affected_tuples_mismatch", CFGCXT_RELOAD, REPLICATION_CONFIG,
487 			"Starts degeneration, If there's a data mismatch between primary and secondary.",
488 			CONFIG_VAR_TYPE_BOOL, false, 0
489 		},
490 		&g_pool_config.failover_if_affected_tuples_mismatch,
491 		false,
492 		NULL, NULL, NULL
493 	},
494 
495 	{
496 		{"replicate_select", CFGCXT_RELOAD, REPLICATION_CONFIG,
497 			"Replicate SELECT statements when load balancing is disabled.",
498 			CONFIG_VAR_TYPE_BOOL, false, 0
499 		},
500 		&g_pool_config.replicate_select,
501 		false,
502 		NULL, NULL, NULL
503 	},
504 
505 	{
506 		{"connection_cache", CFGCXT_INIT, CONNECTION_POOL_CONFIG,
507 			"Caches connections to backends.",
508 			CONFIG_VAR_TYPE_BOOL, false, 0
509 		},
510 		&g_pool_config.connection_cache,
511 		true,
512 		NULL, NULL, NULL
513 	},
514 
515 	{
516 		{"fail_over_on_backend_error", CFGCXT_RELOAD, FAILOVER_CONFIG,
517 			"Old config parameter for failover_on_backend_error.",
518 			CONFIG_VAR_TYPE_BOOL, false, VAR_HIDDEN_IN_SHOW_ALL
519 		},
520 		NULL,
521 		true,
522 		FailOverOnBackendErrorAssignMessage, NULL, NULL
523 	},
524 
525 	{
526 		{"failover_on_backend_error", CFGCXT_RELOAD, FAILOVER_CONFIG,
527 			"Triggers fail over when reading/writing to backend socket fails.",
528 			CONFIG_VAR_TYPE_BOOL, false, 0
529 		},
530 		&g_pool_config.failover_on_backend_error,
531 		true,
532 		NULL, NULL, NULL
533 	},
534 
535 	{
536 		{"detach_false_primary", CFGCXT_RELOAD, FAILOVER_CONFIG,
537 			"Automatically detaches false primary node.",
538 			CONFIG_VAR_TYPE_BOOL, false, 0
539 		},
540 		&g_pool_config.detach_false_primary,
541 		false,
542 		NULL, NULL, NULL
543 	},
544 
545 	{
546 		{"insert_lock", CFGCXT_RELOAD, REPLICATION_CONFIG,
547 			"Automatically locks table with INSERT to keep SERIAL data consistency",
548 			CONFIG_VAR_TYPE_BOOL, false, 0
549 		},
550 		&g_pool_config.insert_lock,
551 		true,
552 		NULL, NULL, NULL
553 	},
554 
555 	{
556 		{"ignore_leading_white_space", CFGCXT_RELOAD, LOAD_BALANCE_CONFIG,
557 			"Ignores leading white spaces of each query string.",
558 			CONFIG_VAR_TYPE_BOOL, false, 0
559 		},
560 		&g_pool_config.ignore_leading_white_space,
561 		true,
562 		NULL, NULL, NULL
563 	},
564 
565 	{
566 		{"log_statement", CFGCXT_SESSION, LOGING_CONFIG,
567 			"Logs all statements in the pgpool logs.",
568 			CONFIG_VAR_TYPE_BOOL, false, 0
569 		},
570 		&g_pool_config.log_statement,
571 		false,
572 		NULL, NULL, NULL
573 	},
574 
575 	{
576 		{"log_per_node_statement", CFGCXT_SESSION, LOGING_CONFIG,
577 			"Logs per node detailed SQL statements.",
578 			CONFIG_VAR_TYPE_BOOL, false, 0
579 		},
580 		&g_pool_config.log_per_node_statement,
581 		false,
582 		NULL, NULL, NULL
583 	},
584 
585 	{
586 		{"log_client_messages", CFGCXT_SESSION, LOGING_CONFIG,
587 			"Logs any client messages in the pgpool logs.",
588 			CONFIG_VAR_TYPE_BOOL, false, 0
589 		},
590 		&g_pool_config.log_client_messages,
591 		false,
592 		NULL, NULL, NULL
593 	},
594 
595 	{
596 		{"use_watchdog", CFGCXT_INIT, WATCHDOG_CONFIG,
597 			"Enables the pgpool-II watchdog.",
598 			CONFIG_VAR_TYPE_BOOL, false, 0
599 		},
600 		&g_pool_config.use_watchdog,
601 		false,
602 		NULL, NULL, NULL
603 	},
604 
605 	{
606 		{"clear_memqcache_on_escalation", CFGCXT_RELOAD, WATCHDOG_CONFIG,
607 			"Clears the query cache in the shared memory when pgpool-II escaltes to leader watchdog node.",
608 			CONFIG_VAR_TYPE_BOOL, false, 0
609 		},
610 		&g_pool_config.clear_memqcache_on_escalation,
611 		false,
612 		NULL, NULL, NULL
613 	},
614 
615 	{
616 		{"ssl", CFGCXT_INIT, SSL_CONFIG,
617 			"Enables SSL support for frontend and backend connections",
618 			CONFIG_VAR_TYPE_BOOL, false, 0
619 		},
620 		&g_pool_config.ssl,
621 		false,
622 		NULL, NULL, NULL
623 	},
624 
625 	{
626 		{"ssl_prefer_server_ciphers", CFGCXT_INIT, SSL_CONFIG,
627 			"Use server's SSL cipher preferences, rather than the client's",
628 			CONFIG_VAR_TYPE_BOOL, false, 0
629 		},
630 		&g_pool_config.ssl_prefer_server_ciphers,
631 		false,
632 		NULL, NULL, NULL
633 	},
634 
635 	{
636 		{"check_unlogged_table", CFGCXT_SESSION, GENERAL_CONFIG,
637 			"Enables unlogged table check.",
638 			CONFIG_VAR_TYPE_BOOL, false, 0
639 		},
640 		&g_pool_config.check_unlogged_table,
641 		true,
642 		NULL, NULL, NULL
643 	},
644 
645 	{
646 		{"memory_cache_enabled", CFGCXT_INIT, CACHE_CONFIG,
647 			"Enables the memory cache functionality.",
648 			CONFIG_VAR_TYPE_BOOL, false, 0
649 		},
650 		&g_pool_config.memory_cache_enabled,
651 		false,
652 		NULL, NULL, NULL
653 	},
654 
655 	{
656 		{"enable_shared_relcache", CFGCXT_INIT, CACHE_CONFIG,
657 			"relation cache stored in memory cache.",
658 			CONFIG_VAR_TYPE_BOOL, false, 0
659 		},
660 		&g_pool_config.enable_shared_relcache,
661 		true,
662 		NULL, NULL, NULL
663 	},
664 
665 	{
666 		{"memqcache_auto_cache_invalidation", CFGCXT_RELOAD, CACHE_CONFIG,
667 			"Automatically deletes the cache related to the updated tables.",
668 			CONFIG_VAR_TYPE_BOOL, false, 0
669 		},
670 		&g_pool_config.memqcache_auto_cache_invalidation,
671 		true,
672 		NULL, NULL, NULL
673 	},
674 
675 	{
676 		{"allow_sql_comments", CFGCXT_SESSION, LOAD_BALANCE_CONFIG,
677 			"Ignore SQL comments, while judging if load balance or query cache is possible.",
678 			CONFIG_VAR_TYPE_BOOL, false, 0
679 		},
680 		&g_pool_config.allow_sql_comments,
681 		false,
682 		NULL, NULL, NULL
683 	},
684 	{
685 		{"allow_clear_text_frontend_auth", CFGCXT_RELOAD, GENERAL_CONFIG,
686 			"allow to use clear text password authentication with clients, when pool_passwd does not contain the user password.",
687 			CONFIG_VAR_TYPE_BOOL, false, 0
688 		},
689 		&g_pool_config.allow_clear_text_frontend_auth,
690 		false,
691 		NULL, NULL, NULL
692 	},
693 
694 	{
695 		{"statement_level_load_balance", CFGCXT_RELOAD, LOAD_BALANCE_CONFIG,
696 			"Enables statement level load balancing",
697 			CONFIG_VAR_TYPE_BOOL, false, 0
698 		},
699 		&g_pool_config.statement_level_load_balance,
700 		false,
701 		NULL, NULL, NULL
702 	},
703 
704 	{
705 		{"auto_failback", CFGCXT_RELOAD, FAILOVER_CONFIG,
706 			"Enables nodes automatically reattach, when dettached node continue streaming replication.",
707 			CONFIG_VAR_TYPE_BOOL, false, 0
708 		},
709 		&g_pool_config.auto_failback,
710 		false,
711 		NULL, NULL, NULL
712 	},
713 	{
714 		{"logging_collector", CFGCXT_INIT, LOGING_CONFIG,
715 			"Enable capturing of stderr into log files.",
716 			CONFIG_VAR_TYPE_BOOL, false, 0
717 		},
718 		&g_pool_config.logging_collector,
719 		false,
720 		NULL, NULL, NULL
721 	},
722 	{
723 		{"log_truncate_on_rotation", CFGCXT_INIT, LOGING_CONFIG,
724 			"If on, an existing log file gets truncated on time based log rotation.",
725 			CONFIG_VAR_TYPE_BOOL, false, 0
726 		},
727 		&g_pool_config.log_truncate_on_rotation,
728 		false,
729 		NULL, NULL, NULL
730 	},
731 
732 	/* End-of-list marker */
733 	EMPTY_CONFIG_BOOL
734 
735 };
736 
737 static struct config_string ConfigureNamesString[] =
738 {
739 	{
740 		{"database_redirect_preference_list", CFGCXT_RELOAD, STREAMING_REPLICATION_CONFIG,
741 			"redirect by database name.",
742 			CONFIG_VAR_TYPE_STRING, false, 0
743 		},
744 		&g_pool_config.database_redirect_preference_list,	/* variable */
745 		NULL,					/* boot value */
746 		NULL,					/* assign_func */
747 		NULL,					/* check_func */
748 		MakeDBRedirectListRegex,	/* process func */
749 		NULL					/* show hook */
750 	},
751 
752 	{
753 		{"app_name_redirect_preference_list", CFGCXT_RELOAD, STREAMING_REPLICATION_CONFIG,
754 			"redirect by application name.",
755 			CONFIG_VAR_TYPE_STRING, false, 0
756 		},
757 		&g_pool_config.app_name_redirect_preference_list,
758 		NULL,
759 		NULL, NULL,
760 		MakeAppRedirectListRegex, NULL
761 	},
762 
763 	{
764 		{"dml_adaptive_object_relationship_list", CFGCXT_RELOAD, STREAMING_REPLICATION_CONFIG,
765 			"list of relationships between objects.",
766 			CONFIG_VAR_TYPE_STRING, false, 0
767 		},
768 		&g_pool_config.dml_adaptive_object_relationship_list,
769 		NULL,
770 		NULL, NULL,
771 		MakeDMLAdaptiveObjectRelationList, NULL
772 	},
773 
774 	{
775 		{"listen_addresses", CFGCXT_INIT, CONNECTION_CONFIG,
776 			"hostname or IP address on which pgpool will listen on.",
777 			CONFIG_VAR_TYPE_STRING, false, 0
778 		},
779 		&g_pool_config.listen_addresses,
780 		"localhost",
781 		NULL, NULL, NULL, NULL
782 	},
783 
784 	{
785 		{"pcp_listen_addresses", CFGCXT_INIT, CONNECTION_CONFIG,
786 			"hostname(s) or IP address(es) on which pcp will listen on.",
787 			CONFIG_VAR_TYPE_STRING, false, 0
788 		},
789 		&g_pool_config.pcp_listen_addresses,
790 		"*",
791 		NULL, NULL, NULL, NULL
792 	},
793 
794 	{
795 		{"socket_dir", CFGCXT_INIT, CONNECTION_CONFIG,
796 			"The directory to create the UNIX domain socket for accepting pgpool-II client connections.",
797 			CONFIG_VAR_TYPE_STRING, false, 0
798 		},
799 		&g_pool_config.socket_dir,
800 		DEFAULT_SOCKET_DIR,
801 		NULL, NULL, NULL, NULL
802 	},
803 
804 	{
805 		{"pcp_socket_dir", CFGCXT_INIT, CONNECTION_CONFIG,
806 			"The directory to create the UNIX domain socket for accepting pgpool-II PCP connections.",
807 			CONFIG_VAR_TYPE_STRING, false, 0
808 		},
809 		&g_pool_config.pcp_socket_dir,
810 		DEFAULT_SOCKET_DIR,
811 		NULL, NULL, NULL, NULL
812 	},
813 
814 	{
815 		{"wd_ipc_socket_dir", CFGCXT_INIT, CONNECTION_CONFIG,
816 			"The directory to create the UNIX domain socket for accepting pgpool-II watchdog IPC connections.",
817 			CONFIG_VAR_TYPE_STRING, false, 0
818 		},
819 		&g_pool_config.wd_ipc_socket_dir,
820 		DEFAULT_SOCKET_DIR,
821 		NULL, NULL, NULL, NULL
822 	},
823 
824 	{
825 		{"log_destination", CFGCXT_RELOAD, LOGING_CONFIG,
826 			"destination of pgpool-II log",
827 			CONFIG_VAR_TYPE_STRING, false, 0
828 		},
829 		&g_pool_config.log_destination_str,
830 		"stderr",
831 		NULL, NULL,
832 		LogDestinationProcessFunc,
833 		NULL
834 	},
835 
836 	{
837 		{"syslog_ident", CFGCXT_RELOAD, LOGING_CONFIG,
838 			"syslog program ident string.",
839 			CONFIG_VAR_TYPE_STRING, false, 0
840 		},
841 		&g_pool_config.syslog_ident,
842 		"pgpool",
843 		NULL, NULL,
844 		SyslogIdentProcessFunc,
845 		NULL
846 	},
847 
848 	{
849 		{"pid_file_name", CFGCXT_INIT, FILE_LOCATION_CONFIG,
850 			"Path to store pgpool-II pid file.",
851 			CONFIG_VAR_TYPE_STRING, false, 0
852 		},
853 		&g_pool_config.pid_file_name,
854 		DEFAULT_PID_FILE_NAME,
855 		NULL, NULL, NULL, NULL
856 	},
857 
858 	{
859 		{"pool_passwd", CFGCXT_INIT, FILE_LOCATION_CONFIG,
860 			"File name of pool_passwd for md5 authentication.",
861 			CONFIG_VAR_TYPE_STRING, false, VAR_HIDDEN_VALUE
862 		},
863 		&g_pool_config.pool_passwd,
864 		"pool_passwd",
865 		NULL, NULL, NULL, NULL
866 	},
867 
868 	{
869 		{"log_line_prefix", CFGCXT_RELOAD, LOGING_CONFIG,
870 			"printf-style string to output at beginning of each log line.",
871 			CONFIG_VAR_TYPE_STRING, false, 0
872 		},
873 		&g_pool_config.log_line_prefix,
874 		"%t: pid %p: ",
875 		NULL, NULL, NULL, NULL
876 	},
877 
878 	{
879 		{"sr_check_user", CFGCXT_RELOAD, STREAMING_REPLICATION_CONFIG,
880 			"The User name to perform streaming replication delay check.",
881 			CONFIG_VAR_TYPE_STRING, false, 0
882 		},
883 		&g_pool_config.sr_check_user,
884 		"nobody",
885 		NULL, NULL, NULL, NULL
886 	},
887 
888 	{
889 		{"sr_check_password", CFGCXT_RELOAD, STREAMING_REPLICATION_CONFIG,
890 			"The password for user to perform streaming replication delay check.",
891 			CONFIG_VAR_TYPE_STRING, false, VAR_HIDDEN_VALUE
892 		},
893 		&g_pool_config.sr_check_password,
894 		"",
895 		NULL, NULL, NULL, NULL
896 	},
897 
898 	{
899 		{"sr_check_database", CFGCXT_RELOAD, STREAMING_REPLICATION_CONFIG,
900 			"The database name to perform streaming replication delay check.",
901 			CONFIG_VAR_TYPE_STRING, false, 0
902 		},
903 		&g_pool_config.sr_check_database,
904 		"postgres",
905 		NULL, NULL, NULL, NULL
906 	},
907 
908 	{
909 		{"failback_command", CFGCXT_RELOAD, FAILOVER_CONFIG,
910 			"Command to execute when backend node is attached.",
911 			CONFIG_VAR_TYPE_STRING, false, 0
912 		},
913 		&g_pool_config.failback_command,
914 		"",
915 		NULL, NULL, NULL, NULL
916 	},
917 
918 	{
919 		{"follow_primary_command", CFGCXT_RELOAD, FAILOVER_CONFIG,
920 			"Command to execute in streaming replication mode after a primary node failover.",
921 			CONFIG_VAR_TYPE_STRING, false, 0
922 		},
923 		&g_pool_config.follow_primary_command,
924 		"",
925 		NULL, NULL, NULL, NULL
926 	},
927 
928 	{
929 		{"failover_command", CFGCXT_RELOAD, FAILOVER_CONFIG,
930 			"Command to execute when backend node is detached.",
931 			CONFIG_VAR_TYPE_STRING, false, 0
932 		},
933 		&g_pool_config.failover_command,
934 		"",
935 		NULL, NULL, NULL, NULL
936 	},
937 
938 	{
939 		{"recovery_user", CFGCXT_RELOAD, RECOVERY_CONFIG,
940 			"User name for online recovery.",
941 			CONFIG_VAR_TYPE_STRING, false, 0
942 		},
943 		&g_pool_config.recovery_user,
944 		"",
945 		NULL, NULL, NULL, NULL
946 	},
947 
948 	{
949 		{"recovery_password", CFGCXT_RELOAD, RECOVERY_CONFIG,
950 			"Password for online recovery.",
951 			CONFIG_VAR_TYPE_STRING, false, VAR_HIDDEN_VALUE
952 		},
953 		&g_pool_config.recovery_password,
954 		"",
955 		NULL, NULL, NULL, NULL
956 	},
957 
958 	{
959 		{"recovery_1st_stage_command", CFGCXT_RELOAD, RECOVERY_CONFIG,
960 			"Command to execute in first stage recovery.",
961 			CONFIG_VAR_TYPE_STRING, false, 0
962 		},
963 		&g_pool_config.recovery_1st_stage_command,
964 		"",
965 		NULL, NULL, NULL, NULL
966 	},
967 
968 	{
969 		{"recovery_2nd_stage_command", CFGCXT_RELOAD, RECOVERY_CONFIG,
970 			"Command to execute in second stage recovery.",
971 			CONFIG_VAR_TYPE_STRING, false, 0
972 		},
973 		&g_pool_config.recovery_2nd_stage_command,
974 		"",
975 		NULL, NULL, NULL, NULL
976 	},
977 
978 	{
979 		{"lobj_lock_table", CFGCXT_RELOAD, REPLICATION_CONFIG,
980 			"Table name used for large object replication control.",
981 			CONFIG_VAR_TYPE_STRING, false, 0
982 		},
983 		&g_pool_config.lobj_lock_table,
984 		"",
985 		NULL, NULL, NULL, NULL
986 	},
987 
988 	{
989 		{"wd_escalation_command", CFGCXT_RELOAD, WATCHDOG_CONFIG,
990 			"Command to execute when watchdog node becomes cluster leader node.",
991 			CONFIG_VAR_TYPE_STRING, false, 0
992 		},
993 		&g_pool_config.wd_escalation_command,
994 		"",
995 		NULL, NULL, NULL, NULL
996 	},
997 
998 	{
999 		{"wd_de_escalation_command", CFGCXT_RELOAD, WATCHDOG_CONFIG,
1000 			"Command to execute when watchdog node resigns from the cluster leader node.",
1001 			CONFIG_VAR_TYPE_STRING, false, 0
1002 		},
1003 		&g_pool_config.wd_de_escalation_command,
1004 		"",
1005 		NULL, NULL, NULL, NULL
1006 	},
1007 
1008 	{
1009 		{"trusted_servers", CFGCXT_INIT, WATCHDOG_CONFIG,
1010 			"List of servers to verify connectivity.",
1011 			CONFIG_VAR_TYPE_STRING, false, 0
1012 		},
1013 		&g_pool_config.trusted_servers,
1014 		"",
1015 		NULL, NULL, NULL, NULL
1016 	},
1017 
1018 	{
1019 		{"delegate_IP", CFGCXT_INIT, WATCHDOG_CONFIG,
1020 			"Delegate IP address to be used when pgpool node become a watchdog cluster leader.",
1021 			CONFIG_VAR_TYPE_STRING, false, 0
1022 		},
1023 		&g_pool_config.delegate_IP,
1024 		"",
1025 		NULL, NULL, NULL, NULL
1026 	},
1027 
1028 	{
1029 		{"ping_path", CFGCXT_INIT, WATCHDOG_CONFIG,
1030 			"path to ping command.",
1031 			CONFIG_VAR_TYPE_STRING, false, 0
1032 		},
1033 		&g_pool_config.ping_path,
1034 		"/bin",
1035 		NULL, NULL, NULL, NULL
1036 	},
1037 
1038 	{
1039 		{"if_cmd_path", CFGCXT_INIT, WATCHDOG_CONFIG,
1040 			"Path to interface command.",
1041 			CONFIG_VAR_TYPE_STRING, false, 0
1042 		},
1043 		&g_pool_config.if_cmd_path,
1044 		"/sbin",
1045 		NULL, NULL, NULL, NULL
1046 	},
1047 
1048 	{
1049 		{"if_up_cmd", CFGCXT_INIT, WATCHDOG_CONFIG,
1050 			"Complete command to bring UP virtual interface.",
1051 			CONFIG_VAR_TYPE_STRING, false, 0
1052 		},
1053 		&g_pool_config.if_up_cmd,
1054 		"/usr/bin/sudo /sbin/ip addr add $_IP_$/24 dev eth0 label eth0:0",
1055 		NULL, NULL, NULL, NULL
1056 	},
1057 
1058 	{
1059 		{"if_down_cmd", CFGCXT_INIT, WATCHDOG_CONFIG,
1060 			"Complete command to bring down virtual interface.",
1061 			CONFIG_VAR_TYPE_STRING, false, 0
1062 		},
1063 		&g_pool_config.if_down_cmd,
1064 		"/usr/bin/sudo /sbin/ip addr del $_IP_$/24 dev eth0",
1065 		NULL, NULL, NULL, NULL
1066 	},
1067 
1068 	{
1069 		{"arping_path", CFGCXT_INIT, WATCHDOG_CONFIG,
1070 			"path to arping command.",
1071 			CONFIG_VAR_TYPE_STRING, false, 0
1072 		},
1073 		&g_pool_config.arping_path,
1074 		"/usr/sbin",
1075 		NULL, NULL, NULL, NULL
1076 	},
1077 
1078 	{
1079 		{"arping_cmd", CFGCXT_INIT, WATCHDOG_CONFIG,
1080 			"arping command.",
1081 			CONFIG_VAR_TYPE_STRING, false, 0
1082 		},
1083 		&g_pool_config.arping_cmd,
1084 		"/usr/bin/sudo /usr/sbin/arping -U $_IP_$ -w 1 -I eth0",
1085 		NULL, NULL, NULL, NULL
1086 	},
1087 
1088 	{
1089 		{"wd_lifecheck_query", CFGCXT_INIT, WATCHDOG_CONFIG,
1090 			"SQL query to be used by watchdog lifecheck.",
1091 			CONFIG_VAR_TYPE_STRING, false, 0
1092 		},
1093 		&g_pool_config.wd_lifecheck_query,
1094 		"SELECT 1",
1095 		NULL, NULL, NULL, NULL
1096 	},
1097 
1098 	{
1099 		{"wd_lifecheck_dbname", CFGCXT_RELOAD, WATCHDOG_CONFIG,
1100 			"Database name to be used for by watchdog lifecheck.",
1101 			CONFIG_VAR_TYPE_STRING, false, 0
1102 		},
1103 		&g_pool_config.wd_lifecheck_dbname,
1104 		"postgres",
1105 		NULL, NULL, NULL, NULL
1106 	},
1107 
1108 	{
1109 		{"wd_lifecheck_user", CFGCXT_RELOAD, WATCHDOG_CONFIG,
1110 			"User name to be used for by watchdog lifecheck.",
1111 			CONFIG_VAR_TYPE_STRING, false, 0
1112 		},
1113 		&g_pool_config.wd_lifecheck_user,
1114 		"nobody",
1115 		NULL, NULL, NULL, NULL
1116 	},
1117 
1118 	{
1119 		{"wd_lifecheck_password", CFGCXT_RELOAD, WATCHDOG_CONFIG,
1120 			"Password for watchdog user in lifecheck.",
1121 			CONFIG_VAR_TYPE_STRING, false, VAR_HIDDEN_VALUE
1122 		},
1123 		&g_pool_config.wd_lifecheck_password,
1124 		"postgres",
1125 		NULL, NULL, NULL, NULL
1126 	},
1127 
1128 	{
1129 		{"wd_authkey", CFGCXT_RELOAD, WATCHDOG_CONFIG,
1130 			"Authentication key to be used in watchdog communication.",
1131 			CONFIG_VAR_TYPE_STRING, false, 0
1132 		},
1133 		&g_pool_config.wd_authkey,
1134 		"",
1135 		NULL, NULL, NULL, NULL
1136 	},
1137 
1138 	{
1139 		{"ssl_cert", CFGCXT_INIT, SSL_CONFIG,
1140 			"SSL public certificate file.",
1141 			CONFIG_VAR_TYPE_STRING, false, 0
1142 		},
1143 		&g_pool_config.ssl_cert,
1144 		"",
1145 		NULL, NULL, NULL, NULL
1146 	},
1147 
1148 	{
1149 		{"ssl_key", CFGCXT_INIT, SSL_CONFIG,
1150 			"SSL private key file.",
1151 			CONFIG_VAR_TYPE_STRING, false, 0
1152 		},
1153 		&g_pool_config.ssl_key,
1154 		"",
1155 		NULL, NULL, NULL, NULL
1156 	},
1157 
1158 	{
1159 		{"ssl_ca_cert", CFGCXT_INIT, SSL_CONFIG,
1160 			"Single PEM format file containing CA root certificate(s).",
1161 			CONFIG_VAR_TYPE_STRING, false, 0
1162 		},
1163 		&g_pool_config.ssl_ca_cert,
1164 		"",
1165 		NULL, NULL, NULL, NULL
1166 	},
1167 
1168 	{
1169 		{"ssl_ca_cert_dir", CFGCXT_INIT, SSL_CONFIG,
1170 			"Directory containing CA root certificate(s).",
1171 			CONFIG_VAR_TYPE_STRING, false, 0
1172 		},
1173 		&g_pool_config.ssl_ca_cert_dir,
1174 		"",
1175 		NULL, NULL, NULL, NULL
1176 	},
1177 
1178 	{
1179 		{"ssl_crl_file", CFGCXT_INIT, SSL_CONFIG,
1180 			"SSL certificate revocation list file",
1181 			CONFIG_VAR_TYPE_STRING, false, 0
1182 		},
1183 		&g_pool_config.ssl_crl_file,
1184 		"",
1185 		NULL, NULL, NULL, NULL
1186 	},
1187 
1188 	{
1189 		{"ssl_ciphers", CFGCXT_INIT, SSL_CONFIG,
1190 			"Allowed SSL ciphers.",
1191 			CONFIG_VAR_TYPE_STRING, false, 0
1192 		},
1193 		&g_pool_config.ssl_ciphers,
1194 		"HIGH:MEDIUM:+3DES:!aNULL",
1195 		NULL, NULL, NULL, NULL
1196 	},
1197 
1198 	{
1199 		{"ssl_ecdh_curve", CFGCXT_INIT, SSL_CONFIG,
1200 			"The curve to use in ECDH key exchange.",
1201 			CONFIG_VAR_TYPE_STRING, false, 0
1202 		},
1203 		&g_pool_config.ssl_ecdh_curve,
1204 		"prime256v1",
1205 		NULL, NULL, NULL, NULL
1206 	},
1207 
1208 	{
1209 		{"ssl_dh_params_file", CFGCXT_INIT, SSL_CONFIG,
1210 			"Path to the Diffie-Hellman parameters contained file",
1211 			CONFIG_VAR_TYPE_STRING, false, 0
1212 		},
1213 		&g_pool_config.ssl_dh_params_file,
1214 		"",
1215 		NULL, NULL, NULL, NULL
1216 	},
1217 
1218 	{
1219 		{"ssl_passphrase_command", CFGCXT_INIT, SSL_CONFIG,
1220 			"Path to the Diffie-Hellman parameters contained file",
1221 			CONFIG_VAR_TYPE_STRING, false, 0
1222 		},
1223 		&g_pool_config.ssl_passphrase_command,
1224 		"",
1225 		NULL, NULL, NULL, NULL
1226 	},
1227 
1228 
1229 	{
1230 		{"memqcache_oiddir", CFGCXT_INIT, CACHE_CONFIG,
1231 			"Tempory directory to record table oids.",
1232 			CONFIG_VAR_TYPE_STRING, false, 0
1233 		},
1234 		&g_pool_config.memqcache_oiddir,
1235 		"/var/log/pgpool/oiddir",
1236 		NULL, NULL, NULL, NULL
1237 	},
1238 
1239 	{
1240 		{"memqcache_memcached_host", CFGCXT_INIT, CACHE_CONFIG,
1241 			"Hostname or IP address of memcached.",
1242 			CONFIG_VAR_TYPE_STRING, false, 0
1243 		},
1244 		&g_pool_config.memqcache_memcached_host,
1245 		"localhost",
1246 		NULL, NULL, NULL, NULL
1247 	},
1248 
1249 	{
1250 		{"logdir", CFGCXT_INIT, LOGING_CONFIG,
1251 			"PgPool status file logging directory.",
1252 			CONFIG_VAR_TYPE_STRING, false, 0
1253 		},
1254 		&g_pool_config.logdir,
1255 		DEFAULT_LOGDIR,
1256 		NULL, NULL, NULL, NULL
1257 	},
1258 	{
1259 		{"log_directory", CFGCXT_INIT, LOGING_CONFIG,
1260 			"directory where log files are written.",
1261 			CONFIG_VAR_TYPE_STRING, false, 0
1262 		},
1263 		&g_pool_config.log_directory,
1264 		"/tmp/pgpool_logs",
1265 		NULL, NULL, NULL, NULL
1266 	},
1267 
1268 	{
1269 		{"log_filename", CFGCXT_INIT, LOGING_CONFIG,
1270 			"log file name pattern.",
1271 			CONFIG_VAR_TYPE_STRING, false, 0
1272 		},
1273 		&g_pool_config.log_filename,
1274 		"pgpool-%Y-%m-%d_%H%M%S.log",
1275 		NULL, NULL, NULL, NULL
1276 	},
1277 
1278 	/* End-of-list marker */
1279 	EMPTY_CONFIG_STRING
1280 };
1281 
1282 static struct config_string_list ConfigureNamesStringList[] =
1283 {
1284 	{
1285 		{"reset_query_list", CFGCXT_RELOAD, CONNECTION_POOL_CONFIG,
1286 			"list of commands sent to reset the backend connection when user session exits.",
1287 			CONFIG_VAR_TYPE_STRING_LIST, false, 0
1288 		},
1289 		&g_pool_config.reset_query_list,	/* variable */
1290 		&g_pool_config.num_reset_queries,	/* item count var  */
1291 		(const char *) default_reset_query_list,	/* boot value */
1292 		";",					/* token seperator */
1293 		false,					/* compute_regex ? */
1294 		NULL, NULL, NULL		/* assign, check, show funcs */
1295 	},
1296 
1297 	{
1298 		{"read_only_function_list", CFGCXT_RELOAD, CONNECTION_POOL_CONFIG,
1299 			"list of functions that does not writes to database.",
1300 			CONFIG_VAR_TYPE_STRING_LIST, false, 0
1301 		},
1302 		&g_pool_config.read_only_function_list,
1303 		&g_pool_config.num_read_only_function_list,
1304 		NULL,
1305 		",",
1306 		true,
1307 		NULL, NULL, NULL
1308 	},
1309 
1310 	{
1311 		{"write_function_list", CFGCXT_RELOAD, CONNECTION_POOL_CONFIG,
1312 			"list of functions that writes to database.",
1313 			CONFIG_VAR_TYPE_STRING_LIST, false, 0
1314 		},
1315 		&g_pool_config.write_function_list,
1316 		&g_pool_config.num_write_function_list,
1317 		(const char *) default_write_function_list,
1318 		",",
1319 		true,
1320 		NULL, NULL, NULL
1321 	},
1322 	{
1323 		{"cache_safe_memqcache_table_list", CFGCXT_RELOAD, CACHE_CONFIG,
1324 			"list of tables to be cached.",
1325 			CONFIG_VAR_TYPE_STRING_LIST, false, 0
1326 		},
1327 		&g_pool_config.cache_safe_memqcache_table_list,
1328 		&g_pool_config.num_cache_safe_memqcache_table_list,
1329 		NULL,
1330 		",",
1331 		true,
1332 		NULL, NULL, NULL
1333 	},
1334 
1335 	{
1336 		{"cache_unsafe_memqcache_table_list", CFGCXT_RELOAD, CACHE_CONFIG,
1337 			"list of tables should not be cached.",
1338 			CONFIG_VAR_TYPE_STRING_LIST, false, 0
1339 		},
1340 		&g_pool_config.cache_unsafe_memqcache_table_list,
1341 		&g_pool_config.num_cache_unsafe_memqcache_table_list,
1342 		NULL,
1343 		",",
1344 		true,
1345 		NULL, NULL, NULL
1346 	},
1347 
1348 	{
1349 		{"primary_routing_query_pattern_list", CFGCXT_RELOAD, CONNECTION_POOL_CONFIG,
1350 			"list of query patterns that should be sent to primary node.",
1351 			CONFIG_VAR_TYPE_STRING_LIST, false, 0
1352 		},
1353 		&g_pool_config.primary_routing_query_pattern_list,
1354 		&g_pool_config.num_primary_routing_query_pattern_list,
1355 		NULL,
1356 		";",
1357 		true,
1358 		NULL, NULL, NULL
1359 	},
1360 
1361 	{
1362 		{"wd_monitoring_interfaces_list", CFGCXT_INIT, WATCHDOG_CONFIG,
1363 			"List of network device names, to be monitored by the watchdog process for the network link state.",
1364 			CONFIG_VAR_TYPE_STRING, false, 0
1365 		},
1366 		&g_pool_config.wd_monitoring_interfaces_list,
1367 		&g_pool_config.num_wd_monitoring_interfaces_list,
1368 		NULL,
1369 		",",
1370 		false,
1371 		NULL, NULL, NULL
1372 	},
1373 
1374 	/* End-of-list marker */
1375 	EMPTY_CONFIG_STRING_LIST
1376 };
1377 
1378 /* long configs*/
1379 static struct config_long ConfigureNamesLong[] =
1380 {
1381 	{
1382 		{"delay_threshold", CFGCXT_RELOAD, STREAMING_REPLICATION_CONFIG,
1383 			"standby delay threshold in bytes.",
1384 			CONFIG_VAR_TYPE_LONG, false, GUC_UNIT_BYTE
1385 		},
1386 		&g_pool_config.delay_threshold,
1387 		0,
1388 		0, LONG_MAX,
1389 		NULL, NULL, NULL
1390 	},
1391 
1392 	{
1393 		{"relcache_expire", CFGCXT_INIT, CACHE_CONFIG,
1394 			"Relation cache expiration time in seconds.",
1395 			CONFIG_VAR_TYPE_LONG, false, GUC_UNIT_S
1396 		},
1397 		&g_pool_config.relcache_expire,
1398 		0,
1399 		0, LONG_MAX,
1400 		NULL, NULL, NULL
1401 	},
1402 
1403 	{
1404 		{"memqcache_total_size", CFGCXT_INIT, CACHE_CONFIG,
1405 			"Total memory size in bytes for storing memory cache.",
1406 			CONFIG_VAR_TYPE_LONG, false, GUC_UNIT_BYTE
1407 		},
1408 		&g_pool_config.memqcache_total_size,
1409 		(int64) 67108864,
1410 		0, LONG_MAX,
1411 		NULL, NULL, NULL
1412 	},
1413 
1414 	/* End-of-list marker */
1415 	EMPTY_CONFIG_LONG
1416 
1417 };
1418 
1419 
1420 static struct config_int_array ConfigureNamesIntArray[] =
1421 {
1422 	{
1423 		{"backend_port", CFGCXT_RELOAD, CONNECTION_CONFIG,
1424 			"port number of PostgreSQL backend.",
1425 			CONFIG_VAR_TYPE_INT_ARRAY, true, 0, MAX_NUM_BACKENDS
1426 		},
1427 		NULL,
1428 		0,
1429 		1024, 65535,
1430 		EMPTY_CONFIG_INT,
1431 		BackendPortAssignFunc, NULL, BackendPortShowFunc, BackendSlotEmptyCheckFunc
1432 	},
1433 
1434 	{
1435 		{"heartbeat_port", CFGCXT_RELOAD, WATCHDOG_LIFECHECK,
1436 			"Port for sending heartbeat.",
1437 			CONFIG_VAR_TYPE_INT_ARRAY, true, 0, WD_MAX_IF_NUM
1438 		},
1439 		NULL,
1440 		0,
1441 		1024, 65535,
1442 		EMPTY_CONFIG_INT,
1443 		HBDestinationPortAssignFunc, NULL, HBDestinationPortShowFunc, WdIFSlotEmptyCheckFunc
1444 	},
1445 
1446 	{
1447 		{"wd_port", CFGCXT_RELOAD, WATCHDOG_CONFIG,
1448 			"tcp/ip watchdog port number of other pgpool node for watchdog connection..",
1449 			CONFIG_VAR_TYPE_INT_ARRAY, true, 0, MAX_WATCHDOG_NUM
1450 		},
1451 		NULL,
1452 		0,
1453 		1024, 65535,
1454 		EMPTY_CONFIG_INT,
1455 		OtherWDPortAssignFunc, NULL, OtherWDPortShowFunc, WdSlotEmptyCheckFunc
1456 	},
1457 
1458 	{
1459 		{"pgpool_port", CFGCXT_RELOAD, WATCHDOG_CONFIG,
1460 			"tcp/ip pgpool port number of other pgpool node for watchdog connection.",
1461 			CONFIG_VAR_TYPE_INT_ARRAY, true, 0, MAX_WATCHDOG_NUM
1462 		},
1463 		NULL,
1464 		0,
1465 		1024, 65535,
1466 		EMPTY_CONFIG_INT,
1467 		OtherPPPortAssignFunc, NULL, OtherPPPortShowFunc, WdSlotEmptyCheckFunc
1468 	},
1469 
1470 	{
1471 		{"health_check_timeout", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1472 			"Backend node health check timeout value in seconds.",
1473 			CONFIG_VAR_TYPE_INT_ARRAY, true, GUC_UNIT_S, MAX_NUM_BACKENDS
1474 		},
1475 		NULL,
1476 		20,
1477 		0, INT_MAX,
1478 		{
1479 			{"health_check_timeout", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1480 				"Default health check timeout value for node for which health_check_timeout[node-id] is not specified.",
1481 				CONFIG_VAR_TYPE_INT, false, DEFAULT_FOR_NO_VALUE_ARRAY_VAR
1482 			},
1483 			&g_pool_config.health_check_timeout,
1484 			20,
1485 			0, INT_MAX,
1486 			NULL, NULL, NULL
1487 		},
1488 		HealthCheckTimeOutAssignFunc, NULL, HealthCheckTimeOutShowFunc, BackendSlotEmptyCheckFunc
1489 	},
1490 
1491 	{
1492 		{"health_check_period", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1493 			"Time interval in seconds between the health checks.",
1494 			CONFIG_VAR_TYPE_INT_ARRAY, true, GUC_UNIT_S, MAX_NUM_BACKENDS
1495 		},
1496 		NULL,
1497 		0,
1498 		0, INT_MAX,
1499 		{
1500 			{"health_check_period", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1501 				"Default time interval between health checks for node for which health_check_period[node-id] is not specified.",
1502 				CONFIG_VAR_TYPE_INT, false, DEFAULT_FOR_NO_VALUE_ARRAY_VAR
1503 			},
1504 			&g_pool_config.health_check_period,
1505 			0,
1506 			0, INT_MAX,
1507 			NULL, NULL, NULL
1508 		},
1509 		HealthCheckPeriodAssignFunc, NULL, HealthCheckPeriodShowFunc, BackendSlotEmptyCheckFunc
1510 	},
1511 
1512 	{
1513 		{"health_check_max_retries", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1514 			"The maximum number of times to retry a failed health check before giving up and initiating failover.",
1515 			CONFIG_VAR_TYPE_INT_ARRAY, true, 0, MAX_NUM_BACKENDS
1516 		},
1517 		NULL,
1518 		0,
1519 		0, INT_MAX,
1520 		{
1521 			{"health_check_max_retries", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1522 				"Default maximum number of retries for node for which health_check_max_retries[node-id] is not specified.",
1523 				CONFIG_VAR_TYPE_INT, false, DEFAULT_FOR_NO_VALUE_ARRAY_VAR
1524 			},
1525 			&g_pool_config.health_check_max_retries,
1526 			0,
1527 			0, INT_MAX,
1528 			NULL, NULL, NULL
1529 		},
1530 		HealthCheckMaxRetriesAssignFunc, NULL, HealthCheckMaxRetriesShowFunc, BackendSlotEmptyCheckFunc
1531 	},
1532 
1533 	{
1534 		{"health_check_retry_delay", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1535 			"The amount of time in seconds to wait between failed health check retries.",
1536 			CONFIG_VAR_TYPE_INT_ARRAY, true, GUC_UNIT_S, MAX_NUM_BACKENDS
1537 		},
1538 		NULL,
1539 		1,
1540 		0, INT_MAX,
1541 		{
1542 			{"health_check_retry_delay", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1543 				"Default time between failed health check retries for node for which health_check_retry_delay[node-id] is not specified.",
1544 				CONFIG_VAR_TYPE_INT, false, DEFAULT_FOR_NO_VALUE_ARRAY_VAR
1545 			},
1546 			&g_pool_config.health_check_retry_delay,
1547 			1,
1548 			0, INT_MAX,
1549 			NULL, NULL, NULL
1550 		},
1551 		HealthCheckRetryDelayAssignFunc, NULL, HealthCheckRetryDelayShowFunc, BackendSlotEmptyCheckFunc
1552 	},
1553 
1554 	{
1555 		{"connect_timeout", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1556 			"Timeout in milliseconds before giving up connecting to backend.",
1557 			CONFIG_VAR_TYPE_INT_ARRAY, true, GUC_UNIT_MS, MAX_NUM_BACKENDS
1558 		},
1559 		NULL,
1560 		10000,
1561 		0, INT_MAX,
1562 		{
1563 			{"connect_timeout", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1564 				"Default connect_timeout value for node for which connect_timeout[node-id] is not specified.",
1565 				CONFIG_VAR_TYPE_INT, false, DEFAULT_FOR_NO_VALUE_ARRAY_VAR
1566 			},
1567 			&g_pool_config.connect_timeout,
1568 			10000,
1569 			0, INT_MAX,
1570 			NULL, NULL, NULL
1571 		},
1572 		HealthCheckConnectTimeOutAssignFunc, NULL, HealthCheckConnectTimeOutShowFunc, BackendSlotEmptyCheckFunc
1573 	},
1574 	/* End-of-list marker */
1575 	EMPTY_CONFIG_INT_ARRAY
1576 
1577 };
1578 
1579 
1580 static struct config_double ConfigureNamesDouble[] =
1581 {
1582 	/* End-of-list marker */
1583 	EMPTY_CONFIG_DOUBLE
1584 };
1585 
1586 
1587 static struct config_double_array ConfigureNamesDoubleArray[] =
1588 {
1589 	{
1590 		{"backend_weight", CFGCXT_RELOAD, CONNECTION_CONFIG,
1591 			"load balance weight of backend.",
1592 			CONFIG_VAR_TYPE_DOUBLE_ARRAY, true, 0, MAX_NUM_BACKENDS
1593 		},
1594 		NULL,
1595 		0,
1596 		0.0, 100000000.0,
1597 		EMPTY_CONFIG_DOUBLE,
1598 		BackendWeightAssignFunc, NULL, BackendWeightShowFunc, BackendSlotEmptyCheckFunc
1599 	},
1600 
1601 	/* End-of-list marker */
1602 	EMPTY_CONFIG_DOUBLE_ARRAY
1603 };
1604 
1605 
1606 static struct config_string_array ConfigureNamesStringArray[] =
1607 {
1608 	{
1609 		{"backend_hostname", CFGCXT_RELOAD, CONNECTION_CONFIG,
1610 			"hostname or IP address of PostgreSQL backend.",
1611 			CONFIG_VAR_TYPE_STRING_ARRAY, true, 0, MAX_NUM_BACKENDS
1612 		},
1613 		NULL,
1614 		"",
1615 		EMPTY_CONFIG_STRING,
1616 		BackendHostAssignFunc, NULL, BackendHostShowFunc, BackendSlotEmptyCheckFunc
1617 	},
1618 
1619 	{
1620 		{"backend_data_directory", CFGCXT_RELOAD, CONNECTION_CONFIG,
1621 			"data directory of the backend.",
1622 			CONFIG_VAR_TYPE_STRING_ARRAY, true, 0, MAX_NUM_BACKENDS
1623 		},
1624 		NULL,
1625 		"",
1626 		EMPTY_CONFIG_STRING,
1627 		BackendDataDirAssignFunc, NULL, BackendDataDirShowFunc, BackendSlotEmptyCheckFunc
1628 	},
1629 
1630 	{
1631 		{"backend_application_name", CFGCXT_RELOAD, CONNECTION_CONFIG,
1632 			"applicaton_name of the backend.",
1633 			CONFIG_VAR_TYPE_STRING_ARRAY, true, 0, MAX_NUM_BACKENDS
1634 		},
1635 		NULL,
1636 		"",
1637 		EMPTY_CONFIG_STRING,
1638 		BackendAppNameAssignFunc, NULL, BackendAppNameShowFunc, BackendSlotEmptyCheckFunc
1639 	},
1640 
1641 	{
1642 		{"backend_flag", CFGCXT_RELOAD, CONNECTION_CONFIG,
1643 			"Controls various backend behavior.",
1644 			CONFIG_VAR_TYPE_STRING_ARRAY, true, 0, MAX_NUM_BACKENDS
1645 		},
1646 		NULL,
1647 		"ALLOW_TO_FAILOVER",
1648 		EMPTY_CONFIG_STRING,
1649 		BackendFlagsAssignFunc, NULL, BackendFlagsShowFunc, BackendSlotEmptyCheckFunc
1650 	},
1651 
1652 	{
1653 		{"backend_flag", CFGCXT_RELOAD, CONNECTION_CONFIG,
1654 			"Controls various backend behavior.",
1655 			CONFIG_VAR_TYPE_STRING_ARRAY, true, 0, MAX_NUM_BACKENDS
1656 		},
1657 		NULL,
1658 		"",	/* for ALWAYS_PRIMARY */
1659 		EMPTY_CONFIG_STRING,
1660 		BackendFlagsAssignFunc, NULL, BackendFlagsShowFunc, BackendSlotEmptyCheckFunc
1661 	},
1662 
1663 	{
1664 		{"heartbeat_hostname", CFGCXT_RELOAD, WATCHDOG_LIFECHECK,
1665 			"Hostname for sending heartbeat signal.",
1666 			CONFIG_VAR_TYPE_STRING_ARRAY, true, 0, WD_MAX_IF_NUM
1667 		},
1668 		NULL,
1669 		"",
1670 		EMPTY_CONFIG_STRING,
1671 		HBHostnameAssignFunc, NULL, HBHostnameShowFunc, WdIFSlotEmptyCheckFunc
1672 	},
1673 
1674 	{
1675 		{"heartbeat_device", CFGCXT_RELOAD, WATCHDOG_LIFECHECK,
1676 			"Name of NIC device for sending hearbeat.",
1677 			CONFIG_VAR_TYPE_STRING_ARRAY, true, 0, WD_MAX_IF_NUM
1678 		},
1679 		NULL,
1680 		"",
1681 		EMPTY_CONFIG_STRING,
1682 		HBDeviceAssignFunc, NULL, HBDeviceShowFunc, WdIFSlotEmptyCheckFunc
1683 	},
1684 
1685 	{
1686 		{"hostname", CFGCXT_RELOAD, WATCHDOG_LIFECHECK,
1687 			"Hostname of pgpool node for watchdog connection.",
1688 			CONFIG_VAR_TYPE_STRING_ARRAY, true, 0, MAX_WATCHDOG_NUM
1689 		},
1690 		NULL,
1691 		"localhost",
1692 		EMPTY_CONFIG_STRING,
1693 		OtherPPHostAssignFunc, NULL, OtherPPHostShowFunc, WdSlotEmptyCheckFunc
1694 	},
1695 
1696 	{
1697 		{"health_check_user", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1698 			"User name for PostgreSQL backend health check.",
1699 			CONFIG_VAR_TYPE_STRING_ARRAY, true, 0, MAX_NUM_BACKENDS
1700 		},
1701 		NULL,
1702 		"nobody",
1703 		{
1704 			{"health_check_user", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1705 				"Default PostgreSQL user name to perform health check on node for which health_check_user[node-id] is not specified.",
1706 				CONFIG_VAR_TYPE_STRING, false, DEFAULT_FOR_NO_VALUE_ARRAY_VAR
1707 			},
1708 			&g_pool_config.health_check_user,
1709 			"nobody",
1710 			NULL, NULL, NULL, NULL
1711 		},
1712 		HealthCheckUserAssignFunc, NULL, HealthCheckUserShowFunc, BackendSlotEmptyCheckFunc
1713 	},
1714 
1715 	{
1716 		{"health_check_password", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1717 			"Password for PostgreSQL backend health check database user.",
1718 			CONFIG_VAR_TYPE_STRING_ARRAY, true, VAR_HIDDEN_VALUE, MAX_NUM_BACKENDS
1719 		},
1720 		NULL,
1721 		"",
1722 		{
1723 			{"health_check_password", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1724 				"Default PostgreSQL user password to perform health check on node for which health_check_password[node-id] is not specified.",
1725 				CONFIG_VAR_TYPE_STRING, false, VAR_HIDDEN_VALUE | DEFAULT_FOR_NO_VALUE_ARRAY_VAR
1726 			},
1727 			&g_pool_config.health_check_password,
1728 			"",
1729 			NULL, NULL, NULL, NULL
1730 		},
1731 		HealthCheckPasswordAssignFunc, NULL, HealthCheckPasswordShowFunc, BackendSlotEmptyCheckFunc
1732 	},
1733 
1734 	{
1735 		{"health_check_database", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1736 			"The database name to be used to perform PostgreSQL backend health check.",
1737 			CONFIG_VAR_TYPE_STRING_ARRAY, true, 0, MAX_NUM_BACKENDS
1738 		},
1739 		NULL,
1740 		"postgres",
1741 		{
1742 			{"health_check_database", CFGCXT_RELOAD, HEALTH_CHECK_CONFIG,
1743 				"Default PostgreSQL database name to perform health check on node for which health_check_database[node-id] is not specified.",
1744 				CONFIG_VAR_TYPE_STRING, false, DEFAULT_FOR_NO_VALUE_ARRAY_VAR
1745 			},
1746 			&g_pool_config.health_check_database,
1747 			"postgres",
1748 			NULL, NULL, NULL, NULL
1749 		},
1750 		HealthCheckDatabaseAssignFunc, NULL, HealthCheckDatabaseShowFunc, BackendSlotEmptyCheckFunc
1751 	},
1752 
1753 	/* End-of-list marker */
1754 	EMPTY_CONFIG_STRING_ARRAY
1755 
1756 };
1757 
1758 
1759 /* int configs*/
1760 static struct config_int ConfigureNamesInt[] =
1761 {
1762 
1763 	{
1764 		{"port", CFGCXT_INIT, CONNECTION_CONFIG,
1765 			"tcp/IP port number on which pgpool will listen on.",
1766 			CONFIG_VAR_TYPE_INT, false, 0
1767 		},
1768 		&g_pool_config.port,
1769 		9999,
1770 		1024, 65535,
1771 		NULL, NULL, NULL
1772 	},
1773 
1774 	{
1775 		{"pcp_port", CFGCXT_INIT, CONNECTION_CONFIG,
1776 			"tcp/IP port number on which pgpool PCP process will listen on.",
1777 			CONFIG_VAR_TYPE_INT, false, 0
1778 		},
1779 		&g_pool_config.pcp_port,
1780 		9898,
1781 		1024, 65535,
1782 		NULL, NULL, NULL
1783 	},
1784 
1785 	{
1786 		{"num_init_children", CFGCXT_INIT, CONNECTION_POOL_CONFIG,
1787 			"Number of children pre-forked for client connections.",
1788 			CONFIG_VAR_TYPE_INT, false, 0
1789 		},
1790 		&g_pool_config.num_init_children,
1791 		32,
1792 		1, INT_MAX,
1793 		NULL, NULL, NULL
1794 	},
1795 
1796 	{
1797 		{"reserved_connections", CFGCXT_INIT, CONNECTION_POOL_CONFIG,
1798 			"Number of reserved connections.",
1799 			CONFIG_VAR_TYPE_INT, false, 0
1800 		},
1801 		&g_pool_config.reserved_connections,
1802 		0,
1803 		0, INT_MAX,
1804 		NULL, NULL, NULL
1805 	},
1806 
1807 	{
1808 		{"listen_backlog_multiplier", CFGCXT_INIT, CONNECTION_CONFIG,
1809 			"length of connection queue from frontend to pgpool-II",
1810 			CONFIG_VAR_TYPE_INT, false, 0
1811 		},
1812 		&g_pool_config.listen_backlog_multiplier,
1813 		32,
1814 		1, INT_MAX,
1815 		NULL, NULL, NULL
1816 	},
1817 
1818 	{
1819 		{"child_life_time", CFGCXT_INIT, CONNECTION_POOL_CONFIG,
1820 			"pgpool-II child process life time in seconds.",
1821 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1822 		},
1823 		&g_pool_config.child_life_time,
1824 		300,
1825 		0, INT_MAX,
1826 		NULL, NULL, NULL
1827 	},
1828 
1829 	{
1830 		{"client_idle_limit", CFGCXT_SESSION, CONNECTION_POOL_CONFIG,
1831 			"idle time in seconds to disconnects a client.",
1832 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1833 		},
1834 		&g_pool_config.client_idle_limit,
1835 		0,
1836 		0, INT_MAX,
1837 		NULL, NULL, NULL
1838 	},
1839 
1840 	{
1841 		{"connection_life_time", CFGCXT_INIT, CONNECTION_POOL_CONFIG,
1842 			"Cached connections expiration time in seconds.",
1843 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1844 		},
1845 		&g_pool_config.connection_life_time,
1846 		0,
1847 		0, INT_MAX,
1848 		NULL, NULL, NULL
1849 	},
1850 
1851 	{
1852 		{"child_max_connections", CFGCXT_INIT, CONNECTION_POOL_CONFIG,
1853 			"A pgpool-II child process will be terminated after this many connections from clients.",
1854 			CONFIG_VAR_TYPE_INT, false, 0
1855 		},
1856 		&g_pool_config.child_max_connections,
1857 		0,
1858 		0, INT_MAX,
1859 		NULL, NULL, NULL
1860 	},
1861 
1862 	{
1863 		{"authentication_timeout", CFGCXT_INIT, CONNECTION_CONFIG,
1864 			"Time out value in seconds for client authentication.",
1865 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1866 		},
1867 		&g_pool_config.authentication_timeout,
1868 		0,
1869 		0, INT_MAX,
1870 		NULL, NULL, NULL
1871 	},
1872 
1873 	{
1874 		{"max_pool", CFGCXT_INIT, CONNECTION_POOL_CONFIG,
1875 			"Maximum number of connection pools per child process.",
1876 			CONFIG_VAR_TYPE_INT, false, 0
1877 		},
1878 		&g_pool_config.max_pool,
1879 		4,
1880 		0, INT_MAX,
1881 		NULL, NULL, NULL
1882 	},
1883 
1884 	{
1885 		{"sr_check_period", CFGCXT_RELOAD, STREAMING_REPLICATION_CONFIG,
1886 			"Time interval in seconds between the streaming replication delay checks.",
1887 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1888 		},
1889 		&g_pool_config.sr_check_period,
1890 		0,
1891 		0, INT_MAX,
1892 		NULL, NULL, NULL
1893 	},
1894 
1895 	{
1896 		{"recovery_timeout", CFGCXT_RELOAD, RECOVERY_CONFIG,
1897 			"Maximum time in seconds to wait for the recovering PostgreSQL node.",
1898 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1899 		},
1900 		&g_pool_config.recovery_timeout,
1901 		90,
1902 		0, INT_MAX,
1903 		NULL, NULL, NULL
1904 	},
1905 
1906 	{
1907 		{"client_idle_limit_in_recovery", CFGCXT_SESSION, RECOVERY_CONFIG,
1908 			"Time limit is seconds for the child connection, before it is terminated during the 2nd stage recovery.",
1909 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1910 		},
1911 		&g_pool_config.client_idle_limit_in_recovery,
1912 		0,
1913 		-1, INT_MAX,
1914 		NULL, NULL, NULL
1915 	},
1916 
1917 	{
1918 		{"search_primary_node_timeout", CFGCXT_RELOAD, FAILOVER_CONFIG,
1919 			"Max time in seconds to search for primary node after failover.",
1920 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1921 		},
1922 		&g_pool_config.search_primary_node_timeout,
1923 		300,
1924 		0, INT_MAX,
1925 		NULL, NULL, NULL
1926 	},
1927 
1928 	{
1929 		{"wd_priority", CFGCXT_INIT, WATCHDOG_CONFIG,
1930 			"Watchdog node priority for leader election.",
1931 			CONFIG_VAR_TYPE_INT, false, 0
1932 		},
1933 		&g_pool_config.wd_priority,
1934 		1,
1935 		0, INT_MAX,
1936 		NULL, NULL, NULL
1937 	},
1938 
1939 	{
1940 		{"wd_interval", CFGCXT_INIT, WATCHDOG_CONFIG,
1941 			"Time interval in seconds between life check.",
1942 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1943 		},
1944 		&g_pool_config.wd_interval,
1945 		10,
1946 		0, INT_MAX,
1947 		NULL, NULL, NULL
1948 	},
1949 
1950 	{
1951 		{"wd_life_point", CFGCXT_INIT, WATCHDOG_CONFIG,
1952 			"Maximum number of retries before failing the life check.",
1953 			CONFIG_VAR_TYPE_INT, false, 0
1954 		},
1955 		&g_pool_config.wd_life_point,
1956 		3,
1957 		0, INT_MAX,
1958 		NULL, NULL, NULL
1959 	},
1960 
1961 	{
1962 		{"wd_heartbeat_keepalive", CFGCXT_INIT, WATCHDOG_CONFIG,
1963 			"Time interval in seconds between sending the heartbeat siganl.",
1964 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1965 		},
1966 		&g_pool_config.wd_heartbeat_keepalive,
1967 		2,
1968 		1, INT_MAX,
1969 		NULL, NULL, NULL
1970 	},
1971 
1972 	{
1973 		{"wd_heartbeat_deadtime", CFGCXT_INIT, WATCHDOG_CONFIG,
1974 			"Deadtime interval in seconds for heartbeat siganl.",
1975 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
1976 		},
1977 		&g_pool_config.wd_heartbeat_deadtime,
1978 		30,
1979 		1, INT_MAX,
1980 		NULL, NULL, NULL
1981 	},
1982 
1983 	{
1984 		{"relcache_size", CFGCXT_INIT, CACHE_CONFIG,
1985 			"Number of relation cache entry.",
1986 			CONFIG_VAR_TYPE_INT, false, 0
1987 		},
1988 		&g_pool_config.relcache_size,
1989 		256,
1990 		0, INT_MAX,
1991 		NULL, NULL, NULL
1992 	},
1993 
1994 	{
1995 		{"memqcache_memcached_port", CFGCXT_INIT, CACHE_CONFIG,
1996 			"Port number of Memcached server.",
1997 			CONFIG_VAR_TYPE_INT, false, 0
1998 		},
1999 		&g_pool_config.memqcache_memcached_port,
2000 		11211,
2001 		0, INT_MAX,
2002 		NULL, NULL, NULL
2003 	},
2004 
2005 	{
2006 		{"memqcache_max_num_cache", CFGCXT_INIT, CACHE_CONFIG,
2007 			"Total number of cache entries.",
2008 			CONFIG_VAR_TYPE_INT, false, 0
2009 		},
2010 		&g_pool_config.memqcache_max_num_cache,
2011 		1000000,
2012 		0, INT_MAX,
2013 		NULL, NULL, NULL
2014 	},
2015 
2016 	{
2017 		{"memqcache_expire", CFGCXT_INIT, CACHE_CONFIG,
2018 			"Memory cache entry life time specified in seconds.",
2019 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
2020 		},
2021 		&g_pool_config.memqcache_expire,
2022 		0,
2023 		0, INT_MAX,
2024 		NULL, NULL, NULL
2025 	},
2026 
2027 	{
2028 		{"memqcache_maxcache", CFGCXT_INIT, CACHE_CONFIG,
2029 			"Maximum SELECT result size in bytes.",
2030 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_BYTE
2031 		},
2032 		&g_pool_config.memqcache_maxcache,
2033 		409600,
2034 		0, INT_MAX,
2035 		NULL, NULL, NULL
2036 	},
2037 
2038 	{
2039 		{"memqcache_cache_block_size", CFGCXT_INIT, CACHE_CONFIG,
2040 			"Cache block size in bytes.",
2041 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_BYTE
2042 		},
2043 		&g_pool_config.memqcache_cache_block_size,
2044 		1048576,
2045 		512, INT_MAX,
2046 		NULL, NULL, NULL
2047 	},
2048 
2049 	{
2050 		{"auto_failback_interval", CFGCXT_RELOAD, FAILOVER_CONFIG,
2051 			"min interval of executing auto_failback in seconds",
2052 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_S
2053 		},
2054 		&g_pool_config.auto_failback_interval,
2055 		60,
2056 		0, INT_MAX,
2057 		NULL, NULL, NULL
2058 	},
2059 	{
2060 		{"log_rotation_age", CFGCXT_INIT, LOGING_CONFIG,
2061 			"Automatic rotation of logfiles will happen after that (minutes) time.",
2062 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_MIN
2063 		},
2064 		&g_pool_config.log_rotation_age,
2065 		1440,/*1 day*/
2066 		10, INT_MAX,
2067 		NULL, NULL, NULL
2068 	},
2069 	{
2070 		{"log_rotation_size", CFGCXT_INIT, LOGING_CONFIG,
2071 			"Automatic rotation of logfiles will happen after that much (kilobytes) log output.",
2072 			CONFIG_VAR_TYPE_INT, false, GUC_UNIT_KB
2073 		},
2074 		&g_pool_config.log_rotation_size,
2075 		10 * 1024,
2076 		0, INT_MAX/1024,
2077 		NULL, NULL, NULL
2078 	},
2079 	{
2080 		{"log_file_mode", CFGCXT_INIT, LOGING_CONFIG,
2081 			"creation mode for log files.",
2082 			CONFIG_VAR_TYPE_INT, false, 0
2083 		},
2084 		&g_pool_config.log_file_mode,
2085 		0600,
2086 		0, INT_MAX,
2087 		NULL, NULL, NULL
2088 	},
2089 
2090 
2091 	/* End-of-list marker */
2092 	EMPTY_CONFIG_INT
2093 };
2094 
2095 static struct config_enum ConfigureNamesEnum[] =
2096 {
2097 	{
2098 		{"backend_clustering_mode", CFGCXT_INIT, MAIN_REPLICA_CONFIG,
2099 			"backend clustering mode.",
2100 			CONFIG_VAR_TYPE_ENUM, false, 0
2101 		},
2102 		(int *) &g_pool_config.backend_clustering_mode,
2103 		CM_STREAMING_REPLICATION,
2104 		backend_clustering_mode_options,
2105 		NULL, NULL, NULL, NULL
2106 	},
2107 
2108 
2109 	{
2110 		{"syslog_facility", CFGCXT_RELOAD, LOGING_CONFIG,
2111 			"syslog local faclity.",
2112 			CONFIG_VAR_TYPE_ENUM, false, 0
2113 		},
2114 		&g_pool_config.syslog_facility,
2115 		LOG_LOCAL0,
2116 		syslog_facility_options,
2117 		NULL, NULL,
2118 		SyslogFacilityProcessFunc,
2119 		NULL
2120 	},
2121 
2122 	{
2123 		{"log_error_verbosity", CFGCXT_SESSION, LOGING_CONFIG,
2124 			"How much details about error should be emitted.",
2125 			CONFIG_VAR_TYPE_ENUM, false, 0
2126 		},
2127 		&g_pool_config.log_error_verbosity,
2128 		PGERROR_DEFAULT,
2129 		log_error_verbosity_options,
2130 		NULL, NULL, NULL, NULL
2131 	},
2132 
2133 	{
2134 		{"client_min_messages", CFGCXT_SESSION, LOGING_CONFIG,
2135 			"Which messages should be sent to client.",
2136 			CONFIG_VAR_TYPE_ENUM, false, 0
2137 		},
2138 		&g_pool_config.client_min_messages,
2139 		NOTICE,
2140 		server_message_level_options,
2141 		NULL, NULL, NULL, NULL
2142 	},
2143 
2144 	{
2145 		{"log_min_messages", CFGCXT_SESSION, LOGING_CONFIG,
2146 			"Which messages should be emitted to server log.",
2147 			CONFIG_VAR_TYPE_ENUM, false, 0
2148 		},
2149 		&g_pool_config.log_min_messages,
2150 		WARNING,
2151 		server_message_level_options,
2152 		NULL, NULL, NULL, NULL
2153 	},
2154 
2155 	{
2156 		{"log_standby_delay", CFGCXT_RELOAD, MAIN_REPLICA_CONFIG,
2157 			"When to log standby delay.",
2158 			CONFIG_VAR_TYPE_ENUM, false, 0
2159 		},
2160 		(int *) &g_pool_config.log_standby_delay,
2161 		LSD_NONE,
2162 		log_standby_delay_options,
2163 		NULL, NULL, NULL, NULL
2164 	},
2165 
2166 	{
2167 		{"wd_lifecheck_method", CFGCXT_INIT, WATCHDOG_CONFIG,
2168 			"method for watchdog lifecheck.",
2169 			CONFIG_VAR_TYPE_ENUM, false, 0
2170 		},
2171 		(int *) &g_pool_config.wd_lifecheck_method,
2172 		LIFECHECK_BY_HB,
2173 		wd_lifecheck_method_options,
2174 		NULL, NULL, NULL, NULL
2175 	},
2176 
2177 	{
2178 		{"memqcache_method", CFGCXT_INIT, CACHE_CONFIG,
2179 			"Cache store method. either shmem(shared memory) or Memcached. shmem by default.",
2180 			CONFIG_VAR_TYPE_ENUM, false, 0
2181 		},
2182 		(int *) &g_pool_config.memqcache_method,
2183 		SHMEM_CACHE,
2184 		memqcache_method_options,
2185 		NULL, NULL, NULL, NULL
2186 	},
2187 
2188 	{
2189 		{"disable_load_balance_on_write", CFGCXT_RELOAD, LOAD_BALANCE_CONFIG,
2190 			"Load balance behavior when write query is received.",
2191 			CONFIG_VAR_TYPE_ENUM, false, 0
2192 		},
2193 		(int *) &g_pool_config.disable_load_balance_on_write,
2194 		DLBOW_TRANSACTION,
2195 		disable_load_balance_on_write_options,
2196 		NULL, NULL, NULL, NULL
2197 	},
2198 
2199 	{
2200 		{"relcache_query_target", CFGCXT_RELOAD, LOAD_BALANCE_CONFIG,
2201 			"Target node to send relache queries.",
2202 			CONFIG_VAR_TYPE_ENUM, false, 0
2203 		},
2204 		(int *) &g_pool_config.relcache_query_target,
2205 		RELQTARGET_PRIMARY,
2206 		relcache_query_target_options,
2207 		NULL, NULL, NULL, NULL
2208 	},
2209 
2210 	{
2211 		{"check_temp_table", CFGCXT_RELOAD, GENERAL_CONFIG,
2212 			"Enables temporary table check.",
2213 			CONFIG_VAR_TYPE_BOOL, false, 0
2214 		},
2215 		(int *) &g_pool_config.check_temp_table,
2216 		CHECK_TEMP_CATALOG,
2217 		check_temp_table_options,
2218 		NULL, NULL, NULL, NULL
2219 	},
2220 
2221 	/* End-of-list marker */
2222 	EMPTY_CONFIG_ENUM
2223 };
2224 
2225 /* finally the groups */
2226 static struct config_grouped_array_var ConfigureVarGroups[] =
2227 {
2228 	{
2229 		{"backend", CFGCXT_BOOT, CONNECTION_CONFIG,
2230 			"backend configuration group.",
2231 			CONFIG_VAR_TYPE_GROUP, false, 0
2232 		},
2233 		-1,						/* until initialized */
2234 		NULL
2235 	},
2236 	{
2237 		{"other_pgpool", CFGCXT_BOOT, WATCHDOG_CONFIG,
2238 			"watchdog nodes configuration group.",
2239 			CONFIG_VAR_TYPE_GROUP, false, 0
2240 		},
2241 		-1,						/* until initialized */
2242 		NULL
2243 	},
2244 	{
2245 		{"heartbeat", CFGCXT_BOOT, WATCHDOG_LIFECHECK,
2246 			"heartbeat configuration group.",
2247 			CONFIG_VAR_TYPE_GROUP, false, 0
2248 		},
2249 		-1,						/* until initialized */
2250 		NULL
2251 	},
2252 	{
2253 		{"health_check", CFGCXT_BOOT, HEALTH_CHECK_CONFIG,
2254 			"backend health check configuration group.",
2255 			CONFIG_VAR_TYPE_GROUP, false, 0
2256 		},
2257 		-1,						/* until initialized */
2258 		NULL
2259 	},
2260 	/* End-of-list marker */
2261 	EMPTY_CONFIG_GROUP_ARRAY
2262 
2263 };
2264 
2265 static void
initialize_config_gen(struct config_generic * gen)2266 initialize_config_gen(struct config_generic *gen)
2267 {
2268 	if (gen->dynamic_array_var == false)
2269 		gen->max_elements = 1;
2270 
2271 	gen->sources = palloc(sizeof(GucSource) * gen->max_elements);
2272 	gen->reset_sources = palloc(sizeof(GucSource) * gen->max_elements);
2273 	gen->scontexts = palloc(sizeof(ConfigContext) * gen->max_elements);
2274 }
2275 
2276 static void
build_config_variables(void)2277 build_config_variables(void)
2278 {
2279 	struct config_generic **all_vars;
2280 	int			i;
2281 	int			num_vars = 0;
2282 
2283 	for (i = 0; ConfigureNamesBool[i].gen.name; i++)
2284 	{
2285 		struct config_bool *conf = &ConfigureNamesBool[i];
2286 
2287 		/* Rather than requiring vartype to be filled in by hand, do this: */
2288 		initialize_config_gen(&conf->gen);
2289 		conf->gen.vartype = CONFIG_VAR_TYPE_BOOL;
2290 		num_vars++;
2291 	}
2292 
2293 	for (i = 0; ConfigureNamesInt[i].gen.name; i++)
2294 	{
2295 		struct config_int *conf = &ConfigureNamesInt[i];
2296 
2297 		initialize_config_gen(&conf->gen);
2298 		conf->gen.vartype = CONFIG_VAR_TYPE_INT;
2299 		num_vars++;
2300 	}
2301 
2302 	for (i = 0; ConfigureNamesIntArray[i].gen.name; i++)
2303 	{
2304 		struct config_int_array *conf = &ConfigureNamesIntArray[i];
2305 
2306 		conf->gen.vartype = CONFIG_VAR_TYPE_INT_ARRAY;
2307 		conf->gen.dynamic_array_var = true;
2308 		initialize_config_gen(&conf->gen);
2309 
2310 		/* Assign the memory for reset vals */
2311 		conf->reset_vals = palloc0(sizeof(int) * conf->gen.max_elements);
2312 
2313 		if (conf->config_no_index.gen.name)
2314 		{
2315 			conf->config_no_index.gen.dynamic_array_var = false;
2316 			initialize_config_gen(&conf->config_no_index.gen);
2317 			conf->config_no_index.gen.context = conf->gen.context;
2318 			conf->gen.flags |= ARRAY_VAR_ALLOW_NO_INDEX;
2319 		}
2320 		num_vars++;
2321 	}
2322 
2323 	for (i = 0; ConfigureNamesLong[i].gen.name; i++)
2324 	{
2325 		struct config_long *conf = &ConfigureNamesLong[i];
2326 
2327 		initialize_config_gen(&conf->gen);
2328 		conf->gen.vartype = CONFIG_VAR_TYPE_LONG;
2329 		num_vars++;
2330 	}
2331 
2332 	for (i = 0; ConfigureNamesDouble[i].gen.name; i++)
2333 	{
2334 		struct config_double *conf = &ConfigureNamesDouble[i];
2335 
2336 		initialize_config_gen(&conf->gen);
2337 		conf->gen.vartype = CONFIG_VAR_TYPE_DOUBLE;
2338 		num_vars++;
2339 	}
2340 
2341 	for (i = 0; ConfigureNamesString[i].gen.name; i++)
2342 	{
2343 		struct config_string *conf = &ConfigureNamesString[i];
2344 
2345 		initialize_config_gen(&conf->gen);
2346 		conf->gen.vartype = CONFIG_VAR_TYPE_STRING;
2347 		conf->reset_val = NULL;
2348 		num_vars++;
2349 	}
2350 
2351 	for (i = 0; ConfigureNamesEnum[i].gen.name; i++)
2352 	{
2353 		struct config_enum *conf = &ConfigureNamesEnum[i];
2354 
2355 		initialize_config_gen(&conf->gen);
2356 		conf->gen.vartype = CONFIG_VAR_TYPE_ENUM;
2357 		num_vars++;
2358 	}
2359 
2360 	for (i = 0; ConfigureNamesStringList[i].gen.name; i++)
2361 	{
2362 		struct config_string_list *conf = &ConfigureNamesStringList[i];
2363 
2364 		initialize_config_gen(&conf->gen);
2365 		conf->gen.vartype = CONFIG_VAR_TYPE_STRING_LIST;
2366 		conf->reset_val = NULL;
2367 		num_vars++;
2368 	}
2369 
2370 	for (i = 0; ConfigureNamesStringArray[i].gen.name; i++)
2371 	{
2372 		struct config_string_array *conf = &ConfigureNamesStringArray[i];
2373 
2374 		conf->gen.dynamic_array_var = true;
2375 		initialize_config_gen(&conf->gen);
2376 
2377 		conf->gen.vartype = CONFIG_VAR_TYPE_STRING_ARRAY;
2378 		/* Assign the memory for reset vals */
2379 		conf->reset_vals = palloc0(sizeof(char *) * conf->gen.max_elements);
2380 
2381 		if (conf->config_no_index.gen.name)
2382 		{
2383 			conf->config_no_index.gen.dynamic_array_var = false;
2384 			initialize_config_gen(&conf->config_no_index.gen);
2385 			conf->config_no_index.gen.context = conf->gen.context;
2386 			conf->gen.flags |= ARRAY_VAR_ALLOW_NO_INDEX;
2387 		}
2388 
2389 		num_vars++;
2390 	}
2391 
2392 	for (i = 0; ConfigureNamesDoubleArray[i].gen.name; i++)
2393 	{
2394 		struct config_double_array *conf = &ConfigureNamesDoubleArray[i];
2395 
2396 		conf->gen.dynamic_array_var = true;
2397 		initialize_config_gen(&conf->gen);
2398 
2399 		conf->gen.vartype = CONFIG_VAR_TYPE_DOUBLE_ARRAY;
2400 		/* Assign the memory for reset vals */
2401 		conf->reset_vals = palloc0(sizeof(double) * conf->gen.max_elements);
2402 
2403 		if (conf->config_no_index.gen.name)
2404 		{
2405 			conf->config_no_index.gen.dynamic_array_var = false;
2406 			initialize_config_gen(&conf->config_no_index.gen);
2407 			conf->config_no_index.gen.context = conf->gen.context;
2408 			conf->gen.flags |= ARRAY_VAR_ALLOW_NO_INDEX;
2409 		}
2410 
2411 		num_vars++;
2412 	}
2413 
2414 	/* For end marker */
2415 	num_vars++;
2416 
2417 	all_vars = (struct config_generic **)
2418 		palloc(num_vars * sizeof(struct config_generic *));
2419 
2420 	num_vars = 0;
2421 
2422 	for (i = 0; ConfigureNamesBool[i].gen.name; i++)
2423 		all_vars[num_vars++] = &ConfigureNamesBool[i].gen;
2424 
2425 	for (i = 0; ConfigureNamesInt[i].gen.name; i++)
2426 		all_vars[num_vars++] = &ConfigureNamesInt[i].gen;
2427 
2428 	for (i = 0; ConfigureNamesLong[i].gen.name; i++)
2429 		all_vars[num_vars++] = &ConfigureNamesLong[i].gen;
2430 
2431 	for (i = 0; ConfigureNamesDouble[i].gen.name; i++)
2432 		all_vars[num_vars++] = &ConfigureNamesDouble[i].gen;
2433 
2434 	for (i = 0; ConfigureNamesString[i].gen.name; i++)
2435 		all_vars[num_vars++] = &ConfigureNamesString[i].gen;
2436 
2437 	for (i = 0; ConfigureNamesEnum[i].gen.name; i++)
2438 		all_vars[num_vars++] = &ConfigureNamesEnum[i].gen;
2439 
2440 	for (i = 0; ConfigureNamesStringList[i].gen.name; i++)
2441 		all_vars[num_vars++] = &ConfigureNamesStringList[i].gen;
2442 
2443 	for (i = 0; ConfigureNamesStringArray[i].gen.name; i++)
2444 		all_vars[num_vars++] = &ConfigureNamesStringArray[i].gen;
2445 
2446 	for (i = 0; ConfigureNamesIntArray[i].gen.name; i++)
2447 		all_vars[num_vars++] = &ConfigureNamesIntArray[i].gen;
2448 
2449 	for (i = 0; ConfigureNamesDoubleArray[i].gen.name; i++)
2450 		all_vars[num_vars++] = &ConfigureNamesDoubleArray[i].gen;
2451 
2452 	if (all_parameters)
2453 		pfree(all_parameters);
2454 	all_parameters = all_vars;
2455 	num_all_parameters = num_vars;
2456 	sort_config_vars();
2457 	build_variable_groups();
2458 }
2459 
2460 static void
build_variable_groups(void)2461 build_variable_groups(void)
2462 {
2463 	/* we build these by hand */
2464 	/* group 1. Backend config vars */
2465 	ConfigureVarGroups[0].var_count = 6;
2466 	ConfigureVarGroups[0].var_list = palloc0(sizeof(struct config_generic *) * ConfigureVarGroups[0].var_count);
2467 	ConfigureVarGroups[0].var_list[0] = find_option("backend_hostname", FATAL);
2468 	ConfigureVarGroups[0].var_list[0]->flags |= VAR_PART_OF_GROUP;
2469 	ConfigureVarGroups[0].var_list[1] = find_option("backend_port", FATAL);
2470 	ConfigureVarGroups[0].var_list[1]->flags |= VAR_PART_OF_GROUP;
2471 	ConfigureVarGroups[0].var_list[2] = find_option("backend_weight", FATAL);
2472 	ConfigureVarGroups[0].var_list[2]->flags |= VAR_PART_OF_GROUP;
2473 	ConfigureVarGroups[0].var_list[3] = find_option("backend_data_directory", FATAL);
2474 	ConfigureVarGroups[0].var_list[3]->flags |= VAR_PART_OF_GROUP;
2475 	ConfigureVarGroups[0].var_list[4] = find_option("backend_application_name", FATAL);
2476 	ConfigureVarGroups[0].var_list[4]->flags |= VAR_PART_OF_GROUP;
2477 	ConfigureVarGroups[0].var_list[5] = find_option("backend_flag", FATAL);
2478 	ConfigureVarGroups[0].var_list[5]->flags |= VAR_PART_OF_GROUP;
2479 	ConfigureVarGroups[0].gen.max_elements = ConfigureVarGroups[0].var_list[0]->max_elements;
2480 
2481 	/* group 2. other_pgpool config vars */
2482 	ConfigureVarGroups[1].var_count = 3;
2483 	ConfigureVarGroups[1].var_list = palloc0(sizeof(struct config_generic *) * ConfigureVarGroups[1].var_count);
2484 	/* backend hostname */
2485 	ConfigureVarGroups[1].var_list[0] = find_option("hostname", FATAL);
2486 	ConfigureVarGroups[1].var_list[0]->flags |= VAR_PART_OF_GROUP;
2487 	ConfigureVarGroups[1].var_list[1] = find_option("pgpool_port", FATAL);
2488 	ConfigureVarGroups[1].var_list[1]->flags |= VAR_PART_OF_GROUP;
2489 	ConfigureVarGroups[1].var_list[2] = find_option("wd_port", FATAL);
2490 	ConfigureVarGroups[1].var_list[2]->flags |= VAR_PART_OF_GROUP;
2491 	ConfigureVarGroups[1].gen.max_elements = ConfigureVarGroups[1].var_list[0]->max_elements;
2492 
2493 
2494 	/* group 3. heartbeat config vars */
2495 	ConfigureVarGroups[2].var_count = 3;
2496 	ConfigureVarGroups[2].var_list = palloc0(sizeof(struct config_generic *) * ConfigureVarGroups[2].var_count);
2497 	/* backend hostname */
2498 	ConfigureVarGroups[2].var_list[0] = find_option("heartbeat_device", FATAL);
2499 	ConfigureVarGroups[2].var_list[0]->flags |= VAR_PART_OF_GROUP;
2500 	ConfigureVarGroups[2].var_list[1] = find_option("heartbeat_hostname", FATAL);
2501 	ConfigureVarGroups[2].var_list[1]->flags |= VAR_PART_OF_GROUP;
2502 	ConfigureVarGroups[2].var_list[2] = find_option("heartbeat_port", FATAL);
2503 	ConfigureVarGroups[2].var_list[2]->flags |= VAR_PART_OF_GROUP;
2504 	ConfigureVarGroups[2].gen.max_elements = ConfigureVarGroups[2].var_list[0]->max_elements;
2505 
2506 	/* group 4. health_check config vars */
2507 	ConfigureVarGroups[3].var_count = 8;
2508 	ConfigureVarGroups[3].var_list = palloc0(sizeof(struct config_generic *) * ConfigureVarGroups[3].var_count);
2509 	ConfigureVarGroups[3].var_list[0] = find_option("health_check_period", FATAL);
2510 	ConfigureVarGroups[3].var_list[0]->flags |= VAR_PART_OF_GROUP;
2511 	ConfigureVarGroups[3].var_list[1] = find_option("health_check_timeout", FATAL);
2512 	ConfigureVarGroups[3].var_list[1]->flags |= VAR_PART_OF_GROUP;
2513 	ConfigureVarGroups[3].var_list[2] = find_option("health_check_user", FATAL);
2514 	ConfigureVarGroups[3].var_list[2]->flags |= VAR_PART_OF_GROUP;
2515 	ConfigureVarGroups[3].var_list[3] = find_option("health_check_password", FATAL);
2516 	ConfigureVarGroups[3].var_list[3]->flags |= VAR_PART_OF_GROUP;
2517 	ConfigureVarGroups[3].var_list[4] = find_option("health_check_database", FATAL);
2518 	ConfigureVarGroups[3].var_list[4]->flags |= VAR_PART_OF_GROUP;
2519 	ConfigureVarGroups[3].var_list[5] = find_option("health_check_max_retries", FATAL);
2520 	ConfigureVarGroups[3].var_list[5]->flags |= VAR_PART_OF_GROUP;
2521 	ConfigureVarGroups[3].var_list[6] = find_option("health_check_retry_delay", FATAL);
2522 	ConfigureVarGroups[3].var_list[6]->flags |= VAR_PART_OF_GROUP;
2523 	ConfigureVarGroups[3].var_list[7] = find_option("connect_timeout", FATAL);
2524 	ConfigureVarGroups[3].var_list[7]->flags |= VAR_PART_OF_GROUP;
2525 	ConfigureVarGroups[3].gen.max_elements = ConfigureVarGroups[3].var_list[0]->max_elements;
2526 
2527 }
2528 
2529 /* Sort the config variables on based of string length of
2530  * variable names. Since we want to compare long variable names to be compared
2531  * before the smaller ones. This ensure heartbeat_destination should not match
2532  * with heartbeat_destination_port, when we use strncmp to cater for the index at
2533  * the end of the variable names.*/
2534 static void
sort_config_vars(void)2535 sort_config_vars(void)
2536 {
2537 	int			i,
2538 				j;
2539 
2540 	for (i = 0; i < num_all_parameters; ++i)
2541 	{
2542 		struct config_generic *gconfi = all_parameters[i];
2543 		int			leni = strlen(gconfi->name);
2544 
2545 		for (j = i + 1; j < num_all_parameters; ++j)
2546 		{
2547 			struct config_generic *gconfj = all_parameters[j];
2548 			int			lenj = strlen(gconfj->name);
2549 
2550 			if (leni < lenj)
2551 			{
2552 				all_parameters[i] = gconfj;
2553 				all_parameters[j] = gconfi;
2554 				gconfi = all_parameters[i];
2555 				leni = lenj;
2556 			}
2557 		}
2558 	}
2559 }
2560 
2561 /*
2562  * Initialize all variables to its compiled-in default.
2563  */
2564 static void
initialize_variables_with_default(struct config_generic * gconf)2565 initialize_variables_with_default(struct config_generic *gconf)
2566 {
2567 	int			i;
2568 
2569 	for (i = 0; i < gconf->max_elements; i++)
2570 	{
2571 		gconf->sources[i] = PGC_S_DEFAULT;
2572 		gconf->scontexts[i] = CFGCXT_BOOT;
2573 		gconf->reset_sources[i] = gconf->sources[i];
2574 	}
2575 	gconf->sourceline = 0;
2576 
2577 	/* Also set the default value for index free record if any */
2578 	if (gconf->dynamic_array_var && gconf->flags & ARRAY_VAR_ALLOW_NO_INDEX)
2579 	{
2580 		struct config_generic *idx_free_record = get_index_free_record_if_any(gconf);
2581 
2582 		if (idx_free_record)
2583 		{
2584 			ereport(DEBUG1,
2585 					(errmsg("setting array element defaults for parameter \"%s\"",
2586 							gconf->name)));
2587 			initialize_variables_with_default(idx_free_record);
2588 		}
2589 	}
2590 
2591 	switch (gconf->vartype)
2592 	{
2593 		case CONFIG_VAR_TYPE_GROUP: /* just to keep compiler quite */
2594 			break;
2595 
2596 		case CONFIG_VAR_TYPE_BOOL:
2597 			{
2598 				struct config_bool *conf = (struct config_bool *) gconf;
2599 				bool		newval = conf->boot_val;
2600 
2601 				if (conf->assign_func)
2602 				{
2603 					(*conf->assign_func) (CFGCXT_BOOT, newval, ERROR);
2604 				}
2605 				else
2606 				{
2607 					*conf->variable = conf->reset_val = newval;
2608 				}
2609 				break;
2610 			}
2611 
2612 		case CONFIG_VAR_TYPE_INT:
2613 			{
2614 				struct config_int *conf = (struct config_int *) gconf;
2615 				int			newval = conf->boot_val;
2616 
2617 				if (conf->assign_func)
2618 				{
2619 					(*conf->assign_func) (CFGCXT_BOOT, newval, ERROR);
2620 				}
2621 				else
2622 				{
2623 					*conf->variable = newval;
2624 				}
2625 				conf->reset_val = newval;
2626 
2627 				break;
2628 			}
2629 
2630 		case CONFIG_VAR_TYPE_DOUBLE:
2631 			{
2632 				struct config_double *conf = (struct config_double *) gconf;
2633 				double		newval = conf->boot_val;
2634 
2635 				if (conf->assign_func)
2636 				{
2637 					(*conf->assign_func) (CFGCXT_BOOT, newval, ERROR);
2638 				}
2639 				else
2640 				{
2641 					*conf->variable = newval;
2642 				}
2643 				conf->reset_val = newval;
2644 
2645 				break;
2646 			}
2647 
2648 		case CONFIG_VAR_TYPE_INT_ARRAY:
2649 			{
2650 				struct config_int_array *conf = (struct config_int_array *) gconf;
2651 				int			newval = conf->boot_val;
2652 				int			i;
2653 
2654 				for (i = 0; i < gconf->max_elements; i++)
2655 				{
2656 					if (conf->assign_func)
2657 					{
2658 						(*conf->assign_func) (CFGCXT_BOOT, newval, i, ERROR);
2659 					}
2660 					else
2661 					{
2662 						*conf->variable[i] = newval;
2663 					}
2664 					conf->reset_vals[i] = newval;
2665 				}
2666 				break;
2667 			}
2668 
2669 		case CONFIG_VAR_TYPE_DOUBLE_ARRAY:
2670 			{
2671 				struct config_double_array *conf = (struct config_double_array *) gconf;
2672 				double		newval = conf->boot_val;
2673 				int			i;
2674 
2675 				for (i = 0; i < gconf->max_elements; i++)
2676 				{
2677 					if (conf->assign_func)
2678 					{
2679 						(*conf->assign_func) (CFGCXT_BOOT, newval, i, ERROR);
2680 					}
2681 					else
2682 					{
2683 						*conf->variable[i] = newval;
2684 					}
2685 					conf->reset_vals[i] = newval;
2686 				}
2687 				break;
2688 			}
2689 
2690 		case CONFIG_VAR_TYPE_LONG:
2691 			{
2692 				struct config_long *conf = (struct config_long *) gconf;
2693 				long		newval = conf->boot_val;
2694 
2695 				if (conf->assign_func)
2696 				{
2697 					(*conf->assign_func) (CFGCXT_BOOT, newval, ERROR);
2698 				}
2699 				else
2700 				{
2701 					*conf->variable = conf->reset_val = newval;
2702 				}
2703 				break;
2704 			}
2705 
2706 		case CONFIG_VAR_TYPE_STRING:
2707 			{
2708 				struct config_string *conf = (struct config_string *) gconf;
2709 				char	   *newval = (char *) conf->boot_val;
2710 
2711 				if (conf->assign_func)
2712 				{
2713 					(*conf->assign_func) (CFGCXT_BOOT, newval, ERROR);
2714 				}
2715 				else
2716 				{
2717 					*conf->variable = newval ? pstrdup(newval) : NULL;
2718 				}
2719 
2720 				conf->reset_val = newval ? pstrdup(newval) : NULL;
2721 
2722 				if (conf->process_func)
2723 				{
2724 					(*conf->process_func) (newval, ERROR);
2725 				}
2726 				break;
2727 			}
2728 
2729 		case CONFIG_VAR_TYPE_STRING_ARRAY:
2730 			{
2731 				struct config_string_array *conf = (struct config_string_array *) gconf;
2732 				char	   *newval = (char *) conf->boot_val;
2733 				int			i;
2734 
2735 				for (i = 0; i < gconf->max_elements; i++)
2736 				{
2737 
2738 					if (conf->assign_func)
2739 					{
2740 						(*conf->assign_func) (CFGCXT_BOOT, newval, i, ERROR);
2741 					}
2742 					else
2743 					{
2744 						*conf->variable[i] = newval ? pstrdup(newval) : NULL;
2745 					}
2746 
2747 					if (newval)
2748 						conf->reset_vals[i] = pstrdup(newval);
2749 				}
2750 				break;
2751 			}
2752 
2753 		case CONFIG_VAR_TYPE_ENUM:
2754 			{
2755 				struct config_enum *conf = (struct config_enum *) gconf;
2756 				int			newval = conf->boot_val;
2757 
2758 				if (conf->assign_func)
2759 				{
2760 					(*conf->assign_func) (CFGCXT_BOOT, newval, ERROR);
2761 				}
2762 				else
2763 				{
2764 					*conf->variable = newval;
2765 				}
2766 				conf->reset_val = newval;
2767 
2768 				if (conf->process_func)
2769 				{
2770 					(*conf->process_func) (newval, ERROR);
2771 				}
2772 
2773 				break;
2774 			}
2775 
2776 		case CONFIG_VAR_TYPE_STRING_LIST:
2777 			{
2778 				struct config_string_list *conf = (struct config_string_list *) gconf;
2779 				char	   *newval = (char *) conf->boot_val;
2780 
2781 				if (conf->assign_func)
2782 				{
2783 					(*conf->assign_func) (CFGCXT_BOOT, newval, ERROR);
2784 				}
2785 				else
2786 				{
2787 					if (strcmp(gconf->name, "primary_routing_query_pattern_list") == 0)
2788 					{
2789 						*conf->variable = get_list_from_string_regex_delim(newval, conf->seperator, conf->list_elements_count);
2790 					}
2791 					else
2792 					{
2793 						*conf->variable = get_list_from_string(newval, conf->seperator, conf->list_elements_count);
2794 					}
2795 
2796 					if (conf->compute_regex)
2797 					{
2798 						int			i;
2799 
2800 						for (i = 0; i < *conf->list_elements_count; i++)
2801 						{
2802 							add_regex_pattern((const char *) conf->gen.name, (*conf->variable)[i]);
2803 						}
2804 					}
2805 				}
2806 				/* save the string value */
2807 				conf->reset_val = newval ? pstrdup(newval) : NULL;
2808 
2809 				if (conf->current_val)
2810 					pfree(conf->current_val);
2811 
2812 				conf->current_val = newval ? pstrdup(newval) : NULL;
2813 				break;
2814 			}
2815 
2816 	}
2817 }
2818 
2819 /*
2820  * Extract tokens separated by delimi from str. Return value is an
2821  * array of pointers in pallocd strings. number of elements are set to
2822  * n.
2823  */
2824 static char **
get_list_from_string(const char * str,const char * delimi,int * n)2825 get_list_from_string(const char *str, const char *delimi, int *n)
2826 {
2827 	char	   *token;
2828 	char	  **tokens;
2829 	char	   *temp_string;
2830 	const int	MAXTOKENS = 256;
2831 
2832 	*n = 0;
2833 
2834 	if (str == NULL || *str == '\0')
2835 		return NULL;
2836 
2837 	temp_string = pstrdup(str);
2838 	tokens = palloc(MAXTOKENS * sizeof(char *));
2839 
2840 	ereport(DEBUG3,
2841 			(errmsg("extracting string tokens from [%s] based on %s", temp_string, delimi)));
2842 
2843 	for (token = strtok(temp_string, delimi); token != NULL; token = strtok(NULL, delimi))
2844 	{
2845 		tokens[*n] = pstrdup(token);
2846 		ereport(DEBUG3,
2847 				(errmsg("initializing pool configuration"),
2848 				 errdetail("extracting string tokens [token[%d]: %s]", *n, tokens[*n])));
2849 
2850 		(*n)++;
2851 
2852 		if (((*n) % MAXTOKENS) == 0)
2853 		{
2854 			tokens = repalloc(tokens, (MAXTOKENS * sizeof(char *) * (((*n) / MAXTOKENS) + 1)));
2855 		}
2856 	}
2857 	/* how about reclaiming the unused space */
2858 	if (*n > 0)
2859 		tokens = repalloc(tokens, (sizeof(char *) * (*n)));
2860 	pfree(temp_string);
2861 
2862 	return tokens;
2863 }
2864 
2865 /*
2866  * Extract tokens separated by delimiter from str. Allow to
2867  * use regex of delimiter. Return value is an array of
2868  * pointers in pallocd strings. number of elements are set
2869  * to n.
2870  */
2871 static char **
get_list_from_string_regex_delim(const char * input,const char * delimi,int * n)2872 get_list_from_string_regex_delim(const char *input, const char *delimi, int *n)
2873 {
2874 #ifndef POOL_PRIVATE
2875 	int			j = 0;
2876 	char	   *str;
2877 	char	   *buf,
2878 			   *str_temp;
2879 	char	  **tokens;
2880 	const int	MAXTOKENS = 256;
2881 
2882 	*n = 0;
2883 
2884 	if (input == NULL || *input == '\0')
2885 		return NULL;
2886 
2887 	tokens = palloc(MAXTOKENS * sizeof(char *));
2888 	if (*(input + strlen(input) - 1) != *delimi || *(input + strlen(input) - 2) == '\\')
2889 	{
2890 		int			len = strlen(input) + 2;
2891 
2892 		str = palloc(len);
2893 		snprintf(str, len, "%s;", input);
2894 	}
2895 	else
2896 	{
2897 		str = pstrdup(input);
2898 	}
2899 
2900 	buf = str;
2901 	str_temp = str;
2902 
2903 	while (*str_temp != '\0')
2904 	{
2905 		if (*str_temp == '\\')
2906 		{
2907 			j += 2;
2908 			str_temp++;
2909 		}
2910 		else if (*str_temp == *delimi)
2911 		{
2912 			char *output = (char *) palloc(j + 1);
2913 			StrNCpy(output, buf, j + 1);
2914 
2915 			/* replace escape character of "'" */
2916 			tokens[*n] = string_replace(output, "\\'", "'");
2917 			pfree(output);
2918 			ereport(DEBUG3,
2919 					(errmsg("initializing pool configuration"),
2920 					 errdetail("extracting string tokens [token[%d]: %s]", *n, tokens[*n])));
2921 
2922 			(*n)++;
2923 			buf = str_temp + 1;
2924 			j = 0;
2925 
2926 			if (((*n) % MAXTOKENS) == 0)
2927 				tokens = repalloc(tokens, (MAXTOKENS * sizeof(char *) * (((*n) / MAXTOKENS) + 1)));
2928 		}
2929 		else
2930 		{
2931 			j++;
2932 		}
2933 		str_temp++;
2934 	}
2935 
2936 	if (*n > 0)
2937 		tokens = repalloc(tokens, (sizeof(char *) * (*n)));
2938 
2939 	pfree(str);
2940 
2941 	return tokens;
2942 #else
2943 	return NULL;
2944 #endif
2945 }
2946 
2947 /*
2948  * Memory of the array type variables must be initialized befor calling this function
2949  */
2950 void
InitializeConfigOptions(void)2951 InitializeConfigOptions(void)
2952 {
2953 	int			i;
2954 
2955 	/*
2956 	 * Before we do anything set the log_min_messages to ERROR. Reason for
2957 	 * doing that is before the log_min_messages gets initialized with the
2958 	 * actual value the pgpool-II log should not get flooded by DEBUG messages
2959 	 */
2960 	g_pool_config.log_min_messages = ERROR;
2961 	g_pool_config.syslog_facility = LOG_LOCAL0;
2962 	build_config_variables();
2963 
2964 	/*
2965 	 * Load all variables with their compiled-in defaults, and initialize
2966 	 * status fields as needed.
2967 	 */
2968 	for (i = 0; i < num_all_parameters; i++)
2969 	{
2970 		initialize_variables_with_default(all_parameters[i]);
2971 	}
2972 	/* do the post processing */
2973 	config_post_processor(CFGCXT_BOOT, FATAL);
2974 }
2975 
2976 /*
2977  * returns the index value postfixed with the variable name
2978  * for example if the if name contains "backend_hostname11" and
2979  * the record name must be for the variable nameed "backend_hostname"
2980  * if the index is not present at end of the name the function
2981  * will return true and out parameter index will be assigned with -ve value
2982  */
2983 static bool
get_index_in_var_name(struct config_generic * record,const char * name,int * index,int elevel)2984 get_index_in_var_name(struct config_generic *record, const char *name, int *index, int elevel)
2985 {
2986 	char	   *ptr;
2987 	int			index_start_index = strlen(record->name);
2988 
2989 	if (strlen(name) <= index_start_index)
2990 	{
2991 		/* no index is provided */
2992 		*index = -1;
2993 		return true;
2994 	}
2995 	ptr = (char *) (name + index_start_index);
2996 	while (*ptr)
2997 	{
2998 		if (isdigit(*ptr) == 0)
2999 		{
3000 			ereport(elevel,
3001 					(errmsg("invalid index value for parameter \"%s\" ", name),
3002 					 (errdetail("index part contains the invalid non digit character"))));
3003 			return false;
3004 		}
3005 		ptr++;
3006 	}
3007 	*index = atoi(name + index_start_index);
3008 	return true;
3009 }
3010 
3011 /*
3012  * Look up option NAME.  If it exists, return a pointer to its record,
3013  * else return NULL.
3014  */
3015 static struct config_generic *
find_option(const char * name,int elevel)3016 find_option(const char *name, int elevel)
3017 {
3018 	int			i;
3019 
3020 	for (i = 0; i < num_all_parameters; i++)
3021 	{
3022 		struct config_generic *gconf = all_parameters[i];
3023 
3024 		if (gconf->dynamic_array_var)
3025 		{
3026 			int			index_start_index = strlen(gconf->name);
3027 
3028 			/*
3029 			 * For dynamic array type vars the key also have the index at the
3030 			 * end e.g. backend_hostname0 so we only comapare the key's name
3031 			 * part
3032 			 */
3033 			if (!strncmp(gconf->name, name, index_start_index))
3034 				return gconf;
3035 		}
3036 		else
3037 		{
3038 			if (!strcmp(gconf->name, name))
3039 				return gconf;
3040 		}
3041 	}
3042 	/* Unknown name */
3043 	return NULL;
3044 }
3045 
3046 /*
3047  * Lookup the value for an enum option with the selected name
3048  * (case-insensitive).
3049  * If the enum option is found, sets the retval value and returns
3050  * true. If it's not found, return FALSE and retval is set to 0.
3051  */
3052 static bool
config_enum_lookup_by_name(struct config_enum * record,const char * value,int * retval)3053 config_enum_lookup_by_name(struct config_enum *record, const char *value,
3054 						   int *retval)
3055 {
3056 	const struct config_enum_entry *entry;
3057 
3058 	for (entry = record->options; entry && entry->name; entry++)
3059 	{
3060 		if (strcasecmp(value, entry->name) == 0)
3061 		{
3062 			*retval = entry->val;
3063 			return TRUE;
3064 		}
3065 	}
3066 
3067 	*retval = 0;
3068 	return FALSE;
3069 }
3070 
3071 
3072 bool
set_config_options(ConfigVariable * head_p,ConfigContext context,GucSource source,int elevel)3073 set_config_options(ConfigVariable *head_p,
3074 				   ConfigContext context, GucSource source, int elevel)
3075 {
3076 	ConfigVariable *item = head_p;
3077 
3078 	while (item)
3079 	{
3080 		ConfigVariable *next = item->next;
3081 
3082 		setConfigOption(item->name, item->value, context, source, elevel);
3083 		item = next;
3084 	}
3085 	return config_post_processor(context, elevel);
3086 }
3087 
3088 bool
set_one_config_option(const char * name,const char * value,ConfigContext context,GucSource source,int elevel)3089 set_one_config_option(const char *name, const char *value,
3090 					  ConfigContext context, GucSource source, int elevel)
3091 {
3092 	if (setConfigOption(name, value, context, source, elevel) == true)
3093 		return config_post_processor(context, elevel);
3094 	return false;
3095 }
3096 
3097 static bool
setConfigOption(const char * name,const char * value,ConfigContext context,GucSource source,int elevel)3098 setConfigOption(const char *name, const char *value,
3099 				ConfigContext context, GucSource source, int elevel)
3100 {
3101 	struct config_generic *record;
3102 	int			index_val = -1;
3103 
3104 	ereport(DEBUG2,
3105 			(errmsg("set_config_option \"%s\" = \"%s\"", name, value)));
3106 
3107 	record = find_option(name, elevel);
3108 
3109 	if (record == NULL)
3110 	{
3111 		/*
3112 		 * we emit only INFO message when setting the option from
3113 		 * configuration file. As the conf file may still contain some
3114 		 * configuration parameters only exist in older version and does not
3115 		 * exist anymore
3116 		 */
3117 		ereport(source == PGC_S_FILE ? INFO : elevel,
3118 				(errmsg("unrecognized configuration parameter \"%s\"", name)));
3119 		return false;
3120 	}
3121 	if (record->dynamic_array_var)
3122 	{
3123 		if (get_index_in_var_name(record, name, &index_val, elevel) == false)
3124 			return false;
3125 
3126 		if (index_val < 0)
3127 		{
3128 			/* index is not provided */
3129 			if (record->flags & ARRAY_VAR_ALLOW_NO_INDEX)
3130 			{
3131 				ereport(DEBUG2,
3132 						(errmsg("parameter \"%s\" is an array type variable and allows index-free value as well",
3133 								name)));
3134 			}
3135 			else
3136 			{
3137 				ereport(elevel,
3138 						(errmsg("parameter \"%s\" expects the index value",
3139 								name)));
3140 				return false;
3141 			}
3142 		}
3143 	}
3144 
3145 	return setConfigOptionVar(record, name, index_val, value, context, source, elevel);
3146 }
3147 
3148 static bool
setConfigOptionArrayVarWithConfigDefault(struct config_generic * record,const char * name,const char * value,ConfigContext context,int elevel)3149 setConfigOptionArrayVarWithConfigDefault(struct config_generic *record, const char *name,
3150 										 const char *value, ConfigContext context, int elevel)
3151 {
3152 	int			index;
3153 
3154 	if (record->dynamic_array_var == false)
3155 	{
3156 		ereport(elevel,
3157 				(errmsg("parameter \"%s\" is not the array type configuration parameter",
3158 						name)));
3159 		return false;
3160 	}
3161 	if (!(record->flags & ARRAY_VAR_ALLOW_NO_INDEX))
3162 	{
3163 		ereport(elevel,
3164 				(errmsg("parameter \"%s\" does not allow value type default",
3165 						name)));
3166 		return false;
3167 	}
3168 
3169 	for (index = 0; index < record->max_elements; index++)
3170 	{
3171 		/*
3172 		 * only elements having the values from the default source can be
3173 		 * updated
3174 		 */
3175 		if (record->sources[index] != PGC_S_DEFAULT
3176 			&& record->sources[index] != PGC_S_VALUE_DEFAULT)
3177 			continue;
3178 		setConfigOptionVar(record, record->name, index, value,
3179 						   CFGCXT_INIT, PGC_S_VALUE_DEFAULT, elevel);
3180 	}
3181 	return true;
3182 }
3183 
3184 static bool
setConfigOptionVar(struct config_generic * record,const char * name,int index_val,const char * value,ConfigContext context,GucSource source,int elevel)3185 setConfigOptionVar(struct config_generic *record, const char *name, int index_val, const char *value,
3186 				   ConfigContext context, GucSource source, int elevel)
3187 {
3188 	bool		reset = false;
3189 
3190 	switch (record->context)
3191 	{
3192 		case CFGCXT_BOOT:
3193 			if (context != CFGCXT_BOOT)
3194 			{
3195 				if (context == CFGCXT_RELOAD)
3196 				{
3197 					/*
3198 					 * Do not treat it as an error. Since the RELOAD context
3199 					 * is used by reload config mechanism of pgpool-II and the
3200 					 * configuration file always contain all the values,
3201 					 * including those that are not allowed to be changed in
3202 					 * reload context. So silently ignoring this for the time
3203 					 * being is the best way to go until we enhance the logic
3204 					 * around this
3205 					 */
3206 					ereport(DEBUG2,
3207 							(errmsg("invalid Context, value for parameter \"%s\" cannot be changed",
3208 									name)));
3209 					return true;
3210 				}
3211 
3212 				ereport(elevel,
3213 						(errmsg("invalid Context, value for parameter \"%s\" cannot be changed",
3214 								name)));
3215 				return false;
3216 			}
3217 			break;
3218 		case CFGCXT_INIT:
3219 			if (context != CFGCXT_INIT && context != CFGCXT_BOOT)
3220 			{
3221 				if (context == CFGCXT_RELOAD)
3222 				{
3223 					ereport(DEBUG2,
3224 							(errmsg("invalid Context, value for parameter \"%s\" cannot be changed",
3225 									name)));
3226 					return true;
3227 				}
3228 
3229 				ereport(elevel,
3230 						(errmsg("invalid Context, value for parameter \"%s\" cannot be changed",
3231 								name)));
3232 				return false;
3233 			}
3234 			break;
3235 		case CFGCXT_RELOAD:
3236 			if (context > CFGCXT_RELOAD)
3237 			{
3238 				ereport(elevel,
3239 						(errmsg("invalid Context, value for parameter \"%s\" cannot be changed",
3240 								name)));
3241 				return false;
3242 			}
3243 			break;
3244 		case CFGCXT_PCP:
3245 			if (context > CFGCXT_PCP)
3246 			{
3247 				ereport(elevel,
3248 						(errmsg("invalid Context, value for parameter \"%s\" cannot be changed",
3249 								name)));
3250 				return false;
3251 			}
3252 			break;
3253 
3254 		case CFGCXT_SESSION:
3255 			break;
3256 
3257 		default:
3258 			{
3259 				ereport(elevel,
3260 						(errmsg("invalid record context, value for parameter \"%s\" cannot be changed",
3261 								name)));
3262 				return false;
3263 			}
3264 			break;
3265 	}
3266 
3267 	if (record->dynamic_array_var)
3268 	{
3269 		if (index_val < 0)
3270 		{
3271 			ereport(DEBUG1,
3272 					(errmsg("setting value no index value for parameter \"%s\" source = %d",
3273 							name, source)));
3274 
3275 			if (record->flags & ARRAY_VAR_ALLOW_NO_INDEX)
3276 			{
3277 				struct config_generic *idx_free_record = get_index_free_record_if_any(record);
3278 
3279 				if (idx_free_record)
3280 				{
3281 					bool		ret;
3282 
3283 					ereport(DEBUG1,
3284 							(errmsg("parameter \"%s\" is an array type variable and allows index-free value as well",
3285 									name)));
3286 					ret = setConfigOptionVar(idx_free_record, name, index_val, value,
3287 											 context, source, elevel);
3288 					if (idx_free_record->flags & DEFAULT_FOR_NO_VALUE_ARRAY_VAR)
3289 					{
3290 						const char *newVal;
3291 
3292 						/* we need to update the default values */
3293 						ereport(DEBUG2,
3294 								(errmsg("modifying the array index values for parameter \"%s\" source = %d",
3295 										name, source)));
3296 #ifndef POOL_PRIVATE
3297 						if (value == NULL)
3298 						{
3299 							newVal = ShowOption(idx_free_record, -1, elevel);
3300 						}
3301 						else
3302 #endif
3303 							newVal = value;
3304 
3305 						setConfigOptionArrayVarWithConfigDefault(record, name, newVal,
3306 																 context, elevel);
3307 					}
3308 					return ret;
3309 				}
3310 			}
3311 			/* index is not provided */
3312 			ereport(elevel,
3313 					(errmsg("parameter \"%s\" expects the index postfix",
3314 							name)));
3315 			return false;
3316 		}
3317 	}
3318 
3319 	/*
3320 	 * Evaluate value and set variable.
3321 	 */
3322 	switch (record->vartype)
3323 	{
3324 		case CONFIG_VAR_TYPE_GROUP:
3325 			ereport(ERROR, (errmsg("invalid config variable type. operation not allowed")));
3326 			break;
3327 
3328 		case CONFIG_VAR_TYPE_BOOL:
3329 			{
3330 				struct config_bool *conf = (struct config_bool *) record;
3331 				bool		newval = conf->boot_val;
3332 
3333 				if (value != NULL)
3334 				{
3335 					newval = eval_logical(value);
3336 
3337 				}
3338 				else if (source == PGC_S_DEFAULT)
3339 				{
3340 					newval = conf->boot_val;
3341 				}
3342 				else
3343 				{
3344 					/* Reset */
3345 					newval = conf->reset_val;
3346 					reset = true;
3347 				}
3348 				if (conf->assign_func)
3349 				{
3350 					if ((*conf->assign_func) (context, newval, elevel) == false)
3351 						return false;
3352 				}
3353 				else
3354 					*conf->variable = newval;
3355 
3356 				if (context == CFGCXT_INIT)
3357 					conf->reset_val = newval;
3358 			}
3359 			break;
3360 
3361 		case CONFIG_VAR_TYPE_INT:
3362 			{
3363 				struct config_int *conf = (struct config_int *) record;
3364 				int			newval;
3365 
3366 				if (value != NULL)
3367 				{
3368 					int64 newval64;
3369 					const char *hintmsg;
3370 
3371 					if (!parse_int(value, &newval64,
3372 								   conf->gen.flags, &hintmsg, INT_MAX))
3373 					{
3374 						ereport(elevel,
3375 								(errmsg("invalid value for parameter \"%s\": \"%s\"",
3376 										name, value),
3377 								 hintmsg ? errhint("%s", _(hintmsg)) : 0));
3378 						return false;
3379 					}
3380 					newval = (int)newval64;
3381 				}
3382 				else if (source == PGC_S_DEFAULT)
3383 				{
3384 					newval = conf->boot_val;
3385 				}
3386 				else
3387 				{
3388 					/* Reset */
3389 					newval = conf->reset_val;
3390 					reset = true;
3391 				}
3392 
3393 				if (newval < conf->min || newval > conf->max)
3394 				{
3395 					ereport(elevel,
3396 							(errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
3397 									newval, name,
3398 									conf->min, conf->max)));
3399 					return false;
3400 				}
3401 
3402 				if (conf->assign_func)
3403 				{
3404 					if ((*conf->assign_func) (context, newval, elevel) == false)
3405 						return false;
3406 				}
3407 				else
3408 				{
3409 					*conf->variable = newval;
3410 				}
3411 
3412 				if (context == CFGCXT_INIT)
3413 					conf->reset_val = newval;
3414 			}
3415 			break;
3416 
3417 		case CONFIG_VAR_TYPE_DOUBLE:
3418 			{
3419 				struct config_double *conf = (struct config_double *) record;
3420 				double		newval;
3421 
3422 				if (value != NULL)
3423 				{
3424 					newval = atof(value);
3425 				}
3426 				else if (source == PGC_S_DEFAULT)
3427 				{
3428 					newval = conf->boot_val;
3429 				}
3430 				else
3431 				{
3432 					/* Reset */
3433 					newval = conf->reset_val;
3434 					reset = true;
3435 				}
3436 
3437 				if (newval < conf->min || newval > conf->max)
3438 				{
3439 					ereport(elevel,
3440 							(errmsg("%f is outside the valid range for parameter \"%s\" (%f .. %f)",
3441 									newval, name,
3442 									conf->min, conf->max)));
3443 					return false;
3444 				}
3445 
3446 				if (conf->assign_func)
3447 				{
3448 					if ((*conf->assign_func) (context, newval, elevel) == false)
3449 						return false;
3450 				}
3451 				else
3452 				{
3453 					*conf->variable = newval;
3454 				}
3455 
3456 				if (context == CFGCXT_INIT)
3457 					conf->reset_val = newval;
3458 
3459 			}
3460 			break;
3461 
3462 		case CONFIG_VAR_TYPE_INT_ARRAY:
3463 			{
3464 				struct config_int_array *conf = (struct config_int_array *) record;
3465 				int			newval;
3466 
3467 				if (index_val < 0 || index_val > record->max_elements)
3468 				{
3469 					ereport(elevel,
3470 							(errmsg("%d index outside the valid range for parameter \"%s\" (%d .. %d)",
3471 									index_val, name,
3472 									0, record->max_elements)));
3473 					return false;
3474 				}
3475 
3476 				if (value != NULL)
3477 				{
3478 					int64 newval64;
3479 					const char *hintmsg;
3480 
3481 					if (!parse_int(value, &newval64,
3482 								   conf->gen.flags, &hintmsg, INT_MAX))
3483 					{
3484 						ereport(elevel,
3485 								(errmsg("invalid value for parameter \"%s\": \"%s\"",
3486 										name, value),
3487 								 hintmsg ? errhint("%s", _(hintmsg)) : 0));
3488 						return false;
3489 					}
3490 					newval = (int)newval64;
3491 				}
3492 				else if (source == PGC_S_DEFAULT)
3493 				{
3494 					newval = conf->boot_val;
3495 				}
3496 				else
3497 				{
3498 					/* Reset */
3499 					newval = conf->reset_vals[index_val];
3500 					reset = true;
3501 				}
3502 
3503 				if (newval < conf->min || newval > conf->max)
3504 				{
3505 					ereport(elevel,
3506 							(errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
3507 									newval, name,
3508 									conf->min, conf->max)));
3509 					return false;
3510 				}
3511 
3512 				if (conf->assign_func)
3513 				{
3514 					if ((*conf->assign_func) (context, newval, index_val, elevel) == false)
3515 						return false;
3516 				}
3517 				else
3518 				{
3519 					*conf->variable[index_val] = newval;
3520 				}
3521 
3522 				if (context == CFGCXT_INIT)
3523 					conf->reset_vals[index_val] = newval;
3524 
3525 			}
3526 			break;
3527 
3528 		case CONFIG_VAR_TYPE_DOUBLE_ARRAY:
3529 			{
3530 				struct config_double_array *conf = (struct config_double_array *) record;
3531 				double		newval;
3532 
3533 				if (index_val < 0 || index_val > record->max_elements)
3534 				{
3535 					ereport(elevel,
3536 							(errmsg("%d index outside the valid range for parameter \"%s\" (%d .. %d)",
3537 									index_val, name,
3538 									0, record->max_elements)));
3539 					return false;
3540 				}
3541 
3542 				if (value != NULL)
3543 				{
3544 					newval = atof(value);
3545 				}
3546 				else if (source == PGC_S_DEFAULT)
3547 				{
3548 					newval = conf->boot_val;
3549 				}
3550 				else
3551 				{
3552 					/* Reset */
3553 					newval = conf->reset_vals[index_val];
3554 					reset = true;
3555 				}
3556 
3557 				if (newval < conf->min || newval > conf->max)
3558 				{
3559 					ereport(elevel,
3560 							(errmsg("%f is outside the valid range for parameter \"%s\" (%f .. %f)",
3561 									newval, name,
3562 									conf->min, conf->max)));
3563 					return false;
3564 				}
3565 
3566 				if (conf->assign_func)
3567 				{
3568 					if ((*conf->assign_func) (context, newval, index_val, elevel) == false)
3569 						return false;
3570 				}
3571 				else
3572 				{
3573 					*conf->variable[index_val] = newval;
3574 				}
3575 
3576 				if (context == CFGCXT_INIT)
3577 					conf->reset_vals[index_val] = newval;
3578 
3579 			}
3580 			break;
3581 
3582 
3583 		case CONFIG_VAR_TYPE_LONG:
3584 			{
3585 				struct config_long *conf = (struct config_long *) record;
3586 				int64		newval;
3587 
3588 				if (value != NULL)
3589 				{
3590 					const char *hintmsg;
3591 
3592 					if (!parse_int(value, &newval,
3593 								   conf->gen.flags, &hintmsg, conf->max))
3594 					{
3595 						ereport(elevel,
3596 								(errmsg("invalid value for parameter \"%s\": \"%s\"",
3597 										name, value),
3598 								 hintmsg ? errhint("%s", _(hintmsg)) : 0));
3599 						return false;
3600 					}
3601 				}
3602 				else if (source == PGC_S_DEFAULT)
3603 				{
3604 					newval = conf->boot_val;
3605 				}
3606 				else
3607 				{
3608 					/* Reset */
3609 					newval = conf->reset_val;
3610 					reset = true;
3611 				}
3612 
3613 				if (newval < conf->min || newval > conf->max)
3614 				{
3615 					ereport(elevel,
3616 							(errmsg("%ld is outside the valid range for parameter \"%s\" (%ld .. %ld)",
3617 									newval, name,
3618 									conf->min, conf->max)));
3619 					return false;
3620 				}
3621 
3622 				if (conf->assign_func)
3623 				{
3624 					if ((*conf->assign_func) (context, newval, elevel) == false)
3625 						return false;
3626 				}
3627 				else
3628 				{
3629 					*conf->variable = newval;
3630 				}
3631 
3632 				if (context == CFGCXT_INIT)
3633 					conf->reset_val = newval;
3634 
3635 			}
3636 			break;
3637 
3638 		case CONFIG_VAR_TYPE_STRING:
3639 			{
3640 				struct config_string *conf = (struct config_string *) record;
3641 				char	   *newval = NULL;
3642 
3643 				if (value != NULL)
3644 				{
3645 					newval = (char *) value;
3646 				}
3647 				else if (source == PGC_S_DEFAULT)
3648 				{
3649 					newval = (char *) conf->boot_val;
3650 				}
3651 				else
3652 				{
3653 					/* Reset */
3654 					newval = conf->reset_val;
3655 					reset = true;
3656 				}
3657 
3658 				if (conf->assign_func)
3659 				{
3660 					if ((*conf->assign_func) (context, newval, elevel) == false)
3661 						return false;
3662 				}
3663 				else
3664 				{
3665 					if (*conf->variable)
3666 						pfree(*conf->variable);
3667 
3668 					*conf->variable = newval ? pstrdup(newval) : NULL;
3669 				}
3670 				if (context == CFGCXT_INIT)
3671 				{
3672 					if (conf->reset_val)
3673 						pfree(conf->reset_val);
3674 
3675 					conf->reset_val = newval ? pstrdup(newval) : NULL;
3676 				}
3677 				if (conf->process_func)
3678 				{
3679 					(*conf->process_func) (newval, elevel);
3680 				}
3681 			}
3682 			break;
3683 
3684 		case CONFIG_VAR_TYPE_STRING_ARRAY:
3685 			{
3686 				struct config_string_array *conf = (struct config_string_array *) record;
3687 				char	   *newval = NULL;
3688 
3689 				if (index_val < 0 || index_val > record->max_elements)
3690 				{
3691 					ereport(elevel,
3692 							(errmsg("%d index outside the valid range for parameter \"%s\" (%d .. %d)",
3693 									index_val, name,
3694 									0, record->max_elements)));
3695 					return false;
3696 				}
3697 
3698 				if (value != NULL)
3699 				{
3700 					newval = (char *) value;
3701 				}
3702 				else if (source == PGC_S_DEFAULT)
3703 				{
3704 					newval = (char *) conf->boot_val;
3705 				}
3706 				else
3707 				{
3708 					/* Reset */
3709 					newval = conf->reset_vals[index_val];
3710 					reset = true;
3711 				}
3712 
3713 				if (conf->assign_func)
3714 				{
3715 					if ((*conf->assign_func) (context, newval, index_val, elevel) == false)
3716 						return false;
3717 				}
3718 				else
3719 				{
3720 					if (*conf->variable[index_val])
3721 						pfree(*conf->variable[index_val]);
3722 
3723 					*conf->variable[index_val] = newval ? pstrdup(newval) : NULL;
3724 				}
3725 				if (context == CFGCXT_INIT)
3726 				{
3727 					if (conf->reset_vals[index_val])
3728 						pfree(conf->reset_vals[index_val]);
3729 
3730 					conf->reset_vals[index_val] = newval ? pstrdup(newval) : NULL;
3731 				}
3732 			}
3733 			break;
3734 
3735 		case CONFIG_VAR_TYPE_STRING_LIST:
3736 			{
3737 				struct config_string_list *conf = (struct config_string_list *) record;
3738 				char	   *newval = NULL;
3739 
3740 				if (value != NULL)
3741 				{
3742 					newval = (char *) value;
3743 				}
3744 				else if (source == PGC_S_DEFAULT)
3745 				{
3746 					newval = (char *) conf->boot_val;
3747 				}
3748 				else
3749 				{
3750 					/* Reset */
3751 					newval = conf->reset_val;
3752 					reset = true;
3753 				}
3754 
3755 				if (conf->assign_func)
3756 				{
3757 					if ((*conf->assign_func) (context, newval, elevel) == false)
3758 					{
3759 						return false;
3760 					}
3761 				}
3762 				else
3763 				{
3764 					if (*conf->variable)
3765 					{
3766 						int			i;
3767 
3768 						for (i = 0; i < *conf->list_elements_count; i++)
3769 						{
3770 							if ((*conf->variable)[i])
3771 								pfree((*conf->variable)[i]);
3772 							(*conf->variable)[i] = NULL;
3773 						}
3774 						pfree(*conf->variable);
3775 					}
3776 
3777 					if (strcmp(name, "primary_routing_query_pattern_list") == 0)
3778 					{
3779 						*conf->variable = get_list_from_string_regex_delim(newval, conf->seperator, conf->list_elements_count);
3780 					}
3781 					else
3782 					{
3783 						*conf->variable = get_list_from_string(newval, conf->seperator, conf->list_elements_count);
3784 					}
3785 
3786 					if (conf->compute_regex)
3787 					{
3788 						/* TODO clear the old regex array please */
3789 						int			i;
3790 
3791 						for (i = 0; i < *conf->list_elements_count; i++)
3792 						{
3793 							add_regex_pattern(conf->gen.name, (*conf->variable)[i]);
3794 						}
3795 					}
3796 				}
3797 
3798 				if (context == CFGCXT_INIT)
3799 				{
3800 					if (conf->reset_val)
3801 						pfree(conf->reset_val);
3802 
3803 					conf->reset_val = newval ? pstrdup(newval) : NULL;
3804 				}
3805 
3806 				/* save the string value */
3807 				if (conf->current_val)
3808 					pfree(conf->current_val);
3809 
3810 				conf->current_val = newval ? pstrdup(newval) : NULL;
3811 			}
3812 			break;
3813 
3814 		case CONFIG_VAR_TYPE_ENUM:
3815 			{
3816 				struct config_enum *conf = (struct config_enum *) record;
3817 				int			newval;
3818 
3819 				if (value != NULL)
3820 				{
3821 					newval = atoi(value);
3822 				}
3823 				else if (source == PGC_S_DEFAULT)
3824 				{
3825 					newval = conf->boot_val;
3826 				}
3827 				else
3828 				{
3829 					/* Reset */
3830 					newval = conf->reset_val;
3831 					reset = true;
3832 				}
3833 
3834 				if (value && !config_enum_lookup_by_name(conf, value, &newval))
3835 				{
3836 
3837 					char	   *hintmsg = NULL;
3838 
3839 #ifndef POOL_PRIVATE
3840 					hintmsg = config_enum_get_options(conf,
3841 													  "Available values: ",
3842 													  ".", ", ");
3843 #endif
3844 
3845 					ereport(elevel,
3846 							(errmsg("invalid value for parameter \"%s\": \"%s\"",
3847 									name, value),
3848 							 hintmsg ? errhint("%s", hintmsg) : 0));
3849 
3850 					if (hintmsg)
3851 						pfree(hintmsg);
3852 					return false;
3853 				}
3854 
3855 				if (conf->assign_func)
3856 				{
3857 					if ((*conf->assign_func) (context, newval, elevel) == false)
3858 						return false;
3859 				}
3860 				else
3861 				{
3862 					*conf->variable = newval;
3863 				}
3864 
3865 				if (context == CFGCXT_INIT)
3866 					conf->reset_val = newval;
3867 
3868 				if (conf->process_func)
3869 				{
3870 					(*conf->process_func) (newval, elevel);
3871 				}
3872 
3873 				break;
3874 			}
3875 	}
3876 	if (record->dynamic_array_var)
3877 	{
3878 		if (index_val < 0 || index_val > record->max_elements)
3879 		{
3880 			ereport(elevel,
3881 					(errmsg("%d index outside the valid range for parameter \"%s\" (%d .. %d)",
3882 							index_val, name,
3883 							0, record->max_elements)));
3884 			return false;
3885 		}
3886 	}
3887 	else
3888 	{
3889 		index_val = 0;
3890 	}
3891 
3892 	record->scontexts[index_val] = context;
3893 
3894 	if (reset)
3895 		record->sources[index_val] = record->reset_sources[index_val];
3896 	else
3897 		record->sources[index_val] = source;
3898 
3899 	if (context == CFGCXT_INIT)
3900 		record->reset_sources[index_val] = source;
3901 
3902 	return true;
3903 }
3904 
3905 #ifndef POOL_PRIVATE
3906 
3907 /*
3908  * Return a list of all available options for an enum, excluding
3909  * hidden ones, separated by the given separator.
3910  * If prefix is non-NULL, it is added before the first enum value.
3911  * If suffix is non-NULL, it is added to the end of the string.
3912  */
3913 static char *
config_enum_get_options(struct config_enum * record,const char * prefix,const char * suffix,const char * separator)3914 config_enum_get_options(struct config_enum *record, const char *prefix,
3915 						const char *suffix, const char *separator)
3916 {
3917 	const struct config_enum_entry *entry;
3918 	StringInfoData retstr;
3919 	int			seplen;
3920 
3921 	initStringInfo(&retstr);
3922 	appendStringInfoString(&retstr, prefix);
3923 
3924 	seplen = strlen(separator);
3925 	for (entry = record->options; entry && entry->name; entry++)
3926 	{
3927 		if (!entry->hidden)
3928 		{
3929 			appendStringInfoString(&retstr, entry->name);
3930 			appendBinaryStringInfo(&retstr, separator, seplen);
3931 		}
3932 	}
3933 
3934 	/*
3935 	 * All the entries may have been hidden, leaving the string empty if no
3936 	 * prefix was given. This indicates a broken GUC setup, since there is no
3937 	 * use for an enum without any values, so we just check to make sure we
3938 	 * don't write to invalid memory instead of actually trying to do
3939 	 * something smart with it.
3940 	 */
3941 	if (retstr.len >= seplen)
3942 	{
3943 		/* Replace final separator */
3944 		retstr.data[retstr.len - seplen] = '\0';
3945 		retstr.len -= seplen;
3946 	}
3947 
3948 	appendStringInfoString(&retstr, suffix);
3949 
3950 	return retstr.data;
3951 }
3952 #endif
3953 
3954 static bool
BackendWeightAssignFunc(ConfigContext context,double newval,int index,int elevel)3955 BackendWeightAssignFunc(ConfigContext context, double newval, int index, int elevel)
3956 {
3957 	double		old_v = g_pool_config.backend_desc->backend_info[index].unnormalized_weight;
3958 
3959 	g_pool_config.backend_desc->backend_info[index].unnormalized_weight = newval;
3960 
3961 	/*
3962 	 * Log weight change event only when context is reloading of pgpool.conf
3963 	 * and weight is actually changed
3964 	 */
3965 	if (context == CFGCXT_RELOAD && old_v != newval)
3966 	{
3967 		ereport(LOG,
3968 				(errmsg("initializing pool configuration: backend weight for backend:%d changed from %f to %f", index, old_v, newval),
3969 				 errdetail("This change will be effective from next client session")));
3970 	}
3971 	return true;
3972 }
3973 
3974 
3975 static bool
BackendPortAssignFunc(ConfigContext context,int newval,int index,int elevel)3976 BackendPortAssignFunc(ConfigContext context, int newval, int index, int elevel)
3977 {
3978 	BACKEND_STATUS backend_status = g_pool_config.backend_desc->backend_info[index].backend_status;
3979 
3980 	if (context <= CFGCXT_INIT)
3981 	{
3982 		g_pool_config.backend_desc->backend_info[index].backend_port = newval;
3983 		g_pool_config.backend_desc->backend_info[index].backend_status = CON_CONNECT_WAIT;
3984 	}
3985 	else if (backend_status == CON_UNUSED)
3986 	{
3987 		g_pool_config.backend_desc->backend_info[index].backend_port = newval;
3988 		g_pool_config.backend_desc->backend_info[index].backend_status = CON_DOWN;
3989 	}
3990 	else
3991 	{
3992 		if (context != CFGCXT_RELOAD)
3993 			ereport(WARNING,
3994 					(errmsg("backend_port%d cannot be changed in context %d and backend status = %d", index, context, backend_status)));
3995 		return false;
3996 	}
3997 	return true;
3998 }
3999 
4000 
4001 static bool
BackendHostAssignFunc(ConfigContext context,char * newval,int index,int elevel)4002 BackendHostAssignFunc(ConfigContext context, char *newval, int index, int elevel)
4003 {
4004 	BACKEND_STATUS backend_status = g_pool_config.backend_desc->backend_info[index].backend_status;
4005 
4006 	if (context <= CFGCXT_INIT || backend_status == CON_UNUSED)
4007 	{
4008 		if (newval == NULL || strlen(newval) == 0)
4009 			g_pool_config.backend_desc->backend_info[index].backend_hostname[0] = '\0';
4010 		else
4011 			strlcpy(g_pool_config.backend_desc->backend_info[index].backend_hostname, newval, MAX_DB_HOST_NAMELEN - 1);
4012 		return true;
4013 	}
4014 	/* silent the warning in reload contxt */
4015 	if (context != CFGCXT_RELOAD)
4016 		ereport(WARNING,
4017 				(errmsg("backend_hostname%d cannot be changed in context %d and backend status = %d", index, context, backend_status)));
4018 	return false;
4019 }
4020 
4021 static bool
BackendDataDirAssignFunc(ConfigContext context,char * newval,int index,int elevel)4022 BackendDataDirAssignFunc(ConfigContext context, char *newval, int index, int elevel)
4023 {
4024 	BACKEND_STATUS backend_status = g_pool_config.backend_desc->backend_info[index].backend_status;
4025 
4026 	if (context <= CFGCXT_INIT || backend_status == CON_UNUSED || backend_status == CON_DOWN)
4027 	{
4028 		if (newval == NULL || strlen(newval) == 0)
4029 			g_pool_config.backend_desc->backend_info[index].backend_data_directory[0] = '\0';
4030 		else
4031 			strlcpy(g_pool_config.backend_desc->backend_info[index].backend_data_directory, newval, MAX_PATH_LENGTH - 1);
4032 		return true;
4033 	}
4034 	/* silent the warning in reload contxt */
4035 	if (context != CFGCXT_RELOAD)
4036 		ereport(WARNING,
4037 				(errmsg("backend_data_directory%d cannot be changed in context %d and backend status = %d", index, context, backend_status)));
4038 	return false;
4039 }
4040 
4041 static bool
BackendFlagsAssignFunc(ConfigContext context,char * newval,int index,int elevel)4042 BackendFlagsAssignFunc(ConfigContext context, char *newval, int index, int elevel)
4043 {
4044 
4045 	unsigned short flag;
4046 	int			i,
4047 				n;
4048 	bool		allow_to_failover_is_specified = false;
4049 	bool		disallow_to_failover_is_specified = false;
4050 	char	  **flags;
4051 
4052 	flag = g_pool_config.backend_desc->backend_info[index].flag;
4053 
4054 	flags = get_list_from_string(newval, "|", &n);
4055 
4056 	for (i = 0; i < n; i++)
4057 	{
4058 		int			k;
4059 
4060 		if (!strcmp(flags[i], "ALLOW_TO_FAILOVER"))
4061 		{
4062 			if (disallow_to_failover_is_specified)
4063 			{
4064 				for (k = i; k < n; k++)
4065 					pfree(flags[k]);
4066 				pfree(flags);
4067 				ereport(elevel,
4068 						(errmsg("invalid configuration for key \"backend_flag%d\"", index),
4069 						 errdetail("cannot set ALLOW_TO_FAILOVER and DISALLOW_TO_FAILOVER at the same time")));
4070 				return false;
4071 			}
4072 			flag &= ~POOL_FAILOVER;
4073 			allow_to_failover_is_specified = true;
4074 
4075 		}
4076 
4077 		else if (!strcmp(flags[i], "DISALLOW_TO_FAILOVER"))
4078 		{
4079 			if (allow_to_failover_is_specified)
4080 			{
4081 				for (k = i; k < n; k++)
4082 					pfree(flags[k]);
4083 				pfree(flags);
4084 
4085 				ereport(elevel,
4086 						(errmsg("invalid configuration for key \"backend_flag%d\"", index),
4087 						 errdetail("cannot set ALLOW_TO_FAILOVER and DISALLOW_TO_FAILOVER at the same time")));
4088 				return false;
4089 			}
4090 			flag |= POOL_FAILOVER;
4091 			disallow_to_failover_is_specified = true;
4092 		}
4093 
4094 		else if ((!strcmp(flags[i], "ALWAYS_PRIMARY")))
4095 		{
4096 			flag |= POOL_ALWAYS_PRIMARY;
4097 		}
4098 
4099 		else
4100 		{
4101 			ereport(elevel,
4102 					(errmsg("invalid configuration for key \"backend_flag%d\"", index),
4103 					 errdetail("unknown backend flag:%s", flags[i])));
4104 			for (k = i; k < n; k++)
4105 				pfree(flags[k]);
4106 			pfree(flags);
4107 			return false;
4108 		}
4109 		pfree(flags[i]);
4110 	}
4111 
4112 	g_pool_config.backend_desc->backend_info[index].flag = flag;
4113 	ereport(DEBUG1,
4114 			(errmsg("setting \"backend_flag%d\" flag: %04x ", index, flag)));
4115 	if (flags)
4116 		pfree(flags);
4117 	return true;
4118 }
4119 
4120 static bool
BackendAppNameAssignFunc(ConfigContext context,char * newval,int index,int elevel)4121 BackendAppNameAssignFunc(ConfigContext context, char *newval, int index, int elevel)
4122 {
4123 	BACKEND_STATUS backend_status = g_pool_config.backend_desc->backend_info[index].backend_status;
4124 
4125 	if (context <= CFGCXT_INIT || backend_status == CON_UNUSED || backend_status == CON_DOWN)
4126 	{
4127 		if (newval == NULL || strlen(newval) == 0)
4128 			g_pool_config.backend_desc->backend_info[index].backend_application_name[0] = '\0';
4129 		else
4130 			strlcpy(g_pool_config.backend_desc->backend_info[index].backend_application_name, newval, NAMEDATALEN - 1);
4131 		return true;
4132 	}
4133 	/* silent the warning in reload contxt */
4134 	if (context != CFGCXT_RELOAD)
4135 		ereport(WARNING,
4136 				(errmsg("backend_application_name%d cannot be changed in context %d and backend status = %d", index, context, backend_status)));
4137 	return false;
4138 }
4139 
4140 static bool
LogDestinationProcessFunc(char * newval,int elevel)4141 LogDestinationProcessFunc(char *newval, int elevel)
4142 {
4143 #ifndef POOL_PRIVATE
4144 	char	  **destinations;
4145 	int			n,
4146 				i;
4147 	int			log_destination = 0;
4148 
4149 	destinations = get_list_from_string(newval, ",", &n);
4150 	if (!destinations || n < 0)
4151 	{
4152 		if (destinations)
4153 			pfree(destinations);
4154 
4155 		ereport(elevel,
4156 				(errmsg("invalid value \"%s\" for log_destination", newval)));
4157 		return false;
4158 	}
4159 	for (i = 0; i < n; i++)
4160 	{
4161 		if (!strcmp(destinations[i], "syslog"))
4162 		{
4163 			log_destination |= LOG_DESTINATION_SYSLOG;
4164 		}
4165 		else if (!strcmp(destinations[i], "stderr"))
4166 		{
4167 			log_destination |= LOG_DESTINATION_STDERR;
4168 		}
4169 		else
4170 		{
4171 			int			k;
4172 
4173 			ereport(elevel,
4174 					(errmsg("invalid configuration for \"log_destination\""),
4175 					 errdetail("unknown destination :%s", destinations[i])));
4176 			for (k = i; k < n; k++)
4177 				pfree(destinations[k]);
4178 			pfree(destinations);
4179 			return false;
4180 		}
4181 		pfree(destinations[i]);
4182 	}
4183 	if (g_pool_config.log_destination & LOG_DESTINATION_SYSLOG)
4184 	{
4185 		if (!(log_destination & LOG_DESTINATION_SYSLOG))
4186 			closelog();
4187 	}
4188 	g_pool_config.log_destination = log_destination;
4189 	pfree(destinations);
4190 #endif
4191 	return true;
4192 }
4193 
4194 static bool
SyslogFacilityProcessFunc(int newval,int elevel)4195 SyslogFacilityProcessFunc(int newval, int elevel)
4196 {
4197 #ifndef POOL_PRIVATE
4198 #ifdef HAVE_SYSLOG
4199 	/* set syslog parameters */
4200 	set_syslog_parameters(g_pool_config.syslog_ident ? g_pool_config.syslog_ident : "pgpool",
4201 						  g_pool_config.syslog_facility);
4202 #endif
4203 #endif
4204 	return true;
4205 }
4206 
4207 static bool
SyslogIdentProcessFunc(char * newval,int elevel)4208 SyslogIdentProcessFunc(char *newval, int elevel)
4209 {
4210 #ifndef POOL_PRIVATE
4211 #ifdef HAVE_SYSLOG
4212 	/* set syslog parameters */
4213 	set_syslog_parameters(g_pool_config.syslog_ident ? g_pool_config.syslog_ident : "pgpool",
4214 						  g_pool_config.syslog_facility);
4215 #endif
4216 #endif
4217 	return true;
4218 }
4219 
4220 static const char *
IntValueShowFunc(int value)4221 IntValueShowFunc(int value)
4222 {
4223 	static char buffer[10];
4224 
4225 	snprintf(buffer, sizeof(buffer), "%d", value);
4226 	return buffer;
4227 }
4228 
4229 static const char *
BackendWeightShowFunc(int index)4230 BackendWeightShowFunc(int index)
4231 {
4232 	return IntValueShowFunc(g_pool_config.backend_desc->backend_info[index].unnormalized_weight);
4233 }
4234 
4235 static const char *
BackendPortShowFunc(int index)4236 BackendPortShowFunc(int index)
4237 {
4238 	return IntValueShowFunc(g_pool_config.backend_desc->backend_info[index].backend_port);
4239 }
4240 
4241 static const char *
BackendHostShowFunc(int index)4242 BackendHostShowFunc(int index)
4243 {
4244 	return g_pool_config.backend_desc->backend_info[index].backend_hostname;
4245 }
4246 
4247 static const char *
BackendDataDirShowFunc(int index)4248 BackendDataDirShowFunc(int index)
4249 {
4250 	return g_pool_config.backend_desc->backend_info[index].backend_data_directory;
4251 }
4252 
4253 static const char *
BackendFlagsShowFunc(int index)4254 BackendFlagsShowFunc(int index)
4255 {
4256 	static char buffer[1024];
4257 
4258 	unsigned short flag = g_pool_config.backend_desc->backend_info[index].flag;
4259 
4260 	*buffer = '\0';
4261 
4262 	if (POOL_ALLOW_TO_FAILOVER(flag))
4263 		snprintf(buffer, sizeof(buffer), "ALLOW_TO_FAILOVER");
4264 	else if (POOL_DISALLOW_TO_FAILOVER(flag))
4265 		snprintf(buffer, sizeof(buffer), "DISALLOW_TO_FAILOVER");
4266 
4267 	if (POOL_ALWAYS_PRIMARY & flag)
4268 	{
4269 		if (*buffer == '\0')
4270 			snprintf(buffer, sizeof(buffer), "ALWAYS_PRIMARY");
4271 		else
4272 			snprintf(buffer+strlen(buffer), sizeof(buffer), "|ALWAYS_PRIMARY");
4273 	}
4274 	return buffer;
4275 }
4276 
4277 static const char *
BackendAppNameShowFunc(int index)4278 BackendAppNameShowFunc(int index)
4279 {
4280 	return g_pool_config.backend_desc->backend_info[index].backend_application_name;
4281 }
4282 
4283 static bool
BackendSlotEmptyCheckFunc(int index)4284 BackendSlotEmptyCheckFunc(int index)
4285 {
4286 	return (g_pool_config.backend_desc->backend_info[index].backend_port == 0);
4287 }
4288 
4289 static bool
WdSlotEmptyCheckFunc(int index)4290 WdSlotEmptyCheckFunc(int index)
4291 {
4292 	return (g_pool_config.wd_nodes.wd_node_info[index].pgpool_port == 0);
4293 }
4294 
4295 static bool
WdIFSlotEmptyCheckFunc(int index)4296 WdIFSlotEmptyCheckFunc(int index)
4297 {
4298 
4299 	return (index >= g_pool_config.num_hb_dest_if);
4300 }
4301 
4302 static const char *
OtherPPHostShowFunc(int index)4303 OtherPPHostShowFunc(int index)
4304 {
4305 	return g_pool_config.wd_nodes.wd_node_info[index].hostname;
4306 }
4307 
4308 static const char *
OtherPPPortShowFunc(int index)4309 OtherPPPortShowFunc(int index)
4310 {
4311 	return IntValueShowFunc(g_pool_config.wd_nodes.wd_node_info[index].pgpool_port);
4312 }
4313 
4314 static const char *
OtherWDPortShowFunc(int index)4315 OtherWDPortShowFunc(int index)
4316 {
4317 	return IntValueShowFunc(g_pool_config.wd_nodes.wd_node_info[index].wd_port);
4318 }
4319 
4320 static const char *
HBDeviceShowFunc(int index)4321 HBDeviceShowFunc(int index)
4322 {
4323 	return g_pool_config.hb_ifs[index].if_name;
4324 }
4325 
4326 static const char *
HBHostnameShowFunc(int index)4327 HBHostnameShowFunc(int index)
4328 {
4329 	return g_pool_config.hb_ifs[index].addr;
4330 }
4331 
4332 static const char *
HBDestinationPortShowFunc(int index)4333 HBDestinationPortShowFunc(int index)
4334 {
4335 	return IntValueShowFunc(g_pool_config.hb_ifs[index].dest_port);
4336 }
4337 
4338 static const char *
HealthCheckPeriodShowFunc(int index)4339 HealthCheckPeriodShowFunc(int index)
4340 {
4341 	return IntValueShowFunc(g_pool_config.health_check_params[index].health_check_period);
4342 }
4343 static const char *
HealthCheckTimeOutShowFunc(int index)4344 HealthCheckTimeOutShowFunc(int index)
4345 {
4346 	return IntValueShowFunc(g_pool_config.health_check_params[index].health_check_timeout);
4347 }
4348 static const char *
HealthCheckMaxRetriesShowFunc(int index)4349 HealthCheckMaxRetriesShowFunc(int index)
4350 {
4351 	return IntValueShowFunc(g_pool_config.health_check_params[index].health_check_max_retries);
4352 }
4353 static const char *
HealthCheckRetryDelayShowFunc(int index)4354 HealthCheckRetryDelayShowFunc(int index)
4355 {
4356 	return IntValueShowFunc(g_pool_config.health_check_params[index].health_check_retry_delay);
4357 }
4358 static const char *
HealthCheckConnectTimeOutShowFunc(int index)4359 HealthCheckConnectTimeOutShowFunc(int index)
4360 {
4361 	return IntValueShowFunc(g_pool_config.health_check_params[index].connect_timeout);
4362 }
4363 static const char *
HealthCheckUserShowFunc(int index)4364 HealthCheckUserShowFunc(int index)
4365 {
4366 	return g_pool_config.health_check_params[index].health_check_user;
4367 }
4368 static const char *
HealthCheckPasswordShowFunc(int index)4369 HealthCheckPasswordShowFunc(int index)
4370 {
4371 	return g_pool_config.health_check_params[index].health_check_password;
4372 }
4373 static const char *
HealthCheckDatabaseShowFunc(int index)4374 HealthCheckDatabaseShowFunc(int index)
4375 {
4376 	return g_pool_config.health_check_params[index].health_check_database;
4377 }
4378 
4379 
4380 /* Health check configuration assign functions */
4381 
4382 /*health_check_period*/
4383 static bool
HealthCheckPeriodAssignFunc(ConfigContext context,int newval,int index,int elevel)4384 HealthCheckPeriodAssignFunc(ConfigContext context, int newval, int index, int elevel)
4385 {
4386 	g_pool_config.health_check_params[index].health_check_period = newval;
4387 	return true;
4388 }
4389 
4390 /*health_check_timeout*/
4391 static bool
HealthCheckTimeOutAssignFunc(ConfigContext context,int newval,int index,int elevel)4392 HealthCheckTimeOutAssignFunc(ConfigContext context, int newval, int index, int elevel)
4393 {
4394 	g_pool_config.health_check_params[index].health_check_timeout = newval;
4395 	return true;
4396 }
4397 
4398 /*health_check_max_retries*/
4399 static bool
HealthCheckMaxRetriesAssignFunc(ConfigContext context,int newval,int index,int elevel)4400 HealthCheckMaxRetriesAssignFunc(ConfigContext context, int newval, int index, int elevel)
4401 {
4402 	g_pool_config.health_check_params[index].health_check_max_retries = newval;
4403 	return true;
4404 }
4405 
4406 /*health_check_retry_delay*/
4407 static bool
HealthCheckRetryDelayAssignFunc(ConfigContext context,int newval,int index,int elevel)4408 HealthCheckRetryDelayAssignFunc(ConfigContext context, int newval, int index, int elevel)
4409 {
4410 	g_pool_config.health_check_params[index].health_check_retry_delay = newval;
4411 	return true;
4412 }
4413 
4414 /*connect_timeout*/
4415 static bool
HealthCheckConnectTimeOutAssignFunc(ConfigContext context,int newval,int index,int elevel)4416 HealthCheckConnectTimeOutAssignFunc(ConfigContext context, int newval, int index, int elevel)
4417 {
4418 	g_pool_config.health_check_params[index].connect_timeout = newval;
4419 	return true;
4420 }
4421 
4422 static bool
HealthCheckUserAssignFunc(ConfigContext context,char * newval,int index,int elevel)4423 HealthCheckUserAssignFunc(ConfigContext context, char *newval, int index, int elevel)
4424 {
4425 	if (g_pool_config.health_check_params[index].health_check_user)
4426 		pfree(g_pool_config.health_check_params[index].health_check_user);
4427 	if (newval)
4428 		g_pool_config.health_check_params[index].health_check_user = pstrdup(newval);
4429 	else
4430 		g_pool_config.health_check_params[index].health_check_user = NULL;
4431 	return true;
4432 }
4433 
4434 static bool
HealthCheckPasswordAssignFunc(ConfigContext context,char * newval,int index,int elevel)4435 HealthCheckPasswordAssignFunc(ConfigContext context, char *newval, int index, int elevel)
4436 {
4437 	if (g_pool_config.health_check_params[index].health_check_password)
4438 		pfree(g_pool_config.health_check_params[index].health_check_password);
4439 	if (newval)
4440 		g_pool_config.health_check_params[index].health_check_password = pstrdup(newval);
4441 	else
4442 		g_pool_config.health_check_params[index].health_check_password = newval;
4443 	return true;
4444 }
4445 
4446 static bool
HealthCheckDatabaseAssignFunc(ConfigContext context,char * newval,int index,int elevel)4447 HealthCheckDatabaseAssignFunc(ConfigContext context, char *newval, int index, int elevel)
4448 {
4449 	if (g_pool_config.health_check_params[index].health_check_database)
4450 		pfree(g_pool_config.health_check_params[index].health_check_database);
4451 
4452 	if (newval)
4453 		g_pool_config.health_check_params[index].health_check_database = pstrdup(newval);
4454 	else
4455 		g_pool_config.health_check_params[index].health_check_database = newval;
4456 	return true;
4457 }
4458 
4459 /* Watchdog hostname and heartbeat hostname assign functions */
4460 /* hostname */
4461 static bool
OtherPPHostAssignFunc(ConfigContext context,char * newval,int index,int elevel)4462 OtherPPHostAssignFunc(ConfigContext context, char *newval, int index, int elevel)
4463 {
4464 	if (newval == NULL || strlen(newval) == 0)
4465 		g_pool_config.wd_nodes.wd_node_info[index].hostname[0] = '\0';
4466 	else
4467 		strlcpy(g_pool_config.wd_nodes.wd_node_info[index].hostname, newval, MAX_DB_HOST_NAMELEN - 1);
4468 	return true;
4469 }
4470 
4471 /* pgpool_port */
4472 static bool
OtherPPPortAssignFunc(ConfigContext context,int newval,int index,int elevel)4473 OtherPPPortAssignFunc(ConfigContext context, int newval, int index, int elevel)
4474 {
4475 	g_pool_config.wd_nodes.wd_node_info[index].pgpool_port = newval;
4476 	return true;
4477 }
4478 
4479 /* wd_port */
4480 static bool
OtherWDPortAssignFunc(ConfigContext context,int newval,int index,int elevel)4481 OtherWDPortAssignFunc(ConfigContext context, int newval, int index, int elevel)
4482 {
4483 	g_pool_config.wd_nodes.wd_node_info[index].wd_port = newval;
4484 	return true;
4485 }
4486 
4487 /*heartbeat_device*/
4488 static bool
HBDeviceAssignFunc(ConfigContext context,char * newval,int index,int elevel)4489 HBDeviceAssignFunc(ConfigContext context, char *newval, int index, int elevel)
4490 {
4491 	if (newval == NULL || strlen(newval) == 0)
4492 		g_pool_config.hb_ifs[index].if_name[0] = '\0';
4493 	else
4494 		strlcpy(g_pool_config.hb_ifs[index].if_name, newval, WD_MAX_IF_NAME_LEN - 1);
4495 	return true;
4496 }
4497 
4498 /*heartbeat_hostname*/
4499 static bool
HBHostnameAssignFunc(ConfigContext context,char * newval,int index,int elevel)4500 HBHostnameAssignFunc(ConfigContext context, char *newval, int index, int elevel)
4501 {
4502 	if (newval == NULL || strlen(newval) == 0)
4503 		g_pool_config.hb_ifs[index].addr[0] = '\0';
4504 	else
4505 		strlcpy(g_pool_config.hb_ifs[index].addr, newval, WD_MAX_HOST_NAMELEN - 1);
4506 	return true;
4507 }
4508 
4509 /*heartbeat_port*/
4510 static bool
HBDestinationPortAssignFunc(ConfigContext context,int newval,int index,int elevel)4511 HBDestinationPortAssignFunc(ConfigContext context, int newval, int index, int elevel)
4512 {
4513 	g_pool_config.hb_ifs[index].dest_port = newval;
4514 	return true;
4515 }
4516 
4517 /*
4518  * Throws warning for if someone uses the removed fail_over_on_backend
4519  * configuration parameter
4520  */
4521 static bool
FailOverOnBackendErrorAssignMessage(ConfigContext scontext,bool newval,int elevel)4522 FailOverOnBackendErrorAssignMessage(ConfigContext scontext, bool newval, int elevel)
4523 {
4524 	if (scontext != CFGCXT_BOOT)
4525 		ereport(WARNING,
4526 				(errmsg("fail_over_on_backend_error is changed to failover_on_backend_error"),
4527 				 errdetail("setting failover_on_backend_error has no effect"),
4528 				 errhint("use failover_on_backend_error instead")));
4529 	return true;
4530 }
4531 /*
4532  * Check DB node spec. node spec should be either "primary", "standby" or
4533  * numeric DB node id.
4534  */
4535 static bool
check_redirect_node_spec(char * node_spec)4536 check_redirect_node_spec(char *node_spec)
4537 {
4538 	int			len = strlen(node_spec);
4539 	int			i;
4540 	int64		val;
4541 
4542 	if (len <= 0)
4543 		return false;
4544 
4545 	if (strcasecmp("primary", node_spec) == 0)
4546 	{
4547 		return true;
4548 	}
4549 
4550 	if (strcasecmp("standby", node_spec) == 0)
4551 	{
4552 		return true;
4553 	}
4554 
4555 	for (i = 0; i < len; i++)
4556 	{
4557 		if (!isdigit((int) node_spec[i]))
4558 			return false;
4559 	}
4560 
4561 	val = pool_atoi64(node_spec);
4562 
4563 	if (val >= 0 && val < MAX_NUM_BACKENDS)
4564 		return true;
4565 
4566 	return false;
4567 }
4568 
4569 static bool
config_post_processor(ConfigContext context,int elevel)4570 config_post_processor(ConfigContext context, int elevel)
4571 {
4572 	double		 total_weight = 0.0;
4573 	sig_atomic_t local_num_backends = 0;
4574 	int			 i;
4575 
4576 	/* read from pgpool_node_id */
4577 	SetPgpoolNodeId(elevel);
4578 
4579 	if (context == CFGCXT_BOOT)
4580 	{
4581 		char		localhostname[128];
4582 		int			res = gethostname(localhostname, sizeof(localhostname));
4583 
4584 		if (res != 0)
4585 		{
4586 			ereport(WARNING,
4587 					(errmsg("initializing pool configuration"),
4588 					 errdetail("failed to get the local hostname")));
4589 			return false;
4590 		}
4591 		strcpy(g_pool_config.wd_nodes.wd_node_info[g_pool_config.pgpool_node_id].hostname, localhostname);
4592 		return true;
4593 	}
4594 	for (i = 0; i < MAX_CONNECTION_SLOTS; i++)
4595 	{
4596 		BackendInfo *backend_info = &g_pool_config.backend_desc->backend_info[i];
4597 
4598 		/* port number == 0 indicates that this server is out of use */
4599 		if (backend_info->backend_port == 0)
4600 		{
4601 			*backend_info->backend_hostname = '\0';
4602 			backend_info->backend_status = CON_UNUSED;
4603 			backend_info->backend_weight = 0.0;
4604 		}
4605 		else
4606 		{
4607 			total_weight += backend_info->unnormalized_weight;
4608 			local_num_backends = i + 1;
4609 
4610 			/* initialize backend_hostname with a default socket path if empty */
4611 			if (*(backend_info->backend_hostname) == '\0')
4612 			{
4613 				ereport(LOG,
4614 						(errmsg("initializing pool configuration"),
4615 						 errdetail("empty backend_hostname%d, use PostgreSQL's default unix socket path (%s)",
4616 								   i, DEFAULT_SOCKET_DIR)));
4617 				strlcpy(backend_info->backend_hostname, DEFAULT_SOCKET_DIR, MAX_DB_HOST_NAMELEN);
4618 			}
4619 		}
4620 	}
4621 
4622 	if (local_num_backends != pool_config->backend_desc->num_backends)
4623 		pool_config->backend_desc->num_backends = local_num_backends;
4624 
4625 	ereport(DEBUG1,
4626 			(errmsg("initializing pool configuration"),
4627 			 errdetail("num_backends: %d total_weight: %f",
4628 					   pool_config->backend_desc->num_backends, total_weight)));
4629 
4630 	/*
4631 	 * Normalize load balancing weights. What we are doing here is, assign 0
4632 	 * to RAND_MAX to each backend's weight according to the value weightN.
4633 	 * For example, if two backends are assigned 1.0, then each backend will
4634 	 * get RAND_MAX/2 normalized weight.
4635 	 */
4636 	for (i = 0; i < MAX_CONNECTION_SLOTS; i++)
4637 	{
4638 		BackendInfo *backend_info = &g_pool_config.backend_desc->backend_info[i];
4639 
4640 		if (backend_info->backend_port != 0)
4641 		{
4642 			backend_info->backend_weight =
4643 				(RAND_MAX) * backend_info->unnormalized_weight / total_weight;
4644 
4645 			ereport(DEBUG1,
4646 					(errmsg("initializing pool configuration"),
4647 					 errdetail("backend %d weight: %f flag: %04x", i, backend_info->backend_weight, backend_info->flag)));
4648 		}
4649 	}
4650 
4651 	/* Set the number of configured Watchdog nodes */
4652 	g_pool_config.wd_nodes.num_wd = 0;
4653 
4654 	if (g_pool_config.use_watchdog)
4655 	{
4656 		for (i = 0; i < MAX_WATCHDOG_NUM; i++)
4657 		{
4658 			WdNodeInfo *wdNode = &g_pool_config.wd_nodes.wd_node_info[i];
4659 
4660 			if (i == g_pool_config.pgpool_node_id && wdNode->wd_port <= 0)
4661 			{
4662 				ereport(elevel,
4663 						(errmsg("invalid watchdog configuration"),
4664 						 errdetail("no watchdog configuration for local pgpool node, pgpool node id: %d ", g_pool_config.pgpool_node_id)));
4665 				return false;
4666 			}
4667 
4668 			if (wdNode->wd_port > 0)
4669 				g_pool_config.wd_nodes.num_wd = i + 1;
4670 		}
4671 	}
4672 
4673 	/* Set configured heartbeat destination interfaces */
4674 	SetHBDestIfFunc(elevel);
4675 
4676 	if (strcmp(pool_config->recovery_1st_stage_command, "") ||
4677 		strcmp(pool_config->recovery_2nd_stage_command, ""))
4678 	{
4679 		for (i = 0; i < MAX_CONNECTION_SLOTS; i++)
4680 		{
4681 			BackendInfo *backend_info = &g_pool_config.backend_desc->backend_info[i];
4682 
4683 			if (backend_info->backend_port != 0 &&
4684 				!strcmp(backend_info->backend_data_directory, ""))
4685 			{
4686 				ereport(elevel,
4687 						(errmsg("invalid configuration, recovery_1st_stage_command and recovery_2nd_stage_command requires backend_data_directory to be set")));
4688 				return false;
4689 			}
4690 		}
4691 	}
4692 
4693 	/*
4694 	 * Quarantine state in native replication mode is dangerous and it can
4695 	 * potentially cause data inconsistency.
4696 	 * So as per the discussions, we agreed on disallowing setting
4697 	 * failover_when_quorum_exists in native replication mode
4698 	 */
4699 
4700 	if (pool_config->failover_when_quorum_exists && pool_config->replication_mode)
4701 	{
4702 		pool_config->failover_when_quorum_exists = false;
4703 		ereport(elevel,
4704 				(errmsg("invalid configuration, failover_when_quorum_exists is not allowed in native replication mode")));
4705 		return false;
4706 	}
4707 	return true;
4708 }
4709 
4710 static bool
MakeDMLAdaptiveObjectRelationList(char * newval,int elevel)4711 MakeDMLAdaptiveObjectRelationList(char *newval, int elevel)
4712 {
4713 	int i;
4714 	int elements_count = 0;
4715 	char **rawList = get_list_from_string(newval, ",", &elements_count);
4716 
4717 	if (rawList == NULL || elements_count == 0)
4718 	{
4719 		pool_config->parsed_dml_adaptive_object_relationship_list = NULL;
4720 		return true;
4721 	}
4722 	pool_config->parsed_dml_adaptive_object_relationship_list = palloc(sizeof(DBObjectRelation) * (elements_count + 1));
4723 
4724 	for (i = 0; i < elements_count; i++)
4725 	{
4726 		char *kvstr = rawList[i];
4727 		char *left_token = strtok(kvstr, ":");
4728 		char *right_token = strtok(NULL, ":");
4729 		DBObjectTypes object_type;
4730 
4731 		ereport(DEBUG5,
4732 				(errmsg("dml_adaptive_init"),
4733 					errdetail("%s -- left_token[%s] right_token[%s]", kvstr, left_token, right_token)));
4734 
4735 		pool_config->parsed_dml_adaptive_object_relationship_list[i].left_token.name =
4736 																	getParsedToken(left_token, &object_type);
4737 		pool_config->parsed_dml_adaptive_object_relationship_list[i].left_token.object_type = object_type;
4738 
4739 		pool_config->parsed_dml_adaptive_object_relationship_list[i].right_token.name =
4740 																	getParsedToken(right_token,&object_type);
4741 		pool_config->parsed_dml_adaptive_object_relationship_list[i].right_token.object_type = object_type;
4742 		pfree(kvstr);
4743 	}
4744 	pool_config->parsed_dml_adaptive_object_relationship_list[i].left_token.name = NULL;
4745 	pool_config->parsed_dml_adaptive_object_relationship_list[i].left_token.object_type = OBJECT_TYPE_UNKNOWN;
4746 	pool_config->parsed_dml_adaptive_object_relationship_list[i].right_token.name = NULL;
4747 	pool_config->parsed_dml_adaptive_object_relationship_list[i].right_token.object_type = OBJECT_TYPE_UNKNOWN;
4748 
4749 	pfree(rawList);
4750 	return true;
4751 }
4752 
4753 /*
4754  * Identify the object type for dml adaptive object
4755  * the function is very primitive and just looks for token
4756  * ending with ().
4757  * We also remove the trailing spaces from the function type token
4758  * and return the palloc'd copy of token in new_token
4759  */
4760 static char*
getParsedToken(char * token,DBObjectTypes * object_type)4761 getParsedToken(char *token, DBObjectTypes *object_type)
4762 {
4763 	int len;
4764 	*object_type = OBJECT_TYPE_UNKNOWN;
4765 
4766 	if (!token)
4767 		return NULL;
4768 
4769 	len = strlen(token);
4770 	if (len > strlen("*()"))
4771 	{
4772 		int namelen = len - 2;
4773 		/* check if token ends with () */
4774 		if (strcmp(token + namelen,"()") == 0)
4775 		{
4776 			/*
4777 			 * Remove the Parentheses from end of
4778 			 * token name
4779 			 */
4780 			char *new_token;
4781 			int new_len = strlen(token) - 2;
4782 			new_token = palloc(new_len + 1);
4783 			strncpy(new_token,token,new_len);
4784 			new_token[new_len] = '\0';
4785 			*object_type = OBJECT_TYPE_FUNCTION;
4786 			return new_token;
4787 		}
4788 	}
4789 	*object_type = OBJECT_TYPE_RELATION;
4790 	return pstrdup(token);
4791 }
4792 
4793 static bool
MakeAppRedirectListRegex(char * newval,int elevel)4794 MakeAppRedirectListRegex(char *newval, int elevel)
4795 {
4796 	/* TODO Deal with the memory */
4797 	int			i;
4798 	Left_right_tokens *lrtokens;
4799 
4800 	if (newval == NULL)
4801 	{
4802 		pool_config->redirect_app_names = NULL;
4803 		pool_config->app_name_redirect_tokens = NULL;
4804 		return true;
4805 	}
4806 
4807 	lrtokens = create_lrtoken_array();
4808 	extract_string_tokens2(newval, ",", ':', lrtokens);
4809 
4810 	pool_config->redirect_app_names = create_regex_array();
4811 	pool_config->app_name_redirect_tokens = lrtokens;
4812 
4813 	for (i = 0; i < lrtokens->pos; i++)
4814 	{
4815 		if (!check_redirect_node_spec(lrtokens->token[i].right_token))
4816 		{
4817 			ereport(elevel,
4818 					(errmsg("invalid configuration for key \"app_name_redirect_preference_list\""),
4819 					 errdetail("wrong redirect db node spec: \"%s\"", lrtokens->token[i].right_token)));
4820 			return false;
4821 		}
4822 
4823 
4824 		if (*(lrtokens->token[i].left_token) == '\0' ||
4825 			add_regex_array(pool_config->redirect_app_names, lrtokens->token[i].left_token))
4826 		{
4827 			ereport(elevel,
4828 					(errmsg("invalid configuration for key \"app_name_redirect_preference_list\""),
4829 					 errdetail("wrong redirect app name regular expression: \"%s\"", lrtokens->token[i].left_token)));
4830 			return false;
4831 		}
4832 	}
4833 
4834 	return true;
4835 }
4836 
4837 static bool
MakeDBRedirectListRegex(char * newval,int elevel)4838 MakeDBRedirectListRegex(char *newval, int elevel)
4839 {
4840 	/* TODO Deal with the memory */
4841 	int			i;
4842 	Left_right_tokens *lrtokens;
4843 
4844 	if (newval == NULL)
4845 	{
4846 		pool_config->redirect_dbnames = NULL;
4847 		pool_config->db_redirect_tokens = NULL;
4848 		return true;
4849 	}
4850 
4851 	lrtokens = create_lrtoken_array();
4852 	extract_string_tokens2(newval, ",", ':', lrtokens);
4853 
4854 	pool_config->redirect_dbnames = create_regex_array();
4855 	pool_config->db_redirect_tokens = lrtokens;
4856 
4857 	for (i = 0; i < lrtokens->pos; i++)
4858 	{
4859 		if (!check_redirect_node_spec(lrtokens->token[i].right_token))
4860 		{
4861 			ereport(elevel,
4862 					(errmsg("invalid configuration for key \"database_redirect_preference_list\""),
4863 					 errdetail("wrong redirect db node spec: \"%s\"", lrtokens->token[i].right_token)));
4864 			return false;
4865 		}
4866 
4867 		if (*(lrtokens->token[i].left_token) == '\0' ||
4868 			add_regex_array(pool_config->redirect_dbnames, lrtokens->token[i].left_token))
4869 		{
4870 			ereport(elevel,
4871 					(errmsg("invalid configuration for key \"database_redirect_preference_list\""),
4872 					 errdetail("wrong redirect dbname regular expression: \"%s\"", lrtokens->token[i].left_token)));
4873 			return false;
4874 		}
4875 	}
4876 	return true;
4877 }
4878 
4879 /* Read the pgpool_node_id file */
4880 static bool
SetPgpoolNodeId(int elevel)4881 SetPgpoolNodeId(int elevel)
4882 {
4883 	char		pgpool_node_id_file[POOLMAXPATHLEN + 1];
4884 	FILE		*fd;
4885 	int         length;
4886 	int         i;
4887 
4888 	if (g_pool_config.use_watchdog)
4889 	{
4890 		snprintf(pgpool_node_id_file, sizeof(pgpool_node_id_file), "%s/%s", config_file_dir, NODE_ID_FILE_NAME);
4891 
4892 #define MAXLINE 10
4893 		char        readbuf[MAXLINE];
4894 
4895 		fd = fopen(pgpool_node_id_file, "r");
4896 		if (!fd)
4897 		{
4898 			ereport(elevel,
4899 					(errmsg("Pgpool node id file %s does not exist", pgpool_node_id_file),
4900 					 errdetail("If watchdog is enable, pgpool_node_id file is required")));
4901 			return false;
4902 		}
4903 
4904 		readbuf[MAXLINE - 1] = '\0';
4905 		if (fgets(readbuf, MAXLINE - 1, fd) == 0)
4906 		{
4907 			ereport(elevel,
4908 					(errmsg("pgpool_node_id file is empty"),
4909 					 errdetail("If watchdog is enable, we need to specify pgpool node id in %s file", pgpool_node_id_file)));
4910 			fclose(fd);
4911 			return false;
4912 		}
4913 
4914 		length = strlen(readbuf);
4915 		if (length > 0 && readbuf[length - 1] == '\n')
4916 		{
4917 			readbuf[length - 1] = '\0';
4918 
4919 			length = strlen(readbuf);
4920 			if (length <= 0)
4921 			{
4922 				ereport(elevel,
4923 						(errmsg("pgpool_node_id file is empty"),
4924 						 errdetail("If watchdog is enable, we need to specify pgpool node id in %s file", pgpool_node_id_file)));
4925 				fclose(fd);
4926 				return false;
4927 			}
4928 		}
4929 
4930 		for (i = 0; i < length; i++)
4931 		{
4932 			if (!isdigit((int) readbuf[i]))
4933 			{
4934 				ereport(elevel,
4935 						(errmsg("pgpool_node_id is not a numeric value"),
4936 						 errdetail("Please specify a numeric value in %s file", pgpool_node_id_file)));
4937 				fclose(fd);
4938 				return false;
4939 			}
4940 		}
4941 
4942 		g_pool_config.pgpool_node_id = atoi(readbuf);
4943 
4944 		if (g_pool_config.pgpool_node_id < 0 || g_pool_config.pgpool_node_id > MAX_WATCHDOG_NUM)
4945 		{
4946 			ereport(elevel,
4947 					(errmsg("Invalid pgpool node id \"%d\", must be between 0 and %d",
4948 							g_pool_config.pgpool_node_id, MAX_WATCHDOG_NUM)));
4949 			fclose(fd);
4950 			return false;
4951 		}
4952 		else
4953 		{
4954 			ereport(DEBUG1,
4955 					(errmsg("read pgpool node id file %s", pgpool_node_id_file),
4956 					 errdetail("pgpool node id: %s", readbuf)));
4957 		}
4958 
4959 		fclose(fd);
4960 	}
4961 
4962 	return true;
4963 }
4964 
4965 /* Set configured heartbeat destination interfaces */
4966 static bool
SetHBDestIfFunc(int elevel)4967 SetHBDestIfFunc(int elevel)
4968 {
4969 	int		idx = 0;
4970 	char	**addrs;
4971 	char	**if_names;
4972 	int		i, j,
4973 			n_addr,
4974 			n_if_name;
4975 
4976 	g_pool_config.num_hb_dest_if = 0;
4977 
4978 	if (g_pool_config.wd_lifecheck_method != LIFECHECK_BY_HB)
4979 	{
4980 		return true;
4981 	}
4982 
4983 	/*
4984 	 * g_pool_config.hb_ifs is the information for sending/receiving heartbeat
4985 	 * for all nodes specied in pgpool.conf.
4986 	 * If it is local pgpool node information, set dest_port to g_pool_config.wd_heartbeat_port
4987 	 * and ignore addr and if_name.
4988 	 * g_pool_config.hb_dest_if is the heartbeat destination information.
4989 	 */
4990 	for (i = 0; i < WD_MAX_IF_NUM; i++)
4991 	{
4992 		if (g_pool_config.hb_ifs[i].dest_port > 0)
4993 		{
4994 			/* Ignore local pgpool node */
4995 			if (i == g_pool_config.pgpool_node_id)
4996 			{
4997 				g_pool_config.wd_heartbeat_port = g_pool_config.hb_ifs[i].dest_port;
4998 				continue;
4999 			}
5000 
5001 			WdHbIf *hbNodeInfo = &g_pool_config.hb_ifs[i];
5002 
5003 			addrs = get_list_from_string(hbNodeInfo->addr, ";", &n_addr);
5004 			if_names = get_list_from_string(hbNodeInfo->if_name, ";", &n_if_name);
5005 
5006 			if (!addrs || n_addr < 0)
5007 			{
5008 				g_pool_config.hb_dest_if[idx].addr[0] = '\0';
5009 
5010 				if (addrs)
5011 					pfree(addrs);
5012 				if (if_names)
5013 					pfree(if_names);
5014 
5015 				ereport(elevel,
5016 						(errmsg("invalid watchdog configuration"),
5017 						 errdetail("heartbeat_hostname%d is not defined", i)));
5018 
5019 				return false;
5020 			}
5021 
5022 			for (j = 0; j < n_addr; j++)
5023 			{
5024 				strlcpy(g_pool_config.hb_dest_if[idx].addr, addrs[j], WD_MAX_HOST_NAMELEN - 1);
5025 				g_pool_config.hb_dest_if[idx].dest_port = hbNodeInfo->dest_port;
5026 				if (n_if_name > j + 1)
5027 				{
5028 					strlcpy(g_pool_config.hb_dest_if[idx].if_name, if_names[j], WD_MAX_IF_NAME_LEN - 1);
5029 					pfree(if_names[j]);
5030 				}
5031 				else
5032 					g_pool_config.hb_dest_if[idx].if_name[0] = '\0';
5033 
5034 				g_pool_config.num_hb_dest_if = idx + 1;
5035 				idx++;
5036 				pfree(addrs[j]);
5037 			}
5038 
5039 			if (addrs)
5040 				pfree(addrs);
5041 			if (if_names)
5042 				pfree(if_names);
5043 		}
5044 	}
5045 	return true;
5046 }
5047 
5048 static struct config_generic *
get_index_free_record_if_any(struct config_generic * record)5049 get_index_free_record_if_any(struct config_generic *record)
5050 {
5051 	struct config_generic *ret = NULL;
5052 
5053 	switch (record->vartype)
5054 	{
5055 		case CONFIG_VAR_TYPE_INT_ARRAY:
5056 			{
5057 				struct config_int_array *conf = (struct config_int_array *) record;
5058 
5059 				if (conf->config_no_index.gen.name)
5060 					ret = (struct config_generic *) &conf->config_no_index;
5061 			}
5062 			break;
5063 
5064 		case CONFIG_VAR_TYPE_DOUBLE_ARRAY:
5065 			{
5066 				struct config_double_array *conf = (struct config_double_array *) record;
5067 
5068 				if (conf->config_no_index.gen.name)
5069 					ret = (struct config_generic *) &conf->config_no_index;
5070 			}
5071 			break;
5072 
5073 		case CONFIG_VAR_TYPE_STRING_ARRAY:
5074 			{
5075 				struct config_string_array *conf = (struct config_string_array *) record;
5076 
5077 				if (conf->config_no_index.gen.name)
5078 					ret = (struct config_generic *) &conf->config_no_index;
5079 			}
5080 			break;
5081 		default:
5082 			break;
5083 	}
5084 	return ret;
5085 }
5086 
5087 /*
5088  * Try to parse value as an integer.  The accepted formats are the
5089  * usual decimal, octal, or hexadecimal formats, as well as floating-point
5090  * formats (which will be rounded to integer after any units conversion).
5091  * Optionally, the value can be followed by a unit name if "flags" indicates
5092  * a unit is allowed.
5093  *
5094  * If the string parses okay, return true, else false.
5095  * If okay and result is not NULL, return the value in *result.
5096  * If not okay and hintmsg is not NULL, *hintmsg is set to a suitable
5097  * HINT message, or NULL if no hint provided.
5098  */
5099 static bool
parse_int(const char * value,int64 * result,int flags,const char ** hintmsg,int64 MaxVal)5100 parse_int(const char *value, int64 *result, int flags, const char **hintmsg, int64 MaxVal)
5101 {
5102 	/*
5103 	 * We assume here that double is wide enough to represent any integer
5104 	 * value with adequate precision.
5105 	 */
5106 	double		val;
5107 	char	   *endptr;
5108 
5109 	/* To suppress compiler warnings, always set output params */
5110 	if (result)
5111 		*result = 0;
5112 	if (hintmsg)
5113 		*hintmsg = NULL;
5114 
5115 	/*
5116 	 * Try to parse as an integer (allowing octal or hex input).  If the
5117 	 * conversion stops at a decimal point or 'e', or overflows, re-parse as
5118 	 * float.  This should work fine as long as we have no unit names starting
5119 	 * with 'e'.  If we ever do, the test could be extended to check for a
5120 	 * sign or digit after 'e', but for now that's unnecessary.
5121 	 */
5122 	errno = 0;
5123 	val = strtol(value, &endptr, 0);
5124 	if (*endptr == '.' || *endptr == 'e' || *endptr == 'E' ||
5125 		errno == ERANGE)
5126 	{
5127 		errno = 0;
5128 		val = strtod(value, &endptr);
5129 	}
5130 
5131 	if (endptr == value || errno == ERANGE)
5132 		return false;			/* no HINT for these cases */
5133 
5134 	/* reject NaN (infinities will fail range check below) */
5135 	if (isnan(val))
5136 		return false;			/* treat same as syntax error; no HINT */
5137 
5138 
5139 	/* allow whitespace between number and unit */
5140 	while (isspace((unsigned char) *endptr))
5141 		endptr++;
5142 
5143 	/* Handle possible unit */
5144 	if (*endptr != '\0')
5145 	{
5146 		if ((flags & GUC_UNIT) == 0)
5147 		{
5148 			return false;		/* this setting does not accept a unit */
5149 		}
5150 		if (!convert_to_base_unit(val,
5151 								  endptr, (flags & GUC_UNIT),
5152 								  &val))
5153 		{
5154 			/* invalid unit, or garbage after the unit; set hint and fail. */
5155 			if (hintmsg)
5156 			{
5157 				if (flags & GUC_UNIT_MEMORY)
5158 					*hintmsg = memory_units_hint;
5159 				else
5160 					*hintmsg = time_units_hint;
5161 			}
5162 			return false;
5163 		}
5164 	}
5165 
5166 	/* Round to int, then check for overflow */
5167 	val = rint(val);
5168 
5169 	if (val > MaxVal || val < INT_MIN)
5170 	{
5171 		if (hintmsg)
5172 			*hintmsg = "Value exceeds allowed range.";
5173 		return false;
5174 	}
5175 
5176 	if (result)
5177 		*result = (int64) val;
5178 	return true;
5179 }
5180 /*
5181  * Convert a value from one of the human-friendly units ("kB", "min" etc.)
5182  * to the given base unit.  'value' and 'unit' are the input value and unit
5183  * to convert from (there can be trailing spaces in the unit string).
5184  * The converted value is stored in *base_value.
5185  * It's caller's responsibility to round off the converted value as necessary
5186  * and check for out-of-range.
5187  *
5188  * Returns true on success, false if the input unit is not recognized.
5189  */
5190 
5191 static bool
convert_to_base_unit(double value,const char * unit,int base_unit,double * base_value)5192 convert_to_base_unit(double value, const char *unit,
5193 					 int base_unit, double *base_value)
5194 {
5195 	char		unitstr[MAX_UNIT_LEN + 1];
5196 	int			unitlen;
5197 	const unit_conversion *table;
5198 	int			i;
5199 
5200 	/* extract unit string to compare to table entries */
5201 	unitlen = 0;
5202 	while (*unit != '\0' && !isspace((unsigned char) *unit) &&
5203 		   unitlen < MAX_UNIT_LEN)
5204 		unitstr[unitlen++] = *(unit++);
5205 	unitstr[unitlen] = '\0';
5206 	/* allow whitespace after unit */
5207 	while (isspace((unsigned char) *unit))
5208 		unit++;
5209 	if (*unit != '\0')
5210 		return false;			/* unit too long, or garbage after it */
5211 
5212 	/* now search the appropriate table */
5213 	if (base_unit & GUC_UNIT_MEMORY)
5214 		table = memory_unit_conversion_table;
5215 	else
5216 		table = time_unit_conversion_table;
5217 
5218 	for (i = 0; *table[i].unit; i++)
5219 	{
5220 		if (base_unit == table[i].base_unit &&
5221 			strcmp(unitstr, table[i].unit) == 0)
5222 		{
5223 			double		cvalue = value * table[i].multiplier;
5224 
5225 			/*
5226 			 * If the user gave a fractional value such as "30.1GB", round it
5227 			 * off to the nearest multiple of the next smaller unit, if there
5228 			 * is one.
5229 			 */
5230 			if (*table[i + 1].unit &&
5231 				base_unit == table[i + 1].base_unit)
5232 				cvalue = rint(cvalue / table[i + 1].multiplier) *
5233 					table[i + 1].multiplier;
5234 
5235 			*base_value = cvalue;
5236 			return true;
5237 		}
5238 	}
5239 	return false;
5240 }
5241 #ifndef POOL_PRIVATE
5242 
5243 /*
5244  * Convert an integer value in some base unit to a human-friendly unit.
5245  *
5246  * The output unit is chosen so that it's the greatest unit that can represent
5247  * the value without loss.  For example, if the base unit is GUC_UNIT_KB, 1024
5248  * is converted to 1 MB, but 1025 is represented as 1025 kB.
5249  */
5250 static void
convert_int_from_base_unit(int64 base_value,int base_unit,int64 * value,const char ** unit)5251 convert_int_from_base_unit(int64 base_value, int base_unit,
5252 						   int64 *value, const char **unit)
5253 {
5254 	const unit_conversion *table;
5255 	int			i;
5256 
5257 	*unit = NULL;
5258 
5259 	if (base_unit & GUC_UNIT_MEMORY)
5260 		table = memory_unit_conversion_table;
5261 	else
5262 		table = time_unit_conversion_table;
5263 
5264 	for (i = 0; *table[i].unit; i++)
5265 	{
5266 		if (base_unit == table[i].base_unit)
5267 		{
5268 			/*
5269 			 * Accept the first conversion that divides the value evenly.  We
5270 			 * assume that the conversions for each base unit are ordered from
5271 			 * greatest unit to the smallest!
5272 			 */
5273 			if (table[i].multiplier <= 1.0 ||
5274 				base_value % (int64) table[i].multiplier == 0)
5275 			{
5276 				*value = (int64) rint(base_value / table[i].multiplier);
5277 				*unit = table[i].unit;
5278 				break;
5279 			}
5280 		}
5281 	}
5282 
5283 	Assert(*unit != NULL);
5284 }
5285 
5286 /*
5287  * Lookup the name for an enum option with the selected value.
5288  * The returned string is a pointer to static data and not
5289  * allocated for modification.
5290  */
5291 static const char *
config_enum_lookup_by_value(struct config_enum * record,int val)5292 config_enum_lookup_by_value(struct config_enum *record, int val)
5293 {
5294 	const struct config_enum_entry *entry;
5295 
5296 	for (entry = record->options; entry && entry->name; entry++)
5297 	{
5298 		if (entry->val == val)
5299 			return entry->name;
5300 	}
5301 	/* should never happen */
5302 	return NULL;
5303 }
5304 
5305 static char *
ShowOption(struct config_generic * record,int index,int elevel)5306 ShowOption(struct config_generic *record, int index, int elevel)
5307 {
5308 	char		buffer[256];
5309 	const char *val;
5310 
5311 	/*
5312 	 * if the value needs to be hidden no need to dig further
5313 	 */
5314 	if (record->flags & VAR_HIDDEN_VALUE)
5315 		return pstrdup("*****");
5316 
5317 	switch (record->vartype)
5318 	{
5319 		case CONFIG_VAR_TYPE_BOOL:
5320 			{
5321 				struct config_bool *conf = (struct config_bool *) record;
5322 
5323 				if (conf->show_hook)
5324 					val = (*conf->show_hook) ();
5325 				else
5326 					val = *conf->variable ? "on" : "off";
5327 			}
5328 			break;
5329 
5330 		case CONFIG_VAR_TYPE_INT:
5331 			{
5332 				struct config_int *conf = (struct config_int *) record;
5333 
5334 				if (conf->show_hook)
5335 					val = (*conf->show_hook) ();
5336 				else
5337 				{
5338 
5339 					/*
5340 					 * Use int64 arithmetic to avoid overflows in units
5341 					 * conversion.
5342 					 */
5343 					int64		result = (int64) *conf->variable;
5344 					const char *unit;
5345 
5346 					if (result > 0 && (record->flags & GUC_UNIT))
5347 						convert_int_from_base_unit(result,
5348 												   record->flags & GUC_UNIT,
5349 												   &result, &unit);
5350 					else
5351 						unit = "";
5352 
5353 					snprintf(buffer, sizeof(buffer), INT64_FORMAT "%s",
5354 							 result, unit);
5355 					val = buffer;
5356 				}
5357 			}
5358 			break;
5359 
5360 		case CONFIG_VAR_TYPE_LONG:
5361 			{
5362 				struct config_long *conf = (struct config_long *) record;
5363 
5364 				if (conf->show_hook)
5365 					val = (*conf->show_hook) ();
5366 				else
5367 				{
5368 					int64		result = (int64) *conf->variable;
5369 					const char *unit;
5370 
5371 					if (result > 0 && (record->flags & GUC_UNIT))
5372 						convert_int_from_base_unit(result,
5373 												   record->flags & GUC_UNIT,
5374 												   &result, &unit);
5375 					else
5376 						unit = "";
5377 
5378 					snprintf(buffer, sizeof(buffer), INT64_FORMAT "%s",
5379 							 result, unit);
5380 					val = buffer;
5381 				}
5382 			}
5383 			break;
5384 
5385 		case CONFIG_VAR_TYPE_DOUBLE:
5386 			{
5387 				struct config_double *conf = (struct config_double *) record;
5388 
5389 				if (conf->show_hook)
5390 					val = (*conf->show_hook) ();
5391 				else
5392 				{
5393 					snprintf(buffer, sizeof(buffer), "%g",
5394 							 *conf->variable);
5395 					val = buffer;
5396 				}
5397 			}
5398 			break;
5399 
5400 		case CONFIG_VAR_TYPE_STRING:
5401 			{
5402 				struct config_string *conf = (struct config_string *) record;
5403 
5404 				if (conf->show_hook)
5405 					val = (*conf->show_hook) ();
5406 				else if (*conf->variable && **conf->variable)
5407 					val = *conf->variable;
5408 				else
5409 					val = "";
5410 			}
5411 			break;
5412 
5413 		case CONFIG_VAR_TYPE_ENUM:
5414 			{
5415 				struct config_enum *conf = (struct config_enum *) record;
5416 
5417 				if (conf->show_hook)
5418 					val = (*conf->show_hook) ();
5419 				else
5420 					val = config_enum_lookup_by_value(conf, *conf->variable);
5421 			}
5422 
5423 			break;
5424 
5425 		case CONFIG_VAR_TYPE_INT_ARRAY:
5426 			{
5427 				struct config_int_array *conf = (struct config_int_array *) record;
5428 
5429 				if (index >= record->max_elements || index < 0)
5430 				{
5431 					ereport(elevel,
5432 							(errmsg("invalid index %d for configuration parameter \"%s\"", index, conf->gen.name),
5433 							 errdetail("allowed index is between 0 and %d", record->max_elements - 1)));
5434 
5435 					val = NULL;
5436 				}
5437 				else
5438 				{
5439 					if (conf->show_hook)
5440 						val = (*conf->show_hook) (index);
5441 					else
5442 					{
5443 						int			result = *conf->variable[index];
5444 
5445 						snprintf(buffer, sizeof(buffer), "%d",
5446 								 result);
5447 						val = buffer;
5448 					}
5449 				}
5450 			}
5451 			break;
5452 
5453 		case CONFIG_VAR_TYPE_DOUBLE_ARRAY:
5454 			{
5455 				struct config_double_array *conf = (struct config_double_array *) record;
5456 
5457 				if (index >= record->max_elements || index < 0)
5458 				{
5459 					ereport(elevel,
5460 							(errmsg("invalid index %d for configuration parameter \"%s\"", index, conf->gen.name),
5461 							 errdetail("allowed index is between 0 and %d", record->max_elements - 1)));
5462 
5463 					val = NULL;
5464 				}
5465 				else
5466 				{
5467 					if (conf->show_hook)
5468 						val = (*conf->show_hook) (index);
5469 					else
5470 					{
5471 						double		result = *conf->variable[index];
5472 
5473 						snprintf(buffer, sizeof(buffer), "%g",
5474 								 result);
5475 						val = buffer;
5476 					}
5477 				}
5478 			}
5479 			break;
5480 
5481 		case CONFIG_VAR_TYPE_STRING_ARRAY:
5482 			{
5483 				struct config_string_array *conf = (struct config_string_array *) record;
5484 
5485 				if (index >= record->max_elements || index < 0)
5486 				{
5487 					ereport(elevel,
5488 							(errmsg("invalid index %d for configuration parameter \"%s\"", index, conf->gen.name),
5489 							 errdetail("allowed index is between 0 and %d", record->max_elements - 1)));
5490 
5491 					val = NULL;
5492 				}
5493 				else
5494 				{
5495 					if (conf->show_hook)
5496 						val = (*conf->show_hook) (index);
5497 					else if ((*conf->variable)[index] && ***conf->variable)
5498 						val = (*conf->variable)[index];
5499 					else
5500 						val = "";
5501 				}
5502 			}
5503 			break;
5504 		case CONFIG_VAR_TYPE_STRING_LIST:
5505 			{
5506 				struct config_string_list *conf = (struct config_string_list *) record;
5507 
5508 				if (conf->show_hook)
5509 					val = (*conf->show_hook) ();
5510 				else if (conf->current_val)
5511 					val = conf->current_val;
5512 				else
5513 					val = "";
5514 			}
5515 			break;
5516 
5517 		default:
5518 			/* just to keep compiler quiet */
5519 			val = "???";
5520 			break;
5521 	}
5522 	if (val)
5523 		return pstrdup(val);
5524 	return NULL;
5525 }
5526 
5527 
5528 static bool
value_slot_for_config_record_is_empty(struct config_generic * record,int index)5529 value_slot_for_config_record_is_empty(struct config_generic *record, int index)
5530 {
5531 	switch (record->vartype)
5532 	{
5533 		case CONFIG_VAR_TYPE_BOOL:
5534 		case CONFIG_VAR_TYPE_INT:
5535 		case CONFIG_VAR_TYPE_LONG:
5536 		case CONFIG_VAR_TYPE_DOUBLE:
5537 		case CONFIG_VAR_TYPE_STRING:
5538 		case CONFIG_VAR_TYPE_ENUM:
5539 			return false;
5540 			break;
5541 
5542 		case CONFIG_VAR_TYPE_INT_ARRAY:
5543 			{
5544 				struct config_int_array *conf = (struct config_int_array *) record;
5545 
5546 				if (conf->empty_slot_check_func)
5547 					return (*conf->empty_slot_check_func) (index);
5548 			}
5549 			break;
5550 
5551 		case CONFIG_VAR_TYPE_DOUBLE_ARRAY:
5552 			{
5553 				struct config_double_array *conf = (struct config_double_array *) record;
5554 
5555 				if (conf->empty_slot_check_func)
5556 					return (*conf->empty_slot_check_func) (index);
5557 			}
5558 			break;
5559 
5560 		case CONFIG_VAR_TYPE_STRING_ARRAY:
5561 			{
5562 				struct config_string_array *conf = (struct config_string_array *) record;
5563 
5564 				if (conf->empty_slot_check_func)
5565 					return (*conf->empty_slot_check_func) (index);
5566 			}
5567 			break;
5568 
5569 		default:
5570 			break;
5571 	}
5572 	return false;
5573 }
5574 
5575 bool
set_config_option_for_session(POOL_CONNECTION * frontend,POOL_CONNECTION_POOL * backend,const char * name,const char * value)5576 set_config_option_for_session(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *name, const char *value)
5577 {
5578 	bool		ret;
5579 	MemoryContext oldCxt = MemoryContextSwitchTo(TopMemoryContext);
5580 
5581 	ret = set_one_config_option(name, value, CFGCXT_SESSION, PGC_S_SESSION, FRONTEND_ONLY_ERROR);
5582 	if (ret == true)
5583 	{
5584 		send_complete_and_ready(frontend, backend, "SET", -1);
5585 	}
5586 	MemoryContextSwitchTo(oldCxt);
5587 	return ret;
5588 }
5589 
5590 bool
reset_all_variables(POOL_CONNECTION * frontend,POOL_CONNECTION_POOL * backend)5591 reset_all_variables(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend)
5592 {
5593 	int			i;
5594 	int			elevel = (frontend == NULL) ? FATAL : FRONTEND_ONLY_ERROR;
5595 	MemoryContext oldCxt = MemoryContextSwitchTo(TopMemoryContext);
5596 
5597 	ereport(DEBUG2,
5598 			(errmsg("RESET ALL CONFIG VARIABLES")));
5599 
5600 	for (i = 0; i < num_all_parameters; ++i)
5601 	{
5602 		struct config_generic *record = all_parameters[i];
5603 
5604 		/* Don't reset if special exclusion from RESET ALL */
5605 		if (record->flags & VAR_NO_RESET_ALL)
5606 			continue;
5607 
5608 		if (record->dynamic_array_var)
5609 		{
5610 			int			index;
5611 
5612 			for (index = 0; index < record->max_elements; index++)
5613 			{
5614 				if (record->scontexts[index] != CFGCXT_SESSION)
5615 					continue;
5616 
5617 				if (value_slot_for_config_record_is_empty(record, index))
5618 					continue;
5619 
5620 				setConfigOptionVar(record, record->name, index, NULL,
5621 								   CFGCXT_INIT, PGC_S_FILE, elevel);
5622 			}
5623 
5624 			/* Also set the default value for index free record if any */
5625 			if (record->flags & ARRAY_VAR_ALLOW_NO_INDEX)
5626 			{
5627 				struct config_generic *idx_free_record = get_index_free_record_if_any(record);
5628 
5629 				if (idx_free_record && idx_free_record->scontexts[0] == CFGCXT_SESSION)
5630 				{
5631 					ereport(DEBUG1,
5632 							(errmsg("reset index-free value of array type parameter \"%s\"",
5633 									record->name)));
5634 					setConfigOptionVar(idx_free_record, idx_free_record->name, -1, NULL,
5635 									   CFGCXT_INIT, PGC_S_FILE, elevel);
5636 				}
5637 			}
5638 		}
5639 		else
5640 		{
5641 			/* do nothing if variable is not changed in session */
5642 			if (record->scontexts[0] != CFGCXT_SESSION)
5643 				continue;
5644 			setConfigOptionVar(record, record->name, -1, NULL,
5645 							   CFGCXT_INIT, PGC_S_FILE, elevel);
5646 		}
5647 	}
5648 	if (frontend)
5649 		send_complete_and_ready(frontend, backend, "RESET", -1);
5650 
5651 	MemoryContextSwitchTo(oldCxt);
5652 
5653 	return true;
5654 }
5655 
5656 bool
report_all_variables(POOL_CONNECTION * frontend,POOL_CONNECTION_POOL * backend)5657 report_all_variables(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend)
5658 {
5659 	int			i;
5660 	int			num_rows = 0;
5661 	const char *value;
5662 
5663 	send_row_description_for_detail_view(frontend, backend);
5664 
5665 	/*
5666 	 * grouped variables are not listed in all parameter array so start with
5667 	 * group variables.
5668 	 */
5669 	for (i = 0; ConfigureVarGroups[i].gen.name; i++)
5670 	{
5671 		int			rows = send_grouped_type_variable_to_frontend(&ConfigureVarGroups[i], frontend, backend);
5672 
5673 		if (rows < 0)
5674 			return false;
5675 		num_rows += rows;
5676 	}
5677 
5678 	for (i = 0; i < num_all_parameters; ++i)
5679 	{
5680 		struct config_generic *record = all_parameters[i];
5681 
5682 		if (record->flags & VAR_PART_OF_GROUP)
5683 			continue;
5684 
5685 		if (record->flags & VAR_HIDDEN_IN_SHOW_ALL)
5686 			continue;
5687 
5688 		if (record->dynamic_array_var)
5689 		{
5690 			int			rows = send_array_type_variable_to_frontend(record, frontend, backend);
5691 
5692 			if (rows < 0)
5693 				return false;
5694 			num_rows += rows;
5695 
5696 		}
5697 		else
5698 		{
5699 			value = ShowOption(record, 0, FRONTEND_ONLY_ERROR);
5700 			if (value == NULL)
5701 				return false;
5702 			send_config_var_detail_row(frontend, backend, record->name, value, record->description);
5703 			pfree((void *) value);
5704 			num_rows++;
5705 		}
5706 	}
5707 	send_complete_and_ready(frontend, backend, "SELECT", num_rows);
5708 	return true;
5709 }
5710 
5711 
5712 bool
report_config_variable(POOL_CONNECTION * frontend,POOL_CONNECTION_POOL * backend,const char * var_name)5713 report_config_variable(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, const char *var_name)
5714 {
5715 	int			index = 0;
5716 	char	   *value;
5717 	int			num_rows = 0;
5718 	struct config_generic *record;
5719 
5720 	if (strcmp(var_name, "all") == 0)
5721 		return report_all_variables(frontend, backend);
5722 
5723 	/* search the variable */
5724 	record = find_option(var_name, WARNING);
5725 	if (record == NULL)
5726 	{
5727 		int			i;
5728 
5729 		/* check if the var name match the grouped var */
5730 		for (i = 0; ConfigureVarGroups[i].gen.name; i++)
5731 		{
5732 			if (strcmp(ConfigureVarGroups[i].gen.name, var_name) == 0)
5733 			{
5734 				send_row_description_for_detail_view(frontend, backend);
5735 				num_rows = send_grouped_type_variable_to_frontend(&ConfigureVarGroups[i], frontend, backend);
5736 				if (num_rows < 0)
5737 					return false;
5738 				send_complete_and_ready(frontend, backend, "SELECT", num_rows);
5739 				return true;
5740 			}
5741 		}
5742 
5743 		ereport(FRONTEND_ONLY_ERROR,
5744 				(errmsg("config variable \"%s\" not found", var_name)));
5745 
5746 		return false;
5747 	}
5748 
5749 	if (record->dynamic_array_var)
5750 	{
5751 		if (get_index_in_var_name(record, var_name, &index, FRONTEND_ONLY_ERROR) == false)
5752 			return false;
5753 
5754 		if (index < 0)
5755 		{
5756 			/*
5757 			 * Index is not included in parameter name. this is the multi
5758 			 * value config variable
5759 			 */
5760 			ereport(DEBUG3,
5761 					(errmsg("show parameter \"%s\" with out index", var_name)));
5762 			send_row_description_for_detail_view(frontend, backend);
5763 			num_rows = send_array_type_variable_to_frontend(record, frontend, backend);
5764 			if (num_rows < 0)
5765 				return false;
5766 		}
5767 	}
5768 
5769 	if (index >= 0)
5770 	{
5771 		/* single value only */
5772 		num_rows = 1;
5773 		send_row_description(frontend, backend, 1, (char **) &var_name);
5774 		value = ShowOption(record, index, FRONTEND_ONLY_ERROR);
5775 		if (value == NULL)
5776 			return false;
5777 		send_config_var_value_only_row(frontend, backend, value);
5778 		pfree((void *) value);
5779 	}
5780 
5781 	send_complete_and_ready(frontend, backend, "SELECT", num_rows);
5782 
5783 	return true;
5784 }
5785 
5786 static int
send_array_type_variable_to_frontend(struct config_generic * record,POOL_CONNECTION * frontend,POOL_CONNECTION_POOL * backend)5787 send_array_type_variable_to_frontend(struct config_generic *record, POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend)
5788 {
5789 	if (record->dynamic_array_var)
5790 	{
5791 		const int	MAX_NAME_LEN = 255;
5792 		char		name[MAX_NAME_LEN + 1];
5793 		int			index;
5794 		int			num_rows = 0;
5795 		const char *value;
5796 		struct config_generic *idx_free_record = get_index_free_record_if_any(record);
5797 
5798 		/* Before printing the array, print the index free value if any */
5799 		if (idx_free_record)
5800 		{
5801 			value = ShowOption(idx_free_record, 0, FRONTEND_ONLY_ERROR);
5802 			if (value)
5803 			{
5804 				send_config_var_detail_row(frontend, backend, (const char *) idx_free_record->name, value, record->description);
5805 				pfree((void *) value);
5806 				num_rows++;
5807 			}
5808 		}
5809 		for (index = 0; index < record->max_elements; index++)
5810 		{
5811 			if (value_slot_for_config_record_is_empty(record, index))
5812 				continue;
5813 			/* construct the name with index */
5814 			snprintf(name, MAX_NAME_LEN, "%s%d", record->name, index);
5815 			value = ShowOption(record, index, FRONTEND_ONLY_ERROR);
5816 			if (value == NULL)
5817 				return -1;
5818 			send_config_var_detail_row(frontend, backend, (const char *) name, value, record->description);
5819 			pfree((void *) value);
5820 			num_rows++;
5821 		}
5822 		return num_rows;
5823 	}
5824 	return -1;					/* error */
5825 }
5826 
5827 static int
send_grouped_type_variable_to_frontend(struct config_grouped_array_var * grouped_record,POOL_CONNECTION * frontend,POOL_CONNECTION_POOL * backend)5828 send_grouped_type_variable_to_frontend(struct config_grouped_array_var *grouped_record, POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend)
5829 {
5830 	int			k,
5831 				index;
5832 	int			max_elements = grouped_record->var_list[0]->max_elements;
5833 	int			num_rows = 0;
5834 	const char *value;
5835 
5836 	for (k = 0; k < grouped_record->var_count; k++)
5837 	{
5838 		struct config_generic *record = grouped_record->var_list[k];
5839 		struct config_generic *idx_free_record = get_index_free_record_if_any(record);
5840 
5841 		if (idx_free_record)
5842 		{
5843 			value = ShowOption(idx_free_record, 0, FRONTEND_ONLY_ERROR);
5844 			if (value)
5845 			{
5846 				send_config_var_detail_row(frontend, backend, (const char *) idx_free_record->name, value, record->description);
5847 				pfree((void *) value);
5848 				num_rows++;
5849 			}
5850 		}
5851 	}
5852 
5853 	for (index = 0; index < max_elements; index++)
5854 	{
5855 		for (k = 0; k < grouped_record->var_count; k++)
5856 		{
5857 			const int	MAX_NAME_LEN = 255;
5858 			char		name[MAX_NAME_LEN + 1];
5859 
5860 			struct config_generic *record = grouped_record->var_list[k];
5861 
5862 			if (value_slot_for_config_record_is_empty(record, index))
5863 				break;
5864 			/* construct the name with index */
5865 			snprintf(name, MAX_NAME_LEN, "%s%d", record->name, index);
5866 			value = ShowOption(record, index, FRONTEND_ONLY_ERROR);
5867 			if (value == NULL)
5868 				return -1;
5869 			send_config_var_detail_row(frontend, backend, (const char *) name, value, record->description);
5870 			pfree((void *) value);
5871 			num_rows++;
5872 		}
5873 	}
5874 
5875 	return num_rows;
5876 }
5877 
5878 static void
send_row_description_for_detail_view(POOL_CONNECTION * frontend,POOL_CONNECTION_POOL * backend)5879 send_row_description_for_detail_view(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend)
5880 {
5881 	static char *field_names[] = {"item", "value", "description"};
5882 
5883 	send_row_description(frontend, backend, 3, field_names);
5884 }
5885 #endif							/* not defined POOL_FRONTEND */
5886