1 /*-------------------------------------------------------------------------
2  *
3  * shared_library_init.c
4  *	  Functionality related to the initialization of the Citus extension.
5  *
6  * Copyright (c) Citus Data, Inc.
7  *-------------------------------------------------------------------------
8  */
9 
10 #include "postgres.h"
11 
12 #include <limits.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 
16 /* necessary to get alloca on illumos */
17 #ifdef __sun
18 #include <alloca.h>
19 #endif
20 
21 #include "fmgr.h"
22 #include "miscadmin.h"
23 
24 #include "safe_lib.h"
25 
26 #include "citus_version.h"
27 #include "commands/explain.h"
28 #include "executor/executor.h"
29 #include "distributed/backend_data.h"
30 #include "distributed/citus_nodefuncs.h"
31 #include "distributed/citus_safe_lib.h"
32 #include "distributed/commands.h"
33 #include "distributed/commands/multi_copy.h"
34 #include "distributed/commands/utility_hook.h"
35 #include "distributed/connection_management.h"
36 #include "distributed/cte_inline.h"
37 #include "distributed/distributed_deadlock_detection.h"
38 #include "distributed/insert_select_executor.h"
39 #include "distributed/intermediate_result_pruning.h"
40 #include "distributed/local_multi_copy.h"
41 #include "distributed/local_executor.h"
42 #include "distributed/local_distributed_join_planner.h"
43 #include "distributed/locally_reserved_shared_connections.h"
44 #include "distributed/maintenanced.h"
45 #include "distributed/metadata_utility.h"
46 #include "distributed/coordinator_protocol.h"
47 #include "distributed/metadata_cache.h"
48 #include "distributed/metadata_sync.h"
49 #include "distributed/multi_physical_planner.h"
50 #include "distributed/multi_executor.h"
51 #include "distributed/multi_explain.h"
52 #include "distributed/multi_join_order.h"
53 #include "distributed/multi_logical_optimizer.h"
54 #include "distributed/distributed_planner.h"
55 #include "distributed/combine_query_planner.h"
56 #include "distributed/multi_router_planner.h"
57 #include "distributed/multi_server_executor.h"
58 #include "distributed/pg_dist_partition.h"
59 #include "distributed/placement_connection.h"
60 #include "distributed/recursive_planning.h"
61 #include "distributed/reference_table_utils.h"
62 #include "distributed/relation_access_tracking.h"
63 #include "distributed/repair_shards.h"
64 #include "distributed/run_from_same_connection.h"
65 #include "distributed/shard_cleaner.h"
66 #include "distributed/shared_connection_stats.h"
67 #include "distributed/query_pushdown_planning.h"
68 #include "distributed/time_constants.h"
69 #include "distributed/query_stats.h"
70 #include "distributed/remote_commands.h"
71 #include "distributed/shard_rebalancer.h"
72 #include "distributed/shared_library_init.h"
73 #include "distributed/statistics_collection.h"
74 #include "distributed/subplan_execution.h"
75 
76 #include "distributed/transaction_management.h"
77 #include "distributed/transaction_recovery.h"
78 #include "distributed/worker_log_messages.h"
79 #include "distributed/worker_manager.h"
80 #include "distributed/worker_protocol.h"
81 #include "distributed/worker_shard_visibility.h"
82 #include "distributed/adaptive_executor.h"
83 #include "libpq/auth.h"
84 #include "port/atomics.h"
85 #include "postmaster/postmaster.h"
86 #include "storage/ipc.h"
87 #include "optimizer/planner.h"
88 #include "optimizer/paths.h"
89 #include "tcop/tcopprot.h"
90 #include "utils/guc.h"
91 #include "utils/guc_tables.h"
92 #include "utils/varlena.h"
93 
94 #include "columnar/mod.h"
95 
96 /* marks shared object as one loadable by the postgres version compiled against */
97 PG_MODULE_MAGIC;
98 
99 #define DUMMY_REAL_TIME_EXECUTOR_ENUM_VALUE 9999999
100 static char *CitusVersion = CITUS_VERSION;
101 
102 /* deprecated GUC value that should not be used anywhere outside this file */
103 static int ReplicationModel = REPLICATION_MODEL_STREAMING;
104 
105 
106 void _PG_init(void);
107 void _PG_fini(void);
108 
109 static void DoInitialCleanup(void);
110 static void ResizeStackToMaximumDepth(void);
111 static void multi_log_hook(ErrorData *edata);
112 static void RegisterConnectionCleanup(void);
113 static void RegisterClientBackendCounterDecrement(void);
114 static void CitusCleanupConnectionsAtExit(int code, Datum arg);
115 static void DecrementClientBackendCounterAtExit(int code, Datum arg);
116 static void CreateRequiredDirectories(void);
117 static void RegisterCitusConfigVariables(void);
118 static bool ErrorIfNotASuitableDeadlockFactor(double *newval, void **extra,
119 											  GucSource source);
120 static bool WarnIfDeprecatedExecutorUsed(int *newval, void **extra, GucSource source);
121 static bool WarnIfReplicationModelIsSet(int *newval, void **extra, GucSource source);
122 static bool NoticeIfSubqueryPushdownEnabled(bool *newval, void **extra, GucSource source);
123 static bool NodeConninfoGucCheckHook(char **newval, void **extra, GucSource source);
124 static void NodeConninfoGucAssignHook(const char *newval, void *extra);
125 static const char * MaxSharedPoolSizeGucShowHook(void);
126 static const char * LocalPoolSizeGucShowHook(void);
127 static bool StatisticsCollectionGucCheckHook(bool *newval, void **extra, GucSource
128 											 source);
129 static void CitusAuthHook(Port *port, int status);
130 
131 
132 static ClientAuthentication_hook_type original_client_auth_hook = NULL;
133 
134 /* *INDENT-OFF* */
135 /* GUC enum definitions */
136 static const struct config_enum_entry propagate_set_commands_options[] = {
137 	{"none", PROPSETCMD_NONE, false},
138 	{"local", PROPSETCMD_LOCAL, false},
139 	{NULL, 0, false}
140 };
141 
142 
143 static const struct config_enum_entry task_assignment_policy_options[] = {
144 	{ "greedy", TASK_ASSIGNMENT_GREEDY, false },
145 	{ "first-replica", TASK_ASSIGNMENT_FIRST_REPLICA, false },
146 	{ "round-robin", TASK_ASSIGNMENT_ROUND_ROBIN, false },
147 	{ NULL, 0, false }
148 };
149 
150 static const struct config_enum_entry replication_model_options[] = {
151 	{ "statement", REPLICATION_MODEL_COORDINATOR, false },
152 	{ "streaming", REPLICATION_MODEL_STREAMING, false },
153 	{ NULL, 0, false }
154 };
155 
156 static const struct config_enum_entry task_executor_type_options[] = {
157 	{ "adaptive", MULTI_EXECUTOR_ADAPTIVE, false },
158 	{ "real-time", DUMMY_REAL_TIME_EXECUTOR_ENUM_VALUE, false }, /* keep it for backward comp. */
159 	{ "task-tracker", MULTI_EXECUTOR_ADAPTIVE, false },
160 	{ NULL, 0, false }
161 };
162 
163 static const struct config_enum_entry shard_placement_policy_options[] = {
164 	{ "local-node-first", SHARD_PLACEMENT_LOCAL_NODE_FIRST, false },
165 	{ "round-robin", SHARD_PLACEMENT_ROUND_ROBIN, false },
166 	{ "random", SHARD_PLACEMENT_RANDOM, false },
167 	{ NULL, 0, false }
168 };
169 
170 static const struct config_enum_entry use_secondary_nodes_options[] = {
171 	{ "never", USE_SECONDARY_NODES_NEVER, false },
172 	{ "always", USE_SECONDARY_NODES_ALWAYS, false },
173 	{ NULL, 0, false }
174 };
175 
176 static const struct config_enum_entry coordinator_aggregation_options[] = {
177 	{ "disabled", COORDINATOR_AGGREGATION_DISABLED, false },
178 	{ "row-gather", COORDINATOR_AGGREGATION_ROW_GATHER, false },
179 	{ NULL, 0, false }
180 };
181 
182 static const struct config_enum_entry shard_commit_protocol_options[] = {
183 	{ "1pc", COMMIT_PROTOCOL_1PC, false },
184 	{ "2pc", COMMIT_PROTOCOL_2PC, false },
185 	{ NULL, 0, false }
186 };
187 
188 static const struct config_enum_entry log_level_options[] = {
189 	{ "off", CITUS_LOG_LEVEL_OFF, false },
190 	{ "debug5", DEBUG5, false},
191 	{ "debug4", DEBUG4, false},
192 	{ "debug3", DEBUG3, false},
193 	{ "debug2", DEBUG2, false},
194 	{ "debug1", DEBUG1, false},
195 	{ "debug", DEBUG2, true},
196 	{ "log", LOG, false},
197 	{ "info", INFO, true},
198 	{ "notice", NOTICE, false},
199 	{ "warning", WARNING, false},
200 	{ "error", ERROR, false},
201 	{ NULL, 0, false}
202 };
203 
204 
205 static const struct config_enum_entry local_table_join_policies[] = {
206 	{ "never", LOCAL_JOIN_POLICY_NEVER, false},
207 	{ "prefer-local", LOCAL_JOIN_POLICY_PREFER_LOCAL, false},
208 	{ "prefer-distributed", LOCAL_JOIN_POLICY_PREFER_DISTRIBUTED, false},
209 	{ "auto", LOCAL_JOIN_POLICY_AUTO, false},
210 	{ NULL, 0, false}
211 };
212 
213 
214 static const struct config_enum_entry multi_shard_modify_connection_options[] = {
215 	{ "parallel", PARALLEL_CONNECTION, false },
216 	{ "sequential", SEQUENTIAL_CONNECTION, false },
217 	{ NULL, 0, false }
218 };
219 
220 static const struct config_enum_entry explain_analyze_sort_method_options[] = {
221 	{ "execution-time", EXPLAIN_ANALYZE_SORT_BY_TIME, false },
222 	{ "taskId", EXPLAIN_ANALYZE_SORT_BY_TASK_ID, false },
223 	{ NULL, 0, false }
224 };
225 
226 /* *INDENT-ON* */
227 
228 
229 /* shared library initialization function */
230 void
_PG_init(void)231 _PG_init(void)
232 {
233 	if (!process_shared_preload_libraries_in_progress)
234 	{
235 		ereport(ERROR, (errmsg("Citus can only be loaded via shared_preload_libraries"),
236 						errhint("Add citus to shared_preload_libraries configuration "
237 								"variable in postgresql.conf in master and workers. Note "
238 								"that citus should be at the beginning of "
239 								"shared_preload_libraries.")));
240 	}
241 
242 	/*
243 	 * Register contstraint_handler hooks of safestringlib first. This way
244 	 * loading the extension will error out if one of these constraints are hit
245 	 * during load.
246 	 */
247 	set_str_constraint_handler_s(ereport_constraint_handler);
248 	set_mem_constraint_handler_s(ereport_constraint_handler);
249 
250 	/*
251 	 * Perform checks before registering any hooks, to avoid erroring out in a
252 	 * partial state.
253 	 *
254 	 * In many cases (e.g. planner and utility hook, to run inside
255 	 * pg_stat_statements et. al.) we have to be loaded before other hooks
256 	 * (thus as the innermost/last running hook) to be able to do our
257 	 * duties. For simplicity insist that all hooks are previously unused.
258 	 */
259 	if (planner_hook != NULL || ProcessUtility_hook != NULL ||
260 		ExecutorStart_hook != NULL || ExecutorRun_hook != NULL ||
261 		ExplainOneQuery_hook != NULL)
262 	{
263 		ereport(ERROR, (errmsg("Citus has to be loaded first"),
264 						errhint("Place citus at the beginning of "
265 								"shared_preload_libraries.")));
266 	}
267 
268 	ResizeStackToMaximumDepth();
269 
270 	/*
271 	 * Extend the database directory structure before continuing with
272 	 * initialization - one of the later steps might require them to exist.
273 	 * If in a sub-process (windows / EXEC_BACKEND) this already has been
274 	 * done.
275 	 */
276 	if (!IsUnderPostmaster)
277 	{
278 		CreateRequiredDirectories();
279 	}
280 
281 	InitConnParams();
282 
283 	/*
284 	 * Register Citus configuration variables. Do so before intercepting
285 	 * hooks or calling initialization functions, in case we want to do the
286 	 * latter in a configuration dependent manner.
287 	 */
288 	RegisterCitusConfigVariables();
289 
290 	/* make our additional node types known */
291 	RegisterNodes();
292 
293 	/* make our custom scan nodes known */
294 	RegisterCitusCustomScanMethods();
295 
296 	/* intercept planner */
297 	planner_hook = distributed_planner;
298 
299 	/* register utility hook */
300 	ProcessUtility_hook = multi_ProcessUtility;
301 
302 	/* register for planner hook */
303 	set_rel_pathlist_hook = multi_relation_restriction_hook;
304 	set_join_pathlist_hook = multi_join_restriction_hook;
305 	ExecutorStart_hook = CitusExecutorStart;
306 	ExecutorRun_hook = CitusExecutorRun;
307 	ExplainOneQuery_hook = CitusExplainOneQuery;
308 
309 	/* register hook for error messages */
310 	emit_log_hook = multi_log_hook;
311 
312 
313 	/*
314 	 * Register hook for counting client backends that
315 	 * are successfully authenticated.
316 	 */
317 	original_client_auth_hook = ClientAuthentication_hook;
318 	ClientAuthentication_hook = CitusAuthHook;
319 
320 	InitializeMaintenanceDaemon();
321 
322 	/* initialize coordinated transaction management */
323 	InitializeTransactionManagement();
324 	InitializeBackendManagement();
325 	InitializeConnectionManagement();
326 	InitPlacementConnectionManagement();
327 	InitializeCitusQueryStats();
328 	InitializeSharedConnectionStats();
329 	InitializeLocallyReservedSharedConnections();
330 
331 	/* enable modification of pg_catalog tables during pg_upgrade */
332 	if (IsBinaryUpgrade)
333 	{
334 		SetConfigOption("allow_system_table_mods", "true", PGC_POSTMASTER,
335 						PGC_S_OVERRIDE);
336 	}
337 
338 	/*
339 	 * In postmasters execution of _PG_init, IsUnderPostmaster will be false and
340 	 * we want to do the cleanup at that time only, otherwise there is a chance that
341 	 * there will be parallel queries and we might do a cleanup for things that are
342 	 * already in use. This is only needed in Windows.
343 	 */
344 	if (!IsUnderPostmaster)
345 	{
346 		DoInitialCleanup();
347 	}
348 	columnar_init();
349 }
350 
351 
352 /* shared library deconstruction function */
353 void
_PG_fini(void)354 _PG_fini(void)
355 {
356 	columnar_fini();
357 }
358 
359 
360 /*
361  * DoInitialCleanup does cleanup at start time.
362  * Currently it:
363  * - Removes repartition directories ( in case there are any leftovers)
364  */
365 static void
DoInitialCleanup(void)366 DoInitialCleanup(void)
367 {
368 	RepartitionCleanupJobDirectories();
369 }
370 
371 
372 /*
373  * Stack size increase during high memory load may cause unexpected crashes.
374  * With this alloca call, we are increasing stack size explicitly, so that if
375  * it is not possible to increase stack size, we will get an OOM error instead
376  * of a crash.
377  *
378  * This function is called on backend startup. The allocated memory will
379  * automatically be released at the end of the function's scope. However, we'd
380  * have already expanded the stack and it wouldn't shrink back. So, in a sense,
381  * per backend we're securing max_stack_depth kB's of memory on the stack upfront.
382  *
383  * Not all the backends require max_stack_depth kB's on the stack, so we might end
384  * up with unnecessary allocations. However, the default value is 2MB, which seems
385  * an acceptable trade-off. Also, allocating memory upfront may perform better
386  * under some circumstances.
387  */
388 static void
ResizeStackToMaximumDepth(void)389 ResizeStackToMaximumDepth(void)
390 {
391 #ifndef WIN32
392 	long max_stack_depth_bytes = max_stack_depth * 1024L;
393 
394 	/*
395 	 * Explanation of IGNORE-BANNED:
396 	 * alloca is safe to use here since we limit the allocated size. We cannot
397 	 * use malloc as a replacement, since we actually want to grow the stack
398 	 * here.
399 	 */
400 	volatile char *stack_resizer = alloca(max_stack_depth_bytes); /* IGNORE-BANNED */
401 
402 	/*
403 	 * Different architectures might have different directions while
404 	 * growing the stack. So, touch both ends.
405 	 */
406 	stack_resizer[0] = 0;
407 	stack_resizer[max_stack_depth_bytes - 1] = 0;
408 
409 	/*
410 	 * Passing the address to external function also prevents the function
411 	 * from being optimized away, and the debug elog can also help with
412 	 * diagnosis if needed.
413 	 */
414 	elog(DEBUG5, "entry stack is at %p, increased to %p, the top and bottom values of "
415 				 "the stack is %d and %d", &stack_resizer[0],
416 		 &stack_resizer[max_stack_depth_bytes - 1],
417 		 stack_resizer[max_stack_depth_bytes - 1], stack_resizer[0]);
418 
419 #endif
420 }
421 
422 
423 /*
424  * multi_log_hook intercepts postgres log commands. We use this to override
425  * postgres error messages when they're not specific enough for the users.
426  */
427 static void
multi_log_hook(ErrorData * edata)428 multi_log_hook(ErrorData *edata)
429 {
430 	/*
431 	 * Show the user a meaningful error message when a backend is cancelled
432 	 * by the distributed deadlock detection. Also reset the state for this,
433 	 * since the next cancelation of the backend might have another reason.
434 	 */
435 	bool clearState = true;
436 	if (edata->elevel == ERROR && edata->sqlerrcode == ERRCODE_QUERY_CANCELED &&
437 		MyBackendGotCancelledDueToDeadlock(clearState))
438 	{
439 		edata->sqlerrcode = ERRCODE_T_R_DEADLOCK_DETECTED;
440 		edata->message = "canceling the transaction since it was "
441 						 "involved in a distributed deadlock";
442 	}
443 }
444 
445 
446 /*
447  * StartupCitusBackend initializes per-backend infrastructure, and is called
448  * the first time citus is used in a database.
449  *
450  * NB: All code here has to be able to cope with this routine being called
451  * multiple times in the same backend.  This will e.g. happen when the
452  * extension is created or upgraded.
453  */
454 void
StartupCitusBackend(void)455 StartupCitusBackend(void)
456 {
457 	InitializeMaintenanceDaemonBackend();
458 	InitializeBackendData();
459 	RegisterConnectionCleanup();
460 }
461 
462 
463 /*
464  * RegisterConnectionCleanup cleans up any resources left at the end of the
465  * session. We prefer to cleanup before shared memory exit to make sure that
466  * this session properly releases anything hold in the shared memory.
467  */
468 static void
RegisterConnectionCleanup(void)469 RegisterConnectionCleanup(void)
470 {
471 	static bool registeredCleanup = false;
472 	if (registeredCleanup == false)
473 	{
474 		before_shmem_exit(CitusCleanupConnectionsAtExit, 0);
475 
476 		registeredCleanup = true;
477 	}
478 }
479 
480 
481 /*
482  * RegisterClientBackendCounterDecrement is called when the backend terminates.
483  * For all client backends, we register a callback that will undo
484  */
485 static void
RegisterClientBackendCounterDecrement(void)486 RegisterClientBackendCounterDecrement(void)
487 {
488 	static bool registeredCleanup = false;
489 	if (registeredCleanup == false)
490 	{
491 		before_shmem_exit(DecrementClientBackendCounterAtExit, 0);
492 
493 		registeredCleanup = true;
494 	}
495 }
496 
497 
498 /*
499  * CitusCleanupConnectionsAtExit is called before_shmem_exit() of the
500  * backend for the purposes of any clean-up needed.
501  */
502 static void
CitusCleanupConnectionsAtExit(int code,Datum arg)503 CitusCleanupConnectionsAtExit(int code, Datum arg)
504 {
505 	/* properly close all the cached connections */
506 	ShutdownAllConnections();
507 
508 	/*
509 	 * Make sure that we give the shared connections back to the shared
510 	 * pool if any. This operation is a no-op if the reserved connections
511 	 * are already given away.
512 	 */
513 	DeallocateReservedConnections();
514 }
515 
516 
517 /*
518  * DecrementClientBackendCounterAtExit is called before_shmem_exit() of the
519  * backend for the purposes decrementing
520  */
521 static void
DecrementClientBackendCounterAtExit(int code,Datum arg)522 DecrementClientBackendCounterAtExit(int code, Datum arg)
523 {
524 	DecrementClientBackendCounter();
525 }
526 
527 
528 /*
529  * CreateRequiredDirectories - Create directories required for Citus to
530  * function.
531  *
532  * These used to be created by initdb, but that's not possible anymore.
533  */
534 static void
CreateRequiredDirectories(void)535 CreateRequiredDirectories(void)
536 {
537 	const char *subdirs[] = {
538 		"pg_foreign_file",
539 		"pg_foreign_file/cached",
540 		"base/" PG_JOB_CACHE_DIR
541 	};
542 
543 	for (int dirNo = 0; dirNo < lengthof(subdirs); dirNo++)
544 	{
545 		int ret = mkdir(subdirs[dirNo], S_IRWXU);
546 
547 		if (ret != 0 && errno != EEXIST)
548 		{
549 			ereport(ERROR, (errcode_for_file_access(),
550 							errmsg("could not create directory \"%s\": %m",
551 								   subdirs[dirNo])));
552 		}
553 	}
554 }
555 
556 
557 /* Register Citus configuration variables. */
558 static void
RegisterCitusConfigVariables(void)559 RegisterCitusConfigVariables(void)
560 {
561 	DefineCustomBoolVariable(
562 		"citus.all_modifications_commutative",
563 		gettext_noop("Bypasses commutativity checks when enabled"),
564 		NULL,
565 		&AllModificationsCommutative,
566 		false,
567 		PGC_USERSET,
568 		GUC_STANDARD,
569 		NULL, NULL, NULL);
570 
571 	DefineCustomBoolVariable(
572 		"citus.binary_worker_copy_format",
573 		gettext_noop("Use the binary worker copy format."),
574 		gettext_noop("When enabled, data is copied from workers to workers "
575 					 "in PostgreSQL's binary serialization format when "
576 					 "joining large tables."),
577 		&BinaryWorkerCopyFormat,
578 #if PG_VERSION_NUM >= PG_VERSION_14
579 		true,
580 #else
581 		false,
582 #endif
583 		PGC_SIGHUP,
584 		GUC_STANDARD,
585 		NULL, NULL, NULL);
586 
587 	DefineCustomBoolVariable(
588 		"citus.check_available_space_before_move",
589 		gettext_noop("When enabled will check free disk space before a shard move"),
590 		gettext_noop(
591 			"Free disk space will be checked when this setting is enabled before each shard move."),
592 		&CheckAvailableSpaceBeforeMove,
593 		true,
594 		PGC_USERSET,
595 		GUC_NO_SHOW_ALL,
596 		NULL, NULL, NULL);
597 
598 	DefineCustomStringVariable(
599 		"citus.cluster_name",
600 		gettext_noop("Which cluster this node is a part of"),
601 		NULL,
602 		&CurrentCluster,
603 		"default",
604 		PGC_SU_BACKEND,
605 		GUC_STANDARD,
606 		NULL, NULL, NULL);
607 
608 	DefineCustomEnumVariable(
609 		"citus.coordinator_aggregation_strategy",
610 		gettext_noop("Sets the strategy for when an aggregate cannot be pushed down. "
611 					 "'row-gather' will pull up intermediate rows to the coordinator, "
612 					 "while 'disabled' will error if coordinator aggregation is necessary"),
613 		NULL,
614 		&CoordinatorAggregationStrategy,
615 		COORDINATOR_AGGREGATION_ROW_GATHER,
616 		coordinator_aggregation_options,
617 		PGC_USERSET,
618 		GUC_STANDARD,
619 		NULL, NULL, NULL);
620 
621 	DefineCustomIntVariable(
622 		"citus.copy_switchover_threshold",
623 		gettext_noop("Sets the threshold for copy to be switched "
624 					 "over per connection."),
625 		gettext_noop("Data size threshold to switch over the active placement for "
626 					 "a connection. If this is too low, overhead of starting COPY "
627 					 "commands will hurt the performance. If this is too high, "
628 					 "buffered data will use lots of memory. 4MB is a good balance "
629 					 "between memory usage and performance. Note that this is irrelevant "
630 					 "in the common case where we open one connection per placement."),
631 		&CopySwitchOverThresholdBytes,
632 		4 * 1024 * 1024, 1, INT_MAX,
633 		PGC_USERSET,
634 		GUC_UNIT_BYTE | GUC_NO_SHOW_ALL,
635 		NULL, NULL, NULL);
636 
637 	DefineCustomRealVariable(
638 		"citus.count_distinct_error_rate",
639 		gettext_noop("Desired error rate when calculating count(distinct) "
640 					 "approximates using the postgresql-hll extension. "
641 					 "0.0 disables approximations for count(distinct); 1.0 "
642 					 "provides no guarantees about the accuracy of results."),
643 		NULL,
644 		&CountDistinctErrorRate,
645 		0.0, 0.0, 1.0,
646 		PGC_USERSET,
647 		GUC_STANDARD,
648 		NULL, NULL, NULL);
649 
650 	DefineCustomBoolVariable(
651 		"citus.defer_drop_after_shard_move",
652 		gettext_noop("When enabled a shard move will mark the original shards "
653 					 "for deletion after a successful move, instead of deleting "
654 					 "them right away."),
655 		gettext_noop("The deletion of a shard can sometimes run into a conflict with a "
656 					 "long running transactions on a the shard during the drop phase of "
657 					 "the shard move. This causes some moves to be rolled back after "
658 					 "resources have been spend on moving the shard. To prevent "
659 					 "conflicts this feature lets you skip the actual deletion till a "
660 					 "later point in time. When used one should set "
661 					 "citus.defer_shard_delete_interval to make sure defered deletions "
662 					 "will be executed"),
663 		&DeferShardDeleteOnMove,
664 		true,
665 		PGC_USERSET,
666 		0,
667 		NULL, NULL, NULL);
668 
669 	DefineCustomIntVariable(
670 		"citus.defer_shard_delete_interval",
671 		gettext_noop("Sets the time to wait between background deletion for shards."),
672 		gettext_noop("Shards that are marked for deferred deletion need to be deleted in "
673 					 "the background at a later time. This is done at a regular interval "
674 					 "configured here. The deletion is executed optimistically, it tries "
675 					 "to take a lock on a shard to clean, if the lock can't be acquired "
676 					 "the background worker moves on. When set to -1 this background "
677 					 "process is skipped."),
678 		&DeferShardDeleteInterval,
679 		15000, -1, 7 * 24 * 3600 * 1000,
680 		PGC_SIGHUP,
681 		GUC_UNIT_MS,
682 		NULL, NULL, NULL);
683 
684 	DefineCustomRealVariable(
685 		"citus.desired_percent_disk_available_after_move",
686 		gettext_noop(
687 			"Sets how many percentage of free disk space should be after a shard move"),
688 		gettext_noop(
689 			"This setting controls how much free space should be available after a shard move."
690 			"If the free disk space will be lower than this parameter, then shard move will result in"
691 			"an error."),
692 		&DesiredPercentFreeAfterMove,
693 		10.0, 0.0, 100.0,
694 		PGC_SIGHUP,
695 		GUC_STANDARD,
696 		NULL, NULL, NULL);
697 
698 	DefineCustomRealVariable(
699 		"citus.distributed_deadlock_detection_factor",
700 		gettext_noop("Sets the time to wait before checking for distributed "
701 					 "deadlocks. Postgres' deadlock_timeout setting is "
702 					 "multiplied with the value. If the value is set to"
703 					 "1000, distributed deadlock detection is disabled."),
704 		NULL,
705 		&DistributedDeadlockDetectionTimeoutFactor,
706 		2.0, -1.0, 1000.0,
707 		PGC_SIGHUP,
708 		GUC_STANDARD,
709 		ErrorIfNotASuitableDeadlockFactor, NULL, NULL);
710 
711 	DefineCustomBoolVariable(
712 		"citus.enable_alter_database_owner",
713 		gettext_noop("Enables propagating ALTER DATABASE ... OWNER TO ... statements to "
714 					 "workers"),
715 		NULL,
716 		&EnableAlterDatabaseOwner,
717 		false,
718 		PGC_USERSET,
719 		GUC_NO_SHOW_ALL,
720 		NULL, NULL, NULL);
721 
722 	DefineCustomBoolVariable(
723 		"citus.enable_alter_role_propagation",
724 		gettext_noop("Enables propagating ALTER ROLE statements to workers (excluding "
725 					 "ALTER ROLE SET)"),
726 		NULL,
727 		&EnableAlterRolePropagation,
728 		true,
729 		PGC_USERSET,
730 		GUC_NO_SHOW_ALL,
731 		NULL, NULL, NULL);
732 
733 	DefineCustomBoolVariable(
734 		"citus.enable_alter_role_set_propagation",
735 		gettext_noop("Enables propagating ALTER ROLE SET statements to workers"),
736 		NULL,
737 		&EnableAlterRoleSetPropagation,
738 		true,
739 		PGC_USERSET,
740 		GUC_NO_SHOW_ALL,
741 		NULL, NULL, NULL);
742 
743 	DefineCustomBoolVariable(
744 		"citus.enable_binary_protocol",
745 		gettext_noop(
746 			"Enables communication between nodes using binary protocol when possible"),
747 		NULL,
748 		&EnableBinaryProtocol,
749 #if PG_VERSION_NUM >= PG_VERSION_14
750 		true,
751 #else
752 		false,
753 #endif
754 		PGC_USERSET,
755 		GUC_STANDARD,
756 		NULL, NULL, NULL);
757 
758 	DefineCustomBoolVariable(
759 		"citus.enable_cost_based_connection_establishment",
760 		gettext_noop("When enabled the connection establishment times "
761 					 "and task execution times into account for deciding "
762 					 "whether or not to establish new connections."),
763 		NULL,
764 		&EnableCostBasedConnectionEstablishment,
765 		true,
766 		PGC_USERSET,
767 		GUC_NO_SHOW_ALL,
768 		NULL, NULL, NULL);
769 
770 	DefineCustomBoolVariable(
771 		"citus.enable_create_type_propagation",
772 		gettext_noop("Enables propagating of CREATE TYPE statements to workers"),
773 		NULL,
774 		&EnableCreateTypePropagation,
775 		true,
776 		PGC_USERSET,
777 		GUC_NO_SHOW_ALL,
778 		NULL, NULL, NULL);
779 
780 	/*
781 	 * We shouldn't need this variable after we drop support to PostgreSQL 11 and
782 	 * below. So, noting it here with PG_VERSION_NUM < PG_VERSION_12
783 	 */
784 	DefineCustomBoolVariable(
785 		"citus.enable_cte_inlining",
786 		gettext_noop("When set to false, CTE inlining feature is disabled."),
787 		gettext_noop(
788 			"This feature is not intended for users and it is deprecated. It is developed "
789 			"to get consistent regression test outputs between Postgres 11"
790 			"and Postgres 12. In Postgres 12+, the user can control the behaviour"
791 			"by [NOT] MATERIALIZED keyword on CTEs. However, in PG 11, we cannot do "
792 			"that."),
793 		&EnableCTEInlining,
794 		true,
795 		PGC_SUSET,
796 		GUC_NO_SHOW_ALL,
797 		NULL, NULL, NULL);
798 
799 	DefineCustomBoolVariable(
800 		"citus.enable_ddl_propagation",
801 		gettext_noop("Enables propagating DDL statements to worker shards"),
802 		NULL,
803 		&EnableDDLPropagation,
804 		true,
805 		PGC_USERSET,
806 		GUC_NO_SHOW_ALL,
807 		NULL, NULL, NULL);
808 
809 	DefineCustomBoolVariable(
810 		"citus.enable_deadlock_prevention",
811 		gettext_noop("Avoids deadlocks by preventing concurrent multi-shard commands"),
812 		gettext_noop("Multi-shard modifications such as UPDATE, DELETE, and "
813 					 "INSERT...SELECT are typically executed in parallel. If multiple "
814 					 "such commands run concurrently and affect the same rows, then "
815 					 "they are likely to deadlock. When enabled, this flag prevents "
816 					 "multi-shard modifications from running concurrently when they "
817 					 "affect the same shards in order to prevent deadlocks."),
818 		&EnableDeadlockPrevention,
819 		true,
820 		PGC_USERSET,
821 		GUC_STANDARD,
822 		NULL, NULL, NULL);
823 
824 	DefineCustomBoolVariable(
825 		"citus.enable_fast_path_router_planner",
826 		gettext_noop("Enables fast path router planner"),
827 		NULL,
828 		&EnableFastPathRouterPlanner,
829 		true,
830 		PGC_USERSET,
831 		GUC_NO_SHOW_ALL,
832 		NULL, NULL, NULL);
833 
834 	DefineCustomBoolVariable(
835 		"citus.enable_local_execution",
836 		gettext_noop("Enables queries on shards that are local to the current node "
837 					 "to be planned and executed locally."),
838 		NULL,
839 		&EnableLocalExecution,
840 		true,
841 		PGC_USERSET,
842 		GUC_STANDARD,
843 		NULL, NULL, NULL);
844 
845 	DefineCustomBoolVariable(
846 		"citus.enable_local_reference_table_foreign_keys",
847 		gettext_noop("Enables foreign keys from/to local tables"),
848 		gettext_noop("When enabled, foreign keys between local tables and reference "
849 					 "tables supported."),
850 		&EnableLocalReferenceForeignKeys,
851 		true,
852 		PGC_USERSET,
853 		GUC_STANDARD,
854 		NULL, NULL, NULL);
855 
856 	DefineCustomBoolVariable(
857 		"citus.enable_manual_changes_to_shards",
858 		gettext_noop("Enables dropping and truncating known shards."),
859 		gettext_noop("Set to false by default. If set to true, enables "
860 					 "dropping and truncating shards on the coordinator "
861 					 "(or the workers with metadata)"),
862 		&EnableManualChangesToShards,
863 		false,
864 		PGC_USERSET,
865 		GUC_NO_SHOW_ALL,
866 		NULL, NULL, NULL);
867 
868 	DefineCustomStringVariable(
869 		"citus.enable_manual_metadata_changes_for_user",
870 		gettext_noop("Enables some helper UDFs to modify metadata "
871 					 "for the given user"),
872 		NULL,
873 		&EnableManualMetadataChangesForUser,
874 		"",
875 		PGC_SIGHUP,
876 		GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL,
877 		NULL, NULL, NULL);
878 
879 	DefineCustomBoolVariable(
880 		"citus.enable_object_propagation",
881 		gettext_noop("Enables propagating object creation for more complex objects, "
882 					 "schema's will always be created"),
883 		NULL,
884 		&EnableDependencyCreation,
885 		true,
886 		PGC_USERSET,
887 		GUC_NO_SHOW_ALL,
888 		NULL, NULL, NULL);
889 
890 	DefineCustomBoolVariable(
891 		"citus.enable_repartition_joins",
892 		gettext_noop("Allows Citus to repartition data between nodes."),
893 		NULL,
894 		&EnableRepartitionJoins,
895 		false,
896 		PGC_USERSET,
897 		GUC_STANDARD,
898 		NULL, NULL, NULL);
899 
900 	DefineCustomBoolVariable(
901 		"citus.enable_repartitioned_insert_select",
902 		gettext_noop("Enables repartitioned INSERT/SELECTs"),
903 		NULL,
904 		&EnableRepartitionedInsertSelect,
905 		true,
906 		PGC_USERSET,
907 		GUC_NO_SHOW_ALL,
908 		NULL, NULL, NULL);
909 
910 	DefineCustomBoolVariable(
911 		"citus.enable_router_execution",
912 		gettext_noop("Enables router execution"),
913 		NULL,
914 		&EnableRouterExecution,
915 		true,
916 		PGC_USERSET,
917 		GUC_NO_SHOW_ALL,
918 		NULL, NULL, NULL);
919 
920 	DefineCustomBoolVariable(
921 		"citus.enable_single_hash_repartition_joins",
922 		gettext_noop("Enables single hash repartitioning between hash "
923 					 "distributed tables"),
924 		NULL,
925 		&EnableSingleHashRepartitioning,
926 		false,
927 		PGC_USERSET,
928 		GUC_NO_SHOW_ALL,
929 		NULL, NULL, NULL);
930 
931 	DefineCustomBoolVariable(
932 		"citus.enable_statistics_collection",
933 		gettext_noop("Enables sending basic usage statistics to Citus."),
934 		gettext_noop("Citus uploads daily anonymous usage reports containing "
935 					 "rounded node count, shard size, distributed table count, "
936 					 "and operating system name. This configuration value controls "
937 					 "whether these reports are sent."),
938 		&EnableStatisticsCollection,
939 #if defined(HAVE_LIBCURL) && defined(ENABLE_CITUS_STATISTICS_COLLECTION)
940 		true,
941 #else
942 		false,
943 #endif
944 		PGC_SIGHUP,
945 		GUC_SUPERUSER_ONLY,
946 		&StatisticsCollectionGucCheckHook,
947 		NULL, NULL);
948 
949 	DefineCustomBoolVariable(
950 		"citus.enable_unique_job_ids",
951 		gettext_noop("Enables unique job IDs by prepending the local process ID and "
952 					 "group ID. This should usually be enabled, but can be disabled "
953 					 "for repeatable output in regression tests."),
954 		NULL,
955 		&EnableUniqueJobIds,
956 		true,
957 		PGC_USERSET,
958 		GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL,
959 		NULL, NULL, NULL);
960 
961 	DefineCustomBoolVariable(
962 		"citus.enable_version_checks",
963 		gettext_noop("Enables version checks during CREATE/ALTER EXTENSION commands"),
964 		NULL,
965 		&EnableVersionChecks,
966 		true,
967 		PGC_USERSET,
968 		GUC_NO_SHOW_ALL,
969 		NULL, NULL, NULL);
970 
971 	DefineCustomBoolVariable(
972 		"citus.enforce_foreign_key_restrictions",
973 		gettext_noop("Enforce restrictions while querying distributed/reference "
974 					 "tables with foreign keys"),
975 		gettext_noop("When enabled, cascading modifications from reference tables "
976 					 "to distributed tables are traced and acted accordingly "
977 					 "to avoid creating distributed deadlocks and ensure correctness."),
978 		&EnforceForeignKeyRestrictions,
979 		true,
980 		PGC_USERSET,
981 		GUC_NO_SHOW_ALL,
982 		NULL, NULL, NULL);
983 
984 	DefineCustomIntVariable(
985 		"citus.executor_slow_start_interval",
986 		gettext_noop("Time to wait between opening connections to the same worker node"),
987 		gettext_noop("When the individual tasks of a multi-shard query take very "
988 					 "little time, they can often be finished over a single (often "
989 					 "already cached) connection. To avoid redundantly opening "
990 					 "additional connections, the executor waits between connection "
991 					 "attempts for the configured number of milliseconds. At the end "
992 					 "of the interval, it increases the number of connections it is "
993 					 "allowed to open next time."),
994 		&ExecutorSlowStartInterval,
995 		10, 0, INT_MAX,
996 		PGC_USERSET,
997 		GUC_UNIT_MS | GUC_NO_SHOW_ALL,
998 		NULL, NULL, NULL);
999 
1000 	DefineCustomBoolVariable(
1001 		"citus.explain_all_tasks",
1002 		gettext_noop("Enables showing output for all tasks in Explain."),
1003 		gettext_noop("The Explain command for distributed queries shows "
1004 					 "the remote plan for a single task by default. When "
1005 					 "this configuration entry is enabled, the plan for "
1006 					 "all tasks is shown, but the Explain takes longer."),
1007 		&ExplainAllTasks,
1008 		false,
1009 		PGC_USERSET,
1010 		GUC_STANDARD,
1011 		NULL, NULL, NULL);
1012 
1013 	DefineCustomEnumVariable(
1014 		"citus.explain_analyze_sort_method",
1015 		gettext_noop("Sets the sorting method for EXPLAIN ANALYZE queries."),
1016 		gettext_noop("This parameter is intended for testing. It is developed "
1017 					 "to get consistent regression test outputs. When it is set "
1018 					 "to 'time', EXPLAIN ANALYZE output is sorted by execution "
1019 					 "duration on workers. When it is set to 'taskId', it is "
1020 					 "sorted by task id. By default, it is set to 'time'; but "
1021 					 "in regression tests, it's set to 'taskId' for consistency."),
1022 		&ExplainAnalyzeSortMethod,
1023 		EXPLAIN_ANALYZE_SORT_BY_TIME, explain_analyze_sort_method_options,
1024 		PGC_USERSET,
1025 		0,
1026 		NULL, NULL, NULL);
1027 
1028 	DefineCustomBoolVariable(
1029 		"citus.explain_distributed_queries",
1030 		gettext_noop("Enables Explain for distributed queries."),
1031 		gettext_noop("When enabled, the Explain command shows remote and local "
1032 					 "plans when used with a distributed query. It is enabled "
1033 					 "by default, but can be disabled for regression tests."),
1034 		&ExplainDistributedQueries,
1035 		true,
1036 		PGC_USERSET,
1037 		GUC_NO_SHOW_ALL,
1038 		NULL, NULL, NULL);
1039 
1040 	DefineCustomBoolVariable(
1041 		"citus.force_max_query_parallelization",
1042 		gettext_noop("Open as many connections as possible to maximize query "
1043 					 "parallelization"),
1044 		gettext_noop("When enabled, Citus will force the executor to use "
1045 					 "as many connections as possible while executing a "
1046 					 "parallel distributed query. If not enabled, the executor"
1047 					 "might choose to use less connections to optimize overall "
1048 					 "query execution throughput. Internally, setting this true "
1049 					 "will end up with using one connection per task."),
1050 		&ForceMaxQueryParallelization,
1051 		false,
1052 		PGC_USERSET,
1053 		GUC_NO_SHOW_ALL,
1054 		NULL, NULL, NULL);
1055 
1056 	DefineCustomBoolVariable(
1057 		"citus.function_opens_transaction_block",
1058 		gettext_noop("Open transaction blocks for function calls"),
1059 		gettext_noop("When enabled, Citus will always send a BEGIN to workers when "
1060 					 "running distributed queres in a function. When disabled, the "
1061 					 "queries may be committed immediately after the statemnent "
1062 					 "completes. Disabling this flag is dangerous, it is only provided "
1063 					 "for backwards compatibility with pre-8.2 behaviour."),
1064 		&FunctionOpensTransactionBlock,
1065 		true,
1066 		PGC_USERSET,
1067 		GUC_NO_SHOW_ALL,
1068 		NULL, NULL, NULL);
1069 
1070 	DefineCustomIntVariable(
1071 		"citus.isolation_test_session_process_id",
1072 		NULL,
1073 		NULL,
1074 		&IsolationTestSessionProcessID,
1075 		-1, -1, INT_MAX,
1076 		PGC_USERSET,
1077 		GUC_NO_SHOW_ALL,
1078 		NULL, NULL, NULL);
1079 
1080 	DefineCustomIntVariable(
1081 		"citus.isolation_test_session_remote_process_id",
1082 		NULL,
1083 		NULL,
1084 		&IsolationTestSessionRemoteProcessID,
1085 		-1, -1, INT_MAX,
1086 		PGC_USERSET,
1087 		GUC_NO_SHOW_ALL,
1088 		NULL, NULL, NULL);
1089 
1090 	DefineCustomIntVariable(
1091 		"citus.limit_clause_row_fetch_count",
1092 		gettext_noop("Number of rows to fetch per task for limit clause optimization."),
1093 		gettext_noop("Select queries get partitioned and executed as smaller "
1094 					 "tasks. In some cases, select queries with limit clauses "
1095 					 "may need to fetch all rows from each task to generate "
1096 					 "results. In those cases, and where an approximation would "
1097 					 "produce meaningful results, this configuration value sets "
1098 					 "the number of rows to fetch from each task."),
1099 		&LimitClauseRowFetchCount,
1100 		-1, -1, INT_MAX,
1101 		PGC_USERSET,
1102 		GUC_STANDARD,
1103 		NULL, NULL, NULL);
1104 
1105 	DefineCustomIntVariable(
1106 		"citus.local_copy_flush_threshold",
1107 		gettext_noop("Sets the threshold for local copy to be flushed."),
1108 		NULL,
1109 		&LocalCopyFlushThresholdByte,
1110 		512 * 1024, 1, INT_MAX,
1111 		PGC_USERSET,
1112 		GUC_UNIT_BYTE | GUC_NO_SHOW_ALL,
1113 		NULL, NULL, NULL);
1114 
1115 	DefineCustomStringVariable(
1116 		"citus.local_hostname",
1117 		gettext_noop("Sets the hostname when connecting back to itself."),
1118 		gettext_noop("For some operations nodes, mostly the coordinator, connect back to "
1119 					 "itself. When configuring SSL certificates it sometimes is required "
1120 					 "to use a specific hostname to match the CN of the certificate when "
1121 					 "verify-full is used."),
1122 		&LocalHostName,
1123 		"localhost",
1124 		PGC_SUSET,
1125 		GUC_STANDARD,
1126 		NULL, NULL, NULL);
1127 
1128 	DefineCustomIntVariable(
1129 		"citus.local_shared_pool_size",
1130 		gettext_noop(
1131 			"Sets the maximum number of connections allowed for the shards on the "
1132 			"local node across all the backends from this node. Setting to -1 disables "
1133 			"connections throttling. Setting to 0 makes it auto-adjust, meaning "
1134 			"equal to the half of max_connections on the coordinator."),
1135 		gettext_noop("As a rule of thumb, the value should be at most equal to the "
1136 					 "max_connections on the local node."),
1137 		&LocalSharedPoolSize,
1138 		0, -1, INT_MAX,
1139 		PGC_SIGHUP,
1140 		GUC_SUPERUSER_ONLY,
1141 		NULL, NULL, LocalPoolSizeGucShowHook);
1142 
1143 	DefineCustomEnumVariable(
1144 		"citus.local_table_join_policy",
1145 		gettext_noop("defines the behaviour when a distributed table "
1146 					 "is joined with a local table"),
1147 		gettext_noop(
1148 			"There are 4 values available. The default, 'auto' will recursively plan"
1149 			"distributed tables if there is a constant filter on a unique index."
1150 			"'prefer-local' will choose local tables if possible."
1151 			"'prefer-distributed' will choose distributed tables if possible"
1152 			"'never' will basically skip local table joins."
1153 			),
1154 		&LocalTableJoinPolicy,
1155 		LOCAL_JOIN_POLICY_AUTO,
1156 		local_table_join_policies,
1157 		PGC_USERSET,
1158 		GUC_STANDARD,
1159 		NULL, NULL, NULL);
1160 
1161 	DefineCustomBoolVariable(
1162 		"citus.log_distributed_deadlock_detection",
1163 		gettext_noop("Log distributed deadlock detection related processing in "
1164 					 "the server log"),
1165 		NULL,
1166 		&LogDistributedDeadlockDetection,
1167 		false,
1168 		PGC_SIGHUP,
1169 		GUC_NO_SHOW_ALL,
1170 		NULL, NULL, NULL);
1171 
1172 	DefineCustomBoolVariable(
1173 		"citus.log_intermediate_results",
1174 		gettext_noop("Log intermediate results sent to other nodes"),
1175 		NULL,
1176 		&LogIntermediateResults,
1177 		false,
1178 		PGC_USERSET,
1179 		GUC_NO_SHOW_ALL,
1180 		NULL, NULL, NULL);
1181 
1182 	DefineCustomBoolVariable(
1183 		"citus.log_local_commands",
1184 		gettext_noop("Log queries that are executed locally, can be overriden by "
1185 					 "citus.log_remote_commands"),
1186 		NULL,
1187 		&LogLocalCommands,
1188 		false,
1189 		PGC_USERSET,
1190 		GUC_NO_SHOW_ALL,
1191 		NULL, NULL, NULL);
1192 
1193 	DefineCustomBoolVariable(
1194 		"citus.log_multi_join_order",
1195 		gettext_noop("Logs the distributed join order to the server log."),
1196 		gettext_noop("We use this private configuration entry as a debugging aid. "
1197 					 "If enabled, we print the distributed join order."),
1198 		&LogMultiJoinOrder,
1199 		false,
1200 		PGC_USERSET,
1201 		GUC_NO_SHOW_ALL,
1202 		NULL, NULL, NULL);
1203 
1204 	DefineCustomBoolVariable(
1205 		"citus.log_remote_commands",
1206 		gettext_noop("Log queries sent to other nodes in the server log"),
1207 		NULL,
1208 		&LogRemoteCommands,
1209 		false,
1210 		PGC_USERSET,
1211 		GUC_STANDARD,
1212 		NULL, NULL, NULL);
1213 
1214 	DefineCustomIntVariable(
1215 		"citus.max_adaptive_executor_pool_size",
1216 		gettext_noop("Sets the maximum number of connections per worker node used by "
1217 					 "the adaptive executor to execute a multi-shard command"),
1218 		gettext_noop("The adaptive executor may open multiple connections per worker "
1219 					 "node when running multi-shard commands to parallelize the command "
1220 					 "across multiple cores on the worker. This setting specifies the "
1221 					 "maximum number of connections it will open. The number of "
1222 					 "connections is also bounded by the number of shards on the node. "
1223 					 "This setting can be used to reduce the memory usage of a query "
1224 					 "and allow a higher degree of concurrency when concurrent "
1225 					 "multi-shard queries open too many connections to a worker."),
1226 		&MaxAdaptiveExecutorPoolSize,
1227 		16, 1, INT_MAX,
1228 		PGC_USERSET,
1229 		GUC_STANDARD,
1230 		NULL, NULL, NULL);
1231 
1232 	DefineCustomIntVariable(
1233 		"citus.max_cached_connection_lifetime",
1234 		gettext_noop("Sets the maximum lifetime of cached connections to other nodes."),
1235 		NULL,
1236 		&MaxCachedConnectionLifetime,
1237 		10 * MS_PER_MINUTE, -1, INT_MAX,
1238 		PGC_USERSET,
1239 		GUC_UNIT_MS | GUC_STANDARD,
1240 		NULL, NULL, NULL);
1241 
1242 	DefineCustomIntVariable(
1243 		"citus.max_cached_conns_per_worker",
1244 		gettext_noop("Sets the maximum number of connections to cache per worker."),
1245 		gettext_noop("Each backend opens connections to the workers to query the "
1246 					 "shards. At the end of the transaction, the configurated number "
1247 					 "of connections is kept open to speed up subsequent commands. "
1248 					 "Increasing this value will reduce the latency of multi-shard "
1249 					 "queries, but increases overhead on the workers"),
1250 		&MaxCachedConnectionsPerWorker,
1251 		1, 0, INT_MAX,
1252 		PGC_USERSET,
1253 		GUC_STANDARD,
1254 		NULL, NULL, NULL);
1255 
1256 	DefineCustomIntVariable(
1257 		"citus.max_intermediate_result_size",
1258 		gettext_noop("Sets the maximum size of the intermediate results in KB for "
1259 					 "CTEs and complex subqueries."),
1260 		NULL,
1261 		&MaxIntermediateResult,
1262 		1048576, -1, MAX_KILOBYTES,
1263 		PGC_USERSET,
1264 		GUC_UNIT_KB | GUC_STANDARD,
1265 		NULL, NULL, NULL);
1266 
1267 	DefineCustomIntVariable(
1268 		"citus.max_rebalancer_logged_ignored_moves",
1269 		gettext_noop("Sets the maximum number of ignored moves the rebalance logs"),
1270 		NULL,
1271 		&MaxRebalancerLoggedIgnoredMoves,
1272 		5, -1, INT_MAX,
1273 		PGC_USERSET,
1274 		GUC_NO_SHOW_ALL,
1275 		NULL, NULL, NULL);
1276 
1277 	DefineCustomIntVariable(
1278 		"citus.max_shared_pool_size",
1279 		gettext_noop("Sets the maximum number of connections allowed per worker node "
1280 					 "across all the backends from this node. Setting to -1 disables "
1281 					 "connections throttling. Setting to 0 makes it auto-adjust, meaning "
1282 					 "equal to max_connections on the coordinator."),
1283 		gettext_noop("As a rule of thumb, the value should be at most equal to the "
1284 					 "max_connections on the remote nodes."),
1285 		&MaxSharedPoolSize,
1286 		0, -1, INT_MAX,
1287 		PGC_SIGHUP,
1288 		GUC_SUPERUSER_ONLY,
1289 		NULL, NULL, MaxSharedPoolSizeGucShowHook);
1290 
1291 	DefineCustomIntVariable(
1292 		"citus.max_worker_nodes_tracked",
1293 		gettext_noop("Sets the maximum number of worker nodes that are tracked."),
1294 		gettext_noop("Worker nodes' network locations, their membership and "
1295 					 "health status are tracked in a shared hash table on "
1296 					 "the master node. This configuration value limits the "
1297 					 "size of the hash table, and consequently the maximum "
1298 					 "number of worker nodes that can be tracked."
1299 					 "Citus keeps some information about the worker nodes "
1300 					 "in the shared memory for certain optimizations. The "
1301 					 "optimizations are enforced up to this number of worker "
1302 					 "nodes. Any additional worker nodes may not benefit from"
1303 					 "the optimizations."),
1304 		&MaxWorkerNodesTracked,
1305 		2048, 1024, INT_MAX,
1306 		PGC_POSTMASTER,
1307 		GUC_STANDARD,
1308 		NULL, NULL, NULL);
1309 
1310 	DefineCustomIntVariable(
1311 		"citus.metadata_sync_interval",
1312 		gettext_noop("Sets the time to wait between metadata syncs."),
1313 		gettext_noop("metadata sync needs to run every so often "
1314 					 "to synchronize metadata to metadata nodes "
1315 					 "that are out of sync."),
1316 		&MetadataSyncInterval,
1317 		60 * MS_PER_SECOND, 1, 7 * MS_PER_DAY,
1318 		PGC_SIGHUP,
1319 		GUC_UNIT_MS | GUC_NO_SHOW_ALL,
1320 		NULL, NULL, NULL);
1321 
1322 	DefineCustomIntVariable(
1323 		"citus.metadata_sync_retry_interval",
1324 		gettext_noop("Sets the interval to retry failed metadata syncs."),
1325 		gettext_noop("metadata sync needs to run every so often "
1326 					 "to synchronize metadata to metadata nodes "
1327 					 "that are out of sync."),
1328 		&MetadataSyncRetryInterval,
1329 		5 * MS_PER_SECOND, 1, 7 * MS_PER_DAY,
1330 		PGC_SIGHUP,
1331 		GUC_UNIT_MS | GUC_NO_SHOW_ALL,
1332 		NULL, NULL, NULL);
1333 
1334 	DefineCustomEnumVariable(
1335 		"citus.multi_shard_commit_protocol",
1336 		gettext_noop("Sets the commit protocol for commands modifying multiple shards."),
1337 		gettext_noop("When a failure occurs during commands that modify multiple "
1338 					 "shards, two-phase commit is required to ensure data is never lost "
1339 					 "and this is the default. However, changing to 1pc may give small "
1340 					 "performance benefits."),
1341 		&MultiShardCommitProtocol,
1342 		COMMIT_PROTOCOL_2PC,
1343 		shard_commit_protocol_options,
1344 		PGC_USERSET,
1345 		GUC_STANDARD,
1346 		NULL, NULL, NULL);
1347 
1348 	DefineCustomEnumVariable(
1349 		"citus.multi_shard_modify_mode",
1350 		gettext_noop("Sets the connection type for multi shard modify queries"),
1351 		NULL,
1352 		&MultiShardConnectionType,
1353 		PARALLEL_CONNECTION, multi_shard_modify_connection_options,
1354 		PGC_USERSET,
1355 		GUC_STANDARD,
1356 		NULL, NULL, NULL);
1357 
1358 	DefineCustomEnumVariable(
1359 		"citus.multi_task_query_log_level",
1360 		gettext_noop("Sets the level of multi task query execution log messages"),
1361 		NULL,
1362 		&MultiTaskQueryLogLevel,
1363 		CITUS_LOG_LEVEL_OFF, log_level_options,
1364 		PGC_USERSET,
1365 		GUC_STANDARD,
1366 		NULL, NULL, NULL);
1367 
1368 	DefineCustomIntVariable(
1369 		"citus.next_placement_id",
1370 		gettext_noop("Set the next placement ID to use in placement creation."),
1371 		gettext_noop("Placement IDs are normally generated using a sequence. If "
1372 					 "next_placement_id is set to a non-zero value, placement IDs will "
1373 					 "instead be generated by incrementing from the value of "
1374 					 "this GUC and this will be reflected in the GUC. This is "
1375 					 "mainly useful to ensure consistent placement IDs when running "
1376 					 "tests in parallel."),
1377 		&NextPlacementId,
1378 		0, 0, INT_MAX,
1379 		PGC_USERSET,
1380 		GUC_NO_SHOW_ALL,
1381 		NULL, NULL, NULL);
1382 
1383 	DefineCustomIntVariable(
1384 		"citus.next_shard_id",
1385 		gettext_noop("Set the next shard ID to use in shard creation."),
1386 		gettext_noop("Shard IDs are normally generated using a sequence. If "
1387 					 "next_shard_id is set to a non-zero value, shard IDs will "
1388 					 "instead be generated by incrementing from the value of "
1389 					 "this GUC and this will be reflected in the GUC. This is "
1390 					 "mainly useful to ensure consistent shard IDs when running "
1391 					 "tests in parallel."),
1392 		&NextShardId,
1393 		0, 0, INT_MAX,
1394 		PGC_USERSET,
1395 		GUC_NO_SHOW_ALL,
1396 		NULL, NULL, NULL);
1397 
1398 	DefineCustomIntVariable(
1399 		"citus.node_connection_timeout",
1400 		gettext_noop("Sets the maximum duration to connect to worker nodes."),
1401 		NULL,
1402 		&NodeConnectionTimeout,
1403 		30 * MS_PER_SECOND, 10 * MS, MS_PER_HOUR,
1404 		PGC_USERSET,
1405 		GUC_UNIT_MS | GUC_STANDARD,
1406 		NULL, NULL, NULL);
1407 
1408 	DefineCustomStringVariable(
1409 		"citus.node_conninfo",
1410 		gettext_noop("Sets parameters used for outbound connections."),
1411 		NULL,
1412 		&NodeConninfo,
1413 #ifdef USE_SSL
1414 		"sslmode=require",
1415 #else
1416 		"sslmode=prefer",
1417 #endif
1418 		PGC_SIGHUP,
1419 		GUC_SUPERUSER_ONLY,
1420 		NodeConninfoGucCheckHook,
1421 		NodeConninfoGucAssignHook,
1422 		NULL);
1423 
1424 	DefineCustomBoolVariable(
1425 		"citus.override_table_visibility",
1426 		gettext_noop("Enables replacing occurencens of pg_catalog.pg_table_visible() "
1427 					 "with pg_catalog.citus_table_visible()"),
1428 		gettext_noop("When enabled, shards on the Citus MX worker (data) nodes would be "
1429 					 "filtered out by many psql commands to provide better user "
1430 					 "experience."),
1431 		&OverrideTableVisibility,
1432 		true,
1433 		PGC_USERSET,
1434 		GUC_NO_SHOW_ALL,
1435 		NULL, NULL, NULL);
1436 
1437 	DefineCustomIntVariable(
1438 		"citus.partition_buffer_size",
1439 		gettext_noop("Sets the buffer size to use for partition operations."),
1440 		gettext_noop("Worker nodes allow for table data to be repartitioned "
1441 					 "into multiple text files, much like Hadoop's Map "
1442 					 "command. This configuration value sets the buffer size "
1443 					 "to use per partition operation. After the buffer fills "
1444 					 "up, we flush the repartitioned data into text files."),
1445 		&PartitionBufferSize,
1446 		8192, 0, (INT_MAX / 1024), /* result stored in int variable */
1447 		PGC_USERSET,
1448 		GUC_UNIT_KB | GUC_STANDARD,
1449 		NULL, NULL, NULL);
1450 
1451 	DefineCustomBoolVariable(
1452 		"citus.prevent_incomplete_connection_establishment",
1453 		gettext_noop("When enabled, the executor waits until all the connections "
1454 					 "are successfully established."),
1455 		gettext_noop("Under some load, the executor may decide to establish some "
1456 					 "extra connections to further parallelize the execution. However,"
1457 					 "before the connection establishment is done, the execution might "
1458 					 "have already finished. When this GUC is set to true, the execution "
1459 					 "waits for such connections to be established."),
1460 		&PreventIncompleteConnectionEstablishment,
1461 		true,
1462 		PGC_USERSET,
1463 		GUC_NO_SHOW_ALL,
1464 		NULL, NULL, NULL);
1465 
1466 	DefineCustomEnumVariable(
1467 		"citus.propagate_set_commands",
1468 		gettext_noop("Sets which SET commands are propagated to workers."),
1469 		NULL,
1470 		&PropagateSetCommands,
1471 		PROPSETCMD_NONE,
1472 		propagate_set_commands_options,
1473 		PGC_USERSET,
1474 		GUC_STANDARD,
1475 		NULL, NULL, NULL);
1476 
1477 	DefineCustomIntVariable(
1478 		"citus.recover_2pc_interval",
1479 		gettext_noop("Sets the time to wait between recovering 2PCs."),
1480 		gettext_noop("2PC transaction recovery needs to run every so often "
1481 					 "to clean up records in pg_dist_transaction and "
1482 					 "potentially roll failed 2PCs forward. This setting "
1483 					 "determines how often recovery should run, "
1484 					 "use -1 to disable."),
1485 		&Recover2PCInterval,
1486 		60 * MS_PER_SECOND, -1, 7 * MS_PER_DAY,
1487 		PGC_SIGHUP,
1488 		GUC_UNIT_MS | GUC_STANDARD,
1489 		NULL, NULL, NULL);
1490 
1491 	DefineCustomIntVariable(
1492 		"citus.remote_copy_flush_threshold",
1493 		gettext_noop("Sets the threshold for remote copy to be flushed."),
1494 		gettext_noop("When sending data over remote connections via the COPY protocol, "
1495 					 "bytes are first buffered internally by libpq. If the number of "
1496 					 "bytes buffered exceeds the threshold, Citus waits for all the "
1497 					 "bytes to flush."),
1498 		&RemoteCopyFlushThreshold,
1499 		8 * 1024 * 1024, 0, INT_MAX,
1500 		PGC_USERSET,
1501 		GUC_UNIT_BYTE | GUC_NO_SHOW_ALL,
1502 		NULL, NULL, NULL);
1503 
1504 	DefineCustomIntVariable(
1505 		"citus.remote_task_check_interval",
1506 		gettext_noop("Sets the frequency at which we check job statuses."),
1507 		gettext_noop("The master node assigns tasks to workers nodes, and "
1508 					 "then regularly checks with them about each task's "
1509 					 "progress. This configuration value sets the time "
1510 					 "interval between two consequent checks."),
1511 		&RemoteTaskCheckInterval,
1512 		10, 1, INT_MAX,
1513 		PGC_USERSET,
1514 		GUC_UNIT_MS | GUC_STANDARD,
1515 		NULL, NULL, NULL);
1516 
1517 	DefineCustomIntVariable(
1518 		"citus.repartition_join_bucket_count_per_node",
1519 		gettext_noop("Sets the bucket size for repartition joins per node"),
1520 		gettext_noop("Repartition joins create buckets in each node and "
1521 					 "uses those to shuffle data around nodes. "),
1522 		&RepartitionJoinBucketCountPerNode,
1523 		4, 1, INT_MAX,
1524 		PGC_SIGHUP,
1525 		GUC_STANDARD | GUC_NO_SHOW_ALL,
1526 		NULL, NULL, NULL);
1527 
1528 	DefineCustomBoolVariable(
1529 		"citus.replicate_reference_tables_on_activate",
1530 		NULL,
1531 		NULL,
1532 		&ReplicateReferenceTablesOnActivate,
1533 		true,
1534 		PGC_USERSET,
1535 		GUC_NO_SHOW_ALL,
1536 		NULL, NULL, NULL);
1537 
1538 	DefineCustomEnumVariable(
1539 		"citus.replication_model",
1540 		gettext_noop("Deprecated. Please use citus.shard_replication_factor instead"),
1541 		gettext_noop(
1542 			"Shard replication model is determined by the shard replication factor."
1543 			"'statement' replication is used only when the replication factor is one."),
1544 		&ReplicationModel,
1545 		REPLICATION_MODEL_STREAMING,
1546 		replication_model_options,
1547 		PGC_SUSET,
1548 		GUC_NO_SHOW_ALL,
1549 		WarnIfReplicationModelIsSet, NULL, NULL);
1550 
1551 	DefineCustomBoolVariable(
1552 		"citus.running_under_isolation_test",
1553 		gettext_noop(
1554 			"Only useful for testing purposes, when set to true, Citus does some "
1555 			"tricks to implement useful isolation tests with rebalancing. Should "
1556 			"never be set to true on production systems "),
1557 		gettext_noop("for details of the tricks implemented, refer to the source code"),
1558 		&RunningUnderIsolationTest,
1559 		false,
1560 		PGC_SUSET,
1561 		GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL,
1562 		NULL, NULL, NULL);
1563 
1564 	DefineCustomBoolVariable(
1565 		"citus.select_opens_transaction_block",
1566 		gettext_noop("Open transaction blocks for SELECT commands"),
1567 		gettext_noop("When enabled, Citus will always send a BEGIN to workers when "
1568 					 "running a distributed SELECT in a transaction block (the "
1569 					 "default). When disabled, Citus will only send BEGIN before "
1570 					 "the first write or other operation that requires a distributed "
1571 					 "transaction, meaning the SELECT on the worker commits "
1572 					 "immediately, releasing any locks and apply any changes made "
1573 					 "through function calls even if the distributed transaction "
1574 					 "aborts."),
1575 		&SelectOpensTransactionBlock,
1576 		true,
1577 		PGC_USERSET,
1578 		GUC_NO_SHOW_ALL,
1579 		NULL, NULL, NULL);
1580 
1581 	DefineCustomIntVariable(
1582 		"citus.shard_count",
1583 		gettext_noop("Sets the number of shards for a new hash-partitioned table"
1584 					 "created with create_distributed_table()."),
1585 		NULL,
1586 		&ShardCount,
1587 		32, 1, MAX_SHARD_COUNT,
1588 		PGC_USERSET,
1589 		GUC_STANDARD,
1590 		NULL, NULL, NULL);
1591 
1592 	DefineCustomIntVariable(
1593 		"citus.shard_max_size",
1594 		gettext_noop("Sets the maximum size a shard will grow before it gets split."),
1595 		gettext_noop("Shards store table and file data. When the source "
1596 					 "file's size for one shard exceeds this configuration "
1597 					 "value, the database ensures that either a new shard "
1598 					 "gets created, or the current one gets split. Note that "
1599 					 "shards read this configuration value at sharded table "
1600 					 "creation time, and later reuse the initially read value."),
1601 		&ShardMaxSize,
1602 		1048576, 256, INT_MAX, /* max allowed size not set to MAX_KILOBYTES on purpose */
1603 		PGC_USERSET,
1604 		GUC_UNIT_KB | GUC_STANDARD,
1605 		NULL, NULL, NULL);
1606 
1607 	DefineCustomEnumVariable(
1608 		"citus.shard_placement_policy",
1609 		gettext_noop("Sets the policy to use when choosing nodes for shard placement."),
1610 		gettext_noop("The master node chooses which worker nodes to place new shards "
1611 					 "on. This configuration value specifies the policy to use when "
1612 					 "selecting these nodes. The local-node-first policy places the "
1613 					 "first replica on the client node and chooses others randomly. "
1614 					 "The round-robin policy aims to distribute shards evenly across "
1615 					 "the cluster by selecting nodes in a round-robin fashion."
1616 					 "The random policy picks all workers randomly."),
1617 		&ShardPlacementPolicy,
1618 		SHARD_PLACEMENT_ROUND_ROBIN, shard_placement_policy_options,
1619 		PGC_USERSET,
1620 		GUC_STANDARD,
1621 		NULL, NULL, NULL);
1622 
1623 	DefineCustomIntVariable(
1624 		"citus.shard_replication_factor",
1625 		gettext_noop("Sets the replication factor for shards."),
1626 		gettext_noop("Shards are replicated across nodes according to this "
1627 					 "replication factor. Note that shards read this "
1628 					 "configuration value at sharded table creation time, "
1629 					 "and later reuse the initially read value."),
1630 		&ShardReplicationFactor,
1631 		1, 1, MAX_SHARD_REPLICATION_FACTOR,
1632 		PGC_USERSET,
1633 		GUC_STANDARD,
1634 		NULL, NULL, NULL);
1635 
1636 	DefineCustomEnumVariable(
1637 		"citus.single_shard_commit_protocol",
1638 		gettext_noop(
1639 			"Sets the commit protocol for commands modifying a single shards with multiple replicas."),
1640 		gettext_noop("When a failure occurs during commands that modify multiple "
1641 					 "replicas, two-phase commit is required to ensure data is never lost "
1642 					 "and this is the default. However, changing to 1pc may give small "
1643 					 "performance benefits."),
1644 		&SingleShardCommitProtocol,
1645 		COMMIT_PROTOCOL_2PC,
1646 		shard_commit_protocol_options,
1647 		PGC_USERSET,
1648 		GUC_NO_SHOW_ALL,
1649 		NULL, NULL, NULL);
1650 
1651 	DefineCustomBoolVariable(
1652 		"citus.sort_returning",
1653 		gettext_noop("Sorts the RETURNING clause to get consistent test output"),
1654 		gettext_noop("This feature is not intended for users. It is developed "
1655 					 "to get consistent regression test outputs. When enabled, "
1656 					 "the RETURNING clause returns the tuples sorted. The sort "
1657 					 "is done for all the entries, starting from the first one."
1658 					 "Finally, the sorting is done in ASC order."),
1659 		&SortReturning,
1660 		false,
1661 		PGC_SUSET,
1662 		GUC_NO_SHOW_ALL,
1663 		NULL, NULL, NULL);
1664 
1665 	DefineCustomBoolVariable(
1666 		"citus.subquery_pushdown",
1667 		gettext_noop("Usage of this GUC is highly discouraged, please read the long "
1668 					 "description"),
1669 		gettext_noop("When enabled, the planner skips many correctness checks "
1670 					 "for subqueries and pushes down the queries to shards as-is. "
1671 					 "It means that the queries are likely to return wrong results "
1672 					 "unless the user is absolutely sure that pushing down the "
1673 					 "subquery is safe. This GUC is maintained only for backward "
1674 					 "compatibility, no new users are supposed to use it. The planner"
1675 					 "is capable of pushing down as much computation as possible to the "
1676 					 "shards depending on the query."),
1677 		&SubqueryPushdown,
1678 		false,
1679 		PGC_USERSET,
1680 		GUC_NO_SHOW_ALL,
1681 		NoticeIfSubqueryPushdownEnabled, NULL, NULL);
1682 
1683 	DefineCustomEnumVariable(
1684 		"citus.task_assignment_policy",
1685 		gettext_noop("Sets the policy to use when assigning tasks to worker nodes."),
1686 		gettext_noop("The master node assigns tasks to worker nodes based on shard "
1687 					 "locations. This configuration value specifies the policy to "
1688 					 "use when making these assignments. The greedy policy aims to "
1689 					 "evenly distribute tasks across worker nodes, first-replica just "
1690 					 "assigns tasks in the order shard placements were created, "
1691 					 "and the round-robin policy assigns tasks to worker nodes in "
1692 					 "a round-robin fashion."),
1693 		&TaskAssignmentPolicy,
1694 		TASK_ASSIGNMENT_GREEDY,
1695 		task_assignment_policy_options,
1696 		PGC_USERSET,
1697 		GUC_STANDARD,
1698 		NULL, NULL, NULL);
1699 
1700 	DefineCustomEnumVariable(
1701 		"citus.task_executor_type",
1702 		gettext_noop("Sets the executor type to be used for distributed queries."),
1703 		gettext_noop("The master node chooses between two different executor types "
1704 					 "when executing a distributed query.The adaptive executor is "
1705 					 "optimal for simple key-value lookup queries and queries that "
1706 					 "involve aggregations and/or co-located joins on multiple shards. "),
1707 		&TaskExecutorType,
1708 		MULTI_EXECUTOR_ADAPTIVE,
1709 		task_executor_type_options,
1710 		PGC_USERSET,
1711 		GUC_STANDARD,
1712 		WarnIfDeprecatedExecutorUsed, NULL, NULL);
1713 
1714 	DefineCustomEnumVariable(
1715 		"citus.use_secondary_nodes",
1716 		gettext_noop("Sets the policy to use when choosing nodes for SELECT queries."),
1717 		NULL,
1718 		&ReadFromSecondaries,
1719 		USE_SECONDARY_NODES_NEVER, use_secondary_nodes_options,
1720 		PGC_SU_BACKEND,
1721 		GUC_STANDARD,
1722 		NULL, NULL, NULL);
1723 
1724 	DefineCustomIntVariable(
1725 		"citus.values_materialization_threshold",
1726 		gettext_noop("Sets the maximum number of rows allowed for pushing down "
1727 					 "VALUES clause in multi-shard queries. If the number of "
1728 					 "rows exceeds the threshold, the VALUES is materialized "
1729 					 "via pull-push execution. When set to -1, materialization "
1730 					 "is disabled. When set to 0, all VALUES are materialized."),
1731 		gettext_noop("When the VALUES is pushed down (i.e., not materialized), "
1732 					 "the VALUES clause needs to be deparsed for every shard on "
1733 					 "the coordinator - and parsed on the workers. As this "
1734 					 "setting increased, the associated overhead is multiplied "
1735 					 "by the shard count. When materialized, the VALUES is "
1736 					 "deparsed and parsed once. The downside of materialization "
1737 					 "is that Postgres may choose a poor plan when joining "
1738 					 "the materialized result with tables."),
1739 		&ValuesMaterializationThreshold,
1740 		100, -1, INT_MAX,
1741 		PGC_USERSET,
1742 		GUC_STANDARD,
1743 		NULL, NULL, NULL);
1744 
1745 	DefineCustomStringVariable(
1746 		"citus.version",
1747 		gettext_noop("Shows the Citus library version"),
1748 		NULL,
1749 		&CitusVersion,
1750 		CITUS_VERSION,
1751 		PGC_INTERNAL,
1752 		GUC_STANDARD,
1753 		NULL, NULL, NULL);
1754 
1755 	DefineCustomEnumVariable(
1756 		"citus.worker_min_messages",
1757 		gettext_noop("Log messages from workers only if their log level is at or above "
1758 					 "the configured level"),
1759 		NULL,
1760 		&WorkerMinMessages,
1761 		NOTICE,
1762 		log_level_options,
1763 		PGC_USERSET,
1764 		GUC_STANDARD,
1765 		NULL, NULL, NULL);
1766 
1767 	DefineCustomBoolVariable(
1768 		"citus.writable_standby_coordinator",
1769 		gettext_noop("Enables simple DML via a streaming replica of the coordinator"),
1770 		NULL,
1771 		&WritableStandbyCoordinator,
1772 		false,
1773 		PGC_USERSET,
1774 		GUC_STANDARD,
1775 		NULL, NULL, NULL);
1776 
1777 	/* warn about config items in the citus namespace that are not registered above */
1778 	EmitWarningsOnPlaceholders("citus");
1779 }
1780 
1781 
1782 /*
1783  * We don't want to allow values less than 1.0. However, we define -1 as the value to disable
1784  * distributed deadlock checking. Here we enforce our special constraint.
1785  */
1786 static bool
ErrorIfNotASuitableDeadlockFactor(double * newval,void ** extra,GucSource source)1787 ErrorIfNotASuitableDeadlockFactor(double *newval, void **extra, GucSource source)
1788 {
1789 	if (*newval <= 1.0 && *newval != -1.0)
1790 	{
1791 		ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1792 						  errmsg(
1793 							  "citus.distributed_deadlock_detection_factor cannot be less than 1. "
1794 							  "To disable distributed deadlock detection set the value to -1.")));
1795 
1796 		return false;
1797 	}
1798 
1799 	return true;
1800 }
1801 
1802 
1803 /*
1804  * WarnIfDeprecatedExecutorUsed prints a warning and sets the config value to
1805  * adaptive executor (a.k.a., ignores real-time executor).
1806  */
1807 static bool
WarnIfDeprecatedExecutorUsed(int * newval,void ** extra,GucSource source)1808 WarnIfDeprecatedExecutorUsed(int *newval, void **extra, GucSource source)
1809 {
1810 	if (*newval == DUMMY_REAL_TIME_EXECUTOR_ENUM_VALUE)
1811 	{
1812 		ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1813 						  errmsg("Ignoring the setting, real-time executor is "
1814 								 "deprecated")));
1815 
1816 		/* adaptive executor is superset of real-time, so switch to that */
1817 		*newval = MULTI_EXECUTOR_ADAPTIVE;
1818 	}
1819 
1820 	return true;
1821 }
1822 
1823 
1824 /*
1825  * NoticeIfSubqueryPushdownEnabled prints a notice when a user sets
1826  * citus.subquery_pushdown to ON. It doesn't print the notice if the
1827  * value is already true.
1828  */
1829 static bool
NoticeIfSubqueryPushdownEnabled(bool * newval,void ** extra,GucSource source)1830 NoticeIfSubqueryPushdownEnabled(bool *newval, void **extra, GucSource source)
1831 {
1832 	/* notice only when the value changes */
1833 	if (*newval == true && SubqueryPushdown == false)
1834 	{
1835 		ereport(NOTICE, (errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
1836 						 errmsg("Setting citus.subquery_pushdown flag is "
1837 								"discouraged becuase it forces the planner "
1838 								"to pushdown certain queries, skipping "
1839 								"relevant correctness checks."),
1840 						 errdetail(
1841 							 "When enabled, the planner skips many correctness checks "
1842 							 "for subqueries and pushes down the queries to shards as-is. "
1843 							 "It means that the queries are likely to return wrong results "
1844 							 "unless the user is absolutely sure that pushing down the "
1845 							 "subquery is safe. This GUC is maintained only for backward "
1846 							 "compatibility, no new users are supposed to use it. The planner "
1847 							 "is capable of pushing down as much computation as possible to the "
1848 							 "shards depending on the query.")));
1849 	}
1850 
1851 	return true;
1852 }
1853 
1854 
1855 /*
1856  * WarnIfReplicationModelIsSet prints a warning when a user sets
1857  * citus.replication_model.
1858  */
1859 static bool
WarnIfReplicationModelIsSet(int * newval,void ** extra,GucSource source)1860 WarnIfReplicationModelIsSet(int *newval, void **extra, GucSource source)
1861 {
1862 	/* print a warning only when user sets the guc */
1863 	if (source == PGC_S_SESSION)
1864 	{
1865 		ereport(NOTICE, (errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
1866 						 errmsg(
1867 							 "Setting citus.replication_model has no effect. Please use "
1868 							 "citus.shard_replication_factor instead."),
1869 						 errdetail(
1870 							 "Citus determines the replication model based on the "
1871 							 "replication factor and the replication models of the colocated "
1872 							 "shards. If a colocated table is present, the replication model "
1873 							 "is inherited. Otherwise 'streaming' replication is preferred if "
1874 							 "supported by the replication factor.")));
1875 	}
1876 
1877 	return true;
1878 }
1879 
1880 
1881 /*
1882  * NodeConninfoGucCheckHook ensures conninfo settings are in the expected form
1883  * and that the keywords of all non-null settings are on a allowlist devised to
1884  * keep users from setting options that may result in confusion.
1885  */
1886 static bool
NodeConninfoGucCheckHook(char ** newval,void ** extra,GucSource source)1887 NodeConninfoGucCheckHook(char **newval, void **extra, GucSource source)
1888 {
1889 	/* this array _must_ be kept in an order usable by bsearch */
1890 	const char *allowedConninfoKeywords[] = {
1891 		"application_name",
1892 		"connect_timeout",
1893 			#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
1894 		"gsslib",
1895 			#endif
1896 		"keepalives",
1897 		"keepalives_count",
1898 		"keepalives_idle",
1899 		"keepalives_interval",
1900 			#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
1901 		"krbsrvname",
1902 			#endif
1903 		"sslcompression",
1904 		"sslcrl",
1905 		"sslmode",
1906 		"sslrootcert",
1907 		"tcp_user_timeout",
1908 	};
1909 	char *errorMsg = NULL;
1910 	bool conninfoValid = CheckConninfo(*newval, allowedConninfoKeywords,
1911 									   lengthof(allowedConninfoKeywords), &errorMsg);
1912 
1913 	if (!conninfoValid)
1914 	{
1915 		GUC_check_errdetail("%s", errorMsg);
1916 	}
1917 
1918 	return conninfoValid;
1919 }
1920 
1921 
1922 /*
1923  * NodeConninfoGucAssignHook is the assignment hook for the node_conninfo GUC
1924  * variable. Though this GUC is a "string", we actually parse it as a non-URI
1925  * PQconninfo key/value setting, storing the resultant PQconninfoOption values
1926  * using the public functions in connection_configuration.c.
1927  */
1928 static void
NodeConninfoGucAssignHook(const char * newval,void * extra)1929 NodeConninfoGucAssignHook(const char *newval, void *extra)
1930 {
1931 	if (newval == NULL)
1932 	{
1933 		newval = "";
1934 	}
1935 
1936 	if (strcmp(newval, NodeConninfo) == 0)
1937 	{
1938 		/* It did not change, no need to do anything */
1939 		return;
1940 	}
1941 
1942 	PQconninfoOption *optionArray = PQconninfoParse(newval, NULL);
1943 	if (optionArray == NULL)
1944 	{
1945 		ereport(FATAL, (errmsg("cannot parse node_conninfo value"),
1946 						errdetail("The GUC check hook should prevent "
1947 								  "all malformed values.")));
1948 	}
1949 
1950 	ResetConnParams();
1951 
1952 	for (PQconninfoOption *option = optionArray; option->keyword != NULL; option++)
1953 	{
1954 		if (option->val == NULL || option->val[0] == '\0')
1955 		{
1956 			continue;
1957 		}
1958 
1959 		AddConnParam(option->keyword, option->val);
1960 	}
1961 
1962 	PQconninfoFree(optionArray);
1963 
1964 	/*
1965 	 * Mark all connections for shutdown, since they have been opened using old
1966 	 * connection settings. This is mostly important when changing SSL
1967 	 * parameters, otherwise these would not be applied and connections could
1968 	 * be unencrypted when the user doesn't want that.
1969 	 */
1970 	CloseAllConnectionsAfterTransaction();
1971 }
1972 
1973 
1974 /*
1975  * MaxSharedPoolSizeGucShowHook overrides the value that is shown to the
1976  * user when the default value has not been set.
1977  */
1978 static const char *
MaxSharedPoolSizeGucShowHook(void)1979 MaxSharedPoolSizeGucShowHook(void)
1980 {
1981 	StringInfo newvalue = makeStringInfo();
1982 
1983 	if (MaxSharedPoolSize == 0)
1984 	{
1985 		appendStringInfo(newvalue, "%d", GetMaxSharedPoolSize());
1986 	}
1987 	else
1988 	{
1989 		appendStringInfo(newvalue, "%d", MaxSharedPoolSize);
1990 	}
1991 
1992 	return (const char *) newvalue->data;
1993 }
1994 
1995 
1996 /*
1997  * LocalPoolSizeGucShowHook overrides the value that is shown to the
1998  * user when the default value has not been set.
1999  */
2000 static const char *
LocalPoolSizeGucShowHook(void)2001 LocalPoolSizeGucShowHook(void)
2002 {
2003 	StringInfo newvalue = makeStringInfo();
2004 
2005 	appendStringInfo(newvalue, "%d", GetLocalSharedPoolSize());
2006 
2007 	return (const char *) newvalue->data;
2008 }
2009 
2010 
2011 static bool
StatisticsCollectionGucCheckHook(bool * newval,void ** extra,GucSource source)2012 StatisticsCollectionGucCheckHook(bool *newval, void **extra, GucSource source)
2013 {
2014 #ifdef HAVE_LIBCURL
2015 	return true;
2016 #else
2017 
2018 	/* if libcurl is not installed, only accept false */
2019 	if (*newval)
2020 	{
2021 		GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED);
2022 		GUC_check_errdetail("Citus was compiled without libcurl support.");
2023 		return false;
2024 	}
2025 	else
2026 	{
2027 		return true;
2028 	}
2029 #endif
2030 }
2031 
2032 
2033 /*
2034  * CitusAuthHook is a callback for client authentication that Postgres provides.
2035  * Citus uses this hook to count the number of active backends.
2036  */
2037 static void
CitusAuthHook(Port * port,int status)2038 CitusAuthHook(Port *port, int status)
2039 {
2040 	/* let other authentication hooks to kick in first */
2041 	if (original_client_auth_hook)
2042 	{
2043 		original_client_auth_hook(port, status);
2044 	}
2045 
2046 	RegisterClientBackendCounterDecrement();
2047 	IncrementClientBackendCounter();
2048 }
2049