/**
   * Bind this LockTableNode. This means looking up the table, verifying it exists and getting the
   * heap conglomerate number.
   *
   * @exception StandardException Thrown on error
   */
  public void bindStatement() throws StandardException {
    CompilerContext cc = getCompilerContext();
    ConglomerateDescriptor cd;
    DataDictionary dd = getDataDictionary();
    SchemaDescriptor sd;

    String schemaName = tableName.getSchemaName();
    sd = getSchemaDescriptor(schemaName);

    // Users are not allowed to lock system tables
    if (sd.isSystemSchema()) {
      throw StandardException.newException(
          SQLState.LANG_NO_USER_DDL_IN_SYSTEM_SCHEMA, statementToString(), schemaName);
    }

    lockTableDescriptor = getTableDescriptor(tableName.getTableName(), sd);

    if (lockTableDescriptor == null) {
      // Check if the reference is for a synonym.
      TableName synonymTab = resolveTableToSynonym(tableName);
      if (synonymTab == null)
        throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND, tableName);
      tableName = synonymTab;
      sd = getSchemaDescriptor(tableName.getSchemaName());

      lockTableDescriptor = getTableDescriptor(synonymTab.getTableName(), sd);
      if (lockTableDescriptor == null)
        throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND, tableName);
    }

    // throw an exception if user is attempting to lock a temporary table
    if (lockTableDescriptor.getTableType() == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE) {
      throw StandardException.newException(
          SQLState.LANG_NOT_ALLOWED_FOR_DECLARED_GLOBAL_TEMP_TABLE);
    }

    conglomerateNumber = lockTableDescriptor.getHeapConglomerateId();

    /* Get the base conglomerate descriptor */
    cd = lockTableDescriptor.getConglomerateDescriptor(conglomerateNumber);

    /* Statement is dependent on the TableDescriptor and ConglomerateDescriptor */
    cc.createDependency(lockTableDescriptor);
    cc.createDependency(cd);

    if (isPrivilegeCollectionRequired()) {
      // need SELECT privilege to perform lock table statement.
      cc.pushCurrentPrivType(Authorizer.SELECT_PRIV);
      cc.addRequiredTablePriv(lockTableDescriptor);
      cc.popCurrentPrivType();
    }
  }
  /*
   ** Create an sps that is used by the trigger.
   */
  private SPSDescriptor createSPS(
      LanguageConnectionContext lcc,
      DataDescriptorGenerator ddg,
      DataDictionary dd,
      TransactionController tc,
      UUID triggerId,
      SchemaDescriptor sd,
      UUID spsId,
      UUID compSchemaId,
      String text,
      boolean isWhen,
      TableDescriptor triggerTable)
      throws StandardException {
    if (text == null) {
      return null;
    }

    /*
     ** Note: the format of this string is very important.
     ** Dont change it arbitrarily -- see sps code.
     */
    String spsName =
        "TRIGGER"
            + (isWhen ? "WHEN_" : "ACTN_")
            + triggerId
            + "_"
            + triggerTable.getUUID().toString();

    SPSDescriptor spsd =
        new SPSDescriptor(
            dd,
            spsName,
            (spsId == null) ? dd.getUUIDFactory().createUUID() : spsId,
            sd.getUUID(),
            compSchemaId == null ? lcc.getDefaultSchema().getUUID() : compSchemaId,
            SPSDescriptor.SPS_TYPE_TRIGGER,
            true, // it is valid
            text, // the text
            true); // no defaults

    /*
     ** Prepared the stored prepared statement
     ** and release the activation class -- we
     ** know we aren't going to execute statement
     ** after create it, so for now we are finished.
     */
    spsd.prepareAndRelease(lcc, triggerTable);

    dd.addSPSDescriptor(spsd, tc);

    return spsd;
  }
 /**
  * Make the ConstantAction for a CREATE TRIGGER statement.
  *
  * @param triggerSchemaName name for the schema that trigger lives in.
  * @param triggerName Name of trigger
  * @param eventMask TriggerDescriptor.TRIGGER_EVENT_XXXX
  * @param isBefore is this a before (as opposed to after) trigger
  * @param isRow is this a row trigger or statement trigger
  * @param isEnabled is this trigger enabled or disabled
  * @param triggerTable the table upon which this trigger is defined
  * @param whenSPSId the sps id for the when clause (may be null)
  * @param whenText the text of the when clause (may be null)
  * @param actionSPSId the spsid for the trigger action (may be null)
  * @param actionText the text of the trigger action
  * @param spsCompSchemaId the compilation schema for the action and when spses. If null, will be
  *     set to the current default schema
  * @param creationTimestamp when was this trigger created? if null, will be set to the time that
  *     executeConstantAction() is invoked
  * @param referencedCols what columns does this trigger reference (may be null)
  * @param originalActionText The original user text of the trigger action
  * @param referencingOld whether or not OLD appears in REFERENCING clause
  * @param referencingNew whether or not NEW appears in REFERENCING clause
  * @param oldReferencingName old referencing table name, if any, that appears in REFERENCING
  *     clause
  * @param newReferencingName new referencing table name, if any, that appears in REFERENCING
  *     clause
  */
 CreateTriggerConstantAction(
     String triggerSchemaName,
     String triggerName,
     int eventMask,
     boolean isBefore,
     boolean isRow,
     boolean isEnabled,
     TableDescriptor triggerTable,
     UUID whenSPSId,
     String whenText,
     UUID actionSPSId,
     String actionText,
     UUID spsCompSchemaId,
     Timestamp creationTimestamp,
     int[] referencedCols,
     String originalActionText,
     boolean referencingOld,
     boolean referencingNew,
     String oldReferencingName,
     String newReferencingName) {
   super(triggerTable.getUUID());
   this.triggerName = triggerName;
   this.triggerSchemaName = triggerSchemaName;
   this.triggerTable = triggerTable;
   this.eventMask = eventMask;
   this.isBefore = isBefore;
   this.isRow = isRow;
   this.isEnabled = isEnabled;
   this.whenSPSId = whenSPSId;
   this.whenText = whenText;
   this.actionSPSId = actionSPSId;
   this.actionText = actionText;
   this.spsCompSchemaId = spsCompSchemaId;
   this.creationTimestamp = creationTimestamp;
   this.referencedCols = referencedCols;
   this.originalActionText = originalActionText;
   this.referencingOld = referencingOld;
   this.referencingNew = referencingNew;
   this.oldReferencingName = oldReferencingName;
   this.newReferencingName = newReferencingName;
   if (SanityManager.DEBUG) {
     SanityManager.ASSERT(triggerSchemaName != null, "triggerSchemaName sd is null");
     SanityManager.ASSERT(triggerName != null, "trigger name is null");
     SanityManager.ASSERT(triggerTable != null, "triggerTable is null");
     SanityManager.ASSERT(actionText != null, "actionText is null");
   }
 }
  /**
   * Make a ConstraintDescriptor out of a SYSCONSTRAINTS row
   *
   * @param row a SYSCONSTRAINTS row
   * @param parentTupleDescriptor Subconstraint descriptor with auxiliary info.
   * @param dd dataDictionary
   * @exception 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");
        }
        keyColumns = conglomDesc.getIndexDescriptor().baseColumnPositions();
        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;
  }
 /**
  * Return true if the node references SESSION schema tables (temporary or permanent)
  *
  * @return true if references SESSION schema tables, else false
  * @exception StandardException Thrown on error
  */
 public boolean referencesSessionSchema() throws StandardException {
   // If lock table is on a SESSION schema table, then return true.
   return isSessionSchema(lockTableDescriptor.getSchemaName());
 }
  /**
   * This is the guts of the Execution-time logic for CREATE TRIGGER.
   *
   * @see ConstantAction#executeConstantAction
   * @exception StandardException Thrown on failure
   */
  public void executeConstantAction(Activation activation) throws StandardException {
    SPSDescriptor whenspsd = null;
    SPSDescriptor actionspsd;

    LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
    DataDictionary dd = lcc.getDataDictionary();
    DependencyManager dm = dd.getDependencyManager();
    TransactionController tc = lcc.getTransactionExecute();

    /*
     ** Indicate that we are about to modify the data dictionary.
     **
     ** We tell the data dictionary we're done writing at the end of
     ** the transaction.
     */
    dd.startWriting(lcc);

    SchemaDescriptor triggerSd = getSchemaDescriptorForCreate(dd, activation, triggerSchemaName);

    if (spsCompSchemaId == null) {
      SchemaDescriptor def = lcc.getDefaultSchema();
      if (def.getUUID() == null) {
        // Descriptor for default schema is stale,
        // look it up in the dictionary
        def = dd.getSchemaDescriptor(def.getDescriptorName(), tc, false);
      }

      /*
       ** It is possible for spsCompSchemaId to be null.  For instance,
       ** the current schema may not have been physically created yet but
       ** it exists "virtually".  In this case, its UUID will have the
       ** value of null meaning that it is not persistent.  e.g.:
       **
       ** CONNECT 'db;create=true' user 'ernie';
       ** CREATE TABLE bert.t1 (i INT);
       ** CREATE TRIGGER bert.tr1 AFTER INSERT ON bert.t1
       **    FOR EACH STATEMENT MODE DB2SQL
       **    SELECT * FROM SYS.SYSTABLES;
       **
       ** Note that in the above case, the trigger action statement have a
       ** null compilation schema.  A compilation schema with null value
       ** indicates that the trigger action statement text does not have
       ** any dependencies with the CURRENT SCHEMA.  This means:
       **
       ** o  It is safe to compile this statement in any schema since
       **    there is no dependency with the CURRENT SCHEMA. i.e.: All
       **    relevent identifiers are qualified with a specific schema.
       **
       ** o  The statement cache mechanism can utilize this piece of
       **    information to enable better statement plan sharing across
       **    connections in different schemas; thus, avoiding unnecessary
       **    statement compilation.
       */
      if (def != null) spsCompSchemaId = def.getUUID();
    }

    String tabName;
    if (triggerTable != null) {
      triggerTableId = triggerTable.getUUID();
      tabName = triggerTable.getName();
    } else tabName = "with UUID " + triggerTableId;

    /* We need to get table descriptor again.  We simply can't trust the
     * one we got at compile time, the lock on system table was released
     * when compile was done, and the table might well have been dropped.
     */
    triggerTable = dd.getTableDescriptor(triggerTableId);
    if (triggerTable == null) {
      throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION, tabName);
    }
    /* Lock the table for DDL.  Otherwise during our execution, the table
     * might be changed, even dropped.  Beetle 4269
     */
    lockTableForDDL(tc, triggerTable.getHeapConglomerateId(), true);
    /* get triggerTable again for correctness, in case it's changed before
     * the lock is aquired
     */
    triggerTable = dd.getTableDescriptor(triggerTableId);
    if (triggerTable == null) {
      throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION, tabName);
    }

    /*
     ** Send an invalidate on the table from which
     ** the triggering event emanates.  This it
     ** to make sure that DML statements on this table
     ** will be recompiled.  Do this before we create
     ** our trigger spses lest we invalidate them just
     ** after creating them.
     */
    dm.invalidateFor(triggerTable, DependencyManager.CREATE_TRIGGER, lcc);

    /*
     ** Lets get our trigger id up front, we'll use it when
     ** we create our spses.
     */
    UUID tmpTriggerId = dd.getUUIDFactory().createUUID();

    actionSPSId = (actionSPSId == null) ? dd.getUUIDFactory().createUUID() : actionSPSId;

    DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();

    /*
     ** Create the trigger descriptor first so the trigger action
     ** compilation can pick up the relevant trigger especially in
     ** the case of self triggering.
     */
    TriggerDescriptor triggerd =
        ddg.newTriggerDescriptor(
            triggerSd,
            tmpTriggerId,
            triggerName,
            eventMask,
            isBefore,
            isRow,
            isEnabled,
            triggerTable,
            whenspsd == null ? null : whenspsd.getUUID(),
            actionSPSId,
            creationTimestamp == null
                ? new Timestamp(System.currentTimeMillis())
                : creationTimestamp,
            referencedCols,
            originalActionText,
            referencingOld,
            referencingNew,
            oldReferencingName,
            newReferencingName);

    dd.addDescriptor(triggerd, triggerSd, DataDictionary.SYSTRIGGERS_CATALOG_NUM, false, tc);

    /*
     ** If we have a WHEN action we create it now.
     */
    if (whenText != null) {
      whenspsd =
          createSPS(
              lcc,
              ddg,
              dd,
              tc,
              tmpTriggerId,
              triggerSd,
              whenSPSId,
              spsCompSchemaId,
              whenText,
              true,
              triggerTable);
    }

    /*
     ** Create the trigger action
     */
    actionspsd =
        createSPS(
            lcc,
            ddg,
            dd,
            tc,
            tmpTriggerId,
            triggerSd,
            actionSPSId,
            spsCompSchemaId,
            actionText,
            false,
            triggerTable);

    /*
     ** Make underlying spses dependent on the trigger.
     */
    if (whenspsd != null) {
      dm.addDependency(triggerd, whenspsd, lcc.getContextManager());
    }
    dm.addDependency(triggerd, actionspsd, lcc.getContextManager());
    dm.addDependency(triggerd, triggerTable, lcc.getContextManager());
    dm.addDependency(actionspsd, triggerTable, lcc.getContextManager());
    // store trigger's dependency on various privileges in the dependeny system
    storeViewTriggerDependenciesOnPrivileges(activation, triggerd);
  }