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