mirror of
https://github.com/microsoft/HybridRow.git
synced 2026-01-20 09:53:13 +00:00
Progressed on deserializing schemas.
This commit is contained in:
@@ -3,29 +3,53 @@
|
||||
|
||||
package com.azure.data.cosmos.core;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
import com.fasterxml.jackson.databind.ObjectWriter;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.google.common.base.Strings.lenientFormat;
|
||||
|
||||
public final class Json {
|
||||
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
private static final ObjectReader reader = mapper.reader();
|
||||
private static final ObjectWriter writer = mapper.writer();
|
||||
private static final Logger logger = LoggerFactory.getLogger(Json.class);
|
||||
|
||||
private static final ObjectMapper mapper = new ObjectMapper(new JsonFactory()
|
||||
.enable(JsonParser.Feature.ALLOW_COMMENTS));
|
||||
|
||||
private static final ObjectReader reader = mapper.reader()
|
||||
.withFeatures(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
|
||||
|
||||
private static final ObjectWriter writer = mapper.writer()
|
||||
.withFeatures(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
|
||||
|
||||
private Json() {
|
||||
}
|
||||
|
||||
public static <T> Optional<T> parse(String value) {
|
||||
public static <T> Optional<T> parse(InputStream stream, Class<T> type) {
|
||||
try {
|
||||
return Optional.of(reader.<T>readValue(value));
|
||||
} catch (IOException e) {
|
||||
return Optional.of(reader.forType(type).readValue(stream));
|
||||
} catch (IOException error) {
|
||||
logger.error("", error);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Optional<T> parse(String value, Class<T> type) {
|
||||
try {
|
||||
return Optional.of(reader.forType(type).readValue(value));
|
||||
} catch (IOException error) {
|
||||
logger.error("", error);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,15 +61,15 @@ public final class LayoutCompiler {
|
||||
|
||||
for (Property p : properties) {
|
||||
|
||||
LayoutType type = LayoutCompiler.logicalToPhysicalType(ns, p.propertyType(), typeArgs);
|
||||
LayoutType type = LayoutCompiler.logicalToPhysicalType(ns, p.type(), typeArgs);
|
||||
|
||||
switch (LayoutCodeTraits.clearImmutableBit(type.layoutCode())) {
|
||||
|
||||
case OBJECT_SCOPE: {
|
||||
if (!p.propertyType().nullable()) {
|
||||
if (!p.type().nullable()) {
|
||||
throw new LayoutCompilationException("Non-nullable sparse column are not supported.");
|
||||
}
|
||||
ObjectPropertyType op = (ObjectPropertyType)p.propertyType();
|
||||
ObjectPropertyType op = (ObjectPropertyType)p.type();
|
||||
builder.addObjectScope(p.path(), type);
|
||||
LayoutCompiler.addProperties(builder, ns, type.layoutCode(), op.properties());
|
||||
builder.EndObjectScope();
|
||||
@@ -87,7 +87,7 @@ public final class LayoutCompiler {
|
||||
case TAGGED_SCOPE:
|
||||
case TAGGED2_SCOPE:
|
||||
case SCHEMA: {
|
||||
if (!p.propertyType().nullable()) {
|
||||
if (!p.type().nullable()) {
|
||||
throw new LayoutCompilationException("Non-nullable sparse column are not supported.");
|
||||
}
|
||||
builder.addTypedScope(p.path(), type, typeArgs.get());
|
||||
@@ -100,9 +100,9 @@ public final class LayoutCompiler {
|
||||
|
||||
default: {
|
||||
|
||||
if (p.propertyType() instanceof PrimitivePropertyType) {
|
||||
if (p.type() instanceof PrimitivePropertyType) {
|
||||
|
||||
PrimitivePropertyType pp = (PrimitivePropertyType) p.propertyType();
|
||||
PrimitivePropertyType pp = (PrimitivePropertyType) p.type();
|
||||
|
||||
switch (pp.storage()) {
|
||||
|
||||
|
||||
@@ -9,9 +9,13 @@ import com.google.common.base.Suppliers;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.CodeSource;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -41,17 +45,20 @@ public final class SystemSchema {
|
||||
|
||||
final String json;
|
||||
|
||||
try (final InputStream stream = SystemSchema.class.getResourceAsStream("SystemSchema.json")) {
|
||||
try (final InputStream stream = getResourceAsStream("SystemSchema.json")) {
|
||||
|
||||
Optional<Namespace> namespace = Namespace.parse(stream);
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
while (buffer.writeBytes(stream, 8192) == 8192) { }
|
||||
json = buffer.readCharSequence(buffer.readableBytes(), StandardCharsets.UTF_8).toString();
|
||||
|
||||
} catch (IOException cause) {
|
||||
String message = lenientFormat("failed to load SystemSchema.json due to %s", cause);
|
||||
String message = lenientFormat("Failed to load SystemSchema.json due to %s", cause);
|
||||
throw new IllegalStateException(message, cause);
|
||||
}
|
||||
|
||||
Optional<Namespace> namespace = Namespace.parse(json);
|
||||
checkState(namespace.isPresent(), "failed to load SystemSchema.json");
|
||||
checkState(namespace.isPresent(), "Failed to parse SystemSchema.json");
|
||||
|
||||
return new LayoutResolverNamespace(namespace.get());
|
||||
});
|
||||
@@ -62,4 +69,23 @@ public final class SystemSchema {
|
||||
public static LayoutResolver layoutResolver() {
|
||||
return layoutResolver.get();
|
||||
}
|
||||
|
||||
private static InputStream getResourceAsStream(final String name) throws IOException {
|
||||
|
||||
final CodeSource codeSource = SystemSchema.class.getProtectionDomain().getCodeSource();
|
||||
final ClassLoader classLoader = SystemSchema.class.getClassLoader();
|
||||
final String location = codeSource.getLocation().toString();
|
||||
final Enumeration<URL> urls;
|
||||
|
||||
urls = classLoader.getResources(name);
|
||||
|
||||
while (urls.hasMoreElements()) {
|
||||
final URL url = urls.nextElement();
|
||||
if (url.toString().startsWith(location)) {
|
||||
return url.openStream();
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileNotFoundException(lenientFormat("cannot find resource at code source location %s", location));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,31 +4,39 @@
|
||||
package com.azure.data.cosmos.serialization.hybridrow.schemas;
|
||||
|
||||
import com.azure.data.cosmos.core.Json;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class Namespace {
|
||||
|
||||
@JsonProperty(required = true)
|
||||
private String name;
|
||||
private SchemaLanguageVersion version = SchemaLanguageVersion.values()[0];
|
||||
|
||||
@JsonProperty(required = true)
|
||||
private ArrayList<Schema> schemas;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the {@link Namespace} class.
|
||||
*/
|
||||
public Namespace() {
|
||||
this.schemas(new ArrayList<Schema>());
|
||||
}
|
||||
@JsonProperty(required = true)
|
||||
private SchemaLanguageVersion version;
|
||||
|
||||
/**
|
||||
* The fully qualified identifier of the namespace.
|
||||
* The fully qualified name of the namespace.
|
||||
*
|
||||
* @return fully qualified name of the {@linkplain Namespace namespace}.
|
||||
*/
|
||||
public final String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fully qualified name of the namespace.
|
||||
*
|
||||
* @param value fully qualified name of the {@linkplain Namespace namespace}.
|
||||
* @return a reference to this {@linkplain Namespace namespace}.
|
||||
*/
|
||||
public final Namespace name(String value) {
|
||||
this.name = value;
|
||||
return this;
|
||||
@@ -54,11 +62,21 @@ public class Namespace {
|
||||
|
||||
/**
|
||||
* The version of the HybridRow Schema Definition Language used to encode this namespace.
|
||||
*
|
||||
* @return {linkplain SchemaLanguageVersion version} of the HybridRow Schema Definition Language used to encode this
|
||||
* {@linkplain Namespace namespace}.
|
||||
*/
|
||||
public final SchemaLanguageVersion version() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the version of the HybridRow Schema Definition Language used to encode this namespace.
|
||||
*
|
||||
* @param value {linkplain SchemaLanguageVersion version} of the HybridRow Schema Definition Language that will be
|
||||
* used to encode this {@linkplain Namespace namespace}.
|
||||
* @return a reference to this {@linkplain Namespace namespace}.
|
||||
*/
|
||||
public final Namespace version(SchemaLanguageVersion value) {
|
||||
this.version = value;
|
||||
return this;
|
||||
@@ -67,12 +85,24 @@ public class Namespace {
|
||||
/**
|
||||
* Parse a JSON document and return a full namespace.
|
||||
*
|
||||
* @param value The JSON text to parse
|
||||
* @param value The JSON text to parse.
|
||||
* @return A namespace containing a set of logical schemas.
|
||||
*/
|
||||
public static Optional<Namespace> parse(String value) {
|
||||
Optional<Namespace> ns = Json.<Namespace>parse(value);
|
||||
ns.ifPresent(SchemaValidator::validate);
|
||||
return ns;
|
||||
Optional<Namespace> namespace = Json.parse(value, Namespace.class);
|
||||
namespace.ifPresent(SchemaValidator::validate);
|
||||
return namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a JSON document and return a full namespace.
|
||||
*
|
||||
* @param stream The JSON input stream to parse.
|
||||
* @return A namespace containing a set of logical schemas.
|
||||
*/
|
||||
public static Optional<Namespace> parse(InputStream stream) {
|
||||
Optional<Namespace> namespace = Json.parse(stream, Namespace.class);
|
||||
namespace.ifPresent(SchemaValidator::validate);
|
||||
return namespace;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,22 +3,26 @@
|
||||
|
||||
package com.azure.data.cosmos.serialization.hybridrow.schemas;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* A primitive property.
|
||||
* <p>
|
||||
* Primitive properties map to columns one-to-one. Primitive properties indicate how the
|
||||
* column should be represented within the row.
|
||||
* Primitive properties map to columns one-to-one. Primitive properties indicate how the column should be represented
|
||||
* within the row.
|
||||
*/
|
||||
public class PrimitivePropertyType extends PropertyType {
|
||||
|
||||
@JsonProperty
|
||||
private int length;
|
||||
private StorageKind storage = StorageKind.values()[0];
|
||||
|
||||
@JsonProperty
|
||||
private StorageKind storage;
|
||||
|
||||
/**
|
||||
* The maximum allowable length in bytes.
|
||||
* <p>
|
||||
* This annotation is only valid for non-fixed length types. A value of 0 means the maximum
|
||||
* allowable length.
|
||||
* This annotation is only valid for non-fixed length types. A value of 0 means the maximum allowable length.
|
||||
*/
|
||||
public final int length() {
|
||||
return this.length;
|
||||
|
||||
@@ -3,56 +3,102 @@
|
||||
|
||||
package com.azure.data.cosmos.serialization.hybridrow.schemas;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Describes a single property definition.
|
||||
*/
|
||||
public class Property {
|
||||
|
||||
@JsonProperty(required = false)
|
||||
private String comment;
|
||||
|
||||
@JsonProperty(required = true)
|
||||
private String path;
|
||||
private PropertyType propertyType;
|
||||
|
||||
@JsonProperty(required = true)
|
||||
private PropertyType type;
|
||||
|
||||
/**
|
||||
* An (optional) comment describing the purpose of this property
|
||||
* An (optional) comment describing the purpose of this {@linkplain Property property}.
|
||||
* <p>
|
||||
* Comments are for documentary purpose only and do not affect the property at runtime.
|
||||
*
|
||||
* @return the comment on this {@linkplain Schema property} or {@code null}, if there is no comment.
|
||||
*/
|
||||
public final String comment() {
|
||||
return this.comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the (optional) comment describing the purpose of this {@linkplain Property property}.
|
||||
* <p>
|
||||
* Comments are for documentary purpose only and do not affect the property at runtime.
|
||||
*
|
||||
* @param value a comment on this {@linkplain Property property} or {@code null} to remove the comment, if any, on
|
||||
* this {@linkplain Property property}.
|
||||
* @return a reference to this {@linkplain Property property}.
|
||||
*/
|
||||
public final Property comment(String value) {
|
||||
this.comment = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The logical path of this property.
|
||||
* <p>
|
||||
* The logical path of this {@linkplain Property property}.
|
||||
* <p>.
|
||||
* For complex properties (e.g. objects) the logical path forms a prefix to relative paths of properties defined
|
||||
* within nested structures.
|
||||
* <p>
|
||||
* See the logical path specification for full details on both relative and absolute paths.
|
||||
*
|
||||
* @return the logical path of this {@linkplain Property property}.
|
||||
*/
|
||||
public final String path() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public final void setPath(String value) {
|
||||
/**
|
||||
* Sets the logical path of this {@linkplain Property property}.
|
||||
* <p>.
|
||||
* For complex properties (e.g. objects) the logical path forms a prefix to relative paths of properties defined
|
||||
* within nested structures.
|
||||
* <p>
|
||||
* See the logical path specification for full details on both relative and absolute paths.
|
||||
*
|
||||
* @param value the logical path of this {@linkplain Property property}.
|
||||
* @return a reference to this {@linkplain Property property}.
|
||||
*/
|
||||
public final Property path(@Nonnull String value) {
|
||||
this.path = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the property.
|
||||
* The type of this {@linkplain Property property}.
|
||||
* <p>
|
||||
* Types may be simple (e.g. int8) or complex (e.g. object). Simple types always define a single column. Complex
|
||||
* Types may be simple (e.g. int8) or complex (e.g. object). Simple types always define a single column. Complex
|
||||
* types may define one or more columns depending on their structure.
|
||||
*
|
||||
* @return the type of this {@linkplain Property property}.
|
||||
*/
|
||||
public final PropertyType propertyType() {
|
||||
return this.propertyType;
|
||||
public final PropertyType type() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public final void setPropertyType(PropertyType value) {
|
||||
this.propertyType = value;
|
||||
/**
|
||||
* Sets the type of this {@linkplain Property property}.
|
||||
* <p>
|
||||
* Types may be simple (e.g. int8) or complex (e.g. object). Simple types always define a single column. Complex
|
||||
* types may define one or more columns depending on their structure.
|
||||
*
|
||||
* @param value the type of this {@linkplain Property property}.
|
||||
* @return a reference to this {@linkplain Property property}.
|
||||
*/
|
||||
public final Property type(PropertyType value) {
|
||||
this.type = value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,54 +3,106 @@
|
||||
|
||||
package com.azure.data.cosmos.serialization.hybridrow.schemas;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
||||
|
||||
import java.util.PrimitiveIterator;
|
||||
|
||||
/**
|
||||
* The base class for property types both primitive and complex.
|
||||
*/
|
||||
@JsonTypeInfo(use = Id.NAME, property = "type")
|
||||
@JsonSubTypes({
|
||||
// Composite types
|
||||
@Type(value = ArrayPropertyType.class, name = "array"),
|
||||
@Type(value = MapPropertyType.class, name = "map"),
|
||||
@Type(value = ObjectPropertyType.class, name="object"),
|
||||
@Type(value = ScopePropertyType.class, name="scope"),
|
||||
@Type(value = SetPropertyType.class, name="set"),
|
||||
@Type(value = TaggedPropertyType.class, name="tagged"),
|
||||
@Type(value = TuplePropertyType.class, name="tuple"),
|
||||
// Primitive types
|
||||
@Type(value = PrimitivePropertyType.class, name="int32"),
|
||||
@Type(value = PrimitivePropertyType.class, name="uint32"),
|
||||
@Type(value = PrimitivePropertyType.class, name="utf8")
|
||||
})
|
||||
public abstract class PropertyType {
|
||||
|
||||
@JsonProperty(required = true)
|
||||
private TypeKind type;
|
||||
|
||||
@JsonProperty
|
||||
private String apiType;
|
||||
|
||||
@JsonProperty(defaultValue = "true")
|
||||
private boolean nullable;
|
||||
private TypeKind type = TypeKind.values()[0];
|
||||
|
||||
protected PropertyType() {
|
||||
this.nullable(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Api-specific type annotations for the property.
|
||||
* API-specific type annotations for this {@linkplain Property property}.
|
||||
*
|
||||
* @return API-specific type annotations for this {@linkplain Property property}.
|
||||
*/
|
||||
public final String apiType() {
|
||||
return this.apiType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets API-specific type annotations for this {@linkplain Property property}.
|
||||
*
|
||||
* @param value API-specific type annotations for this {@linkplain Property property}.
|
||||
* @return a reference to this {@linkplain Property property}.
|
||||
*/
|
||||
public final PropertyType apiType(String value) {
|
||||
this.apiType = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code true} if the property can be {@code null}
|
||||
* {@code true} if the {@linkplain Property property} can be {@code null}.
|
||||
* <p>
|
||||
* Default: {@code true}
|
||||
*
|
||||
* @return {@code true} if the {@linkplain Property property} can be {@code null, otherwise {@code false}}.
|
||||
*/
|
||||
public final boolean nullable() {
|
||||
return this.nullable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a flag indicating whether the {@linkplain Property property} can be {@code null}.
|
||||
*
|
||||
* @param value {@code true} indicates that this {@linkplain Property property} can be {@code null}.
|
||||
* @return a reference to this {@linkplain Property property}.
|
||||
*/
|
||||
public final PropertyType nullable(boolean value) {
|
||||
this.nullable = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The logical type of the property
|
||||
* The logical type of this {@linkplain Property property}.
|
||||
*
|
||||
* @return the logical type of this {@linkplain Property property}.
|
||||
*/
|
||||
public final TypeKind type() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logical type of this {@linkplain Property property}.
|
||||
*
|
||||
* @param value the logical type of this {@linkplain Property property}.
|
||||
* @return a reference to this {@linkplain Property property}.
|
||||
*/
|
||||
public final PropertyType type(TypeKind value) {
|
||||
this.type = value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.azure.data.cosmos.core.Json;
|
||||
import com.azure.data.cosmos.serialization.hybridrow.SchemaId;
|
||||
import com.azure.data.cosmos.serialization.hybridrow.layouts.Layout;
|
||||
import com.azure.data.cosmos.serialization.hybridrow.layouts.LayoutCompiler;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -27,23 +28,42 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
*/
|
||||
public class Schema {
|
||||
|
||||
private String comment;
|
||||
// Required fields
|
||||
|
||||
@JsonProperty(required = true)
|
||||
private SchemaId id;
|
||||
|
||||
@JsonProperty(required = true)
|
||||
private String name;
|
||||
|
||||
@JsonProperty(defaultValue = "schema", required = true)
|
||||
private TypeKind type;
|
||||
|
||||
// Optional fields
|
||||
|
||||
@JsonProperty
|
||||
private String comment;
|
||||
|
||||
@JsonProperty
|
||||
private SchemaOptions options;
|
||||
|
||||
@JsonProperty
|
||||
private List<Property> properties;
|
||||
|
||||
@JsonProperty
|
||||
private SchemaLanguageVersion version;
|
||||
|
||||
// TODO: DANOBLE: how do these properties serialize?
|
||||
|
||||
private List<PartitionKey> partitionKeys;
|
||||
private List<PrimarySortKey> primaryKeys;
|
||||
private List<Property> properties;
|
||||
private SchemaId schemaId = SchemaId.NONE;
|
||||
private List<StaticKey> staticKeys;
|
||||
private TypeKind type = TypeKind.values()[0];
|
||||
private SchemaLanguageVersion version = SchemaLanguageVersion.values()[0];
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the {@link Schema} class.
|
||||
*/
|
||||
public Schema() {
|
||||
this.type(TypeKind.SCHEMA);
|
||||
this.properties = Collections.emptyList();
|
||||
private Schema() {
|
||||
this.type = TypeKind.SCHEMA;
|
||||
this.partitionKeys = Collections.emptyList();
|
||||
this.primaryKeys = Collections.emptyList();
|
||||
this.staticKeys = Collections.emptyList();
|
||||
@@ -51,12 +71,24 @@ public class Schema {
|
||||
|
||||
/**
|
||||
* An (optional) comment describing the purpose of this schema.
|
||||
* <p>
|
||||
* Comments are for documentary purpose only and do not affect the schema at runtime.
|
||||
*
|
||||
* @return the comment on this {@linkplain Schema schema} or {@code null}, if there is no comment.
|
||||
*/
|
||||
public final String comment() {
|
||||
return this.comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the (optional) comment describing the purpose of this schema.
|
||||
* <p>
|
||||
* Comments are for documentary purpose only and do not affect the schema at runtime.
|
||||
*
|
||||
* @param value a comment on this {@linkplain Schema schema} or {@code null} to remove the comment, if any, on this
|
||||
* {@linkplain Schema schema}.
|
||||
* @return a reference to this {@linkplain Schema schema}.
|
||||
*/
|
||||
public final Schema comment(String value) {
|
||||
this.comment = value;
|
||||
return this;
|
||||
@@ -65,30 +97,41 @@ public class Schema {
|
||||
/**
|
||||
* Compiles this logical schema into a physical layout that can be used to read and write rows.
|
||||
*
|
||||
* @param ns The namespace within which this schema is defined.
|
||||
* @param namespace The namespace within which this schema is defined.
|
||||
* @return The layout for the schema.
|
||||
*/
|
||||
public final Layout compile(Namespace ns) {
|
||||
public final Layout compile(Namespace namespace) {
|
||||
|
||||
checkNotNull(ns, "expected non-null ns");
|
||||
checkArgument(ns.schemas().contains(this));
|
||||
checkNotNull(namespace, "expected non-null ns");
|
||||
checkArgument(namespace.schemas().contains(this));
|
||||
|
||||
return LayoutCompiler.compile(ns, this);
|
||||
return LayoutCompiler.compile(namespace, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the schema.
|
||||
* The name of this {@linkplain Schema schema}.
|
||||
* <p>
|
||||
* The name of a schema MUST be unique within its namespace.
|
||||
* <para />
|
||||
* Names must begin with an alpha-numeric character and can only contain alpha-numeric characters and
|
||||
* underscores.
|
||||
* The name of a schema MUST be unique within its namespace. Names must begin with an alpha-numeric character and
|
||||
* can only contain alpha-numeric characters and underscores.
|
||||
*
|
||||
* @return the name of this {@linkplain Schema schema} or {@code null}, if the name has not yet been set.
|
||||
*/
|
||||
public final String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public final Schema name(String value) {
|
||||
/**
|
||||
* Sets the name of this {@linkplain Schema schema}.
|
||||
* <p>
|
||||
* The name of a schema MUST be unique within its namespace. Names must begin with an alpha-numeric character and
|
||||
* can only contain alpha-numeric characters and underscores.
|
||||
*
|
||||
* @param value a name for this {@linkplain Schema schema}.
|
||||
* @return a reference to this {@linkplain Schema schema}.
|
||||
*/
|
||||
@Nonnull
|
||||
public final Schema name(@Nonnull String value) {
|
||||
checkNotNull(value);
|
||||
this.name = value;
|
||||
return this;
|
||||
}
|
||||
@@ -112,8 +155,8 @@ public class Schema {
|
||||
* @return A logical schema, if the value parses.
|
||||
*/
|
||||
public static Optional<Schema> parse(String value) {
|
||||
return Json.<Schema>parse(value); // TODO: DANOBLE: perform structural validation on the Schema after JSON
|
||||
// parsing
|
||||
return Json.parse(value, Schema.class);
|
||||
// TODO: DANOBLE: perform structural validation on the Schema after JSON parsing
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,11 +216,11 @@ public class Schema {
|
||||
* Identifiers must be unique within the scope of the database in which they are used.
|
||||
*/
|
||||
public final SchemaId schemaId() {
|
||||
return this.schemaId;
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public final Schema schemaId(SchemaId value) {
|
||||
this.schemaId = value;
|
||||
this.id = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ public final class SchemaHash {
|
||||
HashCode128 hash = seed;
|
||||
|
||||
hash = Murmur3Hash.Hash128(p.path(), hash);
|
||||
hash = SchemaHash.computeHash(ns, p.propertyType(), hash);
|
||||
hash = SchemaHash.computeHash(ns, p.type(), hash);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -12,26 +12,49 @@ public enum SchemaLanguageVersion {
|
||||
/**
|
||||
* Initial version of the HybridRow Schema Description Lanauge.
|
||||
*/
|
||||
V1((byte)0);
|
||||
V1((byte) 0, "v1");
|
||||
|
||||
public static final int BYTES = Byte.BYTES;
|
||||
|
||||
private static HashMap<Byte, SchemaLanguageVersion> mappings;
|
||||
private String friendlyName;
|
||||
private byte value;
|
||||
|
||||
SchemaLanguageVersion(byte value) {
|
||||
SchemaLanguageVersion(byte value, String text) {
|
||||
this.value = value;
|
||||
this.friendlyName = text;
|
||||
mappings().put(value, this);
|
||||
}
|
||||
|
||||
public byte getValue() {
|
||||
return this.value;
|
||||
/**
|
||||
* Returns the friendly name of this enum constant.
|
||||
*
|
||||
* @return the friendly name of this enum constant.
|
||||
* @see #toString()
|
||||
*/
|
||||
public String friendlyName() {
|
||||
return this.friendlyName;
|
||||
}
|
||||
|
||||
public static SchemaLanguageVersion forValue(byte value) {
|
||||
public static SchemaLanguageVersion from(byte value) {
|
||||
return mappings().get(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the friendly name of this enum constant.
|
||||
*
|
||||
* @return the friendly name of this enum constant.
|
||||
* @see #friendlyName()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.friendlyName;
|
||||
}
|
||||
|
||||
public byte value() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
private static HashMap<Byte, SchemaLanguageVersion> mappings() {
|
||||
if (mappings == null) {
|
||||
synchronized (SchemaLanguageVersion.class) {
|
||||
@@ -42,4 +65,6 @@ public enum SchemaLanguageVersion {
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public final class SchemaValidator {
|
||||
Property p, Schema s, Map<SchemaIdentification, Schema> schemas, Map<SchemaId, Schema> ids) {
|
||||
|
||||
Assert.isValidIdentifier(p.path(), "Property path");
|
||||
SchemaValidator.visit(p.propertyType(), null, schemas, ids);
|
||||
SchemaValidator.visit(p.type(), null, schemas, ids);
|
||||
}
|
||||
|
||||
private static void visit(
|
||||
@@ -164,7 +164,7 @@ public final class SchemaValidator {
|
||||
Map<String, Property> pathDupCheck = new HashMap<>(op.properties().size());
|
||||
for (Property nested : op.properties()) {
|
||||
Assert.duplicateCheck(nested.path(), nested, pathDupCheck, "Property path", "Object");
|
||||
SchemaValidator.visit(nested.propertyType(), p, schemas, ids);
|
||||
SchemaValidator.visit(nested.type(), p, schemas, ids);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public enum StorageKind {
|
||||
* <p>
|
||||
* This is indicative of an error in the the column specification.
|
||||
*/
|
||||
NONE(-1),
|
||||
NONE(-1, "none"),
|
||||
|
||||
/**
|
||||
* The property defines a sparse column
|
||||
@@ -28,14 +28,14 @@ public enum StorageKind {
|
||||
* linked list at the end of the row. Access time for sparse columns is proportional to the number of sparse columns
|
||||
* in the row.
|
||||
*/
|
||||
SPARSE(0),
|
||||
SPARSE(0, "sparse"),
|
||||
|
||||
/**
|
||||
* The property is a fixed-length, space-reserved column
|
||||
* <p>
|
||||
* The column will consume 1 null-bit, and its byte-width regardless of whether the value is present in the row.
|
||||
*/
|
||||
FIXED(1),
|
||||
FIXED(1, "fixed"),
|
||||
|
||||
/**
|
||||
* The property is a variable-length column.
|
||||
@@ -46,7 +46,7 @@ public enum StorageKind {
|
||||
* When a <em>long</em> value is marked variable then a null-bit is reserved and the value is optionally encoded as
|
||||
* variable if small enough to fit, otherwise the null-bit is set and the value is encoded as sparse.
|
||||
*/
|
||||
VARIABLE(2);
|
||||
VARIABLE(2, "variable");
|
||||
|
||||
public static final int BYTES = Integer.BYTES;
|
||||
|
||||
@@ -57,17 +57,28 @@ public enum StorageKind {
|
||||
return new Int2ObjectArrayMap<StorageKind>(values, storageKinds);
|
||||
});
|
||||
|
||||
private int value;
|
||||
private final String friendlyName;
|
||||
private final int value;
|
||||
|
||||
StorageKind(int value) {
|
||||
StorageKind(int value, String friendlyName) {
|
||||
this.friendlyName = friendlyName;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return this.value;
|
||||
public String friendlyName() {
|
||||
return this.friendlyName;
|
||||
}
|
||||
|
||||
public static StorageKind from(int value) {
|
||||
return mappings.get().get(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.friendlyName;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package com.azure.data.cosmos.serialization.hybridrow.schemas;
|
||||
|
||||
// TODO: DANOBLE: Fixup JSON-serialized naming for agreement with the dotnet code
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
|
||||
import com.google.common.base.Suppliers;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
@@ -19,7 +20,8 @@ public enum TypeKind {
|
||||
/**
|
||||
* Reserved.
|
||||
*/
|
||||
INVALID(0),
|
||||
@JsonEnumDefaultValue
|
||||
INVALID(0, "invalid"),
|
||||
|
||||
/**
|
||||
* The literal null.
|
||||
@@ -27,166 +29,166 @@ public enum TypeKind {
|
||||
* When used as a fixed column, only a presence bit is allocated. When used as a sparse column, a sparse value with
|
||||
* 0-length payload is written.
|
||||
*/
|
||||
NULL(1),
|
||||
NULL(1, "null"),
|
||||
|
||||
/**
|
||||
* A boolean property.
|
||||
* <p>
|
||||
* Boolean properties are allocated a single bit when schematized within a row.
|
||||
*/
|
||||
BOOLEAN(2),
|
||||
BOOLEAN(2, "bool"),
|
||||
|
||||
/**
|
||||
* 8-bit signed integer.
|
||||
*/
|
||||
INT_8(3),
|
||||
INT_8(3, "int8"),
|
||||
|
||||
/**
|
||||
* 16-bit signed integer.
|
||||
*/
|
||||
INT_16(4),
|
||||
INT_16(4, "int16"),
|
||||
|
||||
/**
|
||||
* 32-bit signed integer.
|
||||
*/
|
||||
INT_32(5),
|
||||
INT_32(5, "int32"),
|
||||
|
||||
/**
|
||||
* 64-bit signed integer.
|
||||
*/
|
||||
INT_64(6),
|
||||
INT_64(6, "int64"),
|
||||
|
||||
/**
|
||||
* 8-bit unsigned integer.
|
||||
*/
|
||||
UINT_8(7),
|
||||
UINT_8(7, "uint8"),
|
||||
|
||||
/**
|
||||
* 16-bit unsigned integer.
|
||||
*/
|
||||
UINT_16(8),
|
||||
UINT_16(8, "uint16"),
|
||||
|
||||
/**
|
||||
* 32-bit unsigned integer.
|
||||
*/
|
||||
UINT_32(9),
|
||||
UINT_32(9, "uint32"),
|
||||
|
||||
/**
|
||||
* 64-bit unsigned integer.
|
||||
*/
|
||||
UINT_64(10),
|
||||
UINT_64(10, "uint64"),
|
||||
|
||||
/**
|
||||
* Variable length encoded signed integer.
|
||||
*/
|
||||
VAR_INT(11),
|
||||
VAR_INT(11, "varint"),
|
||||
|
||||
/**
|
||||
* Variable length encoded unsigned integer.
|
||||
*/
|
||||
VAR_UINT(12),
|
||||
VAR_UINT(12, "varuint"),
|
||||
|
||||
/**
|
||||
* 32-bit IEEE 754 floating point value.
|
||||
*/
|
||||
FLOAT_32(13),
|
||||
FLOAT_32(13, "float32"),
|
||||
|
||||
/**
|
||||
* 64-bit IEEE 754 floating point value.
|
||||
*/
|
||||
FLOAT_64(14),
|
||||
FLOAT_64(14, "float64"),
|
||||
|
||||
/**
|
||||
* 128-bit IEEE 754-2008 floating point value.
|
||||
*/
|
||||
FLOAT_128(15),
|
||||
FLOAT_128(15, "float128"),
|
||||
|
||||
/**
|
||||
* 128-bit floating point value.
|
||||
*
|
||||
* @see java.math.BigDecimal
|
||||
*/
|
||||
DECIMAL(16),
|
||||
DECIMAL(16, "decimal"),
|
||||
|
||||
/**
|
||||
* 64-bit date/time value in 100ns increments from C# epoch.
|
||||
*
|
||||
* @see java.time.OffsetDateTime
|
||||
*/
|
||||
DATE_TIME(17),
|
||||
DATE_TIME(17, "datetime"),
|
||||
|
||||
/**
|
||||
* 64-bit date/time value in milliseconds increments from Unix epoch.
|
||||
*
|
||||
* @see com.azure.data.cosmos.serialization.hybridrow.UnixDateTime
|
||||
*/
|
||||
UNIX_DATE_TIME(18),
|
||||
UNIX_DATE_TIME(18, "unixdatetime"),
|
||||
|
||||
/**
|
||||
* 128-bit globally unique identifier (in little-endian byte order).
|
||||
*/
|
||||
GUID(19),
|
||||
GUID(19, "guid"),
|
||||
|
||||
/**
|
||||
* 12-byte MongoDB Object Identifier (in little-endian byte order).
|
||||
*/
|
||||
MONGODB_OBJECT_ID(20),
|
||||
MONGODB_OBJECT_ID(20, "mongodb.objectid"),
|
||||
|
||||
/**
|
||||
* Zero to MAX_ROW_SIZE bytes encoded as UTF-8 code points.
|
||||
*/
|
||||
UTF_8(21),
|
||||
UTF_8(21, "utf8"),
|
||||
|
||||
/**
|
||||
* Zero to MAX_ROW_SIZE untyped bytes.
|
||||
*/
|
||||
BINARY(22),
|
||||
BINARY(22, "binary"),
|
||||
|
||||
/**
|
||||
* An object property.
|
||||
*/
|
||||
OBJECT(23),
|
||||
OBJECT(23, "object"),
|
||||
|
||||
/**
|
||||
* An array property, either typed or untyped.
|
||||
*/
|
||||
ARRAY(24),
|
||||
ARRAY(24, "array"),
|
||||
|
||||
/**
|
||||
* A set property, either typed or untyped.
|
||||
*/
|
||||
SET(25),
|
||||
SET(25, "set"),
|
||||
|
||||
/**
|
||||
* A map property, either typed or untyped.
|
||||
*/
|
||||
MAP(26),
|
||||
MAP(26, "map"),
|
||||
|
||||
/**
|
||||
* A tuple property. Tuples are typed, finite, ordered, sets.
|
||||
*/
|
||||
TUPLE(27),
|
||||
TUPLE(27, "tuple"),
|
||||
|
||||
/**
|
||||
* A tagged property.
|
||||
* <p>
|
||||
* Tagged properties pair one or more typed values with an API-specific uint8 type code.
|
||||
*/
|
||||
TAGGED(28),
|
||||
TAGGED(28, "tagged"),
|
||||
|
||||
/**
|
||||
* A row with schema.
|
||||
* <p>
|
||||
* May define either a top-level table schema or a UDT (nested row).
|
||||
*/
|
||||
SCHEMA(29),
|
||||
SCHEMA(29, "schema"),
|
||||
|
||||
/**
|
||||
* An untyped sparse field.
|
||||
* <p>
|
||||
* May only be used to define the type within a nested scope.
|
||||
*/
|
||||
ANY(30);
|
||||
ANY(30, "any");
|
||||
|
||||
public static final int BYTES = Integer.BYTES;
|
||||
|
||||
@@ -197,12 +199,35 @@ public enum TypeKind {
|
||||
return new Int2ObjectOpenHashMap<>(values, typeKinds);
|
||||
});
|
||||
|
||||
private int value;
|
||||
private final String friendlyName;
|
||||
private final int value;
|
||||
|
||||
TypeKind(int value) {
|
||||
TypeKind(final int value, final String friendlyName) {
|
||||
this.friendlyName = friendlyName;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the friendly name of this enum constant.
|
||||
*
|
||||
* @return the friendly name of this enum constant.
|
||||
* @see #toString()
|
||||
*/
|
||||
public String friendlyName() {
|
||||
return this.friendlyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the friendly name of this enum constant.
|
||||
*
|
||||
* @return the friendly name of this enum constant.
|
||||
* @see #friendlyName()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.friendlyName;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ public class UdtPropertyType extends ScopePropertyType {
|
||||
/**
|
||||
* The identifier of the UDT schema defining the structure for the nested row.
|
||||
* <p>
|
||||
* The UDT schema MUST be defined within the same {@link Namespace} as the schema that
|
||||
* references it.
|
||||
* The UDT schema MUST be defined within the same {@link Namespace} as the schema that references it.
|
||||
*/
|
||||
public final String name() {
|
||||
return this.name;
|
||||
|
||||
@@ -13,9 +13,9 @@ import static org.testng.Assert.*;
|
||||
@Test(groups = "unit")
|
||||
public class SystemSchemaTest {
|
||||
|
||||
private static final Path SchemaFile = Paths.get("data", "CustomerSchema.json");
|
||||
private static final Path SCHEMA_FILE = Paths.get("test-data", "CustomerSchema.json");
|
||||
|
||||
@Test(enabled = false)
|
||||
@Test
|
||||
public void testLoadSchema() {
|
||||
|
||||
final LayoutResolver layoutResolver = SystemSchema.layoutResolver();
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
# this is the log4j configuration for tests
|
||||
# log4j configuration for tests
|
||||
|
||||
# Set root logger level to DEBUG and its only appender to A1.
|
||||
log4j.rootLogger=INFO, A1
|
||||
|
||||
# Set HTTP components' logger to INFO
|
||||
|
||||
log4j.category.io.netty=INFO
|
||||
log4j.category.io.reactivex=INFO
|
||||
log4j.category.com.microsoft.azure.cosmosdb=INFO
|
||||
# A1 is set to be a ConsoleAppender.
|
||||
log4j.appender.A1=org.apache.log4j.ConsoleAppender
|
||||
|
||||
# A1 uses PatternLayout.
|
||||
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.A1.layout.ConversionPattern=%d %5X{pid} [%t] %-5p %c - %m%n
|
||||
|
||||
Reference in New Issue
Block a user