Skip to content

Commit 57e8531

Browse files
committed
wip
1 parent e01634f commit 57e8531

File tree

10 files changed

+152
-64
lines changed

10 files changed

+152
-64
lines changed

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/metadata/expressions/TupleFieldsHelper.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import com.apple.foundationdb.annotation.API;
2424
import com.apple.foundationdb.record.RecordCoreArgumentException;
2525
import com.apple.foundationdb.record.TupleFieldsProto;
26+
import com.apple.foundationdb.record.query.plan.cascades.SemanticException;
27+
import com.apple.foundationdb.record.query.plan.cascades.typing.Type;
2628
import com.google.common.collect.ImmutableSet;
2729
import com.google.protobuf.ByteString;
2830
import com.google.protobuf.Descriptors;
@@ -89,6 +91,48 @@ public static Object fromProto(@Nonnull Message value, @Nonnull Descriptors.Desc
8991
}
9092
}
9193

94+
public static Descriptors.Descriptor getNullableWrapperDescriptorForTypeCode(Type.TypeCode typeCode) {
95+
switch (typeCode) {
96+
case INT:
97+
return TupleFieldsProto.NullableInt32.getDescriptor();
98+
case LONG:
99+
return TupleFieldsProto.NullableInt64.getDescriptor();
100+
case DOUBLE:
101+
return TupleFieldsProto.NullableDouble.getDescriptor();
102+
case FLOAT:
103+
return TupleFieldsProto.NullableFloat.getDescriptor();
104+
case BYTES:
105+
return TupleFieldsProto.NullableBytes.getDescriptor();
106+
case BOOLEAN:
107+
return TupleFieldsProto.NullableBool.getDescriptor();
108+
default:
109+
throw new SemanticException(SemanticException.ErrorCode.UNSUPPORTED, "nullable for type " + typeCode.name() + "is not supported (yet).", null);
110+
}
111+
}
112+
113+
public static Type getTypeForNullableWrapper(@Nonnull Descriptors.Descriptor descriptor) {
114+
if (descriptor == TupleFieldsProto.UUID.getDescriptor()) {
115+
// just for simplicity, lets just say that nullable UUID do exist.
116+
return Type.uuidType(false);
117+
} else if (descriptor == TupleFieldsProto.NullableDouble.getDescriptor()) {
118+
return Type.primitiveType(Type.TypeCode.DOUBLE);
119+
} else if (descriptor == TupleFieldsProto.NullableFloat.getDescriptor()) {
120+
return Type.primitiveType(Type.TypeCode.FLOAT);
121+
} else if (descriptor == TupleFieldsProto.NullableInt32.getDescriptor()) {
122+
return Type.primitiveType(Type.TypeCode.INT);
123+
} else if (descriptor == TupleFieldsProto.NullableInt64.getDescriptor()) {
124+
return Type.primitiveType(Type.TypeCode.LONG);
125+
} else if (descriptor == TupleFieldsProto.NullableBool.getDescriptor()) {
126+
return Type.primitiveType(Type.TypeCode.BOOLEAN);
127+
} else if (descriptor == TupleFieldsProto.NullableString.getDescriptor()) {
128+
return Type.primitiveType(Type.TypeCode.STRING);
129+
} else if (descriptor == TupleFieldsProto.NullableBytes.getDescriptor()) {
130+
return Type.primitiveType(Type.TypeCode.BYTES);
131+
} else {
132+
throw new RecordCoreArgumentException("value is not of a known message type");
133+
}
134+
}
135+
92136
/**
93137
* Convert a Protobuf {@code UUID} to a Java {@link UUID}.
94138
* @param proto the value of a Protobuf {@code UUID} field

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/typing/Type.java

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.apple.foundationdb.record.RecordCoreException;
2727
import com.apple.foundationdb.record.TupleFieldsProto;
2828
import com.apple.foundationdb.record.logging.LogMessageKeys;
29+
import com.apple.foundationdb.record.metadata.expressions.TupleFieldsHelper;
2930
import com.apple.foundationdb.record.planprotos.PType;
3031
import com.apple.foundationdb.record.planprotos.PType.PAnyRecordType;
3132
import com.apple.foundationdb.record.planprotos.PType.PAnyType;
@@ -77,6 +78,8 @@
7778
import java.util.stream.Collectors;
7879
import java.util.stream.IntStream;
7980

81+
import static com.apple.foundationdb.record.metadata.expressions.TupleFieldsHelper.getNullableWrapperDescriptorForTypeCode;
82+
8083
/**
8184
* Provides type information about the output of an expression such as {@link Value} in a QGM.
8285
* <br>
@@ -404,6 +407,20 @@ private static Type fromProtoType(@Nullable Descriptors.GenericDescriptor descri
404407
@Nonnull Descriptors.FieldDescriptor.Type protoType,
405408
@Nonnull FieldDescriptorProto.Label protoLabel,
406409
boolean isNullable) {
410+
// A MESSAGE field type can be descriptive of types other than the nested type. Hence, first check for those.
411+
if (protoType == Descriptors.FieldDescriptor.Type.MESSAGE) {
412+
Objects.requireNonNull(descriptor);
413+
final var messageDescriptor = (Descriptors.Descriptor)descriptor;
414+
if (TupleFieldsHelper.isTupleField((Descriptors.Descriptor) descriptor)) {
415+
return TupleFieldsHelper.getTypeForNullableWrapper((Descriptors.Descriptor) descriptor);
416+
}
417+
if (NullableArrayTypeUtils.describesWrappedArray(messageDescriptor)) {
418+
// find TypeCode of array elements
419+
final var elementField = messageDescriptor.findFieldByName(NullableArrayTypeUtils.getRepeatedFieldName());
420+
final var elementTypeCode = TypeCode.fromProtobufType(elementField.getType());
421+
return fromProtoTypeToArray(descriptor, protoType, elementTypeCode, true);
422+
}
423+
}
407424
final var typeCode = TypeCode.fromProtobufType(protoType);
408425
if (protoLabel == FieldDescriptorProto.Label.LABEL_REPEATED) {
409426
// collection type
@@ -414,18 +431,7 @@ private static Type fromProtoType(@Nullable Descriptors.GenericDescriptor descri
414431
final var enumDescriptor = (Descriptors.EnumDescriptor)Objects.requireNonNull(descriptor);
415432
return Enum.fromProtoValues(isNullable, enumDescriptor.getValues());
416433
} else if (typeCode == TypeCode.RECORD) {
417-
Objects.requireNonNull(descriptor);
418-
final var messageDescriptor = (Descriptors.Descriptor)descriptor;
419-
if (NullableArrayTypeUtils.describesWrappedArray(messageDescriptor)) {
420-
// find TypeCode of array elements
421-
final var elementField = messageDescriptor.findFieldByName(NullableArrayTypeUtils.getRepeatedFieldName());
422-
final var elementTypeCode = TypeCode.fromProtobufType(elementField.getType());
423-
return fromProtoTypeToArray(descriptor, protoType, elementTypeCode, true);
424-
} else if (TupleFieldsProto.UUID.getDescriptor().equals(messageDescriptor)) {
425-
return Type.uuidType(isNullable);
426-
} else {
427-
return Record.fromFieldDescriptorsMap(isNullable, Record.toFieldDescriptorMap(messageDescriptor.getFields()));
428-
}
434+
return Record.fromFieldDescriptorsMap(isNullable, Record.toFieldDescriptorMap(((Descriptors.Descriptor) descriptor).getFields()));
429435
}
430436

431437
throw new IllegalStateException("unable to translate protobuf descriptor to type");
@@ -970,12 +976,22 @@ public void addProtoField(@Nonnull final TypeRepository.Builder typeRepositoryBu
970976
@Nonnull final Optional<String> ignored,
971977
@Nonnull final FieldDescriptorProto.Label label) {
972978
final var protoType = Objects.requireNonNull(getTypeCode().getProtoType());
973-
descriptorBuilder.addField(FieldDescriptorProto.newBuilder()
974-
.setNumber(fieldNumber)
975-
.setName(fieldName)
976-
.setType(protoType)
977-
.setLabel(label)
978-
.build());
979+
if (isNullable) {
980+
final var nullableWrapperDescriptor = getNullableWrapperDescriptorForTypeCode(typeCode);
981+
descriptorBuilder.addField(FieldDescriptorProto.newBuilder()
982+
.setNumber(fieldNumber)
983+
.setName(fieldName)
984+
.setTypeName(nullableWrapperDescriptor.getFullName())
985+
.setLabel(label)
986+
.build());
987+
} else {
988+
descriptorBuilder.addField(FieldDescriptorProto.newBuilder()
989+
.setNumber(fieldNumber)
990+
.setName(fieldName)
991+
.setType(protoType)
992+
.setLabel(label)
993+
.build());
994+
}
979995
}
980996

981997
@Override
@@ -2944,8 +2960,6 @@ public Array fromProto(@Nonnull final PlanSerializationContext serializationCont
29442960

29452961
class Uuid implements Type {
29462962

2947-
public static final String MESSAGE_NAME = TupleFieldsProto.UUID.getDescriptor().getName();
2948-
29492963
private final boolean isNullable;
29502964

29512965
private Uuid(boolean isNullable) {

fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/query/plan/cascades/values/RecordConstructorValue.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import com.apple.foundationdb.record.PlanDeserializer;
2828
import com.apple.foundationdb.record.PlanHashable;
2929
import com.apple.foundationdb.record.PlanSerializationContext;
30-
import com.apple.foundationdb.record.TupleFieldsProto;
30+
import com.apple.foundationdb.record.metadata.expressions.TupleFieldsHelper;
3131
import com.apple.foundationdb.record.planprotos.PRecordConstructorValue;
3232
import com.apple.foundationdb.record.planprotos.PValue;
3333
import com.apple.foundationdb.record.provider.foundationdb.FDBRecordStoreBase;
@@ -173,12 +173,7 @@ public static Object deepCopyIfNeeded(@Nonnull TypeRepository typeRepository,
173173
}
174174

175175
if (fieldType.isUuid()) {
176-
Verify.verify(field instanceof UUID);
177-
final var uuidObject = (UUID) field;
178-
return TupleFieldsProto.UUID.newBuilder()
179-
.setMostSignificantBits(uuidObject.getMostSignificantBits())
180-
.setLeastSignificantBits(uuidObject.getLeastSignificantBits())
181-
.build();
176+
return TupleFieldsHelper.toProto((UUID) field);
182177
}
183178

184179
if (fieldType instanceof Type.Array) {
@@ -215,7 +210,9 @@ public static Object deepCopyIfNeeded(@Nonnull TypeRepository typeRepository,
215210
}
216211

217212
private static Object protoObjectForPrimitive(@Nonnull Type type, @Nonnull Object field) {
218-
if (type.getTypeCode() == Type.TypeCode.BYTES) {
213+
if (type.isNullable()) {
214+
return TupleFieldsHelper.toProto(field, TupleFieldsHelper.getNullableWrapperDescriptorForTypeCode(type.getTypeCode()));
215+
} else if (type.getTypeCode() == Type.TypeCode.BYTES) {
219216
if (field instanceof byte[]) {
220217
// todo: we're a little inconsistent about whether the field should be byte[] or ByteString for BYTES fields
221218
return ZeroCopyByteString.wrap((byte[]) field);

fdb-relational-api/src/main/java/com/apple/foundationdb/relational/api/metadata/DataType.java

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,12 @@
2424
import com.apple.foundationdb.relational.api.exceptions.RelationalException;
2525
import com.apple.foundationdb.relational.util.Assert;
2626
import com.apple.foundationdb.relational.util.SpotBugsSuppressWarnings;
27-
2827
import com.google.common.base.Suppliers;
29-
import com.google.common.collect.BiMap;
30-
import com.google.common.collect.HashBiMap;
3128
import com.google.common.collect.ImmutableList;
3229

3330
import javax.annotation.Nonnull;
3431
import java.sql.Types;
32+
import java.util.HashMap;
3533
import java.util.List;
3634
import java.util.Map;
3735
import java.util.Objects;
@@ -54,19 +52,19 @@
5452
*/
5553
public abstract class DataType {
5654
@Nonnull
57-
private static final BiMap<Code, Integer> typeCodeJdbcTypeMap;
55+
private static final Map<Code, Integer> typeCodeJdbcTypeMap;
5856

5957
static {
60-
typeCodeJdbcTypeMap = HashBiMap.create();
58+
typeCodeJdbcTypeMap = new HashMap<>();
6159

6260
typeCodeJdbcTypeMap.put(Code.BOOLEAN, Types.BOOLEAN);
6361
typeCodeJdbcTypeMap.put(Code.LONG, Types.BIGINT);
6462
typeCodeJdbcTypeMap.put(Code.INTEGER, Types.INTEGER);
6563
typeCodeJdbcTypeMap.put(Code.FLOAT, Types.FLOAT);
6664
typeCodeJdbcTypeMap.put(Code.DOUBLE, Types.DOUBLE);
6765
typeCodeJdbcTypeMap.put(Code.STRING, Types.VARCHAR);
68-
typeCodeJdbcTypeMap.put(Code.ENUM, Types.JAVA_OBJECT); // TODO (Rethink Relational Enum mapping to SQL type)
69-
typeCodeJdbcTypeMap.put(Code.UUID, Types.OTHER); // TODO (Rethink Relational Enum mapping to SQL type)
66+
typeCodeJdbcTypeMap.put(Code.ENUM, Types.OTHER); // TODO (Rethink Relational Enum mapping to SQL type)
67+
typeCodeJdbcTypeMap.put(Code.UUID, Types.OTHER); // TODO (Rethink Relational UUID mapping to SQL type)
7068
typeCodeJdbcTypeMap.put(Code.BYTES, Types.BINARY);
7169
typeCodeJdbcTypeMap.put(Code.STRUCT, Types.STRUCT);
7270
typeCodeJdbcTypeMap.put(Code.ARRAY, Types.ARRAY);
@@ -696,11 +694,8 @@ private VersionType(boolean isNullable) {
696694
@Override
697695
@Nonnull
698696
public DataType withNullable(boolean isNullable) {
699-
if (isNullable) {
700-
return Primitives.NULLABLE_VERSION.type();
701-
} else {
702-
return Primitives.VERSION.type();
703-
}
697+
Assert.thatUnchecked(!isNullable, ErrorCode.UNSUPPORTED_OPERATION, "Nullable VersionType not supported");
698+
return Primitives.VERSION.type();
704699
}
705700

706701
@Override
@@ -769,11 +764,8 @@ private UuidType(boolean isNullable) {
769764
@Override
770765
@Nonnull
771766
public DataType withNullable(boolean isNullable) {
772-
if (isNullable) {
773-
return Primitives.NULLABLE_UUID.type();
774-
} else {
775-
return Primitives.UUID.type();
776-
}
767+
Assert.thatUnchecked(!isNullable, ErrorCode.UNSUPPORTED_OPERATION, "Nullable UUID not supported");
768+
return Primitives.UUID.type();
777769
}
778770

779771
@Override
@@ -1355,9 +1347,7 @@ public enum Primitives {
13551347
NULLABLE_FLOAT(FloatType.nullable()),
13561348
NULLABLE_DOUBLE(DoubleType.nullable()),
13571349
NULLABLE_STRING(StringType.nullable()),
1358-
NULLABLE_BYTES(BytesType.nullable()),
1359-
NULLABLE_VERSION(VersionType.nullable()),
1360-
NULLABLE_UUID(UuidType.nullable())
1350+
NULLABLE_BYTES(BytesType.nullable())
13611351
;
13621352

13631353
@Nonnull

fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/MessageTuple.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,10 @@
2121
package com.apple.foundationdb.relational.recordlayer;
2222

2323
import com.apple.foundationdb.annotation.API;
24-
import com.apple.foundationdb.record.TupleFieldsProto;
24+
import com.apple.foundationdb.record.metadata.expressions.TupleFieldsHelper;
2525
import com.apple.foundationdb.relational.api.exceptions.InvalidColumnReferenceException;
2626
import com.google.protobuf.Descriptors;
2727
import com.google.protobuf.Message;
28-
import com.google.protobuf.MessageOrBuilder;
29-
30-
import java.util.UUID;
3128

3229
@API(API.Status.EXPERIMENTAL)
3330
public class MessageTuple extends AbstractRow {
@@ -52,10 +49,14 @@ public Object getObject(int position) throws InvalidColumnReferenceException {
5249
final var field = message.getField(message.getDescriptorForType().getFields().get(position));
5350
if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.ENUM) {
5451
return ((Descriptors.EnumValueDescriptor) field).getName();
55-
} else if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE && fieldDescriptor.getMessageType().equals(TupleFieldsProto.UUID.getDescriptor())) {
56-
final var dynamicMsg = (MessageOrBuilder) field;
57-
return new UUID((Long) dynamicMsg.getField(dynamicMsg.getDescriptorForType().findFieldByName("most_significant_bits")),
58-
(Long) dynamicMsg.getField(dynamicMsg.getDescriptorForType().findFieldByName("least_significant_bits")));
52+
} else if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
53+
if (TupleFieldsHelper.isTupleField(fieldDescriptor.getMessageType())) {
54+
if (field == null) {
55+
return null;
56+
} else {
57+
return TupleFieldsHelper.fromProto((Message) field, fieldDescriptor.getMessageType());
58+
}
59+
}
5960
}
6061
return field;
6162
} else {

fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/metadata/DataTypeUtils.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,5 @@ public static Type toRecordLayerType(@Nonnull final DataType type) {
157157
primitivesMap.put(DataType.Primitives.NULLABLE_FLOAT.type(), Type.primitiveType(Type.TypeCode.FLOAT, true));
158158
primitivesMap.put(DataType.Primitives.NULLABLE_BYTES.type(), Type.primitiveType(Type.TypeCode.BYTES, true));
159159
primitivesMap.put(DataType.Primitives.NULLABLE_STRING.type(), Type.primitiveType(Type.TypeCode.STRING, true));
160-
primitivesMap.put(DataType.Primitives.NULLABLE_VERSION.type(), Type.primitiveType(Type.TypeCode.VERSION, true));
161-
primitivesMap.put(DataType.Primitives.NULLABLE_UUID.type(), Type.uuidType(true));
162160
}
163161
}

fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/SemanticAnalyzer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,8 @@ public DataType lookupType(@Nonnull Identifier typeIdentifier, boolean isNullabl
501501
type = isNullable ? DataType.Primitives.NULLABLE_FLOAT.type() : DataType.Primitives.FLOAT.type();
502502
break;
503503
case "UUID":
504-
type = isNullable ? DataType.Primitives.NULLABLE_UUID.type() : DataType.Primitives.UUID.type();
504+
Assert.thatUnchecked(!isNullable, ErrorCode.UNSUPPORTED_OPERATION, "Nullable UUID not supported");
505+
type = DataType.Primitives.UUID.type();
505506
break;
506507
default:
507508
Assert.notNullUnchecked(metadataCatalog);

fdb-relational-core/src/main/java/com/apple/foundationdb/relational/recordlayer/query/visitors/DdlVisitor.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,6 @@ public RecordLayerColumn visitColumnDefinition(@Nonnull RelationalParser.ColumnD
119119
final var columnId = visitUid(ctx.colName);
120120
final var isRepeated = ctx.ARRAY() != null;
121121
final var isNullable = ctx.columnConstraint() != null ? (Boolean) ctx.columnConstraint().accept(this) : true;
122-
// TODO: We currently do not support NOT NULL for any type other than ARRAY. This is because there is no way to
123-
// specify not "nullability" at the RecordMetaData level. For ARRAY, specifying that is actually possible
124-
// by means of NullableArrayWrapper. In essence, we don't actually need a wrapper per se for non-array types,
125-
// but a way to represent it in RecordMetadata.
126-
Assert.thatUnchecked(isRepeated || isNullable, ErrorCode.UNSUPPORTED_OPERATION, "NOT NULL is only allowed for ARRAY column type");
127122
containsNullableArray = containsNullableArray || (isRepeated && isNullable);
128123
final var columnTypeId = ctx.columnType().customType != null ? visitUid(ctx.columnType().customType) : Identifier.of(ctx.columnType().getText());
129124
final var semanticAnalyzer = getDelegate().getSemanticAnalyzer();

yaml-tests/src/test/java/YamlIntegrationTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,4 +258,9 @@ public void enumTest(YamlTest.Runner runner) throws Exception {
258258
public void uuidTest(YamlTest.Runner runner) throws Exception {
259259
runner.runYamsql("uuid.yamsql");
260260
}
261+
262+
@TestTemplate
263+
public void nullColumnConstraintTest(YamlTest.Runner runner) throws Exception {
264+
runner.runYamsql("null-column-constraint.yamsql");
265+
}
261266
}

0 commit comments

Comments
 (0)