forked from apache/calcite
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CALCITE-3175] AssertionError while serializing to JSON a RexLiteral …
…with Enum type (Wang Yanlin) An example of this is the "LEADING" flag in a call to the "TRIM" function. "LEADING" is an instance of enum SqlTrimFunction.Flag, and becomes a RexLiteral of type SYMBOL. The solution is to serialize enum values as strings, and build a registry of enum classes that may be serialized to JSON and the names of their constants. Since the enums have distinct names (for instance, no other enum class has a constant called "LEADING"), when we deserialize we can use a map to convert them to the correct type. Close apache#1301
- Loading branch information
1 parent
f82353c
commit 61b7280
Showing
5 changed files
with
174 additions
and
2 deletions.
There are no files selected for viewing
106 changes: 106 additions & 0 deletions
106
core/src/main/java/org/apache/calcite/rel/externalize/RelEnumTypes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* 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.rel.externalize; | ||
|
||
import org.apache.calcite.avatica.util.TimeUnitRange; | ||
import org.apache.calcite.sql.JoinConditionType; | ||
import org.apache.calcite.sql.JoinType; | ||
import org.apache.calcite.sql.SqlExplain; | ||
import org.apache.calcite.sql.SqlExplainFormat; | ||
import org.apache.calcite.sql.SqlExplainLevel; | ||
import org.apache.calcite.sql.SqlInsertKeyword; | ||
import org.apache.calcite.sql.SqlJsonConstructorNullClause; | ||
import org.apache.calcite.sql.SqlJsonQueryWrapperBehavior; | ||
import org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior; | ||
import org.apache.calcite.sql.SqlMatchRecognize; | ||
import org.apache.calcite.sql.SqlSelectKeyword; | ||
import org.apache.calcite.sql.fun.SqlTrimFunction; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
|
||
/** Registry of {@link Enum} classes that can be serialized to JSON. | ||
* | ||
* <p>Suppose you want to serialize the value | ||
* {@link SqlTrimFunction.Flag#LEADING} to JSON. | ||
* First, make sure that {@link SqlTrimFunction.Flag} is registered. | ||
* The type will be serialized as "SYMBOL". | ||
* The value will be serialized as the string "LEADING". | ||
* | ||
* <p>When we deserialize, we rely on the fact that the registered | ||
* {@code enum} classes have distinct values. Therefore, knowing that | ||
* {@code (type="SYMBOL", value="LEADING")} we can convert the string "LEADING" | ||
* to the enum {@code Flag.LEADING}. */ | ||
@SuppressWarnings({"rawtypes", "unchecked"}) | ||
public abstract class RelEnumTypes { | ||
private RelEnumTypes() {} | ||
|
||
private static final ImmutableMap<String, Enum<?>> ENUM_BY_NAME; | ||
|
||
static { | ||
// Build a mapping from enum constants (e.g. LEADING) to the enum | ||
// that contains them (e.g. SqlTrimFunction.Flag). If there two | ||
// enum constants have the same name, the builder will throw. | ||
final ImmutableMap.Builder<String, Enum<?>> enumByName = | ||
ImmutableMap.builder(); | ||
register(enumByName, JoinConditionType.class); | ||
register(enumByName, JoinType.class); | ||
register(enumByName, SqlExplain.Depth.class); | ||
register(enumByName, SqlExplainFormat.class); | ||
register(enumByName, SqlExplainLevel.class); | ||
register(enumByName, SqlInsertKeyword.class); | ||
register(enumByName, SqlJsonConstructorNullClause.class); | ||
register(enumByName, SqlJsonQueryWrapperBehavior.class); | ||
register(enumByName, SqlJsonValueEmptyOrErrorBehavior.class); | ||
register(enumByName, SqlMatchRecognize.AfterOption.class); | ||
register(enumByName, SqlSelectKeyword.class); | ||
register(enumByName, SqlTrimFunction.Flag.class); | ||
register(enumByName, TimeUnitRange.class); | ||
ENUM_BY_NAME = enumByName.build(); | ||
} | ||
|
||
private static void register(ImmutableMap.Builder<String, Enum<?>> builder, | ||
Class<? extends Enum> aClass) { | ||
for (Enum enumConstant : aClass.getEnumConstants()) { | ||
builder.put(enumConstant.name(), enumConstant); | ||
} | ||
} | ||
|
||
/** Converts a literal into a value that can be serialized to JSON. | ||
* In particular, if is an enum, converts it to its name. */ | ||
public static Object fromEnum(Object value) { | ||
return value instanceof Enum ? fromEnum((Enum) value) : value; | ||
} | ||
|
||
/** Converts an enum into its name. | ||
* Throws if the enum's class is not registered. */ | ||
public static String fromEnum(Enum enumValue) { | ||
if (ENUM_BY_NAME.get(enumValue.name()) != enumValue) { | ||
throw new AssertionError("cannot serialize enum value to JSON: " | ||
+ enumValue.getDeclaringClass().getCanonicalName() + "." | ||
+ enumValue); | ||
} | ||
return enumValue.name(); | ||
} | ||
|
||
/** Converts a string to an enum value. | ||
* The converse of {@link #fromEnum(Enum)}. */ | ||
static <E extends Enum<E>> E toEnum(String name) { | ||
return (E) ENUM_BY_NAME.get(name); | ||
} | ||
} | ||
|
||
// End RelEnumTypes.java |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters