/**
  * Determine whether or not any columns in the current index row are being changed by the update.
  * No need to update the index if no columns changed.
  *
  * @return Nothing.
  * @exception StandardException Thrown on error
  */
 private boolean indexRowChanged() throws StandardException {
   int numColumns = ourIndexRow.nColumns();
   for (int index = 1; index <= numColumns; index++) {
     DataValueDescriptor oldOrderable = ourIndexRow.getColumn(index);
     DataValueDescriptor newOrderable = ourUpdatedIndexRow.getColumn(index);
     if (!(oldOrderable.compare(DataValueDescriptor.ORDER_OP_EQUALS, newOrderable, true, true))) {
       return true;
     }
   }
   return false;
 }
  /**
   * 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);
      }
    }
  }
  /**
   * Delete a row from our index. This assumes our index ScanController is positioned before the row
   * by setScan if we own the SC, otherwise it is positioned on the row by the underlying index
   * scan.
   *
   * <p>This verifies the row exists and is unique.
   *
   * @exception StandardException Thrown on error
   */
  private void doDelete() throws StandardException {
    if (ownIndexSC) {
      if (!indexSC.next()) {
        // This means that the entry for the index does not exist, this
        // is a serious problem with the index.  Past fixed problems
        // like track 3703 can leave db's in the field with this problem
        // even though the bug in the code which caused it has long
        // since been fixed.  Then the problem can surface months later
        // when the customer attempts to upgrade.  By "ignoring" the
        // missing row here the problem is automatically "fixed" and
        // since the code is trying to delete the row anyway it doesn't
        // seem like such a bad idea.  It also then gives a tool to
        // support to be able to fix some system catalog problems where
        // they can delete the base rows by dropping the system objects
        // like stored statements.

        /*
                     dem 2013/04/10 - temporarily comment this out while
                     we are getting snapshot isolation DDL support in
                     place

        if (SanityManager.DEBUG)
        	SanityManager.THROWASSERT(
                            "Index row "+RowUtil.toString(ourIndexRow)+
                            " not found in conglomerateid " + indexCID +
                            "Current scan = " + indexSC);
                    */

        Object[] args = new Object[2];
        args[0] = ourIndexRow.getRowArray()[ourIndexRow.getRowArray().length - 1];
        args[1] = new Long(indexCID);

        Monitor.getStream()
            .println(
                MessageService.getCompleteMessage(
                    SQLState.LANG_IGNORE_MISSING_INDEX_ROW_DURING_DELETE, args));

        // just return indicating the row has been deleted.
        return;
      }
    }

    indexSC.delete();
  }
  /**
   * Position our index scan to 'ourIndexRow'.
   *
   * <p>This creates the scan the first time it is called.
   *
   * @exception StandardException Thrown on error
   */
  private void setScan() throws StandardException {
    /*
     * -sf- Derby makes an assumption about system tables that isn't true
     * for Splice land, which results in WriteConflicts occurring
     * when you try to drop tables
     *
     * With indices, Derby only ever creates start and stop keys for the scan.
     * However, if the entire index is to be scanned, one or more column in the start/stop
     * key may be null. With db this was apparently treated acceptably, but in Splice
     * this results in garbage start and stop row keys, which in turn results in deleting
     * every row in the index instead of deleting just the rows of interest.
     *
     * Thus, the following hack. When the row is not entirely filled, we convert
     * the start/stop key into a single ANDed equals qualifier[], and use that instead
     */
    DataValueDescriptor[] ourRowDvds = ourIndexRow.getRowArray();
    int numNonNull = ourRowDvds.length;
    for (int i = 0; i < ourRowDvds.length; i++) {
      if (ourRowDvds[i].isNull()) {
        numNonNull--;
      }
    }
    Qualifier[][] qualifiers = null;
    if (numNonNull < ourRowDvds.length) {
      qualifiers = new Qualifier[1][];
      qualifiers[0] = new Qualifier[numNonNull];
      for (int dvdPos = 0, qualPos = 0; dvdPos < ourRowDvds.length; dvdPos++) {
        if (ourRowDvds[dvdPos].isNull()) continue;

        ScanQualifier qualifier = new GenericScanQualifier();
        qualifier.setQualifier(
            dvdPos, ourRowDvds[dvdPos], DataValueDescriptor.ORDER_OP_EQUALS, false, false, false);
        qualifiers[0][qualPos] = qualifier;
      }
    }
    /* Get the SC from the activation if re-using */
    if (!ownIndexSC) {
      indexSC = activation.getIndexScanController();
    } else if (indexSC == null) {
      RowLocation templateBaseRowLocation = baseCC.newRowLocationTemplate();
      /* DataDictionary doesn't have compiled info */
      if (indexSCOCI == null) {
        indexSC =
            tc.openScan(
                indexCID,
                false, /* hold */
                TransactionController.OPENMODE_FORUPDATE, /* forUpdate */
                lockMode,
                isolationLevel,
                (FormatableBitSet) null, /* all fields */
                ourIndexRow.getRowArray(), /* startKeyValue */
                ScanController.GE, /* startSearchOp */
                qualifiers, /* qualifier */
                ourIndexRow.getRowArray(), /* stopKeyValue */
                ScanController.GT /* stopSearchOp */);
      } else {
        indexSC =
            tc.openCompiledScan(
                false, /* hold */
                TransactionController.OPENMODE_FORUPDATE, /* forUpdate */
                lockMode,
                isolationLevel,
                (FormatableBitSet) null, /* all fields */
                ourIndexRow.getRowArray(), /* startKeyValue */
                ScanController.GE, /* startSearchOp */
                qualifiers, /* qualifier */
                ourIndexRow.getRowArray(), /* stopKeyValue */
                ScanController.GT, /* stopSearchOp */
                indexSCOCI,
                indexDCOCI);
      }
    } else {
      indexSC.reopenScan(
          ourIndexRow.getRowArray(), /* startKeyValue */
          ScanController.GE, /* startSearchOperator */
          qualifiers, /* qualifier */
          ourIndexRow.getRowArray(), /* stopKeyValue */
          ScanController.GT /* stopSearchOperator */);
    }
  }