/* -------------------------------------------------------------------------
 *
 * rel.h
 *	  openGauss relation descriptor (a/k/a relcache entry) definitions.
 *
 *
 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 * Portions Copyright (c) 2010-2012 Postgres-XC Development Group
 *
 * Portions Copyright (c) 2021, openGauss Contributors
 * src/include/utils/rel.h
 *
 * -------------------------------------------------------------------------
 */
#ifndef REL_H
#define REL_H

#include "access/tupdesc.h"
#include "catalog/pg_am.h"
#include "catalog/pg_class.h"
#include "catalog/pg_index.h"

#include "fmgr.h"
#include "nodes/bitmapset.h"
#include "nodes/nodes.h"
#ifdef PGXC
#include "pgxc/locator.h"
#endif
#include "rewrite/prs2lock.h"
#include "storage/buf/block.h"
#include "storage/smgr/relfilenode.h"
#include "tcop/stmt_retry.h"
#include "utils/relcache.h"
#include "utils/partcache.h"
#include "utils/reltrigger.h"
#include "utils/partitionmap.h"
#include "catalog/pg_hashbucket_fn.h"


#ifndef HDFS
#define HDFS "hdfs"
#endif
#ifndef OBS
#define OBS "obs"
#endif

/*
 * LockRelId and LockInfo really belong to lmgr.h, but it's more convenient
 * to declare them here so we can have a LockInfoData field in a Relation.
 */

typedef struct LockRelId {
    Oid relId; /* a relation identifier */
    Oid dbId;  /* a database identifier */
    Oid bktId; /* a bucket identifier = bucketid + 1 */
} LockRelId;

typedef struct LockInfoData {
    LockRelId lockRelId;
} LockInfoData;

typedef struct LockInfo
{
    char backup_id[10];
    char backup_dir[MAXPGPATH];
    bool exclusive;
} LockInfo;
#define InvalidLockRelId  { InvalidOid, InvalidOid, InvalidOid}
#define LockRelIdIsInvalid(__lockrelid) (((__lockrelid).relId == InvalidOid) && ((__lockrelid).dbId == InvalidOid))

/*
 * Cached lookup information for the index access method functions defined
 * by the pg_am row associated with an index relation.
 */
typedef struct RelationAmInfo {
    FmgrInfo aminsert;
    FmgrInfo ambeginscan;
    FmgrInfo amgettuple;
    FmgrInfo amgetbitmap;
    FmgrInfo amrescan;
    FmgrInfo amendscan;
    FmgrInfo ammarkpos;
    FmgrInfo amrestrpos;
    FmgrInfo ammerge;
    FmgrInfo ambuild;
    FmgrInfo ambuildempty;
    FmgrInfo ambulkdelete;
    FmgrInfo amvacuumcleanup;
    FmgrInfo amcanreturn;
    FmgrInfo amcostestimate;
    FmgrInfo amoptions;
} RelationAmInfo;

//describe bucket info of hash Bucketed-Table
typedef  struct  RelationBucketKey
{
    int2vector  *bucketKey;			/*bucket key*/
    Oid         *bucketKeyType;		/*the data type of partition key*/
}RelationBucketKey;

/* page compress related reloptions. */
typedef struct PageCompressOpts {
    int compressType;                /* compress algorithm */
    int compressLevel;        /* compress level */
    uint32 compressChunkSize;        /* chunk size of compressed data */
    uint32 compressPreallocChunks;    /* prealloced chunks to store compressed data */
    bool compressByteConvert;  /* byte row-coll-convert */
    bool compressDiffConvert;  /* make difference convert */
} PageCompressOpts;

/* describe commit sequence number of object in pg_object */
typedef struct ObjectCSN
{
    CommitSeqNo  createcsn;
    CommitSeqNo  changecsn;
}ObjectCSN;

typedef struct PgObjectOption {
    bool hasCtime; 
    bool hasMtime;
    bool hasCreatecsn; 
    bool hasChangecsn;
} PgObjectOption;

/*
 * Here are the contents of a relation cache entry.
 */

typedef struct RelationData {
    RelFileNode rd_node; /* relation physical identifier */
    /* use "struct" here to avoid needing to include smgr.h: */
    struct SMgrRelationData* rd_smgr; /* cached file handle, or NULL */
    int rd_refcnt;                    /* reference count */
    BackendId rd_backend;             /* owning backend id, if temporary relation */
    bool rd_isscannable;              /* rel can be scanned */
    bool rd_isnailed;                 /* rel is nailed in cache */
    bool rd_isvalid;                  /* relcache entry is valid */
    char rd_indexvalid;               /* state of rd_indexlist: 0 = not valid, 1 =
                                       * valid, 2 = temporarily forced */
    bool rd_islocaltemp;              /* rel is a temp rel of this session */
    bool is_compressed;
    bool rd_isblockchain; /* relation is in blockchain schema */

    /*
     * rd_createSubid is the ID of the highest subtransaction the rel has
     * survived into; or zero if the rel was not created in the current top
     * transaction.  This should be relied on only for optimization purposes;
     * it is possible for new-ness to be "forgotten" (eg, after CLUSTER).
     * Likewise, rd_newRelfilenodeSubid is the ID of the highest
     * subtransaction the relfilenode change has survived into, or zero if not
     * changed in the current transaction (or we have forgotten changing it).
     */
    SubTransactionId rd_createSubid;         /* rel was created in current xact */
    SubTransactionId rd_newRelfilenodeSubid; /* new relfilenode assigned in
                                              * current xact */

    Form_pg_class rd_rel; /* RELATION tuple */
    TupleDesc rd_att;     /* tuple descriptor */
    const TableAmRoutine* rd_tam_ops; /* implementation of table AM */
    Oid rd_id;            /* relation's object id */
    char relreplident;    /* see REPLICA_IDENTITY_xxx constants  */

    LockInfoData rd_lockInfo;  /* lock mgr's info for locking relation */
    RuleLock* rd_rules;        /* rewrite rules */
    MemoryContext rd_rulescxt; /* private memory cxt for rd_rules, if any */
    TriggerDesc* trigdesc;     /* Trigger info, or NULL if rel has none */
    /* use "struct" here to avoid needing to include rewriteRlsPolicy.h */
    struct RlsPoliciesDesc* rd_rlsdesc; /* Row level security policies, or NULL */
    /* data managed by RelationGetIndexList: */
    List* rd_indexlist; /* list of OIDs of indexes on relation */
    Oid rd_oidindex;    /* OID of unique index on OID, if any */
    Oid rd_pkindex;     /* OID of primary key, if any */
    Oid rd_refSynOid;   /* OID of referenced synonym Oid, if mapping indeed. */

    /* data managed by RelationGetIndexAttrBitmap: */
    Bitmapset* rd_indexattr; /* identifies columns used in indexes */
    Bitmapset* rd_keyattr;   /* cols that can be ref'd by foreign keys */
    Bitmapset* rd_pkattr;    /* cols included in primary key */
    Bitmapset* rd_idattr;    /* included in replica identity index */

    void* rd_pubactions;  /* publication actions, PublicationActions */

    /*
     * The index chosen as the relation's replication identity or
     * InvalidOid. Only set correctly if RelationGetIndexList has been
     * called/rd_indexvalid > 0.
     */
    Oid rd_replidindex;

    /*
     * rd_options is set whenever rd_rel is loaded into the relcache entry.
     * Note that you can NOT look into rd_rel for this data.  NULL means "use
     * defaults".
     */
    bytea* rd_options; /* parsed pg_class.reloptions */

    /* These are non-NULL only for an index relation: */
    Oid rd_partHeapOid;   /* partition index's partition oid */
    Form_pg_index rd_index; /* pg_index tuple describing this index */
    /* use "struct" here to avoid needing to include htup.h: */
    struct HeapTupleData* rd_indextuple; /* all of pg_index tuple */
    Form_pg_am rd_am;                    /* pg_am tuple for index's AM */
    /* use "struct" here to avoid needing to include amapi.h*/
    struct IndexAmRoutine* rd_amroutine; /* index AM's API struct */

    int rd_indnkeyatts;     /* index relation's indexkey nums */
    int1 rd_indexsplit;  /* determines the page split method to use */
    int1 rd_ubtreeindextype; /* type of ubtree index */

    /*
     * index access support info (used only for an index relation)
     *
     * Note: only default support procs for each opclass are cached, namely
     * those with lefttype and righttype equal to the opclass's opcintype. The
     * arrays are indexed by support function number, which is a sufficient
     * identifier given that restriction.
     *
     * Note: rd_amcache is available for index AMs to cache private data about
     * an index.  This must be just a cache since it may get reset at any time
     * (in particular, it will get reset by a relcache inval message for the
     * index).	If used, it must point to a single memory chunk palloc'd in
     * rd_indexcxt.  A relcache reset will include freeing that chunk and
     * setting rd_amcache = NULL.
     */
    MemoryContext rd_indexcxt; /* private memory cxt for this stuff */
    RelationAmInfo* rd_aminfo; /* lookup info for funcs found in pg_am */
    Oid* rd_opfamily;          /* OIDs of op families for each index col */
    Oid* rd_opcintype;         /* OIDs of opclass declared input data types */
    RegProcedure* rd_support;  /* OIDs of support procedures */
    FmgrInfo* rd_supportinfo;  /* lookup info for support procedures */
    int16* rd_indoption;       /* per-column AM-specific flags */
    List* rd_indexprs;         /* index expression trees, if any */
    List* rd_indpred;          /* index predicate tree, if any */
    Oid* rd_exclops;           /* OIDs of exclusion operators, if any */
    Oid* rd_exclprocs;         /* OIDs of exclusion ops' procs, if any */
    uint16* rd_exclstrats;     /* exclusion ops' strategy numbers, if any */
    void* rd_amcache;          /* available for use by index AM */
    Oid* rd_indcollation;      /* OIDs of index collations */
    Buffer rd_rootcache;       /* for root caching */
    bool rd_ind_partition_all_usable;

    /*
     * foreign-table support
     *
     * rd_fdwroutine must point to a single memory chunk palloc'd in
     * t_thrd.mem_cxt.cache_mem_cxt.	It will be freed and reset to NULL on a relcache
     * reset.
     */

    /* use "struct" here to avoid needing to include fdwapi.h: */
    struct FdwRoutine* rd_fdwroutine; /* cached function pointers, or NULL */

    /*
     * Hack for CLUSTER, rewriting ALTER TABLE, etc: when writing a new
     * version of a table, we need to make any toast pointers inserted into it
     * have the existing toast table's OID, not the OID of the transient toast
     * table.  If rd_toastoid isn't InvalidOid, it is the OID to place in
     * toast pointers inserted into this rel.  (Note it's set on the new
     * version of the main heap, not the toast table itself.)  This also
     * causes toast_save_datum() to try to preserve toast value OIDs.
     */
    Oid rd_toastoid; /* Real TOAST table's OID, or InvalidOid */
    Oid rd_bucketoid;/* bucket OID in pg_hashbucket*/

    CommitSeqNo rd_changecsn; /* the commit sequence number when the old version expires */
    CommitSeqNo rd_createcsn; /* the commit sequence number when object create */
    CommitSeqNo xmin_csn;  /* the commit sequence number when the xmin of tuple commit */

    /* bucket key info, indicating which keys are used to comoute hash value */
    int rd_bucketmapsize; /* Size of bucket map */
    StorageType storage_type; /* storage type */
    
    /*bucket key info, indicating which keys are used to comoute hash value */
    RelationBucketKey *rd_bucketkey;

    /* For 1-level hash table, it points into a HashBucketMap instances;
     * For 2-level hash table, e.g. range-hash, it points into a RangePartitionMap
     * instances. */

    PartitionMap* partMap;
    Oid parentId; /* if this is construct by partitionGetRelation,this is Partition Oid,else this is InvalidOid */
    Oid grandparentId; /* if this is construct by partitionGetRelation,this is subpartition table Oid */
    /* 
     * Different from rd_rel->parttype, It's for subpartition table,
     * 'p' for level-1 partition, 's' for level-2 partition 
     */
    char subpartitiontype; 
    /* use "struct" here to avoid needing to include pgstat.h: */
    struct PgStat_TableStatus* pgstat_info; /* statistics collection area */

#ifdef PGXC
    RelationLocInfo* rd_locator_info;
    PartitionMap* sliceMap;
#endif
    Relation   parent;

    /* double linked list node, partition and bucket relation would be stored in fakerels list of resource owner */
    dlist_node node;

    /* only valid if has mlog and used by incremental matview */
    Oid rd_mlogoid;
    /* Is under the context of creating crossbucket index? */
    bool newcbi;

    bool come_from_partrel;
    /* used only for gsc, keep it preserved if you modify the rel, otherwise set it null */
    struct LocalRelationEntry *entry;

    /* used only for datavec pq */
    char *pqTable;
    float *pqDistanceTable; 
    bool rd_optionsValid;
} RelationData;

/*
 * StdRdOptions
 *		Standard contents of rd_options for heaps and generic indexes.
 *
 * RelationGetFillFactor() and RelationGetTargetPageFreeSpace() can only
 * be applied to relations that use this format or a superset for
 * private options data.
 */
/* autovacuum-related reloptions. */
typedef struct AutoVacOpts {
    bool enabled;
    int vacuum_threshold;
    int analyze_threshold;
    int vacuum_cost_delay;
    int vacuum_cost_limit;
    int64 freeze_min_age;
    int64 freeze_max_age;
    int64 freeze_table_age;
    float8 vacuum_scale_factor;
    float8 analyze_scale_factor;
} AutoVacOpts;

typedef enum RedisCtidType { REDIS_START_CTID = 0, REDIS_END_CTID } RedisCtidType;

typedef enum RedisRelAction {
    REDIS_REL_INVALID = -1,
    REDIS_REL_NORMAL,
    REDIS_REL_APPEND,
    REDIS_REL_READ_ONLY,
    REDIS_REL_END_CATCHUP,
    REDIS_REL_DESTINATION,
    REDIS_REL_RESET_CTID
} RedisHtlAction;

/* PageCompressOpts->compressType values */
typedef enum CompressTypeOption {
    COMPRESS_TYPE_NONE = 0,
    COMPRESS_TYPE_PGLZ = 1,
    COMPRESS_TYPE_ZSTD = 2,
    COMPRESS_TYPE_PGZSTD = 3,
    COMPRESS_TYPE_ZLIB = 4
} CompressTypeOption;

typedef struct StdRdOptions {
    int32 vl_len_;           /* varlena header (do not touch directly!) */
    int fillfactor;          /* page fill factor in percent (0..100) */
    bool deduplication;
    AutoVacOpts autovacuum;  /* autovacuum-related options */
    bool security_barrier;   /* for views */
    bool enable_rowsecurity; /* enable row level security or not */
    bool force_rowsecurity;  /* force row level security or not */
    bool enable_tsdb_delta; /* enable delta table for timeseries relations */

    int tsdb_deltamerge_interval;   /* interval for tsdb delta merge job */
    int tsdb_deltamerge_threshold;   /* data threshold for tsdb delta merge job */
    int tsdb_deltainsert_threshold;   /* data threshold for tsdb delta insert */
    int max_batch_rows;            /* the upmost rows at each batch inserting */
    int delta_rows_threshold;      /* the upmost rows delta table holds */
    int partial_cluster_rows;      /* row numbers of partial cluster feature */
    int compresslevel;             /* compress level, see relation storage options 'compresslevel' */
    int internalMask;              /*internal mask*/
    bool ignore_enable_hadoop_env; /* ignore enable_hadoop_env */
    bool user_catalog_table;       /* use as an additional catalog relation */
    bool segment;              /* enable segment-page storage for this relation */
    bool hashbucket;        /* enable hash bucket for this relation */
    bool primarynode;       /* enable primarynode mode for replication table */
    bool crossbucket;       /* enable crossbucket index creation for this index relation */
    char* wait_clean_cbi;
    int bucketcnt;          /* number of bucket counts */
    int parallel_workers;   /* max number of parallel workers */
    bool hasuids;           /* enable uids for this relation */
    /* info for redistribution */
    Oid rel_cn_oid;
    int exec_step;
    int64 create_time;
    RedisHtlAction append_mode_internal;

    int initTd;

    // Important:
    // for string type, data is appended at the tail of its parent struct.
    // CHAR* member of this STRUCT stores the offset of its string data.
    // offset=0 means that it's a NULL string.
    //
    // Take Care !!!
    // CHAR* member CANNOT be accessed directly.
    // StdRdOptionsGetStringData macro must be used for accessing CHAR* type member.
    //
    char* compression; /* compress or not compress */
    char* storage_type; /*table access method kind */
    char* orientation; /* row-store or column-store */
    char* indexsplit; /* page split method */
    char* index_type; /* index type of ubtree */
    char* ttl; /* time to live for tsdb data management */
    char* period; /* partition range for tsdb data management */
    char* partition_interval; /* partition interval for streaming contquery table */
    char* time_column; /* time column for streaming contquery table */
    char* ttl_interval; /* ttl interval for streaming contquery table */
    char* gather_interval; /* gather interval for streaming contquery table */
    char* string_optimize; /* string optimize for streaming contquery table */
    char* sw_interval; /* sliding window interval for streaming contquery table */
    char* version;
    char* wait_clean_gpi; /* pg_partition system catalog wait gpi-clean or not */
    /* item for online expand */
    char* append_mode;
    char* start_ctid_internal;
    char* end_ctid_internal;
    char* merge_list;
    char* dek_cipher;
    char* cmk_id;
    char* encrypt_algo;
    bool enable_tde;     /* switch flag for table-level TDE encryption */
    bool on_commit_delete_rows; /* global temp table */
    PageCompressOpts compress; /* page compress related reloptions. */
    int check_option_offset; /* for views */
    int view_security_option_offset; /* for views */
    Oid collate; /* table's default collation in b format. */
#ifdef USE_SPQ
    /* SPQ OPTIONS */
    int spq_bt_build_offset;
#endif
    Oid relrewrite;
} StdRdOptions;

#define HEAP_MIN_FILLFACTOR 10
#define HEAP_DEFAULT_FILLFACTOR 100
#define UHEAP_DEFAULT_FILLFACTOR 100

#define UHEAP_MIN_TD 2
#define UHEAP_MAX_TD 128
#define UHEAP_DEFAULT_TD 4

/*
 * RelationIsUsedAsCatalogTable
 *     Returns whether the relation should be treated as a catalog table
 *      from the pov of logical decoding.
 */
#define RelationIsUsedAsCatalogTable(relation) \
    ((relation)->rd_options ? ((StdRdOptions*)(relation)->rd_options)->user_catalog_table : false)

#define RelationIsInternal(relation) (RelationGetInternalMask(relation) != INTERNAL_MASK_DISABLE)

/*
 * RelationGetFillFactor
 *		Returns the relation's fillfactor.  Note multiple eval of argument!
 */
#define RelationGetFillFactor(relation, defaultff) \
    ((relation)->rd_options ? ((StdRdOptions*)(relation)->rd_options)->fillfactor : (defaultff))

/*
 * RelationGetTargetPageUsage
 *		Returns the relation's desired space usage per page in bytes.
 */
#define RelationGetTargetPageUsage(relation, defaultff) (BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)

/*
 * RelationGetTargetPageFreeSpace
 *		Returns the relation's desired freespace per page in bytes.
 */
#define RelationGetTargetPageFreeSpace(relation, defaultff) \
    (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)

/*
 * RelationGetTargetPageFreeSpace
 *      Returns the relation's desired freespace per page in bytes, for use in page pruning.
 *      Would only desire the page to be full up to 90% of its fillfactor before page pruning.
 */
#define RelationGetTargetPageFreeSpacePrune(relation, defaultff) \
    (BLCKSZ * (100 - 0.9 * RelationGetFillFactor(relation, defaultff)) / 100)

/*
 * RelationHasViewSecurityOption
 *		Returns true if the relation is a view defined with sql security
 *		or the cascaded check option.
 */
#define RelationHasViewSecurityOption(relation)                                  \
    ((relation)->rd_options &&                                            \
     ((StdRdOptions *) (relation)->rd_options)->view_security_option_offset != 0)

/*
 * RelationHasViewSecurityDefinerOption
 *		Returns true if the relation is a view defined sql security definer
 *		option.
 */
#define RelationHasViewSecurityDefinerOption(relation)                              \
    (RelationHasViewSecurityOption(relation) &&                                     \
     strcmp((char *) (relation)->rd_options +                                       \
            ((StdRdOptions *) (relation)->rd_options)->view_security_option_offset, \
            "definer") == 0)

/*
 * RelationHasViewSecurityInvokerOption
 *		Returns true if the relation is a view defined with sql security invoker
 *		option.
 */
#define RelationHasViewSecurityInvokerOption(relation)                              \
    (RelationHasViewSecurityOption(relation) &&                                     \
     strcmp((char *) (relation)->rd_options +                                       \
            ((StdRdOptions *) (relation)->rd_options)->view_security_option_offset, \
            "invoker") == 0)
/*
 * RelationIsSecurityView
 *		Returns whether the relation is security view, or not
 */
#define RelationIsSecurityView(relation) \
    ((relation)->rd_options ? ((StdRdOptions*)(relation)->rd_options)->security_barrier : false)

/*
 * RelationHasCheckOption
 *		Returns true if the relation is a view defined with either the local
 *		or the cascaded check option.
 */
#define RelationHasCheckOption(relation)                                  \
    ((relation)->rd_options &&                                            \
     ((StdRdOptions *) (relation)->rd_options)->check_option_offset != 0)

/*
 * RelationHasLocalCheckOption
 *		Returns true if the relation is a view defined with the local check
 *		option.
 */
#define RelationHasLocalCheckOption(relation)                               \
    ((relation)->rd_options &&                                              \
    ((StdRdOptions *) (relation)->rd_options)->check_option_offset != 0 ?   \
    strcmp((char *) (relation)->rd_options +                                \
            ((StdRdOptions *) (relation)->rd_options)->check_option_offset, \
            "local") == 0 : false)

/*
 * RelationHasCascadedCheckOption
 *		Returns true if the relation is a view defined with the cascaded check
 *		option.
 */
#define RelationHasCascadedCheckOption(relation)                            \
    ((relation)->rd_options &&                                              \
     ((StdRdOptions *) (relation)->rd_options)->check_option_offset != 0 ?  \
     strcmp((char *) (relation)->rd_options +                               \
            ((StdRdOptions *) (relation)->rd_options)->check_option_offset, \
            "cascaded") == 0 : false)

/*
 * RelationGetParallelWorkers
 *      Returns the relation's parallel_workers reloption setting.
 *      Note multiple eval of argument!
 */
#define RelationGetParallelWorkers(relation, defaultpw) \
    ((relation)->rd_options ? \
     ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))

#define RelationGetRelrewriteOption(relation)                            \
    ((relation)->rd_options ? ((StdRdOptions*)(relation)->rd_options)->relrewrite : InvalidOid)
/*
 * RelationIsValid
 *		True iff relation descriptor is valid.
 */
#define RelationIsValid(relation) PointerIsValid(relation)

#define InvalidRelation ((Relation)NULL)

/*
 * RelationHasReferenceCountZero
 *		True iff relation reference count is zero.
 *
 * Note:
 *		Assumes relation descriptor is valid.
 */
#define RelationHasReferenceCountZero(relation) ((bool)((relation)->rd_refcnt == 0))

/*
 * RelationGetForm
 *		Returns pg_class tuple for a relation.
 *
 * Note:
 *		Assumes relation descriptor is valid.
 */
#define RelationGetForm(relation) ((relation)->rd_rel)

/*
 * RelationGetRelid
 *		Returns the OID of the relation
 */
#define RelationGetRelid(relation) ((relation)->rd_id)

#define RelationGetBktid(relation) \
    (IsBucketFileNode((relation)->rd_node) ? (relation)->rd_node.bucketNode : InvalidBktId)

#define RelationGetStorageType(relation) ((relation)->storage_type)

/*
 * RelationGetNumberOfAttributes
 *		Returns the total number of attributes in a relation.
 */
#define RelationGetNumberOfAttributes(relation) ((relation)->rd_rel->relnatts)

/*
 * IndexRelationGetNumberOfAttributes
 *		Returns the number of attributes in an index.
 */
#define IndexRelationGetNumberOfAttributes(relation) ((relation)->rd_index->indnatts)

/*
 * IndexRelationGetNumberOfKeyAttributes
 *		Returns the number of key attributes in an index.
 */
#define IndexRelationGetNumberOfKeyAttributes(relation) \
    (AssertMacro((relation)->rd_indnkeyatts != 0), ((relation)->rd_indnkeyatts))

/*
 * Use to judge if a ubtree relation is a including index
 *         Returns the tag of the include index
 */
#define UBTreeHasIncluding(rel) (RelationIsGlobalIndex(rel) ?   \
    (IndexRelationGetNumberOfAttributes(rel) > IndexRelationGetNumberOfKeyAttributes(rel) + 1) :    \
    (IndexRelationGetNumberOfAttributes(rel) > IndexRelationGetNumberOfKeyAttributes(rel)))

/*
 * RelationGetDescr
 *		Returns tuple descriptor for a relation.
 */
#define RelationGetDescr(relation) ((relation)->rd_att)

/*
 * RelationGetRelationName
 *      Returns the rel's name.
 *
 * Note that the name is only unique within the containing namespace.
 */
#define RelationGetRelationName(relation) (NameStr((relation)->rd_rel->relname))

#define PartitionGetPartitionName(partition) (NameStr((partition)->pd_part->relname))

#define RelationGetPartType(relation) ((relation)->rd_rel->parttype)

/*
 * RelationGetRelkind
 *		Returns the rel's relkind.
 */
#define RelationGetRelkind(relation) ((relation)->rd_rel->relkind)

/*
 * RelationGetNamespace
 *		Returns the rel's namespace OID.
 */
#define RelationGetNamespace(relation) ((relation)->rd_rel->relnamespace)

/*
 * RelationGetOwner
 *		Returns the rel's owner OID.
 */
#define RelationGetOwner(relation) ((relation)->rd_rel->relowner)

/*
 * RelationGetTablespace
 *		Returns the rel's tablespace OID.
 */
#define RelationGetTablespace(relation) ((relation)->rd_rel->reltablespace)

#define RelationGetRnodeSpace(relation) ((relation)->rd_node.spcNode)

/*
 * RelationGetCreatecsn
 *		Returns the rel's create commit sequence number.
 */
#define RelationGetCreatecsn(r) ((r)->rd_createcsn)

/*
 * RelationGetChangecsn
 *		Returns the rel's latest ddl commit sequence number.
 */
#define RelationGetChangecsn(r) ((r)->rd_changecsn)

/*
 * RelationGetRelFrozenxid
 *		Returns the rel's frozenxid.
 */
#define RelationGetRelFrozenxid(r) ((r)->rd_rel->relfrozenxid)

/*
 * RelationGetRelFrozenxid64
 *		Returns the rel's frozenxid64.
 */
extern TransactionId RelationGetRelFrozenxid64(Relation r);
extern TransactionId PartGetRelFrozenxid64(Partition part);
/*
 * RelationGetRelFileNode
 *		Returns the rel's relfilenode.
 */
#define RelationGetRelFileNode(r) ((r)->rd_rel->relfilenode)

/*
 * RelationIsMapped
 *		True if the relation uses the relfilenode map.
 *
 * NB: this is only meaningful for relkinds that have storage, else it
 * will misleadingly say "true".
 */
#define RelationIsMapped(relation) ((relation)->rd_rel->relfilenode == InvalidOid)

extern void TryFreshSmgrCache(struct SMgrRelationData *smgr);
/*
 * RelationOpenSmgr
 *		Open the relation at the smgr level, if not already done.
 */
#define RelationOpenSmgr(relation)                                                                       \
    do {                                                                                                 \
        if ((relation)->rd_smgr == NULL)                                                                 \
            smgrsetowner(&((relation)->rd_smgr), smgropen((relation)->rd_node, (relation)->rd_backend)); \
        else {                                                                                           \
            TryFreshSmgrCache((relation)->rd_smgr);                                                      \
        }                                                                                                \
    } while (0)

/*
 * RelationCloseSmgr
 *		Close the relation at the smgr level, if not already done.
 *
 * Note: smgrclose should unhook from owner pointer, hence the Assert.
 */
#define RelationCloseSmgr(relation)              \
    do {                                         \
        if ((relation)->rd_smgr != NULL) {       \
            smgrclose((relation)->rd_smgr);      \
            Assert(RelationIsBucket(relation) || (relation)->rd_smgr == NULL); \
        }                                        \
    } while (0)

/*
 * RelationGetTargetBlock
 *		Fetch relation's current insertion target block.
 *
 * Returns InvalidBlockNumber if there is no current target block.	Note
 * that the target block status is discarded on any smgr-level invalidation.
 */
#define RelationGetTargetBlock(relation) \
    ((relation)->rd_smgr != NULL ? (relation)->rd_smgr->smgr_targblock : InvalidBlockNumber)

/*
 * RelationGetPrevTargetBlock
 *		Fetch relation's previous insertion target block.
 *
 * Returns InvalidBlockNumber if there is no previous target block.	Note
 * that the target block status is discarded on any smgr-level invalidation.
 */
#define RelationGetPrevTargetBlock(relation) \
    ((relation)->rd_smgr != NULL ? (relation)->rd_smgr->smgr_prevtargblock : InvalidBlockNumber)


/*
 * RelationSetTargetBlock
 *		Set relation's current insertion target block.
 */
#define RelationSetTargetBlock(relation, targblock)        \
    do {                                                   \
        RelationOpenSmgr(relation);                        \
        (relation)->rd_smgr->smgr_targblock = (targblock); \
    } while (0)

/*
 * RelationSetPrevTargetBlock
 *		Set relation's current insertion target block.
 */
#define RelationSetPrevTargetBlock(relation, prevtargblock)        \
    do {                                                   \
        RelationOpenSmgr(relation);                        \
        (relation)->rd_smgr->smgr_prevtargblock = (prevtargblock); \
    } while (0)

/*
 * RelationNeedsWAL
 *		True if relation needs WAL.
 */
#define RelationNeedsWAL(relation)                                     \
    ((relation)->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT || \
        (((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP) && STMT_RETRY_ENABLED))

/*
 * RelationUsesLocalBuffers
 *		True if relation's pages are stored in local buffers.
 */
#define RelationUsesLocalBuffers(relation) \
    ((relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP || \
     (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)
#define RelationIsLocalTemp(relation)                                           \
    ((relation)->rd_rel->relnamespace == u_sess->catalog_cxt.myTempNamespace || \
        (relation)->rd_rel->relnamespace == u_sess->catalog_cxt.myTempToastNamespace)

#define RelationIsRelation(relation) (RELKIND_RELATION == (relation)->rd_rel->relkind)

#define isPartitionedRelation(classForm)                  \
    (PARTTYPE_PARTITIONED_RELATION == (classForm)->parttype|| \
        PARTTYPE_SUBPARTITIONED_RELATION == (classForm)->parttype)

#ifdef PGXC
/*
 * RelationGetLocInfo
 *		Return the location info of relation
 */
#define RelationGetLocInfo(relation) ((relation)->rd_locator_info)
#endif

/*
 * RelationGetBucketKey
 *      Fetch relation's current bucket key.
 *
 * Returns NULL if there is no hash bucket in current Relation
 */
#define RelationGetBucketKey(relation) \
    ((relation)->rd_bucketkey != NULL) ? (relation)->rd_bucketkey->bucketKey : NULL

/*
 * RELATION_IS_LOCAL
 *		If a rel is either local temp or global temp relation
 *		or newly created in the current transaction,
 *		it can be assumed to be accessible only to the current backend.
 *		This is typically used to decide that we can skip acquiring locks.
 *
 * Beware of multiple eval of argument
 */
#define RELATION_IS_LOCAL(relation) \
    ((relation)->rd_islocaltemp || \
     (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP || \
     (relation)->rd_createSubid != InvalidSubTransactionId)

/*
 * RELATION_IS_TEMP
 *        Test a rel is either local temp relation of this session
 *         or global temp relation.
 */
#define RELATION_IS_TEMP(relation) \
    ((relation)->rd_islocaltemp || \
     (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP || \
     (relation)->rd_rel->relpersistence == RELPERSISTENCE_TEMP)

/* global temp table implementations */
#define RELATION_IS_GLOBAL_TEMP(relation) \
    ((relation) != NULL && (relation)->rd_rel != NULL && \
     (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP)

#define RELATION_GTT_ON_COMMIT_DELETE(relation)    \
    ((relation)->rd_options && (relation)->rd_rel->relkind == RELKIND_RELATION && \
    (relation)->rd_rel->relpersistence == RELPERSISTENCE_GLOBAL_TEMP ? \
    (reinterpret_cast<StdRdOptions *>((relation)->rd_options))->on_commit_delete_rows : false)

#define RelationGetRelPersistence(relation) ((relation)->rd_rel->relpersistence)

/* routines in utils/cache/relcache.c */
extern void RelationIncrementReferenceCount(Relation rel);
extern void RelationDecrementReferenceCount(Relation rel);
extern void RelationIncrementReferenceCount(Oid relationId);
extern void RelationDecrementReferenceCount(Oid relationId);

/*
 * RelationIsAccessibleInLogicalDecoding
 *     True if we need to log enough information to have access via
 *     decoding snapshot.
 */
#define RelationIsAccessibleInLogicalDecoding(relation)       \
    (XLogLogicalInfoActive() && RelationNeedsWAL(relation) && \
        (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relation)))

/*
 * RelationIsLogicallyLogged
 *     True if we need to log enough information to extract the data from the
 *     WAL stream.
 *
 * We don't log information for unlogged tables (since they don't WAL log
 * anyway) and for system tables (their content is hard to make sense of, and
 * it would complicate decoding slightly for little gain). Note that we *do*
 * log information for user defined catalog tables since they presumably are
 * interesting to the user...
 */
#define RelationIsLogicallyLogged(relation) \
    (XLogLogicalInfoActive() && RelationNeedsWAL(relation) && !IsCatalogRelation(relation))

/*
 * RelationisEncryptEnable
 *     Returns whether the relation encrypt or not
 */
#define RelationisEncryptEnable(relation) \
        (((relation)->rd_options && (relation)->rd_rel->relkind == RELKIND_RELATION) ? \
        ((StdRdOptions *)(relation)->rd_options)->enable_tde : false)

#define RelationGetDekCipher(relation) \
        StdRdOptionsGetStringData((relation)->rd_options, dek_cipher, NULL)

#define RelationGetCmkId(relation) \
        StdRdOptionsGetStringData((relation)->rd_options, cmk_id, NULL)

#define RelationGetAlgo(relation) \
        StdRdOptionsGetStringData((relation)->rd_options, encrypt_algo, NULL)

/*
 * RelationIsPermanent
 *		True if relation is permanent.
 */
#define RelationIsPermanent(relation) \
	((relation)->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)

extern void GetTdeInfoFromRel(Relation rel, TdeInfo *tde_info);
extern char RelationGetRelReplident(Relation r);
extern void SetupPageCompressForRelation(RelFileNode* node, PageCompressOpts* compressOpts, const char* name);
extern bool IsRelationReplidentKey(Relation r, int attno);

#endif /* REL_H */