use of org.apache.calcite.sql.SqlCollation in project calcite by apache.
the class SqlTypeFactoryImpl method leastRestrictiveSqlType.
private RelDataType leastRestrictiveSqlType(List<RelDataType> types) {
RelDataType resultType = null;
int nullCount = 0;
int nullableCount = 0;
int javaCount = 0;
int anyCount = 0;
for (RelDataType type : types) {
final SqlTypeName typeName = type.getSqlTypeName();
if (typeName == null) {
return null;
}
if (typeName == SqlTypeName.ANY) {
anyCount++;
}
if (type.isNullable()) {
++nullableCount;
}
if (typeName == SqlTypeName.NULL) {
++nullCount;
}
if (isJavaType(type)) {
++javaCount;
}
}
// if any of the inputs are ANY, the output is ANY
if (anyCount > 0) {
return createTypeWithNullability(createSqlType(SqlTypeName.ANY), nullCount > 0 || nullableCount > 0);
}
for (int i = 0; i < types.size(); ++i) {
RelDataType type = types.get(i);
RelDataTypeFamily family = type.getFamily();
final SqlTypeName typeName = type.getSqlTypeName();
if (typeName == SqlTypeName.NULL) {
continue;
}
// Except if all types are either NULL or Java types.
if (isJavaType(type) && javaCount + nullCount < types.size()) {
final RelDataType originalType = type;
type = typeName.allowsPrecScale(true, true) ? createSqlType(typeName, type.getPrecision(), type.getScale()) : typeName.allowsPrecScale(true, false) ? createSqlType(typeName, type.getPrecision()) : createSqlType(typeName);
type = createTypeWithNullability(type, originalType.isNullable());
}
if (resultType == null) {
resultType = type;
if (resultType.getSqlTypeName() == SqlTypeName.ROW) {
return leastRestrictiveStructuredType(types);
}
}
RelDataTypeFamily resultFamily = resultType.getFamily();
SqlTypeName resultTypeName = resultType.getSqlTypeName();
if (resultFamily != family) {
return null;
}
if (SqlTypeUtil.inCharOrBinaryFamilies(type)) {
Charset charset1 = type.getCharset();
Charset charset2 = resultType.getCharset();
SqlCollation collation1 = type.getCollation();
SqlCollation collation2 = resultType.getCollation();
// TODO: refine collation combination rules
final int precision = SqlTypeUtil.maxPrecision(resultType.getPrecision(), type.getPrecision());
// width. Otherwise, result is fixed width.
if (SqlTypeUtil.isLob(resultType)) {
resultType = createSqlType(resultType.getSqlTypeName());
} else if (SqlTypeUtil.isLob(type)) {
resultType = createSqlType(type.getSqlTypeName());
} else if (SqlTypeUtil.isBoundedVariableWidth(resultType)) {
resultType = createSqlType(resultType.getSqlTypeName(), precision);
} else {
// this catch-all case covers type variable, and both fixed
SqlTypeName newTypeName = type.getSqlTypeName();
if (shouldRaggedFixedLengthValueUnionBeVariable()) {
if (resultType.getPrecision() != type.getPrecision()) {
if (newTypeName == SqlTypeName.CHAR) {
newTypeName = SqlTypeName.VARCHAR;
} else if (newTypeName == SqlTypeName.BINARY) {
newTypeName = SqlTypeName.VARBINARY;
}
}
}
resultType = createSqlType(newTypeName, precision);
}
Charset charset = null;
SqlCollation collation = null;
if ((charset1 != null) || (charset2 != null)) {
if (charset1 == null) {
charset = charset2;
collation = collation2;
} else if (charset2 == null) {
charset = charset1;
collation = collation1;
} else if (charset1.equals(charset2)) {
charset = charset1;
collation = collation1;
} else if (charset1.contains(charset2)) {
charset = charset1;
collation = collation1;
} else {
charset = charset2;
collation = collation2;
}
}
if (charset != null) {
resultType = createTypeWithCharsetAndCollation(resultType, charset, collation);
}
} else if (SqlTypeUtil.isExactNumeric(type)) {
if (SqlTypeUtil.isExactNumeric(resultType)) {
// interval + datetime = datetime
if (types.size() > (i + 1)) {
RelDataType type1 = types.get(i + 1);
if (SqlTypeUtil.isDatetime(type1)) {
resultType = type1;
return createTypeWithNullability(resultType, nullCount > 0 || nullableCount > 0);
}
}
if (!type.equals(resultType)) {
if (!typeName.allowsPrec() && !resultTypeName.allowsPrec()) {
// use the bigger primitive
if (type.getPrecision() > resultType.getPrecision()) {
resultType = type;
}
} else {
// Let the result type have precision (p), scale (s)
// and number of whole digits (d) as follows: d =
// max(p1 - s1, p2 - s2) s <= max(s1, s2) p = s + d
int p1 = resultType.getPrecision();
int p2 = type.getPrecision();
int s1 = resultType.getScale();
int s2 = type.getScale();
final int maxPrecision = typeSystem.getMaxNumericPrecision();
final int maxScale = typeSystem.getMaxNumericScale();
int dout = Math.max(p1 - s1, p2 - s2);
dout = Math.min(dout, maxPrecision);
int scale = Math.max(s1, s2);
scale = Math.min(scale, maxPrecision - dout);
scale = Math.min(scale, maxScale);
int precision = dout + scale;
assert precision <= maxPrecision;
assert precision > 0 || (resultType.getSqlTypeName() == SqlTypeName.DECIMAL && precision == 0 && scale == 0);
resultType = createSqlType(SqlTypeName.DECIMAL, precision, scale);
}
}
} else if (SqlTypeUtil.isApproximateNumeric(resultType)) {
// TODO: only promote when required
if (SqlTypeUtil.isDecimal(type)) {
// Only promote to double for decimal types
resultType = createDoublePrecisionType();
}
} else {
return null;
}
} else if (SqlTypeUtil.isApproximateNumeric(type)) {
if (SqlTypeUtil.isApproximateNumeric(resultType)) {
if (type.getPrecision() > resultType.getPrecision()) {
resultType = type;
}
} else if (SqlTypeUtil.isExactNumeric(resultType)) {
if (SqlTypeUtil.isDecimal(resultType)) {
resultType = createDoublePrecisionType();
} else {
resultType = type;
}
} else {
return null;
}
} else if (SqlTypeUtil.isInterval(type)) {
// interval + datetime = datetime
if (types.size() > (i + 1)) {
RelDataType type1 = types.get(i + 1);
if (SqlTypeUtil.isDatetime(type1)) {
resultType = type1;
return createTypeWithNullability(resultType, nullCount > 0 || nullableCount > 0);
}
}
if (!type.equals(resultType)) {
// TODO jvs 4-June-2005: This shouldn't be necessary;
// move logic into IntervalSqlType.combine
Object type1 = resultType;
resultType = ((IntervalSqlType) resultType).combine(this, (IntervalSqlType) type);
resultType = ((IntervalSqlType) resultType).combine(this, (IntervalSqlType) type1);
}
} else if (SqlTypeUtil.isDatetime(type)) {
// datetime +/- interval (or integer) = datetime
if (types.size() > (i + 1)) {
RelDataType type1 = types.get(i + 1);
if (SqlTypeUtil.isInterval(type1) || SqlTypeUtil.isIntType(type1)) {
resultType = type;
return createTypeWithNullability(resultType, nullCount > 0 || nullableCount > 0);
}
}
} else {
// leastRestrictiveByCast handle it
return null;
}
}
if (resultType != null && nullableCount > 0) {
resultType = createTypeWithNullability(resultType, true);
}
return resultType;
}
use of org.apache.calcite.sql.SqlCollation in project calcite by apache.
the class NlsString method concat.
/**
* Concatenates some {@link NlsString} objects. The result has the charset
* and collation of the first element. The other elements must have matching
* (or null) charset and collation. Concatenates all at once, not pairwise,
* to avoid string copies.
*
* @param args array of {@link NlsString} to be concatenated
*/
public static NlsString concat(List<NlsString> args) {
if (args.size() < 2) {
return args.get(0);
}
String charSetName = args.get(0).charsetName;
SqlCollation collation = args.get(0).collation;
int length = args.get(0).value.length();
// sum string lengths and validate
for (int i = 1; i < args.size(); i++) {
final NlsString arg = args.get(i);
length += arg.value.length();
if (!((arg.charsetName == null) || arg.charsetName.equals(charSetName))) {
throw new IllegalArgumentException("mismatched charsets");
}
if (!((arg.collation == null) || arg.collation.equals(collation))) {
throw new IllegalArgumentException("mismatched collations");
}
}
StringBuilder sb = new StringBuilder(length);
for (NlsString arg : args) {
sb.append(arg.value);
}
return new NlsString(sb.toString(), charSetName, collation);
}
use of org.apache.calcite.sql.SqlCollation in project calcite by apache.
the class SqlTesterImpl method checkCollation.
public void checkCollation(String expression, String expectedCollationName, SqlCollation.Coercibility expectedCoercibility) {
for (String sql : buildQueries(expression)) {
RelDataType actualType = getColumnType(sql);
SqlCollation collation = actualType.getCollation();
assertEquals(expectedCollationName, collation.getCollationName());
assertEquals(expectedCoercibility, collation.getCoercibility());
}
}
use of org.apache.calcite.sql.SqlCollation in project calcite by apache.
the class SqlTypeUtil method addCharsetAndCollation.
/**
* Adds collation and charset to a character type, returns other types
* unchanged.
*
* @param type Type
* @param typeFactory Type factory
* @return Type with added charset and collation, or unchanged type if it is
* not a char type.
*/
public static RelDataType addCharsetAndCollation(RelDataType type, RelDataTypeFactory typeFactory) {
if (!inCharFamily(type)) {
return type;
}
Charset charset = type.getCharset();
if (charset == null) {
charset = typeFactory.getDefaultCharset();
}
SqlCollation collation = type.getCollation();
if (collation == null) {
collation = SqlCollation.IMPLICIT;
}
// todo: should get the implicit collation from repository
// instead of null
type = typeFactory.createTypeWithCharsetAndCollation(type, charset, collation);
SqlValidatorUtil.checkCharsetAndCollateConsistentIfCharType(type);
return type;
}
use of org.apache.calcite.sql.SqlCollation in project calcite by apache.
the class RexLiteral method fromJdbcString.
/**
* Converts a Jdbc string into a RexLiteral. This method accepts a string,
* as returned by the Jdbc method ResultSet.getString(), and restores the
* string into an equivalent RexLiteral. It allows one to use Jdbc strings
* as a common format for data.
*
* <p>If a null literal is provided, then a null pointer will be returned.
*
* @param type data type of literal to be read
* @param typeName type family of literal
* @param literal the (non-SQL encoded) string representation, as returned
* by the Jdbc call to return a column as a string
* @return a typed RexLiteral, or null
*/
public static RexLiteral fromJdbcString(RelDataType type, SqlTypeName typeName, String literal) {
if (literal == null) {
return null;
}
switch(typeName) {
case CHAR:
Charset charset = type.getCharset();
SqlCollation collation = type.getCollation();
NlsString str = new NlsString(literal, charset.name(), collation);
return new RexLiteral(str, type, typeName);
case BOOLEAN:
boolean b = ConversionUtil.toBoolean(literal);
return new RexLiteral(b, type, typeName);
case DECIMAL:
case DOUBLE:
BigDecimal d = new BigDecimal(literal);
return new RexLiteral(d, type, typeName);
case BINARY:
byte[] bytes = ConversionUtil.toByteArrayFromString(literal, 16);
return new RexLiteral(new ByteString(bytes), type, typeName);
case NULL:
return new RexLiteral(null, type, typeName);
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
long millis = SqlParserUtil.intervalToMillis(literal, type.getIntervalQualifier());
return new RexLiteral(BigDecimal.valueOf(millis), type, typeName);
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
long months = SqlParserUtil.intervalToMonths(literal, type.getIntervalQualifier());
return new RexLiteral(BigDecimal.valueOf(months), type, typeName);
case DATE:
case TIME:
case TIMESTAMP:
String format = getCalendarFormat(typeName);
TimeZone tz = DateTimeUtils.UTC_ZONE;
final Comparable v;
switch(typeName) {
case DATE:
final Calendar cal = DateTimeUtils.parseDateFormat(literal, new SimpleDateFormat(format, Locale.ROOT), tz);
if (cal == null) {
throw new AssertionError("fromJdbcString: invalid date/time value '" + literal + "'");
}
v = DateString.fromCalendarFields(cal);
break;
default:
// Allow fractional seconds for times and timestamps
assert format != null;
final DateTimeUtils.PrecisionTime ts = DateTimeUtils.parsePrecisionDateTimeLiteral(literal, new SimpleDateFormat(format, Locale.ROOT), tz, -1);
if (ts == null) {
throw new AssertionError("fromJdbcString: invalid date/time value '" + literal + "'");
}
switch(typeName) {
case TIMESTAMP:
v = TimestampString.fromCalendarFields(ts.getCalendar()).withFraction(ts.getFraction());
break;
case TIME:
v = TimeString.fromCalendarFields(ts.getCalendar()).withFraction(ts.getFraction());
break;
default:
throw new AssertionError();
}
}
return new RexLiteral(v, type, typeName);
case SYMBOL:
// Symbols are for internal use
default:
throw new AssertionError("fromJdbcString: unsupported type");
}
}
Aggregations