/**
  * @see TypeCompiler#resolveArithmeticOperation
  * @exception StandardException Thrown on error
  */
 public DataTypeDescriptor resolveArithmeticOperation(
     DataTypeDescriptor leftType, DataTypeDescriptor rightType, String operator)
     throws StandardException {
   throw StandardException.newException(
       SQLState.LANG_BINARY_OPERATOR_NOT_SUPPORTED,
       operator,
       leftType.getTypeId().getSQLTypeName(),
       rightType.getTypeId().getSQLTypeName());
 }
  /**
   * Get the precision of the operation involving two of the same types. Only meaningful for
   * decimals, which override this.
   *
   * @param operator a string representing the operator, null means no operator, just a type merge
   * @param leftType the left type
   * @param rightType the left type
   * @return the resultant precision
   */
  private int getPrecision(
      String operator, DataTypeDescriptor leftType, DataTypeDescriptor rightType) {
    // Only meaningful for decimal
    if (getStoredFormatIdFromTypeId() != StoredFormatIds.DECIMAL_TYPE_ID) {
      return leftType.getPrecision();
    }

    long lscale = (long) leftType.getScale();
    long rscale = (long) rightType.getScale();
    long lprec = (long) leftType.getPrecision();
    long rprec = (long) rightType.getPrecision();
    long val;

    /*
     ** Null means datatype merge.  Take the maximum
     ** left of decimal digits plus the scale.
     */
    if (operator == null) {
      val = this.getScale(operator, leftType, rightType) + Math.max(lprec - lscale, rprec - rscale);
    } else if (operator.equals(TypeCompiler.TIMES_OP)) {
      val = lprec + rprec;
    } else if (operator.equals(TypeCompiler.SUM_OP)) {
      val = lprec - lscale + rprec - rscale + this.getScale(operator, leftType, rightType);
    } else if (operator.equals(TypeCompiler.DIVIDE_OP)) {
      val =
          Math.min(
              NumberDataValue.MAX_DECIMAL_PRECISION_SCALE,
              this.getScale(operator, leftType, rightType) + lprec - lscale + rprec);
    }
    /*
     ** AVG, -, +
     */
    else {
      /*
       ** Take max scale and max left of decimal
       ** plus one.
       */
      val =
          this.getScale(operator, leftType, rightType)
              + Math.max(lprec - lscale, rprec - rscale)
              + 1;

      if (val > Limits.DB2_MAX_DECIMAL_PRECISION_SCALE)
        // then, like DB2, just set it to the max possible.
        val = Limits.DB2_MAX_DECIMAL_PRECISION_SCALE;
    }

    if (val > Integer.MAX_VALUE) {
      val = Integer.MAX_VALUE;
    }
    val = Math.min(NumberDataValue.MAX_DECIMAL_PRECISION_SCALE, val);
    return (int) val;
  }
  /** @see TypeCompiler#getCastToCharWidth */
  public int getCastToCharWidth(DataTypeDescriptor dts) {
    int formatId = getStoredFormatIdFromTypeId();
    switch (formatId) {
      case StoredFormatIds.DECIMAL_TYPE_ID:
        // Need to have space for '-' and decimal point.
        return dts.getPrecision() + 2;

      case StoredFormatIds.DOUBLE_TYPE_ID:
        return TypeCompiler.DOUBLE_MAXWIDTH_AS_CHAR;

      case StoredFormatIds.INT_TYPE_ID:
        return TypeCompiler.INT_MAXWIDTH_AS_CHAR;

      case StoredFormatIds.LONGINT_TYPE_ID:
        return TypeCompiler.LONGINT_MAXWIDTH_AS_CHAR;

      case StoredFormatIds.REAL_TYPE_ID:
        return TypeCompiler.REAL_MAXWIDTH_AS_CHAR;

      case StoredFormatIds.SMALLINT_TYPE_ID:
        return TypeCompiler.SMALLINT_MAXWIDTH_AS_CHAR;

      case StoredFormatIds.TINYINT_TYPE_ID:
        return TypeCompiler.TINYINT_MAXWIDTH_AS_CHAR;

      default:
        if (SanityManager.DEBUG) {
          SanityManager.THROWASSERT("unexpected formatId in getCastToCharWidth() - " + formatId);
        }
        return 0;
    }
  }
  /**
   * Get the scale of the operation involving two of the same types. Since we don't really have a
   * good way to pass the resultant scale and precision around at execution time, we will model that
   * BigDecimal does by default. This is good in most cases, though we would probably like to use
   * something more sophisticated for division.
   *
   * @param operator a string representing the operator, null means no operator, just a type merge
   * @param leftType the left type
   * @param rightType the left type
   * @return the resultant precision
   */
  private int getScale(String operator, DataTypeDescriptor leftType, DataTypeDescriptor rightType) {
    // Only meaningful for decimal
    if (getStoredFormatIdFromTypeId() != StoredFormatIds.DECIMAL_TYPE_ID) {
      return leftType.getScale();
    }

    long val;

    long lscale = (long) leftType.getScale();
    long rscale = (long) rightType.getScale();
    long lprec = (long) leftType.getPrecision();
    long rprec = (long) rightType.getPrecision();

    /*
     ** Retain greatest scale, take sum of left
     ** of decimal
     */
    if (TypeCompiler.TIMES_OP.equals(operator)) {
      val = lscale + rscale;
    } else if (TypeCompiler.DIVIDE_OP.equals(operator)) {
      /*
       ** Take max left scale + right precision - right scale + 1,
       ** or 4, whichever is biggest
       */
      LanguageConnectionContext lcc =
          (LanguageConnectionContext)
              (ContextService.getContext(LanguageConnectionContext.CONTEXT_ID));

      // Scale: 31 - left precision + left scale - right scale
      val = Math.max(NumberDataValue.MAX_DECIMAL_PRECISION_SCALE - lprec + lscale - rscale, 0);

    } else if (TypeCompiler.AVG_OP.equals(operator)) {
      val = Math.max(Math.max(lscale, rscale), NumberDataValue.MIN_DECIMAL_DIVIDE_SCALE);
    }
    /*
     ** SUM, -, + all take max(lscale,rscale)
     */
    else {
      val = Math.max(lscale, rscale);
    }

    if (val > Integer.MAX_VALUE) {
      val = Integer.MAX_VALUE;
    }
    val = Math.min(NumberDataValue.MAX_DECIMAL_PRECISION_SCALE, val);
    return (int) val;
  }
  /**
   * Bind this expression. This means binding the sub-expressions, as well as figuring out what the
   * return type is for this expression.
   *
   * @param fromList The FROM list for the query this expression is in, for binding columns.
   * @param subqueryList The subquery list being built as we find SubqueryNodes
   * @param aggregateVector The aggregate vector being built as we find AggregateNodes
   * @return The new top of the expression tree.
   * @exception StandardException Thrown on error
   */
  public ValueNode bindExpression(
      FromList fromList, SubqueryList subqueryList, List aggregateVector) throws StandardException {
    // method invocations are not allowed in ADD TABLE clauses.
    // And neither are field references.
    javaNode.checkReliability(this);

    /* Bind the expression under us */
    javaNode = javaNode.bindExpression(fromList, subqueryList, aggregateVector);

    if (javaNode instanceof StaticMethodCallNode) {
      AggregateNode agg = ((StaticMethodCallNode) javaNode).getResolvedAggregate();

      if (agg != null) {
        return agg.bindExpression(fromList, subqueryList, aggregateVector);
      }
    }

    DataTypeDescriptor dts = javaNode.getDataType();
    if (dts == null) {
      throw StandardException.newException(
          SQLState.LANG_NO_CORRESPONDING_S_Q_L_TYPE, javaNode.getJavaTypeName());
    }

    TypeDescriptor catalogType = dts.getCatalogType();

    if (catalogType.isRowMultiSet() || (catalogType.getTypeName().equals("java.sql.ResultSet"))) {
      throw StandardException.newException(SQLState.LANG_TABLE_FUNCTION_NOT_ALLOWED);
    }

    setType(dts);

    // For functions returning string types we should set the collation to match the
    // java method's schema DERBY-2972. This is propogated from
    // RoutineAliasInfo to javaNode.
    if (dts.getTypeId().isStringTypeId()) {
      this.setCollationInfo(
          javaNode.getCollationType(), StringDataValue.COLLATION_DERIVATION_IMPLICIT);
    }

    return this;
  }
  /**
   * 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();
    }
  }
  /**
   * @see TypeCompiler#resolveArithmeticOperation
   * @exception StandardException Thrown on error
   */
  public DataTypeDescriptor resolveArithmeticOperation(
      DataTypeDescriptor leftType, DataTypeDescriptor rightType, String operator)
      throws StandardException {
    NumericTypeCompiler higherTC;
    DataTypeDescriptor higherType;
    boolean nullable;
    int precision, scale, maximumWidth;

    /*
     ** Check the right type to be sure it's a number.  By convention,
     ** we call this method off the TypeId of the left operand, so if
     ** we get here, we know the left operand is a number.
     */
    if (SanityManager.DEBUG)
      SanityManager.ASSERT(
          leftType.getTypeId().isNumericTypeId(),
          "The left type is supposed to be a number because we're resolving an arithmetic operator");

    TypeId leftTypeId = leftType.getTypeId();
    TypeId rightTypeId = rightType.getTypeId();

    boolean supported = true;

    if (!(rightTypeId.isNumericTypeId())) {
      supported = false;
    }

    if (TypeCompiler.MOD_OP.equals(operator)) {
      switch (leftTypeId.getJDBCTypeId()) {
        case java.sql.Types.TINYINT:
        case java.sql.Types.SMALLINT:
        case java.sql.Types.INTEGER:
        case java.sql.Types.BIGINT:
          break;
        default:
          supported = false;
          break;
      }
      switch (rightTypeId.getJDBCTypeId()) {
        case java.sql.Types.TINYINT:
        case java.sql.Types.SMALLINT:
        case java.sql.Types.INTEGER:
        case java.sql.Types.BIGINT:
          break;
        default:
          supported = false;
          break;
      }
    }

    if (!supported) {
      throw StandardException.newException(
          SQLState.LANG_BINARY_OPERATOR_NOT_SUPPORTED,
          operator,
          leftType.getTypeId().getSQLTypeName(),
          rightType.getTypeId().getSQLTypeName());
    }

    /*
     ** Take left as the higher precedence if equal
     */
    if (rightTypeId.typePrecedence() > leftTypeId.typePrecedence()) {
      higherType = rightType;
      higherTC = (NumericTypeCompiler) getTypeCompiler(rightTypeId);
    } else {
      higherType = leftType;
      higherTC = (NumericTypeCompiler) getTypeCompiler(leftTypeId);
    }

    /* The calculation of precision and scale should be based upon
     * the type with higher precedence, which is going to be the result
     * type, this is also to be consistent with maximumWidth.  Beetle 3906.
     */
    precision = higherTC.getPrecision(operator, leftType, rightType);
    scale = higherTC.getScale(operator, leftType, rightType);

    if (higherType.getTypeId().isDecimalTypeId()) {
      maximumWidth = (scale > 0) ? precision + 3 : precision + 1;

      /*
       ** Be careful not to overflow
       */
      if (maximumWidth < precision) {
        maximumWidth = Integer.MAX_VALUE;
      }
    } else {
      maximumWidth = higherType.getMaximumWidth();
    }

    /* The result is nullable if either side is nullable */
    nullable = leftType.isNullable() || rightType.isNullable();

    /*
     ** The higher type does not have the right nullability.  Create a
     ** new DataTypeDescriptor that has the correct type and nullability.
     **
     ** It's OK to call the implementation of the DataTypeDescriptorFactory
     ** here, because we're in the same package.
     */
    return new DataTypeDescriptor(higherType.getTypeId(), precision, scale, nullable, maximumWidth);
  }