/*-------------------------------------------------------------------------
 *
 * citus_nodes.h
 *	  Additional node types, and related infrastructure, for Citus.
 *
 * To add a new node type to Citus, perform the following:
 *
 *   * Add a new CitusNodeTag value to use as a tag for the node. Add
 *     the node's name at a corresponding offset within the array named
 *     CitusNodeTagNamesD at the top of citus_nodefuncs.c
 *
 *   * Describe the node in a struct, which must have a CitusNode as
 *     its first element
 *
 *   * Implement an 'outfunc' for the node in citus_outfuncs.c, using
 *     the macros defined within that file. This function will handle
 *     converting the node to a string
 *
 *   * Implement a 'readfunc' for the node in citus_readfuncs.c, using
 *     the macros defined within that file. This function will handle
 *     converting strings into instances of the node
 *
 *   * Use DEFINE_NODE_METHODS within the nodeMethods array (near the
 *     bottom of citus_nodefuncs.c) to register the node in PostgreSQL
 *
 * Copyright (c) 2012-2016, Citus Data, Inc.
 *
 *-------------------------------------------------------------------------
 */

#ifndef CITUS_NODES_H
#define CITUS_NODES_H

#include "nodes/extensible.h"

/*
 * Citus Node Tags
 *
 * These have to be distinct from the ideas used in postgres' nodes.h
 *
 * NOTE: This list must match CitusNodeTagNamesD from citus_nodefuncs.c
 */
#define CITUS_NODE_TAG_START	1200
typedef enum CitusNodeTag
{
	T_MultiNode = CITUS_NODE_TAG_START, /* FIXME: perhaps use something less predicable? */
	T_MultiTreeRoot,
	T_MultiProject,
	T_MultiCollect,
	T_MultiSelect,
	T_MultiTable,
	T_MultiJoin,
	T_MultiPartition,
	T_MultiCartesianProduct,
	T_MultiExtendedOp,
	T_Job,
	T_MapMergeJob,
	T_DistributedPlan,
	T_DistributedSubPlan,
	T_Task,
	T_TaskExecution,
	T_ShardInterval,
	T_ShardPlacement,
	T_RelationShard,
	T_RelationRowLock,
	T_DeferredErrorMessage,
	T_GroupShardPlacement
} CitusNodeTag;


const char** CitusNodeTagNames;


typedef struct CitusNode
{
	ExtensibleNode extensible;
	CitusNodeTag citus_tag; /* for quick type determination */
} CitusNode;

#define CitusNodeTag(nodeptr)		CitusNodeTagI((Node*) nodeptr)

static inline int
CitusNodeTagI(Node *node)
{
	if (!IsA(node, ExtensibleNode))
	{
		return nodeTag(node);
	}

	return ((CitusNode*)(node))->citus_tag;
}

/*
 * Postgres's nodes/nodes.h has more information on why we do this.
 */
#ifdef __GNUC__

/* Citus variant of newNode(), don't use directly. */
#define CitusNewNode(size, tag) \
({	CitusNode   *_result; \
	AssertMacro((size) >= sizeof(CitusNode));		/* need the tag, at least */ \
	_result = (CitusNode *) palloc0fast(size); \
	_result->extensible.type = T_ExtensibleNode; \
	_result->extensible.extnodename = CitusNodeTagNames[tag - CITUS_NODE_TAG_START]; \
	_result->citus_tag =(int) (tag); \
	_result; \
})

#else

extern CitusNode *newCitusNodeMacroHolder;

#define CitusNewNode(size, tag) \
( \
	AssertMacro((size) >= sizeof(CitusNode)),		/* need the tag, at least */ \
	newCitusNodeMacroHolder = (CitusNode *) palloc0fast(size), \
	newCitusNodeMacroHolder->extensible.type = T_ExtensibleNode, \
	newCitusNodeMacroHolder->extensible.extnodename = CitusNodeTagNames[tag - CITUS_NODE_TAG_START], \
	newCitusNodeMacroHolder->citus_tag =(int) (tag), \
	newCitusNodeMacroHolder \
)

#endif


/*
 * IsA equivalent that compares node tags, including Citus-specific nodes.
 */
#define CitusIsA(nodeptr,_type_)	(CitusNodeTag(nodeptr) == T_##_type_)


/*
 * CitusMakeNode is Citus variant of makeNode(). Use it to create nodes of
 * the types listed in the CitusNodeTag enum and plain NodeTag. Initializes
 * memory, besides the node tag, to 0.
 */
#define CitusMakeNode(_type_) ((_type_ *) CitusNewNode(sizeof(_type_),T_##_type_))


#endif /* CITUS_NODES_H */
