/**
   * Insert the given row into the given conglomerate and check for duplicate key error.
   *
   * @param row The row to insert
   * @exception StandardException Thrown on duplicate key error
   */
  private void insertAndCheckDups(ExecIndexRow row) throws StandardException {
    openIndexCC();

    int insertStatus = indexCC.insert(row.getRowArray());

    if (insertStatus == ConglomerateController.ROWISDUPLICATE) {
      /*
       ** We have a duplicate key error.
       */
      String indexOrConstraintName = indexName;
      // now get table name, and constraint name if needed
      LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
      DataDictionary dd = lcc.getDataDictionary();
      // get the descriptors
      ConglomerateDescriptor cd = dd.getConglomerateDescriptor(indexCID);

      UUID tableID = cd.getTableID();
      TableDescriptor td = dd.getTableDescriptor(tableID);
      String tableName = td.getName();

      if (indexOrConstraintName == null) // no index name passed in
      {
        ConstraintDescriptor conDesc = dd.getConstraintDescriptor(td, cd.getUUID());
        indexOrConstraintName = conDesc.getConstraintName();
      }

      throw StandardException.newException(
          SQLState.LANG_DUPLICATE_KEY_CONSTRAINT, indexOrConstraintName, tableName);
    }
    if (SanityManager.DEBUG) {
      if (insertStatus != 0) {
        SanityManager.THROWASSERT("Unknown insert status " + insertStatus);
      }
    }
  }
  static void debugLock(String type, CompatibilitySpace compatibilitySpace, Object group) {

    if (SanityManager.DEBUG) {

      SanityManager.DEBUG(Constants.LOCK_TRACE, type + debugLockString(compatibilitySpace, group));
    }
  }
  /**
   * Finish doing the changes for this index. This is intended for deferred inserts for unique
   * indexes. It has no effect unless we are doing an update of a unique index.
   *
   * @exception StandardException Thrown on error
   */
  public void finish() throws StandardException {
    ExecRow deferredRow;

    /* Deferred processing only necessary for unique indexes */
    if (rowHolder != null) {
      CursorResultSet rs = rowHolder.getResultSet();
      try {
        rs.open();
        while ((deferredRow = rs.getNextRow()) != null) {
          if (SanityManager.DEBUG) {
            if (!(deferredRow instanceof ExecIndexRow)) {
              SanityManager.THROWASSERT(
                  "deferredRow isn't an instance "
                      + "of ExecIndexRow as expected. "
                      + "It is an "
                      + deferredRow.getClass().getName());
            }
          }
          insertAndCheckDups((ExecIndexRow) deferredRow);
        }
      } finally {
        rs.close();

        /*
         ** If row holder was passed in, let the
         ** client of this method clean it up.
         */
        if (!rowHolderPassedIn) {
          rowHolder.close();
        }
      }
    }
  }
  static void debugAddThreadInfo(StringBuffer sb) {

    if (SanityManager.DEBUG) {
      if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE_ADD_THREAD_INFO)) {
        debugAppendObject(sb, " Thread=", Thread.currentThread());
      }
    }
  }
  /**
   * Create a new {@code FileInfoDescriptor} using the supplied arguments.
   *
   * <p>id unique id to be used for the new file descriptor sd schema of the new file to be stored
   * in the database SQLName the SQL name of the new schema object representing the file
   * generationID version numberof the file the descriptor describes
   *
   * @return the newly created file info descriptor
   */
  public FileInfoDescriptor newFileInfoDescriptor(
      UUID id, SchemaDescriptor sd, String sqlName, long generationId) {
    if (SanityManager.DEBUG) {
      SanityManager.ASSERT(id != null);
    }

    return new FileInfoDescriptor(dataDictionary, id, sd, sqlName, generationId);
  }
  /**
   * Read an integer previously written by writeInt().
   *
   * @exception IOException an exception was thrown by a method on in.
   */
  public static final int readInt(DataInput in) throws IOException {

    int value = in.readUnsignedByte();

    if ((value & ~0x3f) == 0) {
      // length is stored in this byte, we also know that the 0x80 bit
      // was not set, so no need to mask off the sign extension from
      // the byte to int conversion.

      // account for 1 byte stored length of field + 1 for all returns
      return (value);
    } else if ((value & 0x80) == 0) {
      // length is stored in 2 bytes.  only use low 6 bits from 1st byte.

      if (SanityManager.DEBUG) {
        SanityManager.ASSERT((value & 0x40) == 0x40);
      }

      // top 8 bits of 2 byte length is stored in this byte, we also
      // know that the 0x80 bit was not set, so no need to mask off the
      // sign extension from the 1st byte to int conversion.  Need to
      // mask the byte in data[offset + 1] to account for possible sign
      // extension.

      return (((value & 0x3f) << 8) | in.readUnsignedByte());
    } else {
      // length is stored in 4 bytes.  only use low 7 bits from 1st byte.

      if (SanityManager.DEBUG) {
        SanityManager.ASSERT((value & 0x80) == 0x80);
      }

      // top 8 bits of 4 byte length is stored in this byte, we also
      // know that the 0x80 bit was set, so need to mask off the
      // sign extension from the 1st byte to int conversion.  Need to
      // mask the bytes from the next 3 bytes data[offset + 1,2,3] to
      // account for possible sign extension.
      //
      return (((value & 0x7f) << 24)
          | (in.readUnsignedByte() << 16)
          | (in.readUnsignedByte() << 8)
          | (in.readUnsignedByte()));
    }
  }
  public static final int readInt(byte[] data, int offset) {
    int value = data[offset++];

    if ((value & ~0x3f) == 0) {
      // length is stored in this byte, we also know that the 0x80 bit
      // was not set, so no need to mask off the sign extension from
      // the byte to int conversion.

      return (value);
    } else if ((value & 0x80) == 0) {
      // length is stored in 2 bytes.  only use low 6 bits from 1st byte.

      if (SanityManager.DEBUG) {
        SanityManager.ASSERT((value & 0x40) == 0x40);
      }

      // top 8 bits of 2 byte length is stored in this byte, we also
      // know that the 0x80 bit was not set, so no need to mask off the
      // sign extension from the 1st byte to int conversion.  Need to
      // mask the byte in data[offset + 1] to account for possible sign
      // extension.

      return (((value & 0x3f) << 8) | (data[offset] & 0xff));
    } else {
      // length is stored in 4 bytes.  only use low 7 bits from 1st byte.

      if (SanityManager.DEBUG) {
        SanityManager.ASSERT((value & 0x80) == 0x80);
      }

      // top 8 bits of 4 byte length is stored in this byte, we also
      // know that the 0x80 bit was set, so need to mask off the
      // sign extension from the 1st byte to int conversion.  Need to
      // mask the bytes from the next 3 bytes data[offset + 1,2,3] to
      // account for possible sign extension.
      //
      return (((value & 0x7f) << 24)
          | ((data[offset++] & 0xff) << 16)
          | ((data[offset++] & 0xff) << 8)
          | ((data[offset] & 0xff)));
    }
  }
  String getOperatorName() {
    switch (opType) {
      case INTERSECT_OP:
        return "INTERSECT";

      case EXCEPT_OP:
        return "EXCEPT";
    }
    if (SanityManager.DEBUG)
      SanityManager.THROWASSERT("Invalid intersectOrExcept opType: " + opType);
    return "?";
  }
  static void debugLock(
      String type,
      CompatibilitySpace compatibilitySpace,
      Object group,
      Lockable ref,
      Object qualifier,
      int timeout) {

    if (SanityManager.DEBUG) {

      SanityManager.DEBUG(
          Constants.LOCK_TRACE,
          type + debugLockString(compatibilitySpace, group, ref, qualifier, timeout));
    }
  }
  /**
   * Create an IndexChanger
   *
   * @param irg the IndexRowGenerator for the index.
   * @param indexCID the conglomerate id for the index.
   * @param indexSCOCI the SCOCI for the idexes.
   * @param indexDCOCI the DCOCI for the idexes.
   * @param baseCC the ConglomerateController for the base table.
   * @param tc The TransactionController
   * @param lockMode The lock mode (granularity) to use
   * @param baseRowReadMap Map of columns read in. 1 based.
   * @param isolationLevel Isolation level to use.
   * @param activation Current activation
   * @exception StandardException Thrown on error
   */
  public IndexChanger(
      IndexRowGenerator irg,
      long indexCID,
      StaticCompiledOpenConglomInfo indexSCOCI,
      DynamicCompiledOpenConglomInfo indexDCOCI,
      String indexName,
      ConglomerateController baseCC,
      TransactionController tc,
      int lockMode,
      FormatableBitSet baseRowReadMap,
      int isolationLevel,
      Activation activation)
      throws StandardException {
    this.irg = irg;
    this.indexCID = indexCID;
    this.indexSCOCI = indexSCOCI;
    this.indexDCOCI = indexDCOCI;
    this.baseCC = baseCC;
    this.tc = tc;
    this.lockMode = lockMode;
    this.baseRowReadMap = baseRowReadMap;
    this.rowHolderPassedIn = false;
    this.isolationLevel = isolationLevel;
    this.activation = activation;
    this.indexName = indexName;

    // activation will be null when called from DataDictionary
    if (activation != null && activation.getIndexConglomerateNumber() == indexCID) {
      ownIndexSC = false;
    }

    if (SanityManager.DEBUG) {
      SanityManager.ASSERT(tc != null, "TransactionController argument to constructor is null");
      SanityManager.ASSERT(irg != null, "IndexRowGenerator argument to constructor is null");
    }
  }
  double getRowCountEstimate(double leftRowCount, double rightRowCount) {
    switch (opType) {
      case INTERSECT_OP:
        // The result has at most min( leftRowCount, rightRowCount). Estimate the actual row count
        // at
        // half that.
        return Math.min(leftRowCount, rightRowCount) / 2;

      case EXCEPT_OP:
        // The result has at most leftRowCount rows and at least
        // max(0, leftRowCount - rightRowCount) rows.  Use the mean
        // of those two as the estimate.
        return (leftRowCount + Math.max(0, leftRowCount - rightRowCount)) / 2;
    }
    if (SanityManager.DEBUG)
      SanityManager.THROWASSERT("Invalid intersectOrExcept opType: " + opType);
    return 1.0;
  } // end of getRowCountEstimate
  /**
   * Initialize the class.
   *
   * <p>Save away pointers to the base table template row, and the rowlocation class. Build default
   * map of base columns to key columns, this map can be changed with setMap().
   *
   * <p>
   *
   * @param template The template for the base table row.
   * @param rowlocation The template for the row location.
   * @param numkeys The total number of columns in the secondary index including the rowlocation
   *     column.
   * @exception StandardException Standard exception policy.
   */
  public void init(DataValueDescriptor[] template, RowLocation rowlocation, int numkeys)
      throws StandardException {
    if (SanityManager.DEBUG) {
      if (numkeys != (template.length + 1))
        SanityManager.THROWASSERT("numkeys = " + numkeys + " template.length = " + template.length);
    }

    init_rowlocation = rowlocation;

    /* create new object array for the row, and copy all object references
     * from template row to new secondary index row.
     */
    row = new DataValueDescriptor[numkeys];

    System.arraycopy(template, 0, row, 0, template.length);

    /* add the reference to the row location column as the last column */
    row[row.length - 1] = rowlocation;
  }
  /**
   * Make a SYSCONTRAINTS row
   *
   * @throws StandardException thrown on failure
   * @return Row suitable for inserting into SYSCONTRAINTS.
   */
  public ExecRow makeRow(TupleDescriptor td, TupleDescriptor parent) throws StandardException {
    DataValueDescriptor col;
    ExecRow row;
    int constraintIType;
    UUID oid;
    String constraintSType = null;
    String constraintID = null;
    String tableID = null;
    String constraintName = null;
    String schemaID = null;
    boolean isEnabled = true;
    int referenceCount = 0;

    if (td != null) {
      ConstraintDescriptor constraint = (ConstraintDescriptor) td;
      /*
       ** We only allocate a new UUID if the descriptor doesn't already have one.
       ** For descriptors replicated from a Source system, we already have an UUID.
       */
      oid = constraint.getUUID();
      constraintID = oid.toString();

      oid = constraint.getTableId();
      tableID = oid.toString();

      constraintName = constraint.getConstraintName();

      constraintIType = constraint.getConstraintType();
      switch (constraintIType) {
        case DataDictionary.PRIMARYKEY_CONSTRAINT:
          constraintSType = "P";
          break;

        case DataDictionary.UNIQUE_CONSTRAINT:
          constraintSType = "U";
          break;

        case DataDictionary.CHECK_CONSTRAINT:
          constraintSType = "C";
          break;

        case DataDictionary.FOREIGNKEY_CONSTRAINT:
          constraintSType = "F";
          break;

        default:
          if (SanityManager.DEBUG) {
            SanityManager.THROWASSERT("invalid constraint type");
          }
      }

      schemaID = constraint.getSchemaDescriptor().getUUID().toString();
      isEnabled = constraint.isEnabled();
      referenceCount = constraint.getReferenceCount();
    }

    /* Insert info into sysconstraints */

    /* RESOLVE - It would be nice to require less knowledge about sysconstraints
     * and have this be more table driven.
     */

    /* Build the row to insert  */
    row = getExecutionFactory().getValueRow(SYSCONSTRAINTS_COLUMN_COUNT);

    /* 1st column is CONSTRAINTID (UUID - char(36)) */
    row.setColumn(SYSCONSTRAINTS_CONSTRAINTID, new SQLChar(constraintID));

    /* 2nd column is TABLEID (UUID - char(36)) */
    row.setColumn(SYSCONSTRAINTS_TABLEID, new SQLChar(tableID));

    /* 3rd column is NAME (varchar(128)) */
    row.setColumn(SYSCONSTRAINTS_CONSTRAINTNAME, new SQLVarchar(constraintName));

    /* 4th column is TYPE (char(1)) */
    row.setColumn(SYSCONSTRAINTS_TYPE, new SQLChar(constraintSType));

    /* 5th column is SCHEMAID (UUID - char(36)) */
    row.setColumn(SYSCONSTRAINTS_SCHEMAID, new SQLChar(schemaID));

    /* 6th column is STATE (char(1)) */
    row.setColumn(SYSCONSTRAINTS_STATE, new SQLChar(isEnabled ? "E" : "D"));

    /* 7th column is REFERENCED */
    row.setColumn(SYSCONSTRAINTS_REFERENCECOUNT, new SQLInteger(referenceCount));

    return row;
  }
  /**
   * Make a ConstraintDescriptor out of a SYSCONSTRAINTS row
   *
   * @param row a SYSCONSTRAINTS row
   * @param parentTupleDescriptor Subconstraint descriptor with auxiliary info.
   * @param dd dataDictionary
   * @throws StandardException thrown on failure
   */
  public TupleDescriptor buildDescriptor(
      ExecRow row, TupleDescriptor parentTupleDescriptor, DataDictionary dd)
      throws StandardException {
    ConstraintDescriptor constraintDesc = null;

    if (SanityManager.DEBUG) {
      SanityManager.ASSERT(
          row.nColumns() == SYSCONSTRAINTS_COLUMN_COUNT,
          "Wrong number of columns for a SYSCONSTRAINTS row");
    }

    DataValueDescriptor col;
    ConglomerateDescriptor conglomDesc;
    DataDescriptorGenerator ddg;
    TableDescriptor td = null;
    int constraintIType = -1;
    int[] keyColumns = null;
    UUID constraintUUID;
    UUID schemaUUID;
    UUID tableUUID;
    UUID referencedConstraintId = null;
    SchemaDescriptor schema;
    String tableUUIDString;
    String constraintName;
    String constraintSType;
    String constraintStateStr;
    boolean constraintEnabled;
    int referenceCount;
    String constraintUUIDString;
    String schemaUUIDString;
    SubConstraintDescriptor scd;

    if (SanityManager.DEBUG) {
      if (!(parentTupleDescriptor instanceof SubConstraintDescriptor)) {
        SanityManager.THROWASSERT(
            "parentTupleDescriptor expected to be instanceof "
                + "SubConstraintDescriptor, not "
                + parentTupleDescriptor.getClass().getName());
      }
    }

    scd = (SubConstraintDescriptor) parentTupleDescriptor;

    ddg = dd.getDataDescriptorGenerator();

    /* 1st column is CONSTRAINTID (UUID - char(36)) */
    col = row.getColumn(SYSCONSTRAINTS_CONSTRAINTID);
    constraintUUIDString = col.getString();
    constraintUUID = getUUIDFactory().recreateUUID(constraintUUIDString);

    /* 2nd column is TABLEID (UUID - char(36)) */
    col = row.getColumn(SYSCONSTRAINTS_TABLEID);
    tableUUIDString = col.getString();
    tableUUID = getUUIDFactory().recreateUUID(tableUUIDString);

    /* Get the TableDescriptor.
     * It may be cached in the SCD,
     * otherwise we need to go to the
     * DD.
     */
    if (scd != null) {
      td = scd.getTableDescriptor();
    }
    if (td == null) {
      td = dd.getTableDescriptor(tableUUID);
    }

    /* 3rd column is NAME (varchar(128)) */
    col = row.getColumn(SYSCONSTRAINTS_CONSTRAINTNAME);
    constraintName = col.getString();

    /* 4th column is TYPE (char(1)) */
    col = row.getColumn(SYSCONSTRAINTS_TYPE);
    constraintSType = col.getString();
    if (SanityManager.DEBUG) {
      SanityManager.ASSERT(constraintSType.length() == 1, "Fourth column type incorrect");
    }

    boolean typeSet = false;
    switch (constraintSType.charAt(0)) {
      case 'P':
        constraintIType = DataDictionary.PRIMARYKEY_CONSTRAINT;
        typeSet = true;
        // fall through

      case 'U':
        if (!typeSet) {
          constraintIType = DataDictionary.UNIQUE_CONSTRAINT;
          typeSet = true;
        }
        // fall through

      case 'F':
        if (!typeSet) constraintIType = DataDictionary.FOREIGNKEY_CONSTRAINT;
        if (SanityManager.DEBUG) {
          if (!(parentTupleDescriptor instanceof SubKeyConstraintDescriptor)) {
            SanityManager.THROWASSERT(
                "parentTupleDescriptor expected to be instanceof "
                    + "SubKeyConstraintDescriptor, not "
                    + parentTupleDescriptor.getClass().getName());
          }
        }
        conglomDesc =
            td.getConglomerateDescriptor(
                ((SubKeyConstraintDescriptor) parentTupleDescriptor).getIndexId());
        /* Take care the rare case of conglomDesc being null.  The
         * reason is that our "td" is out of date.  Another thread
         * which was adding a constraint committed between the moment
         * we got the table descriptor (conglomerate list) and the
         * moment we scanned and got the constraint desc list.  Since
         * that thread just added a new row to SYSCONGLOMERATES,
         * SYSCONSTRAINTS, etc.  We wouldn't have wanted to lock the
         * system tables just to prevent other threads from adding new
         * rows.
         */
        if (conglomDesc == null) {
          // we can't be getting td from cache because if we are
          // here, we must have been in dd's ddl mode (that's why
          // the ddl thread went through), we are not done yet, the
          // dd ref count is not 0, hence it couldn't have turned
          // into COMPILE_ONLY mode
          td = dd.getTableDescriptor(tableUUID);
          if (scd != null) scd.setTableDescriptor(td);
          // try again now
          conglomDesc =
              td.getConglomerateDescriptor(
                  ((SubKeyConstraintDescriptor) parentTupleDescriptor).getIndexId());
        }

        if (SanityManager.DEBUG) {
          SanityManager.ASSERT(
              conglomDesc != null, "conglomDesc is expected to be non-null for backing index");
        }
        referencedConstraintId =
            ((SubKeyConstraintDescriptor) parentTupleDescriptor).getKeyConstraintId();
        keyColumns = conglomDesc.getIndexDescriptor().baseColumnPositions();
        break;

      case 'C':
        constraintIType = DataDictionary.CHECK_CONSTRAINT;
        if (SanityManager.DEBUG) {
          if (!(parentTupleDescriptor instanceof SubCheckConstraintDescriptor)) {
            SanityManager.THROWASSERT(
                "parentTupleDescriptor expected to be instanceof "
                    + "SubCheckConstraintDescriptor, not "
                    + parentTupleDescriptor.getClass().getName());
          }
        }
        break;

      default:
        if (SanityManager.DEBUG) {
          SanityManager.THROWASSERT("Fourth column value invalid");
        }
    }

    /* 5th column is SCHEMAID (UUID - char(36)) */
    col = row.getColumn(SYSCONSTRAINTS_SCHEMAID);
    schemaUUIDString = col.getString();
    schemaUUID = getUUIDFactory().recreateUUID(schemaUUIDString);

    schema = dd.getSchemaDescriptor(schemaUUID, null);

    /* 6th column is STATE (char(1)) */
    col = row.getColumn(SYSCONSTRAINTS_STATE);
    constraintStateStr = col.getString();
    if (SanityManager.DEBUG) {
      SanityManager.ASSERT(constraintStateStr.length() == 1, "Sixth column (state) type incorrect");
    }

    switch (constraintStateStr.charAt(0)) {
      case 'E':
        constraintEnabled = true;
        break;
      case 'D':
        constraintEnabled = false;
        break;
      default:
        constraintEnabled = true;
        if (SanityManager.DEBUG) {
          SanityManager.THROWASSERT(
              "Invalidate state value '" + constraintStateStr + "' for constraint");
        }
    }

    /* 7th column is REFERENCECOUNT, boolean */
    col = row.getColumn(SYSCONSTRAINTS_REFERENCECOUNT);
    referenceCount = col.getInt();

    /* now build and return the descriptor */

    switch (constraintIType) {
      case DataDictionary.PRIMARYKEY_CONSTRAINT:
        constraintDesc =
            ddg.newPrimaryKeyConstraintDescriptor(
                td,
                constraintName,
                false, // deferable,
                false, // initiallyDeferred,
                keyColumns, // genReferencedColumns(dd, td), //int referencedColumns[],
                constraintUUID,
                ((SubKeyConstraintDescriptor) parentTupleDescriptor).getIndexId(),
                schema,
                constraintEnabled,
                referenceCount);
        break;

      case DataDictionary.UNIQUE_CONSTRAINT:
        constraintDesc =
            ddg.newUniqueConstraintDescriptor(
                td,
                constraintName,
                false, // deferable,
                false, // initiallyDeferred,
                keyColumns, // genReferencedColumns(dd, td), //int referencedColumns[],
                constraintUUID,
                ((SubKeyConstraintDescriptor) parentTupleDescriptor).getIndexId(),
                schema,
                constraintEnabled,
                referenceCount);
        break;

      case DataDictionary.FOREIGNKEY_CONSTRAINT:
        if (SanityManager.DEBUG) {
          SanityManager.ASSERT(
              referenceCount == 0, "REFERENCECOUNT column is nonzero for fk constraint");
        }

        constraintDesc =
            ddg.newForeignKeyConstraintDescriptor(
                td,
                constraintName,
                false, // deferable,
                false, // initiallyDeferred,
                keyColumns, // genReferencedColumns(dd, td), //int referencedColumns[],
                constraintUUID,
                ((SubKeyConstraintDescriptor) parentTupleDescriptor).getIndexId(),
                schema,
                referencedConstraintId,
                constraintEnabled,
                ((SubKeyConstraintDescriptor) parentTupleDescriptor).getRaDeleteRule(),
                ((SubKeyConstraintDescriptor) parentTupleDescriptor).getRaUpdateRule());
        break;

      case DataDictionary.CHECK_CONSTRAINT:
        if (SanityManager.DEBUG) {
          SanityManager.ASSERT(
              referenceCount == 0, "REFERENCECOUNT column is nonzero for check constraint");
        }

        constraintDesc =
            ddg.newCheckConstraintDescriptor(
                td,
                constraintName,
                false, // deferable,
                false, // initiallyDeferred,
                constraintUUID,
                ((SubCheckConstraintDescriptor) parentTupleDescriptor).getConstraintText(),
                ((SubCheckConstraintDescriptor) parentTupleDescriptor)
                    .getReferencedColumnsDescriptor(),
                schema,
                constraintEnabled);
        break;
    }
    return constraintDesc;
  }