/*
 * after shared storage operation complete, update persistent table.
 */
void PostPerformSharedStorageOpTasks(SharedStorageOpTasks *tasks) {
  int i;

  Relation gp_relation_node;

  Assert(NULL != tasks);

  if (tasks->numTasks == 0)
    return;

  gp_relation_node = heap_open(GpRelfileNodeRelationId, RowExclusiveLock);

  for (i = 0; i < tasks->numTasks; ++i) {
    SharedStorageOpTask * task = &tasks->tasks[i];
    InsertGpRelfileNodeTuple(gp_relation_node,
    /* relationId */0,
                             task->relname, task->node.relNode, task->segno,
                             /* updateIndex */true,
                             &task->persistentTid, task->persistentSerialNum);
  }
  heap_close(gp_relation_node, RowExclusiveLock);
}
static void PersistentBuild_ScanGpPersistentRelationNodeForGlobal(
	Relation 	gp_relation_node,

	int64		*count)
{
	PersistentFileSysObjData *fileSysObjData;
	PersistentFileSysObjSharedData	*fileSysObjSharedData;

	PersistentStoreScan storeScan;
	 
	Datum values[Natts_gp_persistent_relfile_node];
	 
	ItemPointerData persistentTid;
	int64 persistentSerialNum;

	PersistentFileSysObj_GetDataPtrs(
								PersistentFsObjType_RelationFile,
								&fileSysObjData,
								&fileSysObjSharedData);
		 
	PersistentStore_BeginScan(
						&fileSysObjData->storeData,
						&fileSysObjSharedData->storeSharedData,
						&storeScan);

	while (PersistentStore_GetNext(
							&storeScan,
							values,
							&persistentTid,
							&persistentSerialNum))
	{
		RelFileNode 					relFileNode;
		int32 							segmentFileNum;
		PersistentFileSysRelStorageMgr	relationStorageManager;
		PersistentFileSysState			persistentState;
		PersistentFileSysRelBufpoolKind relBufpoolKind;
		TransactionId					parentXid;
		int64							serialNum;
		ItemPointerData					previousFreeTid;
		
		PersistentFileSysObjName		fsObjName;
		bool							sharedStorage;

		GpPersistentRelfileNode_GetValues(
										values,
										&relFileNode.spcNode,
										&relFileNode.dbNode,
										&relFileNode.relNode,
										&segmentFileNum,
										&relationStorageManager,
										&persistentState,
										&relBufpoolKind,
										&parentXid,
										&serialNum,
										&previousFreeTid,
										&sharedStorage);

		if (persistentState == PersistentFileSysState_Free)
			continue;

		PersistentFileSysObjName_SetRelationFile(
											&fsObjName,
											&relFileNode,
											segmentFileNum,
											NULL);
		fsObjName.hasInited = true;
		fsObjName.sharedStorage = sharedStorage;

		if (relFileNode.spcNode != GLOBALTABLESPACE_OID)
			continue;

		if (relationStorageManager != PersistentFileSysRelStorageMgr_BufferPool)
			elog(ERROR, "Only expecting global tables to be Buffer Pool managed");

		InsertGpRelfileNodeTuple(
						gp_relation_node,
						relFileNode.relNode, 	// pg_class OID
						/* relationName */ NULL,	// Optional.
						relFileNode.relNode,	// pg_class relfilenode
						/* segmentFileNum */ 0,
						/* updateIndex */ false,
						&persistentTid,
						persistentSerialNum);

		(*count)++;
	}

	PersistentStore_EndScan(&storeScan);
}
static void PersistentBuild_PopulateGpRelationNode(
	DatabaseInfo 	*info,

	Oid				defaultTablespace,

	int64			*count)
{
	Relation gp_relfile_node;
	int r;
	RelFileNode indexRelFileNode;
	bool indexFound;
	Relation gp_relfile_node_index;
	struct IndexInfo *indexInfo;

	if (Debug_persistent_print)
		elog(Persistent_DebugPrintLevel(), 
			 "PersistentBuild_PopulateGpRelationNode: Enter for dbOid %u",
			 info->database);

	MemSet(&indexRelFileNode, 0, sizeof(RelFileNode));
	indexFound = false;
	
	gp_relfile_node =
			DirectOpen_GpRelfileNodeOpen(
							defaultTablespace, 
							info->database);

	for (r = 0; r < info->dbInfoRelArrayCount; r++)
	{
		DbInfoRel *dbInfoRel = &info->dbInfoRelArray[r];
		
		RelFileNode relFileNode;

		PersistentFileSysRelStorageMgr relStorageMgr;

		ItemPointerData persistentTid;
		int64 persistentSerialNum;

		if (dbInfoRel->reltablespace == GLOBALTABLESPACE_OID &&
			info->database != TemplateDbOid)
			continue;

		relFileNode.spcNode = dbInfoRel->reltablespace;
		relFileNode.dbNode = 
				(dbInfoRel->reltablespace == GLOBALTABLESPACE_OID ?
														0 : info->database);
		relFileNode.relNode = dbInfoRel->relfilenodeOid;

		if (dbInfoRel->relationOid == GpRelfileNodeOidIndexId)
		{
			indexRelFileNode = relFileNode;
			indexFound = true;
		}

		relStorageMgr = (
				 (dbInfoRel->relstorage == RELSTORAGE_AOROWS ||
				  dbInfoRel->relstorage == RELSTORAGE_PARQUET) ?
								PersistentFileSysRelStorageMgr_AppendOnly :
								PersistentFileSysRelStorageMgr_BufferPool);

		/*
		 * The gp_relation_node mapping table is empty, so use the physical files as
		 * the guide.
		 */
		if (relStorageMgr == PersistentFileSysRelStorageMgr_BufferPool)
		{
			PersistentFileSysRelStorageMgr localRelStorageMgr;
			PersistentFileSysRelBufpoolKind relBufpoolKind;
			
			GpPersistentRelfileNode_GetRelfileInfo(
												dbInfoRel->relkind,
												dbInfoRel->relstorage,
												dbInfoRel->relam,
												&localRelStorageMgr,
												&relBufpoolKind);
			Assert(localRelStorageMgr == PersistentFileSysRelStorageMgr_BufferPool);

			/*
			 * Heap tables only ever add a single segment_file_num=0 entry to
			 * gp_persistent_relation regardless of how many segment files there
			 * really are.
			 */
			PersistentRelfile_AddCreated(
										&relFileNode,
										/* segmentFileNum */ 0,
										relStorageMgr,
										relBufpoolKind,
										dbInfoRel->relname,
										&persistentTid,
										&persistentSerialNum,
										/* flushToXLog */ false);
		
			InsertGpRelfileNodeTuple(
							gp_relfile_node,
							dbInfoRel->relationOid,	// pg_class OID
							dbInfoRel->relname,
							relFileNode.relNode,	// pg_class relfilenode
							/* segmentFileNum */ 0,
							/* updateIndex */ false,
							&persistentTid,
							persistentSerialNum);
			
		}
		else
		{
			int a;
			int p;

			/*
			 * Append-Only.
			 */
			/*if (dbInfoRel->physicalSegmentFilesCount == 0 ||
				dbInfoRel->physicalSegmentFiles[0].segmentFileNum != 0)
			{
				elog(ERROR, "Physical segment file #0 missing for relation '%s'",
				     dbInfoRel->relname);
			}*/

			/*
			 * Merge physical file existence and ao[cs]seg catalog logical EOFs .
			 */
			a = 0;
			for (p = 0; p < dbInfoRel->physicalSegmentFilesCount; p++)
			{
				int physicalSegmentFileNum = dbInfoRel->physicalSegmentFiles[p].segmentFileNum;

				bool	haveCatalogInfo;
				int64	logicalEof;

				/* 
				 * There is mostly a 1:1 matching of physical files and logical
				 * files and we just have to match them up correctly.  However
				 * there are several cases where this can diverge that we have
				 * to be able to handle.
				 *
				 * 1) Segment file 0 always exists as a physical file, but is
				 * only cataloged when it actually contains data - this only
				 * occurs for ao when data is inserted in utility mode.
				 *
				 * 2) Files created in aborted transactions where an initial
				 * frozen tuple never made it to disk may have a physical file
				 * with no logical file.  
				 *     XXX - These are leaked files that should probably be 
				 *     cleaned up at some point.
				 *
				 * 3) It is possible to have files that logically exist with a
				 * logical EOF of 0 but not exist in the filesystem.  
				 *     XXX - how does this happen, is it really safe?
				 */

				logicalEof		= 0;
				haveCatalogInfo = false;

				/* If we exhaust the loop then we are in case 2 */
				while (a < dbInfoRel->appendOnlyCatalogSegmentInfoCount)
				{
					DbInfoAppendOnlyCatalogSegmentInfo *logicalSegInfo = \
						&dbInfoRel->appendOnlyCatalogSegmentInfo[a];

					/* Normal Case: both exist */
					if (logicalSegInfo->segmentFileNum == physicalSegmentFileNum)
					{
						logicalEof		= logicalSegInfo->logicalEof;
						haveCatalogInfo = true;
						a++;
						break;  /* found */
					}
					
					/* case 0 or case 2 */
					else if (logicalSegInfo->segmentFileNum > physicalSegmentFileNum)
					{
						logicalEof		= 0;
						haveCatalogInfo = false;
						break;  /* not found */
					}

					/* case 3 - skip over logical segments w/o physical files */
					else if (logicalSegInfo->logicalEof == 0)
					{
						a++;
						continue;  /* keep looking */
					}

					/* otherwise it is an error */
					else
					{
						elog(ERROR, "logical EOF greater than zero (" INT64_FORMAT ") for segment file #%d in relation '%s' but physical file is missing",
							 logicalSegInfo->logicalEof,
							 logicalSegInfo->segmentFileNum,
							 dbInfoRel->relname);
					}

					/* unreachable */
					Assert(false);
				}

				/* 
				 * case 2) Ignore segment file left over from pre-Release 4.0 aborted
				 * transaction whose initial frozen ao[cs]seg tuple never made it to
				 * disk.  This will be a file that can result in an upgrade complaint...
				 */
				if (physicalSegmentFileNum > 0 && !haveCatalogInfo)
					continue;
				
				PersistentRelfile_AddCreated(
											&relFileNode,
											physicalSegmentFileNum,
											relStorageMgr,
											PersistentFileSysRelBufpoolKind_None,
											dbInfoRel->relname,
											&persistentTid,
											&persistentSerialNum,
											/* flushToXLog */ false);
				
				InsertGpRelfileNodeTuple(
								gp_relfile_node,
								dbInfoRel->relationOid, // pg_class OID
								dbInfoRel->relname,
								relFileNode.relNode,	// pg_class relfilenode
								physicalSegmentFileNum,
								/* updateIndex */ false,
								&persistentTid,
								persistentSerialNum);
			}
		}
		(*count)++;
	}

	if (info->database != TemplateDbOid)
	{
		PersistentBuild_ScanGpPersistentRelationNodeForGlobal(
														gp_relfile_node,
														count);
	}

	/*
	 * Build the index for gp_relation_node.  
	 *
	 * The problem is the session we are using is associated with one particular database
	 * of the cluster, but we need to iterate through all the databases.  So, unfortunately, 
	 * the solution has been to use the "Direct Open" stuff.
	 *
	 * We do this because MyDatabaseId, the default tablespace of the session should not be
	 * changed.  The various caches and many other implicit things assume the object is for
	 * MyDatabaseId and the default tablespace. For example, we cannot use 
	 * CatalogUpdateIndexes called in InsertGpRelationNodeTuple because it will not do
	 * the right thing.
	 *
	 * Also, if they re-indexed gp_relation_node, it will have a different relfilenode and so we 
	 * must have found it (above) and open it with dynamically.
	 */
	Assert(indexFound);
	
	PersistentBuild_NonTransactionTruncate(
									&indexRelFileNode);
	
	gp_relfile_node_index =
			DirectOpen_GpRelfileNodeIndexOpenDynamic(
										GpRelfileNodeOidIndexId,
										indexRelFileNode.spcNode, 
										indexRelFileNode.dbNode,
										indexRelFileNode.relNode);

	indexInfo = makeNode(IndexInfo);
	
	indexInfo->ii_NumIndexAttrs = Natts_gp_relfile_node_index;
	indexInfo->ii_KeyAttrNumbers[0] = 1;
	indexInfo->ii_KeyAttrNumbers[1] = 2;
	indexInfo->ii_KeyAttrNumbers[2] = 6;
	indexInfo->ii_Unique = true;

	if (Debug_persistent_print)
		elog(Persistent_DebugPrintLevel(), 
			 "PersistentBuild_PopulateGpRelationNode: building gp_relfile_node_index %u/%u/%u for gp_relfile_node %u/%u/%u",
			 gp_relfile_node_index->rd_node.spcNode,
			 gp_relfile_node_index->rd_node.dbNode,
			 gp_relfile_node_index->rd_node.relNode,
			 gp_relfile_node->rd_node.spcNode,
			 gp_relfile_node->rd_node.dbNode,
			 gp_relfile_node->rd_node.relNode);

	index_build(
			gp_relfile_node,
			gp_relfile_node_index,
			indexInfo,
			false);

	DirectOpen_GpRelfileNodeIndexClose(gp_relfile_node_index);

	DirectOpen_GpRelfileNodeClose(gp_relfile_node);


	if (Debug_persistent_print)
		elog(Persistent_DebugPrintLevel(), 
			 "PersistentBuild_PopulateGpRelationNode: Exit for dbOid %u",
			 info->database);

}