content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *    http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package org.apache.carbondata.processing.loading.model;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  import org.apache.carbondata.core.constants.CarbonCommonConstants;
  import org.apache.carbondata.core.index.Segment;
  import org.apache.carbondata.core.metadata.schema.table.column.CarbonColumn;
  import org.apache.carbondata.core.statusmanager.LoadMetadataDetails;
  import org.apache.carbondata.core.statusmanager.SegmentStatusManager;
  import org.apache.carbondata.core.statusmanager.SegmentUpdateStatusManager;
  import org.apache.carbondata.core.util.CarbonProperties;
  import org.apache.carbondata.core.util.DataLoadMetrics;
  import org.apache.carbondata.core.util.path.CarbonTablePath;
  
  public class CarbonLoadModel implements Serializable {
  
    private static final long serialVersionUID = 6580168429197697465L;
  
    private String databaseName;
  
    private String tableName;
  
    private String factFilePath;
  
    private CarbonDataLoadSchema carbonDataLoadSchema;
  
    private String tablePath;
  
    /*
       This points if the carbonTable is a Non Transactional Table or not.
       The path will be pointed by the tablePath. And there will be
       no Metadata folder present for the Non Transactional Table.
     */
    private boolean carbonTransactionalTable = true;
  
    /* Number of thread in which sdk writer is used */
    private short sdkWriterCores;
  
    private String csvHeader;
    private String[] csvHeaderColumns;
    private String csvDelimiter;
    private ArrayList<String> complexDelimiters;
  
    private List<LoadMetadataDetails> loadMetadataDetails;
    private transient SegmentUpdateStatusManager segmentUpdateStatusManager;
  
    private String blocksID;
  
    /**
     * task id, each spark task has a unique id
     */
    private String taskNo;
    /**
     * new load start time
     */
    private long factTimeStamp;
    /**
     * load Id
     */
    private Segment segment;
  
    /**
     * escape Char
     */
    private String escapeChar;
  
    /**
     * quote Char
     */
    private String quoteChar;
  
    /**
     * comment Char
     */
    private String commentChar;
  
    private String lineSeparator;
  
    private String timestampFormat;
  
    private String dateFormat;
  
    private String defaultTimestampFormat;
  
    private String defaultDateFormat;
  
    /**
     * defines the string that should be treated as null while loading data
     */
    private String serializationNullFormat;
  
    /**
     * defines the string to specify whether the bad record logger should be enabled or not
     */
    private String badRecordsLoggerEnable;
  
    /**
     * defines the option to specify the bad record logger action
     */
    private String badRecordsAction;
  
    /**
     * Max number of columns that needs to be parsed by univocity parser
     */
    private String maxColumns;
  
    /**
     * defines the string to specify whether empty data is good or bad
     */
    private String isEmptyDataBadRecord;
  
    /**
     * defines the string to specify whether to skip empty line
     */
    private String skipEmptyLine;
  
    /**
     * Pre fetch data from csv reader
     */
    private boolean preFetch;
  
    /**
     * Batch sort should be enabled or not
     */
    private String sortScope;
  
    /**
     * bad record location
     */
    private String badRecordsLocation;
  
    /**
     * Number of partitions in global sort.
     */
    private String globalSortPartitions;
  
    private boolean isAggLoadRequest;
    /**
     * sort columns bounds
     */
    private String sortColumnsBoundsStr;
  
    /**
     * It directly writes data directly to nosort processor bypassing all other processors.
     * For this method there will be no data conversion step. It writes data which is directly
     * pushed into.
     */
    private boolean isLoadWithoutConverterStep;
  
    /**
     * Whether non-schema columns are present. This flag should be set only when all the schema
     * columns are already converted. Now, just need to generate and convert non-schema columns
     * present in data fields.
     */
    private boolean nonSchemaColumnsPresent;
  
    /**
     * for insert into flow, schema is already re-arranged. No need to re-arrange the data
     */
    private boolean isLoadWithoutConverterWithoutReArrangeStep;
  
    /**
     * To identify the suitable input processor step for json file loading.
     */
    private boolean isJsonFileLoad;
  
    /**
     * Folder path to where data should be written for this load.
     */
    private String dataWritePath;
  
    /**
     * sort columns bounds
     */
    private String loadMinSize;
  
    private List<String> mergedSegmentIds;
  
    /**
     * compressor used to compress column page
     */
    private String columnCompressor;
  
    /**
     * carbon binary decoder for loading data
     */
    private String binaryDecoder;
  
    /**
     * the total size of loading data
     */
    private long totalSize;
  
    /**
     * range partition data by this column
     */
    private CarbonColumn rangePartitionColumn;
  
    /**
     * control the file size of input data for each range partition
     * input size = max(bockletSize, (blockSize - blocketSize)) * scaleFactor
     */
    private int scaleFactor;
  
    /**
     * bucket id
     */
    private int bucketId;
  
    private DataLoadMetrics metrics;
  
    private boolean skipParsers = false;
  
    private String latestTableStatusWriteVersion = "";
  
    public void setSkipParsers() {
      skipParsers = true;
    }
  
    public boolean isSkipParsers() {
      return skipParsers;
    }
  
    public boolean isAggLoadRequest() {
      return isAggLoadRequest;
    }
  
    public void setAggLoadRequest(boolean aggLoadRequest) {
      isAggLoadRequest = aggLoadRequest;
    }
  
    /**
     * get escape char
     *
     * @return
     */
    public String getEscapeChar() {
      return escapeChar;
    }
  
    /**
     * set escape char
     *
     * @param escapeChar
     */
    public void setEscapeChar(String escapeChar) {
      this.escapeChar = escapeChar;
    }
  
    public String getCsvDelimiter() {
      return csvDelimiter;
    }
  
    public void setCsvDelimiter(String csvDelimiter) {
      this.csvDelimiter = csvDelimiter;
    }
  
    public void setComplexDelimiter(String delimiter) {
      checkAndInitializeComplexDelimiterList();
      this.complexDelimiters.add(delimiter);
    }
  
    public ArrayList<String> getComplexDelimiters() {
      checkAndInitializeComplexDelimiterList();
      return complexDelimiters;
    }
  
    public String getCsvHeader() {
      return csvHeader;
    }
  
    public void setCsvHeader(String csvHeader) {
      this.csvHeader = csvHeader;
    }
  
    public String[] getCsvHeaderColumns() {
      return csvHeaderColumns;
    }
  
    public void setCsvHeaderColumns(String[] csvHeaderColumns) {
      this.csvHeaderColumns = csvHeaderColumns;
    }
  
    /**
     * @return carbon dataload schema
     */
    public CarbonDataLoadSchema getCarbonDataLoadSchema() {
      return carbonDataLoadSchema;
    }
  
    /**
     * @param carbonDataLoadSchema
     */
    public void setCarbonDataLoadSchema(CarbonDataLoadSchema carbonDataLoadSchema) {
      this.carbonDataLoadSchema = carbonDataLoadSchema;
    }
  
    /**
     * @return the databaseName
     */
    public String getDatabaseName() {
      return databaseName;
    }
  
    /**
     * @param databaseName the databaseName to set
     */
    public void setDatabaseName(String databaseName) {
      this.databaseName = databaseName;
    }
  
    /**
     * @return the tableName
     */
    public String getTableName() {
      return tableName;
    }
  
    /**
     * @param tableName the tableName to set
     */
    public void setTableName(String tableName) {
      this.tableName = tableName;
    }
  
    /**
     * @return the factFilePath
     */
    public String getFactFilePath() {
      return factFilePath;
    }
  
    /**
     * @param factFilePath the factFilePath to set
     */
    public void setFactFilePath(String factFilePath) {
      this.factFilePath = factFilePath;
    }
  
    public String getSortColumnsBoundsStr() {
      return sortColumnsBoundsStr;
    }
  
    public void setSortColumnsBoundsStr(String sortColumnsBoundsStr) {
      this.sortColumnsBoundsStr = sortColumnsBoundsStr;
    }
  
    public String getLoadMinSize() {
      return loadMinSize;
    }
  
    public void setLoadMinSize(String loadMinSize) {
      this.loadMinSize = loadMinSize;
    }
  
    public int getBucketId() {
      return bucketId;
    }
  
    public void setBucketId(int bucketId) {
      this.bucketId = bucketId;
    }
  
    /**
     * Get copy with taskNo.
     * Broadcast value is shared in process, so we need to copy it to make sure the value in each
     * task independently.
     *
     * @return
     */
    public CarbonLoadModel getCopyWithTaskNo(String taskNo) {
      CarbonLoadModel copy = new CarbonLoadModel();
      copy.tableName = tableName;
      copy.factFilePath = factFilePath;
      copy.databaseName = databaseName;
      copy.loadMetadataDetails = loadMetadataDetails;
      copy.csvHeader = csvHeader;
      copy.csvHeaderColumns = csvHeaderColumns;
      copy.csvDelimiter = csvDelimiter;
      copy.complexDelimiters = complexDelimiters;
      copy.carbonDataLoadSchema = carbonDataLoadSchema;
      copy.blocksID = blocksID;
      copy.taskNo = taskNo;
      copy.factTimeStamp = factTimeStamp;
      copy.segment = segment;
      copy.serializationNullFormat = serializationNullFormat;
      copy.badRecordsLoggerEnable = badRecordsLoggerEnable;
      copy.badRecordsAction = badRecordsAction;
      copy.escapeChar = escapeChar;
      copy.quoteChar = quoteChar;
      copy.lineSeparator = lineSeparator;
      copy.commentChar = commentChar;
      copy.timestampFormat = timestampFormat;
      copy.dateFormat = dateFormat;
      copy.defaultTimestampFormat = defaultTimestampFormat;
      copy.maxColumns = maxColumns;
      copy.tablePath = tablePath;
      copy.carbonTransactionalTable = carbonTransactionalTable;
      copy.preFetch = preFetch;
      copy.isEmptyDataBadRecord = isEmptyDataBadRecord;
      copy.skipEmptyLine = skipEmptyLine;
      copy.sortScope = sortScope;
      copy.isAggLoadRequest = isAggLoadRequest;
      copy.badRecordsLocation = badRecordsLocation;
      copy.isLoadWithoutConverterStep = isLoadWithoutConverterStep;
      copy.sortColumnsBoundsStr = sortColumnsBoundsStr;
      copy.loadMinSize = loadMinSize;
      copy.sdkWriterCores = sdkWriterCores;
      copy.columnCompressor = columnCompressor;
      copy.binaryDecoder = binaryDecoder;
      copy.rangePartitionColumn = rangePartitionColumn;
      copy.scaleFactor = scaleFactor;
      copy.totalSize = totalSize;
      copy.metrics = metrics;
      copy.isLoadWithoutConverterWithoutReArrangeStep = isLoadWithoutConverterWithoutReArrangeStep;
      return copy;
    }
  
    /**
     * get CarbonLoadModel with partition
     *
     * @param header
     * @param delimiter
     * @return
     */
    public CarbonLoadModel getCopyWithPartition(String header, String delimiter) {
      CarbonLoadModel copyObj = new CarbonLoadModel();
      copyObj.tableName = tableName;
      copyObj.factFilePath = null;
      copyObj.databaseName = databaseName;
      copyObj.loadMetadataDetails = loadMetadataDetails;
      copyObj.carbonDataLoadSchema = carbonDataLoadSchema;
      copyObj.csvHeader = header;
      copyObj.csvHeaderColumns = csvHeaderColumns;
      copyObj.csvDelimiter = delimiter;
      copyObj.complexDelimiters = complexDelimiters;
      copyObj.blocksID = blocksID;
      copyObj.taskNo = taskNo;
      copyObj.factTimeStamp = factTimeStamp;
      copyObj.segment = segment;
      copyObj.serializationNullFormat = serializationNullFormat;
      copyObj.badRecordsLoggerEnable = badRecordsLoggerEnable;
      copyObj.badRecordsAction = badRecordsAction;
      copyObj.escapeChar = escapeChar;
      copyObj.quoteChar = quoteChar;
      copyObj.lineSeparator = lineSeparator;
      copyObj.commentChar = commentChar;
      copyObj.timestampFormat = timestampFormat;
      copyObj.dateFormat = dateFormat;
      copyObj.defaultTimestampFormat = defaultTimestampFormat;
      copyObj.maxColumns = maxColumns;
      copyObj.tablePath = tablePath;
      copyObj.carbonTransactionalTable = carbonTransactionalTable;
      copyObj.preFetch = preFetch;
      copyObj.isEmptyDataBadRecord = isEmptyDataBadRecord;
      copyObj.skipEmptyLine = skipEmptyLine;
      copyObj.sortScope = sortScope;
      copyObj.badRecordsLocation = badRecordsLocation;
      copyObj.isAggLoadRequest = isAggLoadRequest;
      copyObj.sortColumnsBoundsStr = sortColumnsBoundsStr;
      copyObj.loadMinSize = loadMinSize;
      copyObj.sdkWriterCores = sdkWriterCores;
      copyObj.columnCompressor = columnCompressor;
      copyObj.binaryDecoder = binaryDecoder;
      copyObj.rangePartitionColumn = rangePartitionColumn;
      copyObj.scaleFactor = scaleFactor;
      copyObj.totalSize = totalSize;
      copyObj.metrics = metrics;
      copyObj.isLoadWithoutConverterStep = isLoadWithoutConverterStep;
      copyObj.isLoadWithoutConverterWithoutReArrangeStep = isLoadWithoutConverterWithoutReArrangeStep;
      copyObj.latestTableStatusWriteVersion = latestTableStatusWriteVersion;
      return copyObj;
    }
  
    /**
     * @param tablePath The tablePath to set.
     */
    public void setTablePath(String tablePath) {
      this.tablePath = tablePath;
    }
  
    /**
     * @return Returns the factStoreLocation.
     */
    public String getTablePath() {
      return tablePath;
    }
  
    /**
     * getLoadMetadataDetails.
     *
     * @return
     */
    public List<LoadMetadataDetails> getLoadMetadataDetails() {
      return loadMetadataDetails;
    }
  
    /**
     * Get the current load metadata.
     *
     * @return
     */
    public LoadMetadataDetails getCurrentLoadMetadataDetail() {
      if (loadMetadataDetails != null && loadMetadataDetails.size() > 0) {
        return loadMetadataDetails.get(loadMetadataDetails.size() - 1);
      } else {
        return null;
      }
    }
  
    /**
     * setLoadMetadataDetails.
     *
     * @param loadMetadataDetails
     */
    public void setLoadMetadataDetails(List<LoadMetadataDetails> loadMetadataDetails) {
      this.loadMetadataDetails = loadMetadataDetails;
    }
  
    /**
     * getSegmentUpdateStatusManager
     *
     * @return
     */
    public SegmentUpdateStatusManager getSegmentUpdateStatusManager() {
      return segmentUpdateStatusManager;
    }
  
    /**
     * setSegmentUpdateStatusManager
     *
     * @param segmentUpdateStatusManager
     */
    public void setSegmentUpdateStatusManager(SegmentUpdateStatusManager segmentUpdateStatusManager) {
      this.segmentUpdateStatusManager = segmentUpdateStatusManager;
    }
  
    /**
     * @return
     */
    public String getTaskNo() {
      return taskNo;
    }
  
    /**
     * @param taskNo
     */
    public void setTaskNo(String taskNo) {
      this.taskNo = taskNo;
    }
  
    /**
     * @return
     */
    public long getFactTimeStamp() {
      return factTimeStamp;
    }
  
    /**
     * @param factTimeStamp
     */
    public void setFactTimeStamp(long factTimeStamp) {
      this.factTimeStamp = factTimeStamp;
    }
  
    private void checkAndInitializeComplexDelimiterList() {
      if (null == complexDelimiters) {
        complexDelimiters = new ArrayList<>();
      }
    }
  
    /**
     * @return load Id
     */
    public String getSegmentId() {
      if (segment != null) {
        return segment.getSegmentNo();
      } else {
        return null;
      }
    }
  
    /**
     * @param segmentId
     */
    public void setSegmentId(String segmentId) {
      if (segmentId != null) {
        this.segment = Segment.toSegment(segmentId);
      }
    }
  
    public Segment getSegment() {
      return segment;
    }
  
    /**
     * the method returns the value to be treated as null while data load
     *
     * @return
     */
    public String getSerializationNullFormat() {
      return serializationNullFormat;
    }
  
    /**
     * the method sets the value to be treated as null while data load
     *
     * @param serializationNullFormat
     */
    public void setSerializationNullFormat(String serializationNullFormat) {
      this.serializationNullFormat = serializationNullFormat;
    }
  
    /**
     * returns the string to enable bad record logger
     *
     * @return
     */
    public String getBadRecordsLoggerEnable() {
      return badRecordsLoggerEnable;
    }
  
    /**
     * method sets the string to specify whether to enable or dissable the badrecord logger.
     *
     * @param badRecordsLoggerEnable
     */
    public void setBadRecordsLoggerEnable(String badRecordsLoggerEnable) {
      this.badRecordsLoggerEnable = badRecordsLoggerEnable;
    }
  
    public String getQuoteChar() {
      return quoteChar;
    }
  
    public void setQuoteChar(String quoteChar) {
      this.quoteChar = quoteChar;
    }
  
    public String getLineSeparator() {
      return lineSeparator;
    }
  
    public void setLineSeparator(String lineSeparator) {
      this.lineSeparator = lineSeparator;
    }
  
    public String getCommentChar() {
      return commentChar;
    }
  
    public void setCommentChar(String commentChar) {
      this.commentChar = commentChar;
    }
  
    public String getDateFormat() {
      return dateFormat;
    }
  
    public void setDateFormat(String dateFormat) {
      this.dateFormat = dateFormat;
    }
  
    public String getDefaultTimestampFormat() {
      return defaultTimestampFormat;
    }
  
    public void setDefaultTimestampFormat(String defaultTimestampFormat) {
      this.defaultTimestampFormat = defaultTimestampFormat;
    }
  
    /**
     * @return
     */
    public String getMaxColumns() {
      return maxColumns;
    }
  
    /**
     * @param maxColumns
     */
    public void setMaxColumns(String maxColumns) {
      this.maxColumns = maxColumns;
    }
  
    /**
     * returns option to specify the bad record logger action
     *
     * @return
     */
    public String getBadRecordsAction() {
      return badRecordsAction;
    }
  
    /**
     * set option to specify the bad record logger action
     *
     * @param badRecordsAction
     */
    public void setBadRecordsAction(String badRecordsAction) {
      this.badRecordsAction = badRecordsAction;
    }
  
    public boolean isPreFetch() {
      return preFetch;
    }
  
    public void setPreFetch(boolean preFetch) {
      this.preFetch = preFetch;
    }
  
    public String getDefaultDateFormat() {
      return defaultDateFormat;
    }
  
    public void setDefaultDateFormat(String defaultDateFormat) {
      this.defaultDateFormat = defaultDateFormat;
    }
  
    public String getIsEmptyDataBadRecord() {
      return isEmptyDataBadRecord;
    }
  
    public void setIsEmptyDataBadRecord(String isEmptyDataBadRecord) {
      this.isEmptyDataBadRecord = isEmptyDataBadRecord;
    }
  
    public String getSortScope() {
      return sortScope;
    }
  
    public void setSortScope(String sortScope) {
      this.sortScope = sortScope;
    }
  
    public String getGlobalSortPartitions() {
      return globalSortPartitions;
    }
  
    public void setGlobalSortPartitions(String globalSortPartitions) {
      this.globalSortPartitions = globalSortPartitions;
    }
  
    public String getBadRecordsLocation() {
      return badRecordsLocation;
    }
  
    public void setBadRecordsLocation(String badRecordsLocation) {
      this.badRecordsLocation = badRecordsLocation;
    }
  
    public String getTimestampFormat() {
      return timestampFormat;
    }
  
    public void setTimestampFormat(String timestampFormat) {
      this.timestampFormat = timestampFormat;
    }
  
    public String getSkipEmptyLine() {
      return skipEmptyLine;
    }
  
    public void setSkipEmptyLine(String skipEmptyLine) {
      this.skipEmptyLine = skipEmptyLine;
    }
  
    public boolean isLoadWithoutConverterStep() {
      return isLoadWithoutConverterStep;
    }
  
    public void setLoadWithoutConverterStep(boolean loadWithoutConverterStep) {
      isLoadWithoutConverterStep = loadWithoutConverterStep;
    }
  
    public boolean isJsonFileLoad() {
      return isJsonFileLoad;
    }
  
    public void setJsonFileLoad(boolean isJsonFileLoad) {
      this.isJsonFileLoad = isJsonFileLoad;
    }
  
    public String getDataWritePath() {
      return dataWritePath;
    }
  
    public void setDataWritePath(String dataWritePath) {
      this.dataWritePath = dataWritePath;
    }
  
    /**
     * Read segments metadata from table status file and set it to this load model object
     */
    public void readAndSetLoadMetadataDetails() {
      String tblStatusVersion = carbonDataLoadSchema.getCarbonTable().getTableStatusVersion();
      String metadataPath = CarbonTablePath.getMetadataPath(tablePath);
      LoadMetadataDetails[] details;
      if (tblStatusVersion.isEmpty()) {
        details = SegmentStatusManager.readLoadMetadata(metadataPath);
      } else {
        details = SegmentStatusManager.readLoadMetadata(metadataPath, tblStatusVersion);
      }
      setLoadMetadataDetails(Arrays.asList(details));
    }
  
    public boolean isCarbonTransactionalTable() {
      return carbonTransactionalTable;
    }
  
    public void setCarbonTransactionalTable(boolean carbonTransactionalTable) {
      this.carbonTransactionalTable = carbonTransactionalTable;
    }
  
    public void setMergedSegmentIds(List<String> mergedSegmentIds) {
      this.mergedSegmentIds = mergedSegmentIds;
    }
  
    public List<String> getMergedSegmentIds() {
      if (null == mergedSegmentIds) {
        mergedSegmentIds = new ArrayList<>(CarbonCommonConstants.DEFAULT_COLLECTION_SIZE);
      }
      return mergedSegmentIds;
    }
  
    public short getSdkWriterCores() {
      return sdkWriterCores;
    }
  
    public void setSdkWriterCores(short sdkWriterCores) {
      this.sdkWriterCores = sdkWriterCores;
    }
  
    public String getColumnCompressor() {
      return columnCompressor;
    }
  
    public void setColumnCompressor(String columnCompressor) {
      this.columnCompressor = columnCompressor;
    }
  
    public String getBinaryDecoder() {
      return binaryDecoder;
    }
  
    public void setBinaryDecoder(String binaryDecoder) {
      this.binaryDecoder = binaryDecoder;
    }
  
    public CarbonColumn getRangePartitionColumn() {
      return rangePartitionColumn;
    }
  
    public void setRangePartitionColumn(CarbonColumn rangePartitionColumn) {
      this.rangePartitionColumn = rangePartitionColumn;
    }
  
    public long getTotalSize() {
      return totalSize;
    }
  
    public void setTotalSize(long totalSize) {
      this.totalSize = totalSize;
    }
  
    public void setScaleFactor(int scaleFactor) {
      this.scaleFactor = scaleFactor;
    }
  
    public int getScaleFactor() {
      return scaleFactor;
    }
  
    public DataLoadMetrics getMetrics() {
      return metrics;
    }
  
    public void setMetrics(DataLoadMetrics metrics) {
      this.metrics = metrics;
    }
  
    public boolean isLoadWithoutConverterWithoutReArrangeStep() {
      return isLoadWithoutConverterWithoutReArrangeStep;
    }
  
    public void setLoadWithoutConverterWithoutReArrangeStep(
        boolean loadWithoutConverterWithoutReArrangeStep) {
      isLoadWithoutConverterWithoutReArrangeStep = loadWithoutConverterWithoutReArrangeStep;
    }
  
    public boolean isNonSchemaColumnsPresent() {
      return nonSchemaColumnsPresent;
    }
  
    public void setNonSchemaColumnsPresent(boolean nonSchemaColumnsPresent) {
      this.nonSchemaColumnsPresent = nonSchemaColumnsPresent;
    }
  
    public String getLatestTableStatusWriteVersion() {
      if (!CarbonProperties.isTableStatusMultiVersionEnabled()) {
        return "";
      }
      return latestTableStatusWriteVersion;
    }
  
    public void setLatestTableStatusWriteVersion(String latestTableStatusWriteVersion) {
      if (this.latestTableStatusWriteVersion.isEmpty()) {
        this.latestTableStatusWriteVersion = latestTableStatusWriteVersion;
      }
    }
  }
  