/** * @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); }