Skip to content

Commit

Permalink
[CALCITE-6738] Suport a UUID type natively
Browse files Browse the repository at this point in the history
Signed-off-by: Mihai Budiu <mbudiu@feldera.com>
  • Loading branch information
mihaibudiu committed Dec 26, 2024
1 parent 7352a0c commit 10c8f81
Show file tree
Hide file tree
Showing 28 changed files with 331 additions and 30 deletions.
8 changes: 8 additions & 0 deletions core/src/main/codegen/templates/Parser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -4862,6 +4862,11 @@ SqlLiteral DateTimeLiteral() :
<TIME> { s = span(); } p = SimpleStringLiteral() {
return SqlLiteral.createUnknown("TIME", p, s.end(this));
}
|
LOOKAHEAD(2)
<UUID> { s = span(); } p = SimpleStringLiteral() {
return SqlLiteral.createUnknown("UUID", p, s.end(this));
}
|
LOOKAHEAD(2)
<TIMESTAMP> { s = span(); } p = SimpleStringLiteral() {
Expand Down Expand Up @@ -5898,6 +5903,8 @@ SqlTypeNameSpec SqlTypeName1(Span s) :
<FLOAT> { s.add(this); sqlTypeName = SqlTypeName.FLOAT; }
|
<VARIANT> { s.add(this); sqlTypeName = SqlTypeName.VARIANT; }
|
<UUID> { s.add(this); sqlTypeName = SqlTypeName.UUID; }
)
{
return new SqlBasicTypeNameSpec(sqlTypeName, s.end(this));
Expand Down Expand Up @@ -8696,6 +8703,7 @@ SqlPostfixOperator PostfixRowOperator() :
| < UTF8: "UTF8" >
| < UTF16: "UTF16" >
| < UTF32: "UTF32" >
| < UUID: "UUID" >
| < VALUE: "VALUE" >
| < VALUES: "VALUES" > { afterTableName(); }
| < VALUE_OF: "VALUE_OF" >
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,8 @@ private Expression getConvertExpression(
case VARCHAR:
return Expressions.call(BuiltInMethod.STRING_TO_BINARY.method, operand,
new ConstantExpression(Charset.class, sourceType.getCharset()));

case UUID:
return Expressions.call(BuiltInMethod.UUID_TO_BINARY.method, operand);
default:
return defaultExpression.get();
}
Expand Down Expand Up @@ -416,12 +417,26 @@ private Expression getConvertExpression(
default:
return defaultExpression.get();
}

case UUID:
switch (sourceType.getSqlTypeName()) {
case UUID:
return operand;
case CHAR:
case VARCHAR:
return Expressions.call(BuiltInMethod.UUID_FROM_STRING.method, operand);
case BINARY:
case VARBINARY:
return Expressions.call(BuiltInMethod.BINARY_TO_UUID.method, operand);
default:
return defaultExpression.get();
}
case CHAR:
case VARCHAR:
final SqlIntervalQualifier interval =
sourceType.getIntervalQualifier();
switch (sourceType.getSqlTypeName()) {
case UUID:
return Expressions.call(BuiltInMethod.UUID_TO_STRING.method, operand);
// If format string is supplied, return formatted date/time/timestamp
case DATE:
return RexImpTable.optimize2(operand, Expressions.isConstantNull(format)
Expand Down Expand Up @@ -1019,6 +1034,9 @@ public static Expression translateLiteral(
case VARCHAR:
value2 = literal.getValueAs(String.class);
break;
case UUID:
return Expressions.call(null, BuiltInMethod.UUID_FROM_STRING.method,
Expressions.constant(literal.getValueAs(String.class)));
case BINARY:
case VARBINARY:
return Expressions.new_(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
*
* <p>The sections which contain windowed agg functions become instances of
* {@link org.apache.calcite.rel.logical.LogicalWindow}.
* If the {@link org.apache.calcite.rel.logical.LogicalCalc} does not contain
* If the {@link org.apache.calcite.rel.core.Project} does not contain
* any windowed agg functions, does nothing.
*
* <p>There is also a variant that matches
Expand Down
9 changes: 8 additions & 1 deletion core/src/main/java/org/apache/calcite/rex/RexBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.function.IntPredicate;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -859,7 +860,9 @@ boolean canRemoveCastFromLiteral(RelDataType toType,
return true;
}
final SqlTypeName sqlType = toType.getSqlTypeName();
if (sqlType == SqlTypeName.MEASURE || sqlType == SqlTypeName.VARIANT) {
if (sqlType == SqlTypeName.MEASURE
|| sqlType == SqlTypeName.VARIANT
|| sqlType == SqlTypeName.UUID) {
return false;
}
if (!RexLiteral.valueMatchesType(value, sqlType, false)) {
Expand Down Expand Up @@ -1374,6 +1377,10 @@ public RexLiteral makeLiteral(boolean b) {
return b ? booleanTrue : booleanFalse;
}

public RexLiteral makeUuidLiteral(@Nullable UUID uuid) {
return new RexLiteral(uuid, typeFactory.createSqlType(SqlTypeName.UUID), SqlTypeName.UUID);
}

/**
* Creates a numeric literal.
*/
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/java/org/apache/calcite/rex/RexLiteral.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
import java.util.UUID;

import static com.google.common.base.Preconditions.checkArgument;

Expand Down Expand Up @@ -316,6 +317,8 @@ public static boolean valueMatchesType(
return true;
}
switch (typeName) {
case UUID:
return value instanceof UUID;
case VARIANT:
return value instanceof VariantValue;
case BOOLEAN:
Expand Down Expand Up @@ -695,6 +698,10 @@ private static void appendAsJava(@Nullable Comparable value, StringBuilder sb,
Util.asStringBuilder(sb, sb2 ->
printSarg(sb2, (Sarg) value, type));
break;
case UUID:
assert value instanceof UUID;
sb.append(value);
break;
case SYMBOL:
assert value instanceof Enum;
sb.append("FLAG(");
Expand Down Expand Up @@ -1060,6 +1067,11 @@ public boolean isNull() {
return clazz.cast(value);
}
switch (typeName) {
case UUID:
if (clazz == String.class) {
return clazz.cast(((UUID) value).toString());
}
break;
case BINARY:
if (clazz == byte[].class) {
return clazz.cast(((ByteString) value).getBytes());
Expand Down
16 changes: 16 additions & 0 deletions core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.language.Soundex;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.Conversion;
import org.apache.commons.math3.util.CombinatoricsUtils;
import org.apache.commons.text.similarity.LevenshteinDistance;

Expand Down Expand Up @@ -125,6 +126,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
Expand Down Expand Up @@ -277,6 +279,20 @@ public static boolean throwUnless(boolean condition, String message) {
return condition;
}

public static String uuidToString(UUID uuid) {
return uuid.toString();
}

public static UUID binaryToUuid(ByteString bytes) {
return Conversion.byteArrayToUuid(bytes.getBytes(), 0);
}

public static ByteString uuidToBinary(UUID uuid) {
byte[] dest = new byte[16];
Conversion.uuidToByteArray(uuid, dest, 0, 16);
return new ByteString(dest);
}

/** SQL TO_BASE64(string) function. */
public static String toBase64(String string) {
return toBase64_(string.getBytes(UTF_8));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public enum RuntimeSqlTypeName {
MAP(true),
ROW(true),
GEOMETRY(false),
UUID(false),
// used only for VARIANT.null value
VARIANT(false);

Expand Down Expand Up @@ -243,6 +244,9 @@ public static Expression createExpression(RelDataType type) {
case VARIANT:
return Expressions.new_(BasicSqlTypeRtti.class,
Expressions.constant(RuntimeSqlTypeName.VARIANT));
case UUID:
return Expressions.new_(BasicSqlTypeRtti.class,
Expressions.constant(RuntimeSqlTypeName.UUID));
default:
throw new RuntimeException("Unexpected type " + type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

import static org.apache.calcite.runtime.rtti.RuntimeTypeInformation.RuntimeSqlTypeName.NAME;

Expand All @@ -47,6 +48,10 @@ public class VariantNonNull extends VariantSqlValue {
this.roundingMode = roundingMode;
// sanity check
switch (runtimeType.getTypeName()) {
case UUID:
assert value instanceof UUID;
this.value = value;
break;
case NAME:
assert value instanceof String;
this.value = value;
Expand Down
13 changes: 12 additions & 1 deletion core/src/main/java/org/apache/calcite/sql/SqlLiteral.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import java.nio.charset.UnsupportedCharsetException;
import java.util.Calendar;
import java.util.Objects;
import java.util.UUID;

import static com.google.common.base.Preconditions.checkArgument;

Expand Down Expand Up @@ -236,6 +237,8 @@ public static boolean valueMatchesType(
return value instanceof BitString;
case CHAR:
return value instanceof NlsString;
case UUID:
return value instanceof UUID;
case SYMBOL:
return (value instanceof Enum)
|| (value instanceof SqlSampleSpec);
Expand Down Expand Up @@ -263,7 +266,7 @@ public static boolean valueMatchesType(
* Returns the value of this literal.
*
* <p>Try not to use this method! There are so many different kinds of
* values, it's better to to let SqlLiteral do whatever it is you want to
* values, it's better to let SqlLiteral do whatever it is you want to
* do.
*
* @see #booleanValue()
Expand Down Expand Up @@ -781,6 +784,7 @@ public RelDataType createSqlType(RelDataTypeFactory typeFactory) {
switch (typeName) {
case NULL:
case BOOLEAN:
case UUID:
RelDataType ret = typeFactory.createSqlType(typeName);
ret = typeFactory.createTypeWithNullability(ret, null == value);
return ret;
Expand Down Expand Up @@ -930,6 +934,13 @@ public static SqlTimeTzLiteral createTime(
SqlParserPos pos) {
return new SqlTimeTzLiteral(t, precision, pos);
}

public static SqlUuidLiteral createUuid(
UUID u,
SqlParserPos pos) {
return new SqlUuidLiteral(u, pos);
}

/**
* Creates an interval literal.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public SqlLiteral resolve(SqlTypeName typeName) {
return SqlParserUtil.parseTimestampWithLocalTimeZoneLiteral(getValue(), pos);
case TIMESTAMP_TZ:
return SqlParserUtil.parseTimestampTzLiteral(getValue(), pos);
case UUID:
return SqlParserUtil.parseUuidLiteral(getValue(), pos);
default:
throw Util.unexpected(typeName);
}
Expand Down
63 changes: 63 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/SqlUuidLiteral.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.calcite.sql;

import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.SqlTypeName;

import java.util.UUID;

import static java.util.Objects.requireNonNull;

/**
* A SQL literal representing an UUID value, for example <code>UUID
* '123e4567-e89b-12d3-a456-426655440000'</code>.
*
* <p>Create values using {@link SqlLiteral#createUuid}.
*/
public class SqlUuidLiteral extends SqlLiteral {
//~ Constructors -----------------------------------------------------------

public SqlUuidLiteral(UUID t, SqlParserPos pos) {
super(t, SqlTypeName.UUID, pos);
}

//~ Methods ----------------------------------------------------------------

@Override public SqlUuidLiteral clone(SqlParserPos pos) {
return new SqlUuidLiteral(getUuid(), pos);
}

public UUID getUuid() {
return (UUID) requireNonNull(value, "value");
}

@Override public String toString() {
return "UUID '" + toFormattedString() + "'";
}

public String toFormattedString() {
return getUuid().toString();
}

@Override public void unparse(
SqlWriter writer,
int leftPrec,
int rightPrec) {
writer.literal(this.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.apache.calcite.sql.SqlTimestampLiteral;
import org.apache.calcite.sql.SqlTimestampTzLiteral;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.SqlUuidLiteral;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.impl.SqlParserImpl;
import org.apache.calcite.sql.type.SqlTypeName;
Expand Down Expand Up @@ -71,6 +72,7 @@
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -394,6 +396,11 @@ public static SqlTimestampLiteral parseTimestampWithLocalTimeZoneLiteral(
pos);
}

public static SqlUuidLiteral parseUuidLiteral(String s, SqlParserPos pos) {
UUID uuid = UUID.fromString(s);
return SqlLiteral.createUuid(uuid, pos);
}

public static SqlTimestampTzLiteral parseTimestampTzLiteral(
String s, SqlParserPos pos) {
// We expect the string to end in a timezone.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ private SqlTypeAssignmentRule(
// SYMBOL is assignable from ...
rules.add(SqlTypeName.SYMBOL, EnumSet.of(SqlTypeName.SYMBOL));

// UUID is assignable from
rules.add(SqlTypeName.UUID, EnumSet.of(SqlTypeName.UUID));

// ANY is assignable from ...
rule.clear();
rule.add(SqlTypeName.TINYINT);
Expand Down
Loading

0 comments on commit 10c8f81

Please sign in to comment.