2121package com .apple .foundationdb .relational .recordlayer .metadata ;
2222
2323import com .apple .foundationdb .annotation .API ;
24+
2425import com .apple .foundationdb .record .query .plan .cascades .typing .Type ;
2526import com .apple .foundationdb .relational .api .exceptions .ErrorCode ;
2627import com .apple .foundationdb .relational .api .metadata .DataType ;
2728import com .apple .foundationdb .relational .util .Assert ;
2829import com .apple .foundationdb .relational .util .SpotBugsSuppressWarnings ;
30+
2931import com .google .common .collect .BiMap ;
3032import com .google .common .collect .HashBiMap ;
3133
3436import java .util .Locale ;
3537import java .util .Optional ;
3638import java .util .UUID ;
37- import java .util .regex .Pattern ;
3839import java .util .stream .Collectors ;
3940
4041@ API (API .Status .EXPERIMENTAL )
4142public class DataTypeUtils {
4243
43- private static final String DOUBLE_UNDERSCORE_ESCAPE = "__0" ;
44- private static final String DOLLAR_ESCAPE = "__1" ;
45- private static final String DOT_ESCAPE = "__2" ;
46-
47- private static final List <String > INVALID_START_SEQUENCES = List .of ("." , "$" , DOUBLE_UNDERSCORE_ESCAPE , DOLLAR_ESCAPE , DOT_ESCAPE );
48-
49- private static final Pattern VALID_PROTOBUF_COMPLIANT_NAME_PATTERN = Pattern .compile ("^[A-Za-z_][A-Za-z0-9_]*$" );
50-
5144 @ Nonnull
5245 private static final BiMap <DataType , Type > primitivesMap ;
5346
@@ -62,20 +55,6 @@ public class DataTypeUtils {
6255 @ SpotBugsSuppressWarnings (value = "NP_NONNULL_RETURN_VIOLATION" , justification = "should never happen, there is failUnchecked directly before that." )
6356 @ Nonnull
6457 public static DataType toRelationalType (@ Nonnull final Type type ) {
65- return toRelationalType (type , false );
66- }
67-
68- /**
69- * Converts a Record Layer {@link Type} into a Relational {@link DataType}.
70- *
71- * Note: This method is expensive, use with care, i.e. try to cache its result as much as possible.
72- *
73- * @param type The Relational data type.
74- * @return The corresponding Record Layer type.
75- */
76- @ SpotBugsSuppressWarnings (value = "NP_NONNULL_RETURN_VIOLATION" , justification = "should never happen, there is failUnchecked directly before that." )
77- @ Nonnull
78- public static DataType toRelationalType (@ Nonnull final Type type , boolean toUserIdentifier ) {
7958 if (primitivesMap .containsValue (type )) {
8059 return primitivesMap .inverse ().get (type );
8160 }
@@ -96,63 +75,32 @@ public static DataType toRelationalType(@Nonnull final Type type, boolean toUser
9675 switch (typeCode ) {
9776 case RECORD :
9877 final var record = (Type .Record ) type ;
99- final var columns = record .getFields ().stream ().map (field -> {
100- final var fieldName = toUserIdentifier ? DataTypeUtils .toUserIdentifier (field .getFieldName ()) : field .getFieldName ();
101- return DataType .StructType .Field .from (fieldName , toRelationalType (field .getFieldType (), toUserIdentifier ), field .getFieldIndex ());
102- }).collect (Collectors .toList ());
103- final var name = record .getName () == null ? getUniqueName () : (toUserIdentifier ? DataTypeUtils .toUserIdentifier (record .getName ()) : record .getName ());
104- return DataType .StructType .from (name , columns , record .isNullable ());
78+ final var columns = record .getFields ().stream ().map (field -> DataType .StructType .Field .from (field .getFieldName (), toRelationalType (field .getFieldType ()), field .getFieldIndex ())).collect (Collectors .toList ());
79+ return DataType .StructType .from (record .getName () == null ? toProtoBufCompliantName (UUID .randomUUID ().toString ()) : record .getName (), columns , record .isNullable ());
10580 case ARRAY :
10681 final var asArray = (Type .Array ) type ;
10782 return DataType .ArrayType .from (toRelationalType (Assert .notNullUnchecked (asArray .getElementType ())), asArray .isNullable ());
10883 case ENUM :
10984 final var asEnum = (Type .Enum ) type ;
11085 final var enumValues = asEnum .getEnumValues ().stream ().map (v -> DataType .EnumType .EnumValue .of (v .getName (), v .getNumber ())).collect (Collectors .toList ());
111- return DataType .EnumType .from (asEnum .getName () == null ? getUniqueName ( ) : asEnum .getName (), enumValues , asEnum .isNullable ());
86+ return DataType .EnumType .from (asEnum .getName () == null ? toProtoBufCompliantName ( UUID . randomUUID (). toString () ) : asEnum .getName (), enumValues , asEnum .isNullable ());
11287 default :
11388 Assert .failUnchecked (String .format (Locale .ROOT , "unexpected type %s" , type ));
11489 return null ; // make compiler happy.
11590 }
11691 }
11792
11893 @ Nonnull
119- private static String getUniqueName ( ) {
120- final var uuid = UUID . randomUUID (). toString ( );
121- final var modified = uuid .replace ("-" , "_" );
122- final char c = uuid .charAt (0 );
94+ private static String toProtoBufCompliantName ( @ Nonnull final String input ) {
95+ Assert . thatUnchecked ( input . length () > 0 );
96+ final var modified = input .replace ("-" , "_" );
97+ final char c = input .charAt (0 );
12398 if (c == '_' || ('a' <= c && c <= 'z' ) || ('A' <= c && c <= 'Z' )) {
12499 return modified ;
125100 }
126101 return "id" + modified ;
127102 }
128103
129- @ Nonnull
130- public static String toProtoBufCompliantName (final String name ) {
131- Assert .thatUnchecked (INVALID_START_SEQUENCES .stream ().noneMatch (name ::startsWith ), ErrorCode .INVALID_NAME , "name cannot start with %s" , INVALID_START_SEQUENCES );
132- String translated ;
133- if (name .startsWith ("__" )) {
134- translated = "__" + translateSpecialCharacters (name .substring (2 ));
135- } else {
136- Assert .thatUnchecked (!name .isEmpty (), ErrorCode .INVALID_NAME , "name cannot be empty String." );
137- translated = translateSpecialCharacters (name );
138- }
139- checkValidProtoBufCompliantName (translated );
140- return translated ;
141- }
142-
143- @ Nonnull
144- private static String translateSpecialCharacters (final String userIdentifier ) {
145- return userIdentifier .replace ("__" , DOUBLE_UNDERSCORE_ESCAPE ).replace ("$" , DOLLAR_ESCAPE ).replace ("." , DOT_ESCAPE );
146- }
147-
148- public static void checkValidProtoBufCompliantName (String name ) {
149- Assert .thatUnchecked (VALID_PROTOBUF_COMPLIANT_NAME_PATTERN .matcher (name ).matches (), ErrorCode .INVALID_NAME , name + " is not a valid name!" );
150- }
151-
152- public static String toUserIdentifier (String protoIdentifier ) {
153- return protoIdentifier .replace (DOT_ESCAPE , "." ).replace (DOLLAR_ESCAPE , "$" ).replace (DOUBLE_UNDERSCORE_ESCAPE , "__" );
154- }
155-
156104 /**
157105 * Converts a given Relational {@link DataType} into a corresponding Record Layer {@link Type}.
158106 *
0 commit comments