/*-------------------------------------------------------------------------
 *
 * rumconfig.c
 *	  utilities routines for the configuration management for RUM indexes.
 *
 * Portions Copyright (c) Microsoft Corporation.  All rights reserved.
 * Portions Copyright (c) 2015-2022, Postgres Professional
 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"
#include "utils/guc.h"
#include "access/reloptions.h"
#include "pg_documentdb_rum.h"

/* Kind of relation optioms for rum index */
static relopt_kind rum_relopt_kind;

PGDLLEXPORT void InitializeCommonDocumentDBGUCs(const char *rumGucPrefix, const
												char *documentDBRumGucPrefix);
extern PGDLLEXPORT void DocumentDBSetRumUnredactedLogEmitHook(rum_format_log_hook hook);

PGDLLEXPORT bool DocumentDBRumLoadCommonGUCs = true;

#define RUM_DEFAULT_THROW_ERROR_ON_INVALID_DATA_PAGE false
PGDLLEXPORT bool RumThrowErrorOnInvalidDataPage =
	RUM_DEFAULT_THROW_ERROR_ON_INVALID_DATA_PAGE;

/* rumbtree.c */
#define RUM_DEFAULT_TRACK_INCOMPLETE_SPLIT true
PGDLLEXPORT bool RumTrackIncompleteSplit = RUM_DEFAULT_TRACK_INCOMPLETE_SPLIT;

#define RUM_DEFAULT_FIX_INCOMPLETE_SPLIT true
PGDLLEXPORT bool RumFixIncompleteSplit = RUM_DEFAULT_FIX_INCOMPLETE_SPLIT;

#define RUM_DEFAULT_ENABLE_INJECT_PAGE_SPLIT_INCOMPLETE false
PGDLLEXPORT bool RumInjectPageSplitIncomplete =
	RUM_DEFAULT_ENABLE_INJECT_PAGE_SPLIT_INCOMPLETE;

/* rumentrypage.c */
#define RUM_DEFAULT_FILL_FACTOR 50
PGDLLEXPORT int RumDefaultPageFillFactor = RUM_DEFAULT_FILL_FACTOR;

/* rumdatapage.c */
PGDLLEXPORT int RumDataPageIntermediateSplitSize = -1;

#define RUM_DEFAULT_SKIP_RESET_ON_DEAD_ENTRY_PAGE false
PGDLLEXPORT bool RumSkipResetOnDeadEntryPage = RUM_DEFAULT_SKIP_RESET_ON_DEAD_ENTRY_PAGE;

/* rumget.c */
PGDLLEXPORT int RumFuzzySearchLimit = 0;

#define RUM_DEFAULT_DISABLE_FAST_SCAN false
PGDLLEXPORT bool RumDisableFastScan = RUM_DEFAULT_DISABLE_FAST_SCAN;

#define DEFAULT_FORCE_RUM_ORDERED_INDEX_SCAN false
PGDLLEXPORT bool RumForceOrderedIndexScan = DEFAULT_FORCE_RUM_ORDERED_INDEX_SCAN;

#define RUM_DEFAULT_ENABLE_SKIP_INTERMEDIATE_ENTRY true
PGDLLEXPORT bool RumEnableSkipIntermediateEntry =
	RUM_DEFAULT_ENABLE_SKIP_INTERMEDIATE_ENTRY;

#define RUM_DEFAULT_PARALLEL_INDEX_WORKERS_OVERRIDE -1
PGDLLEXPORT int RumParallelIndexWorkersOverride =
	RUM_DEFAULT_PARALLEL_INDEX_WORKERS_OVERRIDE;

#define RUM_DEFAULT_ENABLE_PARALLEL_INDEX_BUILD true
PGDLLEXPORT bool RumEnableParallelIndexBuild = RUM_DEFAULT_ENABLE_PARALLEL_INDEX_BUILD;

/* rumvacuum.c */
#define RUM_DEFAULT_SKIP_RETRY_ON_DELETE_PAGE true
PGDLLEXPORT bool RumSkipRetryOnDeletePage = RUM_DEFAULT_SKIP_RETRY_ON_DELETE_PAGE;

#define RUM_DEFAULT_PRUNE_EMPTY_PAGES false
PGDLLEXPORT bool RumPruneEmptyPages = RUM_DEFAULT_PRUNE_EMPTY_PAGES;

#define RUM_DEFAULT_ENABLE_NEW_BULK_DELETE false
PGDLLEXPORT bool RumEnableNewBulkDelete = RUM_DEFAULT_ENABLE_NEW_BULK_DELETE;

#define RUM_DEFAULT_ENABLE_NEW_BULK_DELETE_INLINE_DATA_PAGES true
PGDLLEXPORT bool RumNewBulkDeleteInlineDataPages =
	RUM_DEFAULT_ENABLE_NEW_BULK_DELETE_INLINE_DATA_PAGES;

#define RUM_DEFAULT_SKIP_PRUNE_POSTING_TREE_PAGES false
PGDLLEXPORT bool RumVacuumSkipPrunePostingTreePages =
	RUM_DEFAULT_SKIP_PRUNE_POSTING_TREE_PAGES;

#define RUM_DEFAULT_VACUUM_CYCLE_ID_OVERRIDE -1
int32_t RumVacuumCycleIdOverride = RUM_DEFAULT_VACUUM_CYCLE_ID_OVERRIDE;

#define RUM_DEFAULT_TRAVERSE_PAGE_ONLY_ON_BACKTRACK false
PGDLLEXPORT bool RumTraversePageOnlyOnBackTrack =
	RUM_DEFAULT_TRAVERSE_PAGE_ONLY_ON_BACKTRACK;

#define RUM_DEFAULT_SKIP_GLOBAL_VISIBILITY_CHECK_ON_PRUNE false
PGDLLEXPORT bool RumSkipGlobalVisibilityCheckOnPrune =
	RUM_DEFAULT_SKIP_GLOBAL_VISIBILITY_CHECK_ON_PRUNE;

/* rumget.c */
#define RUM_DEFAULT_ENABLE_SUPPORT_DEAD_INDEX_ITEMS false
PGDLLEXPORT bool RumEnableSupportDeadIndexItems =
	RUM_DEFAULT_ENABLE_SUPPORT_DEAD_INDEX_ITEMS;

#define RUM_DEFAULT_ENABLE_ORDERED_OPERATOR_SCANS true
PGDLLEXPORT bool RumEnableOrderedOperatorScans =
	RUM_DEFAULT_ENABLE_ORDERED_OPERATOR_SCANS;

#define RUM_DEFAULT_ENABLE_PAGE_FILL_FACTOR true
PGDLLEXPORT bool RumEnablePageFillFactor =
	RUM_DEFAULT_ENABLE_PAGE_FILL_FACTOR;

PGDLLEXPORT rum_format_log_hook rum_unredacted_log_emit_hook = NULL;


PGDLLEXPORT void
DocumentDBSetRumUnredactedLogEmitHook(rum_format_log_hook hook)
{
	rum_unredacted_log_emit_hook = hook;
}


PGDLLEXPORT void
InitializeCommonDocumentDBGUCs(const char *rumGucPrefix, const
							   char *documentDBRumGucPrefix)
{
	DefineCustomIntVariable(psprintf("%s.rum_fuzzy_search_limit", rumGucPrefix),
							"Sets the maximum allowed result for exact search by RUM.",
							NULL,
							&RumFuzzySearchLimit,
							0, 0, INT_MAX,
							PGC_USERSET, 0,
							NULL, NULL, NULL);

	DefineCustomIntVariable(psprintf("%s.data_page_posting_tree_size", rumGucPrefix),
							"Test GUC that sets the data page size before splits.",
							NULL,
							&RumDataPageIntermediateSplitSize,
							-1, -1, INT_MAX,
							PGC_USERSET, 0,
							NULL, NULL, NULL);

	DefineCustomBoolVariable(psprintf("%s.rum_skip_retry_on_delete_page",
									  documentDBRumGucPrefix),
							 "Sets whether or not to skip retrying on delete pages during vacuuming",
							 NULL,
							 &RumSkipRetryOnDeletePage,
							 RUM_DEFAULT_SKIP_RETRY_ON_DELETE_PAGE,
							 PGC_USERSET, 0,
							 NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.rum_throw_error_on_invalid_data_page", documentDBRumGucPrefix),
		"Sets whether or not to throw an error on invalid data page",
		NULL,
		&RumThrowErrorOnInvalidDataPage,
		RUM_DEFAULT_THROW_ERROR_ON_INVALID_DATA_PAGE,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.rum_disable_fast_scan", documentDBRumGucPrefix),
		"Sets whether or not to disable fast scan",
		NULL,
		&RumDisableFastScan,
		RUM_DEFAULT_DISABLE_FAST_SCAN,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomIntVariable(
		psprintf("%s.parallel_index_workers_override", documentDBRumGucPrefix),
		"Sets the number of parallel index workers to use (default: -1, meaning no override)",
		NULL,
		&RumParallelIndexWorkersOverride,
		RUM_DEFAULT_PARALLEL_INDEX_WORKERS_OVERRIDE, -1, INT_MAX,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.enable_parallel_index_build", documentDBRumGucPrefix),
		"Sets whether or not to enable parallel index build",
		NULL,
		&RumEnableParallelIndexBuild,
		RUM_DEFAULT_ENABLE_PARALLEL_INDEX_BUILD,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.forceRumOrderedIndexScan", documentDBRumGucPrefix),
		"Sets whether or not to force a run ordered index scan",
		NULL,
		&RumForceOrderedIndexScan,
		DEFAULT_FORCE_RUM_ORDERED_INDEX_SCAN,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.enableSkipIntermediateEntry", documentDBRumGucPrefix),
		"Sets whether or not to skip intermediate entries during scan",
		NULL,
		&RumEnableSkipIntermediateEntry,
		RUM_DEFAULT_ENABLE_SKIP_INTERMEDIATE_ENTRY,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.enable_inject_page_split_incomplete", documentDBRumGucPrefix),
		"Test GUC - sets whether or not to enable injecting a failure in the middle of a page split",
		NULL,
		&RumInjectPageSplitIncomplete,
		RUM_DEFAULT_ENABLE_INJECT_PAGE_SPLIT_INCOMPLETE,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.prune_rum_empty_pages", documentDBRumGucPrefix),
		"Sets whether or not to prune empty pages during vacuuming",
		NULL,
		&RumPruneEmptyPages,
		RUM_DEFAULT_PRUNE_EMPTY_PAGES,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.enable_new_bulk_delete", documentDBRumGucPrefix),
		"Sets whether or not to the new bulk delete vacuum framework",
		NULL,
		&RumEnableNewBulkDelete,
		RUM_DEFAULT_ENABLE_NEW_BULK_DELETE,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.enable_new_bulk_delete_inline_data_pages", documentDBRumGucPrefix),
		"Sets whether or not to delete data pages inline in the new bulkdel framework",
		NULL,
		&RumNewBulkDeleteInlineDataPages,
		RUM_DEFAULT_ENABLE_NEW_BULK_DELETE_INLINE_DATA_PAGES,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.vacuum_skip_prune_posting_tree_pages", documentDBRumGucPrefix),
		"Sets whether or not to delete data pages inline in the new bulkdel framework",
		NULL,
		&RumVacuumSkipPrunePostingTreePages,
		RUM_DEFAULT_SKIP_PRUNE_POSTING_TREE_PAGES,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.enable_support_dead_index_items", documentDBRumGucPrefix),
		"Sets whether or not to enable support for handling LP_DEAD items",
		NULL,
		&RumEnableSupportDeadIndexItems,
		RUM_DEFAULT_ENABLE_SUPPORT_DEAD_INDEX_ITEMS,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.skip_reset_dead_page_flag", documentDBRumGucPrefix),
		"Sets whether or not to enable support for handling LP_DEAD items",
		NULL,
		&RumSkipResetOnDeadEntryPage,
		RUM_DEFAULT_SKIP_RESET_ON_DEAD_ENTRY_PAGE,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomIntVariable(
		psprintf("%s.vacuum_cycle_id_override", documentDBRumGucPrefix),
		"test only override for setting the vacuum cycle id",
		NULL,
		&RumVacuumCycleIdOverride,
		RUM_DEFAULT_VACUUM_CYCLE_ID_OVERRIDE, -1, UINT16_MAX,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.default_traverse_rum_page_only_on_backtrack",
				 documentDBRumGucPrefix),
		"test only guc to only traverse vacuum pages on the backtrack path",
		NULL,
		&RumTraversePageOnlyOnBackTrack,
		RUM_DEFAULT_TRAVERSE_PAGE_ONLY_ON_BACKTRACK,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.skip_global_visibility_check_on_prune",
				 documentDBRumGucPrefix),
		"test only guc to skip checking visibility on pruning pages",
		NULL,
		&RumSkipGlobalVisibilityCheckOnPrune,
		RUM_DEFAULT_TRAVERSE_PAGE_ONLY_ON_BACKTRACK,
		PGC_USERSET, 0,
		NULL, NULL, NULL);
	DefineCustomBoolVariable(
		psprintf("%s.track_incomplete_split", documentDBRumGucPrefix),
		"Sets whether or not to track incomplete splits",
		NULL,
		&RumTrackIncompleteSplit,
		RUM_DEFAULT_TRACK_INCOMPLETE_SPLIT,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.fix_incomplete_split", documentDBRumGucPrefix),
		"Sets whether or not to fix incomplete splits",
		NULL,
		&RumFixIncompleteSplit,
		RUM_DEFAULT_FIX_INCOMPLETE_SPLIT,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.enable_ordered_operator_scans", documentDBRumGucPrefix),
		"Sets whether or not to enable ordered operator scans",
		NULL,
		&RumEnableOrderedOperatorScans,
		RUM_DEFAULT_ENABLE_ORDERED_OPERATOR_SCANS,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomBoolVariable(
		psprintf("%s.enable_page_fill_factor", documentDBRumGucPrefix),
		"Sets whether or not to enable honoring the page fill factor ",
		NULL,
		&RumEnablePageFillFactor,
		RUM_DEFAULT_ENABLE_PAGE_FILL_FACTOR,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	DefineCustomIntVariable(
		psprintf("%s.rum_default_page_fill_factor", documentDBRumGucPrefix),
		"Sets the default page fill factor for RUM indexes",
		NULL,
		&RumDefaultPageFillFactor,
		RUM_DEFAULT_FILL_FACTOR, 10, 100,
		PGC_USERSET, 0,
		NULL, NULL, NULL);

	rum_relopt_kind = add_reloption_kind();

	add_string_reloption(rum_relopt_kind, "attach",
						 "Column name to attach as additional info",
						 NULL, NULL, AccessExclusiveLock);
	add_string_reloption(rum_relopt_kind, "to",
						 "Column name to add a order by column",
						 NULL, NULL, AccessExclusiveLock);
	add_bool_reloption(rum_relopt_kind, "order_by_attach",
					   "Use (addinfo, itempointer) order instead of just itempointer",
					   false, AccessExclusiveLock);
	add_int_reloption(rum_relopt_kind, "fill_factor",
					  "Page fill factor for RUM index",
					  RUM_DEFAULT_FILL_FACTOR, 10, 100,
					  AccessExclusiveLock);
}


PGDLLEXPORT bytea *
documentdb_rumoptions(Datum reloptions, bool validate)
{
#if PG_VERSION_NUM >= 180000
	static const int offsetIfDefault = -1;
	static const relopt_parse_elt tab[] = {
		{ "attach", RELOPT_TYPE_STRING, offsetof(RumOptions, attachColumn),
		  offsetIfDefault },
		{ "to", RELOPT_TYPE_STRING, offsetof(RumOptions, addToColumn), offsetIfDefault },
		{ "order_by_attach", RELOPT_TYPE_BOOL, offsetof(RumOptions, useAlternativeOrder),
		  offsetIfDefault },
		{ "fill_factor", RELOPT_TYPE_INT, offsetof(RumOptions, fillFactor),
		  offsetIfDefault }
	};
#else
	static const relopt_parse_elt tab[] = {
		{ "attach", RELOPT_TYPE_STRING, offsetof(RumOptions, attachColumn) },
		{ "to", RELOPT_TYPE_STRING, offsetof(RumOptions, addToColumn) },
		{ "order_by_attach", RELOPT_TYPE_BOOL, offsetof(RumOptions,
														useAlternativeOrder) },
		{ "fill_factor", RELOPT_TYPE_INT, offsetof(RumOptions, fillFactor) }
	};
#endif

	return (bytea *) build_reloptions(reloptions, validate, rum_relopt_kind,
									  sizeof(RumOptions), tab, lengthof(tab));
}
