예제 #1
0
  /**
   * 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;
  }
  /**
   * Create the Constant information that will drive the guts of Execution.
   *
   * @exception StandardException Thrown on failure
   */
  public ConstantAction makeConstantAction() throws StandardException {
    TableElementList coldefs = tableElementList;

    // for each column, stuff system.column
    ColumnInfo[] colInfos = new ColumnInfo[coldefs.countNumberOfColumns()];

    int numConstraints = coldefs.genColumnInfos(colInfos);

    /* If we've seen a constraint, then build a constraint list */
    CreateConstraintConstantAction[] conActions = null;

    SchemaDescriptor sd =
        getSchemaDescriptor(tableType != TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE, true);

    if (numConstraints > 0) {
      conActions = new CreateConstraintConstantAction[numConstraints];

      coldefs.genConstraintActions(true, conActions, getRelativeName(), sd, getDataDictionary());
    }

    // if the any of columns are "long" and user has not specified a
    // page size, set the pagesize to 32k.
    // Also in case where the approximate sum of the column sizes is
    // greater than the bump threshold , bump the pagesize to 32k

    boolean table_has_long_column = false;
    int approxLength = 0;

    for (int i = 0; i < colInfos.length; i++) {
      DataTypeDescriptor dts = colInfos[i].dataType;
      if (dts.getTypeId().isLongConcatableTypeId()) {
        table_has_long_column = true;
        break;
      }

      approxLength += dts.getTypeId().getApproximateLengthInBytes(dts);
    }

    if (table_has_long_column || (approxLength > Property.TBL_PAGE_SIZE_BUMP_THRESHOLD)) {
      if (((properties == null) || (properties.get(Property.PAGE_SIZE_PARAMETER) == null))
          && (PropertyUtil.getServiceProperty(
                  getLanguageConnectionContext().getTransactionCompile(),
                  Property.PAGE_SIZE_PARAMETER)
              == null)) {
        // do not override the user's choice of page size, whether it
        // is set for the whole database or just set on this statement.

        if (properties == null) properties = new Properties();

        properties.put(Property.PAGE_SIZE_PARAMETER, Property.PAGE_SIZE_DEFAULT_LONG);
      }
    }

    return (getGenericConstantActionFactory()
        .getCreateTableConstantAction(
            sd.getSchemaName(),
            getRelativeName(),
            tableType,
            colInfos,
            conActions,
            properties,
            lockGranularity,
            onCommitDeleteRows,
            onRollbackDeleteRows));
  }
  /**
   * Bind this CreateTableNode. This means doing any static error checking that can be done before
   * actually creating the base table or declaring the global temporary table. For eg, verifying
   * that the TableElementList does not contain any duplicate column names.
   *
   * @exception StandardException Thrown on error
   */
  public void bindStatement() throws StandardException {
    DataDictionary dataDictionary = getDataDictionary();
    int numPrimaryKeys = 0;
    int numCheckConstraints = 0;
    int numReferenceConstraints = 0;
    int numUniqueConstraints = 0;
    int numGenerationClauses = 0;

    SchemaDescriptor sd =
        getSchemaDescriptor(tableType != TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE, true);

    if (queryExpression != null) {
      FromList fromList =
          (FromList)
              getNodeFactory()
                  .getNode(
                      C_NodeTypes.FROM_LIST,
                      getNodeFactory().doJoinOrderOptimization(),
                      getContextManager());

      CompilerContext cc = getCompilerContext();
      ProviderList prevAPL = cc.getCurrentAuxiliaryProviderList();
      ProviderList apl = new ProviderList();

      try {
        cc.setCurrentAuxiliaryProviderList(apl);
        cc.pushCurrentPrivType(Authorizer.SELECT_PRIV);

        /* Bind the tables in the queryExpression */
        queryExpression = queryExpression.bindNonVTITables(dataDictionary, fromList);
        queryExpression = queryExpression.bindVTITables(fromList);

        /* Bind the expressions under the resultSet */
        queryExpression.bindExpressions(fromList);

        /* Bind the query expression */
        queryExpression.bindResultColumns(fromList);

        /* Reject any untyped nulls in the RCL */
        /* e.g. CREATE TABLE t1 (x) AS VALUES NULL WITH NO DATA */
        queryExpression.bindUntypedNullsToResultColumns(null);
      } finally {
        cc.popCurrentPrivType();
        cc.setCurrentAuxiliaryProviderList(prevAPL);
      }

      /* If there is an RCL for the table definition then copy the
       * names to the queryExpression's RCL after verifying that
       * they both have the same size.
       */
      ResultColumnList qeRCL = queryExpression.getResultColumns();

      if (resultColumns != null) {
        if (resultColumns.size() != qeRCL.visibleSize()) {
          throw StandardException.newException(
              SQLState.LANG_TABLE_DEFINITION_R_C_L_MISMATCH, getFullName());
        }
        qeRCL.copyResultColumnNames(resultColumns);
      }

      int schemaCollationType = sd.getCollationType();

      /* Create table element list from columns in query expression */
      tableElementList = new TableElementList();

      for (int index = 0; index < qeRCL.size(); index++) {
        ResultColumn rc = (ResultColumn) qeRCL.elementAt(index);
        if (rc.isGenerated()) {
          continue;
        }
        /* Raise error if column name is system generated. */
        if (rc.isNameGenerated()) {
          throw StandardException.newException(SQLState.LANG_TABLE_REQUIRES_COLUMN_NAMES);
        }

        DataTypeDescriptor dtd = rc.getExpression().getTypeServices();
        if ((dtd != null) && !dtd.isUserCreatableType()) {
          throw StandardException.newException(
              SQLState.LANG_INVALID_COLUMN_TYPE_CREATE_TABLE,
              dtd.getFullSQLTypeName(),
              rc.getName());
        }
        // DERBY-2879  CREATE TABLE AS <subquery> does not maintain the
        // collation for character types.
        // eg for a territory based collation database
        // create table t as select tablename from sys.systables with no data;
        // Derby at this point does not support for a table's character
        // columns to have a collation different from it's schema's
        // collation. Which means that in a territory based database,
        // the query above will cause table t's character columns to
        // have collation of UCS_BASIC but the containing schema of t
        // has collation of territory based. This is not supported and
        // hence we will throw an exception below for the query above in
        // a territory based database.
        if (dtd.getTypeId().isStringTypeId() && dtd.getCollationType() != schemaCollationType) {
          throw StandardException.newException(
              SQLState.LANG_CAN_NOT_CREATE_TABLE,
              dtd.getCollationName(),
              DataTypeDescriptor.getCollationName(schemaCollationType));
        }

        ColumnDefinitionNode column =
            (ColumnDefinitionNode)
                getNodeFactory()
                    .getNode(
                        C_NodeTypes.COLUMN_DEFINITION_NODE,
                        rc.getName(),
                        null,
                        rc.getType(),
                        null,
                        getContextManager());
        tableElementList.addTableElement(column);
      }
    } else {
      // Set the collation type and collation derivation of all the
      // character type columns. Their collation type will be same as the
      // collation of the schema they belong to. Their collation
      // derivation will be "implicit".
      // Earlier we did this in makeConstantAction but that is little too
      // late (DERBY-2955)
      // eg
      // CREATE TABLE STAFF9 (EMPNAME CHAR(20),
      //  CONSTRAINT STAFF9_EMPNAME CHECK (EMPNAME NOT LIKE 'T%'))
      // For the query above, when run in a territory based db, we need
      // to have the correct collation set in bind phase of create table
      // so that when LIKE is handled in LikeEscapeOperatorNode, we have
      // the correct collation set for EMPNAME otherwise it will throw an
      // exception for 'T%' having collation of territory based and
      // EMPNAME having the default collation of UCS_BASIC
      tableElementList.setCollationTypesOnCharacterStringColumns(
          getSchemaDescriptor(tableType != TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE, true));
    }

    tableElementList.validate(this, dataDictionary, (TableDescriptor) null);

    /* Only 1012 columns allowed per table */
    if (tableElementList.countNumberOfColumns() > Limits.DB2_MAX_COLUMNS_IN_TABLE) {
      throw StandardException.newException(
          SQLState.LANG_TOO_MANY_COLUMNS_IN_TABLE_OR_VIEW,
          String.valueOf(tableElementList.countNumberOfColumns()),
          getRelativeName(),
          String.valueOf(Limits.DB2_MAX_COLUMNS_IN_TABLE));
    }

    numPrimaryKeys = tableElementList.countConstraints(DataDictionary.PRIMARYKEY_CONSTRAINT);

    /* Only 1 primary key allowed per table */
    if (numPrimaryKeys > 1) {
      throw StandardException.newException(
          SQLState.LANG_TOO_MANY_PRIMARY_KEY_CONSTRAINTS, getRelativeName());
    }

    /* Check the validity of all check constraints */
    numCheckConstraints = tableElementList.countConstraints(DataDictionary.CHECK_CONSTRAINT);

    numReferenceConstraints =
        tableElementList.countConstraints(DataDictionary.FOREIGNKEY_CONSTRAINT);

    numUniqueConstraints = tableElementList.countConstraints(DataDictionary.UNIQUE_CONSTRAINT);

    numGenerationClauses = tableElementList.countGenerationClauses();

    // temp tables can't have primary key or check or foreign key or unique constraints defined on
    // them
    if ((tableType == TableDescriptor.GLOBAL_TEMPORARY_TABLE_TYPE)
        && (numPrimaryKeys > 0
            || numCheckConstraints > 0
            || numReferenceConstraints > 0
            || numUniqueConstraints > 0))
      throw StandardException.newException(
          SQLState.LANG_NOT_ALLOWED_FOR_DECLARED_GLOBAL_TEMP_TABLE);

    // each of these constraints have a backing index in the back. We need to make sure that a table
    // never has more
    // more than 32767 indexes on it and that is why this check.
    if ((numPrimaryKeys + numReferenceConstraints + numUniqueConstraints)
        > Limits.DB2_MAX_INDEXES_ON_TABLE) {
      throw StandardException.newException(
          SQLState.LANG_TOO_MANY_INDEXES_ON_TABLE,
          String.valueOf(numPrimaryKeys + numReferenceConstraints + numUniqueConstraints),
          getRelativeName(),
          String.valueOf(Limits.DB2_MAX_INDEXES_ON_TABLE));
    }

    if ((numCheckConstraints > 0) || (numGenerationClauses > 0) || (numReferenceConstraints > 0)) {
      /* In order to check the validity of the check constraints and
       * generation clauses
       * we must goober up a FromList containing a single table,
       * the table being created, with an RCL containing the
       * new columns and their types.  This will allow us to
       * bind the constraint definition trees against that
       * FromList.  When doing this, we verify that there are
       * no nodes which can return non-deterministic results.
       */
      FromList fromList = makeFromList(null, tableElementList, true);
      FormatableBitSet generatedColumns = new FormatableBitSet();

      /* Now that we've finally goobered stuff up, bind and validate
       * the check constraints and generation clauses.
       */
      if (numGenerationClauses > 0) {
        tableElementList.bindAndValidateGenerationClauses(sd, fromList, generatedColumns, null);
      }
      if (numCheckConstraints > 0) {
        tableElementList.bindAndValidateCheckConstraints(fromList);
      }
      if (numReferenceConstraints > 0) {
        tableElementList.validateForeignKeysOnGenerationClauses(fromList, generatedColumns);
      }
    }

    if (numPrimaryKeys > 0) {
      tableElementList.validatePrimaryKeyNullability();
    }
  }
  /**
   * 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);
  }