/**
   * 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();
    }
  }
Ejemplo n.º 2
0
  /**
   * Bind the expressions in this FromSubquery. This means binding the sub-expressions, as well as
   * figuring out what the return type is for each expression.
   *
   * @exception StandardException Thrown on error
   */
  public void bindExpressions(FromList fromListParam) throws StandardException {
    FromList emptyFromList =
        (FromList)
            getNodeFactory()
                .getNode(
                    C_NodeTypes.FROM_LIST,
                    getNodeFactory().doJoinOrderOptimization(),
                    getContextManager());
    ResultColumnList derivedRCL = resultColumns;
    ResultColumnList subqueryRCL;
    FromList nestedFromList;

    /* From subqueries cannot be correlated, so we pass an empty FromList
     * to subquery.bindExpressions() and .bindResultColumns()
     */
    if (orderByList != null) {
      orderByList.pullUpOrderByColumns(subquery);
    }

    nestedFromList = emptyFromList;

    CompilerContext compilerContext = getCompilerContext();

    if (origCompilationSchema != null) {
      // View expansion needs the definition time schema
      compilerContext.pushCompilationSchema(origCompilationSchema);
    }

    try {
      subquery.bindExpressions(nestedFromList);
      subquery.bindResultColumns(nestedFromList);
    } finally {
      if (origCompilationSchema != null) {
        compilerContext.popCompilationSchema();
      }
    }

    if (orderByList != null) {
      orderByList.bindOrderByColumns(subquery);
    }

    bindOffsetFetch(offset, fetchFirst);

    /* NOTE: If the size of the derived column list is less than
     * the size of the subquery's RCL and the derived column list is marked
     * for allowing a size mismatch, then we have a select * view
     * on top of a table that has had columns added to it via alter table.
     * In this case, we trim out the columns that have been added to
     * the table since the view was created.
     */
    subqueryRCL = subquery.getResultColumns();
    if (resultColumns != null
        && resultColumns.getCountMismatchAllowed()
        && resultColumns.size() < subqueryRCL.size()) {
      for (int index = subqueryRCL.size() - 1; index >= resultColumns.size(); index--) {
        subqueryRCL.removeElementAt(index);
      }
    }

    /*
     * Create RCL based on subquery, adding a level of VCNs.
     */
    ResultColumnList newRcl = subqueryRCL.copyListAndObjects();
    newRcl.genVirtualColumnNodes(subquery, subquery.getResultColumns());
    resultColumns = newRcl;

    /* Propagate the name info from the derived column list */
    if (derivedRCL != null) {
      resultColumns.propagateDCLInfo(derivedRCL, correlationName);
    }
  }