/*-------------------------------------------------------------------------
 * Copyright (c) Microsoft Corporation.  All rights reserved.
 *
 * include/opclass/bson_gin_index_mgmt.h
 *
 * Common declarations of the bson index management methods.
 *
 *-------------------------------------------------------------------------
 */

 #ifndef BSON_GIN_COMPOSITE_PRIVATE_H
 #define BSON_GIN_COMPOSITE_PRIVATE_H

 #include "io/bson_core.h"
 #include "opclass/bson_gin_index_term.h"

/* Internal marker shared by the planner and composite $elemMatch bounds parser. */
#define ReducedCorrelatedBoundsPlanAppliedKey "rctBoundsPlanApplied"

typedef struct CompositeSingleBound
{
	bson_value_t bound;
	bool isBoundInclusive;

	/* The processed bound (post truncation if any) */
	bytea *serializedTerm;
	BsonIndexTerm indexTermValue;
} CompositeSingleBound;

typedef struct IndexRecheckArgs
{
	Pointer queryDatum;

	BsonIndexStrategy queryStrategy;

	/*
	 * Multi-key status of the queried path. On a HasNoArrays path the $ne/$nin null
	 * recheck is exact (an undefined term can only be missing/literal null);
	 * otherwise it defers to the heap recheck (empty array matches $ne null).
	 */
	IndexMultiKeyStatus pathMultiKeyState;
} IndexRecheckArgs;

typedef struct CompositeIndexBounds
{
	CompositeSingleBound lowerBound;
	CompositeSingleBound upperBound;

	bool isEqualityBound;

	bool requiresRuntimeRecheck;
	bool requiresRecheckOnTruncatedIndex;

	/* A list of IndexRecheckArgs that need recheck */
	List *indexRecheckFunctions;
} CompositeIndexBounds;

typedef struct PathScanKeyMap
{
	/* integer list of term indexes - one for each scanKey */
	List *scanIndices;
} PathScanKeyMap;

typedef struct PathScanTermMap
{
	/* Integer list of key indexes - one for each index path */
	List *scanKeyIndexList;
	int32_t numTermsPerPath;
} PathScanTermMap;

/* A set of index bounds specified by a query key for a given index attribute */
typedef struct CompositeIndexBoundsSet
{
	/* The index path attribute (0 based) */
	int32_t indexAttribute;
	int32_t numBounds;

	/* The index path (if it is a wildcard match) */
	const char *wildcardPath;
	CompositeIndexBounds bounds[FLEXIBLE_ARRAY_MEMBER];
} CompositeIndexBoundsSet;

typedef struct CompositeRowBounds
{
	CompositeSingleBound bounds[INDEX_MAX_KEYS];
} CompositeRowBounds;

typedef struct VariableIndexBounds
{
	/* List of CompositeIndexBoundsSet */
	List *variableBoundsList;

	/* OPTIONAL: row level bounds that are applied to the query */
	CompositeRowBounds *minBounds;

	/* OPTIONAL: serialized deduplication state (row-pointer bitmap) carried
	 * across dynamic cursor pages, as a BSON binary value. Present when
	 * dedupState.value_type is BSON_TYPE_BINARY. */
	bson_value_t dedupState;

	/* Whether planning already handled reduced-correlated bound pruning. */
	bool isReducedCorrelatedBoundsPlanApplied;
} VariableIndexBounds;

/* A processed set of index bounds for a given path
 * used in ordered scans to track the current bounds for a given path
 * as we iterate through the scan keys.
 */
typedef struct CompositeProcessedPerPathEntry
{
	CompositeIndexBoundsSet *boundsSet;
	int32_t currentOperatorIndex;
	bool isBoundsAllEquality;
} CompositeProcessedPerPathEntry;

/* A list of CompositeProcessedPerPathEntry associated for a given index path */
typedef struct CompositePerPathEntries
{
	List *entries;
	bool hasUnsatisfiableBounds;

	/* Range bounds case */
	bytea *currentPathEqualityTerm;
	BsonIndexTerm currentPathEqualityTermValue;
	CompositeIndexBounds baseRangeBounds;

	/* The bounds used when the path is out of range */
	CompositeIndexBounds maxRangeBounds;

	/* whether or not the current entry is scanned backwards
	 * true if
	 * - sort is asc and scan is backwards or
	 * - sort is desc and scan is forwards.
	 */
	bool isEntryScanBackwards;
} CompositePerPathEntries;


/* Metadata used to track ordered index scans managed by the opclass
 * (as opposed to externally) during ordered per path scans.
 */
typedef struct CompositeOrderedScanEntryData
{
	/* The index term metadata used to generate per path terms. */
	IndexTermCreateMetadata basePathMetadata;

	/* The key generation context */
	MemoryContext scanKeyMemoryContext;

	/* The index paths for the index being scanned. */
	const char *indexPaths[INDEX_MAX_KEYS];

	/* The index path lengths for the index being scanned. */
	uint32_t indexPathLengths[INDEX_MAX_KEYS];

	/* The sort order for each index path for the index being scanned. */
	int8_t sortOrders[INDEX_MAX_KEYS];

	/* The pre-processed index bounds for each index path.
	 * These are the bounds generated by the query keys that can be inlined
	 * and used across all keys. These include keys that are single valued
	 * in a conjunction clause.
	 */
	CompositeIndexBounds baseIndexBounds[INDEX_MAX_KEYS];

	/* The per-path entries for the index being scanned. */
	CompositePerPathEntries perPathEntries[INDEX_MAX_KEYS];
} CompositeOrderedScanEntryData;

typedef struct CompositeQueryMetaInfo
{
	int32_t numIndexPaths;
	bool hasTruncation;
	int32_t truncationTermIndex;
	bool requiresRuntimeRecheck;
	bool requiresRecheckOnTruncatedIndex;
	int32_t numScanKeys;
	bool hasMultipleScanKeysPerPath;
	bool isBackwardScan;
	bool hasMinMaxBounds;
	PathScanKeyMap *scanKeyMap;
	int32_t wildcardPathIndex;
	CompositeOrderedScanEntryData *orderedScanEntryData;
	bool isOrderedScan;
	bool hasArrayPaths;
	const char *collation;

	/* OPTIONAL: serialized deduplication state (row-pointer bitmap) supplied by
	 * the continuation to restore an ordered scan's dedup tracker, as a BSON
	 * binary value. Present when dedupState.value_type is BSON_TYPE_BINARY. */
	bson_value_t dedupState;
} CompositeQueryMetaInfo;

typedef struct CompositeQueryRunData
{
	CompositeQueryMetaInfo *metaInfo;
	const char *wildcardPath;
	CompositeIndexBounds indexBounds[FLEXIBLE_ARRAY_MEMBER];
} CompositeQueryRunData;

static inline CompositeIndexBoundsSet *
CreateCompositeIndexBoundsSet(int32_t numTerms, int32_t indexAttribute,
							  const char *wildcardPath)
{
	CompositeIndexBoundsSet *set = palloc0(sizeof(CompositeIndexBoundsSet) +
										   (sizeof(CompositeIndexBounds) * numTerms));
	set->numBounds = numTerms;
	set->wildcardPath = wildcardPath;
	set->indexAttribute = indexAttribute;
	return set;
}


bool IsValidRecheckForIndexValue(SerializedCompositeTermPair *compareTerm,
								 IndexRecheckArgs *recheckArgs,
								 const char *indexCollation);

bytea * BuildLowerBoundTermFromIndexBounds(CompositeQueryRunData *runData,
										   IndexTermCreateMetadata *metadata,
										   CompositeRowBounds *minBounds,
										   bool *hasInequalityMatch, const
										   char **indexPaths,
										   uint32_t *indexPathLengths,
										   int8_t *sortOrders);

bool UpdateBoundsForTruncation(CompositeQueryRunData *runData,
							   IndexTermCreateMetadata *metadata,
							   const char **indexPaths, uint32_t *indexPathLengths,
							   int8_t *sortOrders);

void ParseOperatorStrategy(const char **indexPaths, uint32_t *indexPathLengths,
						   int8_t *sortOrders, int32_t numPaths, int32_t wildcardIndex,
						   pgbsonelement *queryElement,
						   BsonIndexStrategy queryStrategy,
						   ScanDirection *scanDirection,
						   VariableIndexBounds *indexBounds,
						   const char *indexCollation,
						   bool hasArrayPaths, uint32_t multiKeyBitMask,
						   bool isGlobalIndexMetadataTracked);

void UpdateRunDataForVariableBounds(CompositeQueryRunData *runData,
									PathScanTermMap *termMap,
									VariableIndexBounds *variableBounds,
									int32_t permutation);

void UpdateRunDataForOrderedBounds(CompositeQueryRunData *runData,
								   CompositeOrderedScanEntryData *orderedScanEntryData,
								   int32_t *unsatisfiablePathIndex);
void UpdateRunDataForMinMaxBounds(CompositeQueryRunData *runData,
								  CompositeRowBounds *minBounds,
								  CompositeRowBounds *maxBounds);

bool TryUpdateBoundsForTruncation(CompositeQueryRunData *runData,
								  IndexTermCreateMetadata *basePathMetadata,
								  const char **indexPaths, uint32_t *indexPathLengths,
								  int8_t *sortOrders);

List * MergeSingleVariableBounds(List *variableBounds, const char **wildcardPath,
								 CompositeIndexBounds *mergedBounds,
								 const char *indexCollation,
								 bool hasArrayPaths, uint32_t multiKeyBitMask);
List * MergeWildCardSingleVariableBounds(List *variableBounds);

void TrimSecondaryVariableBounds(VariableIndexBounds *variableBounds,
								 CompositeQueryRunData *runData,
								 const char *indexPaths[INDEX_MAX_KEYS],
								 bool hasArrayPaths,
								 uint32_t multiKeyBitMask,
								 bool enableMetadataBasedTracking);
void PickVariableBoundsForOrderedScan(VariableIndexBounds *variableBounds,
									  CompositeQueryRunData *runData);
void PickVariableBoundsForWildcardOrderedScan(VariableIndexBounds *variableBounds,
											  CompositeQueryRunData *runData);
void PopulateTermMetadataForTruncation(IndexTermCreateMetadata *metadata, const
									   IndexTermCreateMetadata *baseMetadata,
									   CompositeQueryRunData *runData,
									   const char *indexPath, uint32_t indexPathLength,
									   int8_t sortOrder);
bool UpdateSingleBoundForTruncation(CompositeQueryRunData *runData, int i,
									CompositeIndexBounds *bound,
									IndexTermCreateMetadata *metadata);

void PopulateTermMetadataForTruncation(IndexTermCreateMetadata *metadata, const
									   IndexTermCreateMetadata *baseMetadata,
									   CompositeQueryRunData *runData,
									   const char *indexPath, uint32_t indexPathLength,
									   int8_t sortOrder);
bool UpdateSingleBoundForTruncation(CompositeQueryRunData *runData, int i,
									CompositeIndexBounds *bound,
									IndexTermCreateMetadata *metadata);

/*
 * Simplified version of IsCompositePathWildcardMatch that doesn't do the array index
 * path validation. To be used for pre-validated paths.
 * CODESYNC: IsCompositePathWildcardMatch
 */
inline static bool
IsCompositePathWildcardMatchNoArrayCheck(const char *currentPath, const char *indexPath,
										 uint32_t indexPathLength)
{
	if (strncmp(currentPath, indexPath, indexPathLength) != 0)
	{
		/* not a match */
		return false;
	}

	if (indexPathLength == 0)
	{
		return true;
	}

	if (currentPath[indexPathLength] == '\0' ||
		currentPath[indexPathLength] == '.')
	{
		/* Exact match. */
		return true;
	}

	return false;
}


/* CODESYNC with IsCompositePathWildcardMatchNoArrayCheck */
inline static bool
IsCompositePathWildcardMatch(const char *currentPath, const char *indexPath,
							 uint32_t indexPathLength)
{
	if (strncmp(currentPath, indexPath, indexPathLength) != 0)
	{
		/* not a match */
		return false;
	}

	if (indexPathLength == 0)
	{
		/* Root wildcard */
		StringView parentPath = CreateStringViewFromString(currentPath);
		StringView subPath = StringViewFindPrefix(&parentPath, '.');
		return !SubPathHasArrayIndexElements(&parentPath, subPath);
	}

	if (currentPath[indexPathLength] == '\0')
	{
		/* Exact match. */
		return true;
	}
	else if (currentPath[indexPathLength] == '.')
	{
		/* or dotted suffix */
		StringView parentPath = CreateStringViewFromString(currentPath);
		StringView remainingPath = StringViewSubstring(&parentPath, indexPathLength + 1);
		return !SubPathHasArrayIndexElements(&parentPath, remainingPath);
	}

	return false;
}


#endif
