Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/main/java/org/rumbledb/compiler/InferTypeVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.rumbledb.exceptions.ExceptionMetadata;
import org.rumbledb.exceptions.IsStaticallyUnexpectedTypeException;
import org.rumbledb.exceptions.OurBadException;
import org.rumbledb.exceptions.SemanticException;
import org.rumbledb.exceptions.UnexpectedStaticTypeException;
import org.rumbledb.exceptions.UnknownFunctionCallException;
import org.rumbledb.exceptions.UnsupportedFeatureException;
Expand Down Expand Up @@ -2547,7 +2548,30 @@ public StaticContext visitValidateTypeExpression(ValidateTypeExpression expressi
@Override
public StaticContext visitValidateExpression(ValidateExpression expression, StaticContext argument) {
visitDescendants(expression, expression.getStaticContext());
expression.setStaticSequenceType(expression.getMainExpression().getStaticSequenceType());
if (expression.getValidationMode() == ValidateExpression.ValidationMode.TYPE) {
Name typeName = expression.getTypeName();
if (!Name.XS_NS.equals(typeName.getNamespace()) || !BuiltinTypesCatalogue.typeExists(typeName)) {
throw new SemanticException(
"The type " + typeName + " is not defined in the in-scope schema types.",
ErrorCode.ValidateTypeNotFoundErrorCode,
expression.getMetadata());
}
ItemType targetType = BuiltinTypesCatalogue.getItemTypeByName(typeName);
if (!targetType.isAtomicItemType()
|| targetType.equals(BuiltinTypesCatalogue.atomicItem)
|| targetType.equals(BuiltinTypesCatalogue.NOTATIONItem)) {
throw new UnsupportedFeatureException(
"This first validate type implementation only supports concrete built-in XML Schema atomic types.",
expression.getMetadata());
}
}
ItemType sourceItemType =
expression.getMainExpression().getStaticSequenceType().getItemType();
ItemType resultItemType = sourceItemType.isSubtypeOf(BuiltinTypesCatalogue.elementNode)
|| sourceItemType.isSubtypeOf(BuiltinTypesCatalogue.documentNode)
? sourceItemType
: BuiltinTypesCatalogue.nodeItem;
expression.setStaticSequenceType(new SequenceType(resultItemType, SequenceType.Arity.One));
return argument;
}

Expand Down
16 changes: 12 additions & 4 deletions src/main/java/org/rumbledb/compiler/RuntimeIteratorVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@
import org.rumbledb.runtime.typing.CastIterator;
import org.rumbledb.runtime.typing.CastableIterator;
import org.rumbledb.runtime.typing.InstanceOfIterator;
import org.rumbledb.runtime.typing.JSONiqValidateIterator;
import org.rumbledb.runtime.typing.TreatIterator;
import org.rumbledb.runtime.typing.ValidateTypeIterator;
import org.rumbledb.runtime.update.expression.AppendExpressionIterator;
import org.rumbledb.runtime.update.expression.CreateCollectionIterator;
import org.rumbledb.runtime.update.expression.DeleteExpressionIterator;
Expand Down Expand Up @@ -263,6 +263,7 @@
import org.rumbledb.runtime.xml.TextNodeConstructorRuntimeIterator;
import org.rumbledb.runtime.xml.TextNodeRuntimeIterator;
import org.rumbledb.runtime.xml.UnaryLookupIterator;
import org.rumbledb.runtime.xml.XQueryValidateIterator;
import org.rumbledb.runtime.xml.axis.AxisIterator;
import org.rumbledb.runtime.xml.axis.AxisIteratorVisitor;
import org.rumbledb.types.BuiltinTypesCatalogue;
Expand Down Expand Up @@ -1466,7 +1467,7 @@ public ItemRuntimePlan visitInstanceOfExpression(InstanceOfExpression expression
@Override
public ItemRuntimePlan visitValidateTypeExpression(ValidateTypeExpression expression, ItemRuntimePlan argument) {
ItemRuntimePlan childExpression = this.visit(expression.getMainExpression(), argument);
ItemRuntimePlan runtimeIterator = new ValidateTypeIterator(
ItemRuntimePlan runtimeIterator = new JSONiqValidateIterator(
childExpression,
expression.getSequenceType().getItemType(),
expression.isValidate(),
Expand All @@ -1484,8 +1485,15 @@ public ItemRuntimePlan visitValidateTypeExpression(ValidateTypeExpression expres

@Override
public ItemRuntimePlan visitValidateExpression(ValidateExpression expression, ItemRuntimePlan argument) {
throw new UnsupportedFeatureException(
"XML Schema validate expressions are not executable yet.", expression.getMetadata());
if (expression.getValidationMode() != ValidateExpression.ValidationMode.TYPE) {
throw new UnsupportedFeatureException(
"Strict and lax XML Schema validation are not executable yet.", expression.getMetadata());
}
ItemRuntimePlan operand = this.visit(expression.getMainExpression(), argument);
return new XQueryValidateIterator(
operand,
BuiltinTypesCatalogue.getItemTypeByName(expression.getTypeName()),
expression.getStaticContextForRuntime(this.config, this.visitorConfig));
}

@Override
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/rumbledb/errorcodes/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,11 @@ public String toString() {
public static final ErrorCode UnexpectedTypeErrorCode = registerBuiltIn("XPTY0004");
public static final ErrorCode NodeAndNonNode = registerBuiltIn("XPTY0018");
public static final ErrorCode UnexpectedNode = registerBuiltIn("XPTY0019");
public static final ErrorCode ValidateOperandTypeErrorCode = registerBuiltIn("XQTY0030");

public static final ErrorCode InvalidInstance = registerBuiltIn("XQDY0027");
public static final ErrorCode InvalidProcessingInstructionTargetCastErrorCode = registerBuiltIn("XQDY0041");
public static final ErrorCode InvalidValidateDocumentStructureErrorCode = registerBuiltIn("XQDY0061");
public static final ErrorCode CycleInVariableDeclarationsErrorCode = registerBuiltIn("XQDY0054");
public static final ErrorCode InvalidProcessingInstructionContentErrorCode = registerBuiltIn("XQDY0026");
public static final ErrorCode InvalidProcessingInstructionTargetErrorCode = registerBuiltIn("XQDY0064");
Expand Down Expand Up @@ -203,6 +205,7 @@ public String toString() {
public static final ErrorCode DecimalFormatPropertyInvalidValueErrorCode = registerBuiltIn("XQST0097");
public static final ErrorCode DecimalFormatPropertyConflictErrorCode = registerBuiltIn("XQST0098");
public static final ErrorCode DuplicateDecimalFormatPropertyErrorCode = registerBuiltIn("XQST0114");
public static final ErrorCode ValidateTypeNotFoundErrorCode = registerBuiltIn("XQST0104");

public static final ErrorCode AtomizationError = registerBuiltIn("FOTY0012");
public static final ErrorCode UnexpectedFunctionItem = registerBuiltIn("FOTY0015");
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/org/rumbledb/exceptions/ValidateException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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.rumbledb.exceptions;

import java.io.Serial;

import org.rumbledb.errorcodes.ErrorCode;

public class ValidateException extends RumbleException {

@Serial
private static final long serialVersionUID = 1L;

public ValidateException(String message, ErrorCode errorCode, ExceptionMetadata metadata) {
super(message, errorCode, metadata);
}
}
3 changes: 2 additions & 1 deletion src/main/java/org/rumbledb/items/xml/AttributeItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ public List<Item> atomizedValue() {
Item typedValue = CastIterator.castItemToType(
ItemFactory.getInstance().createUntypedAtomicItem(this.stringValue),
this.typeAnnotation,
ExceptionMetadata.EMPTY_METADATA);
ExceptionMetadata.EMPTY_METADATA,
NamespaceBindingUtils.namespaceResolver(this.parent));
return Collections.singletonList(typedValue);
}
return Collections.singletonList(ItemFactory.getInstance().createUntypedAtomicItem(this.stringValue));
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/rumbledb/items/xml/ElementItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,8 @@ public List<Item> atomizedValue() {
Item typedValue = CastIterator.castItemToType(
ItemFactory.getInstance().createUntypedAtomicItem(this.stringValue),
this.typeAnnotation,
ExceptionMetadata.EMPTY_METADATA);
ExceptionMetadata.EMPTY_METADATA,
NamespaceBindingUtils.namespaceResolver(this));
return Collections.singletonList(typedValue);
}
// For untyped elements, atomization yields the element's typed value as xs:untypedAtomic.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import org.rumbledb.exceptions.OurBadException;
import org.rumbledb.items.structured.HomogeneousItemDataFrame;
import org.rumbledb.runtime.plan.ItemRuntimePlan;
import org.rumbledb.runtime.typing.JSONiqValidateIterator;
import org.rumbledb.runtime.typing.TypeInferrenceUtils;
import org.rumbledb.runtime.typing.ValidateTypeIterator;
import org.rumbledb.types.ItemType;

/**
Expand All @@ -48,7 +48,7 @@ public HomogeneousItemDataFrame fromList(
log.debug("Inferred DataFrame type:\n" + itemType);
}
}
return ValidateTypeIterator.convertLocalItemsToDataFrame(items, itemType, context, true, staticContext);
return JSONiqValidateIterator.convertLocalItemsToDataFrame(items, itemType, context, true, staticContext);
}

@Override
Expand All @@ -59,7 +59,7 @@ public HomogeneousItemDataFrame fromRDD(
itemType = TypeInferrenceUtils.inferItemTypeOfRDDItems(
rdd, staticContext.getMetadata(), TypeInferrenceUtils.TypeMergeMode.LAX);
}
return ValidateTypeIterator.convertRDDToValidDataFrame(rdd, itemType, context, true, staticContext);
return JSONiqValidateIterator.convertRDDToValidDataFrame(rdd, itemType, context, true, staticContext);
}

public HomogeneousItemDataFrame fromPlan(ItemRuntimePlan plan, DynamicContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
import org.rumbledb.runtime.plan.RDDRuntimePlan;
import org.rumbledb.runtime.plan.RuntimePlan;
import org.rumbledb.runtime.plan.UpdatingRuntimePlan;
import org.rumbledb.runtime.typing.ValidateTypeIterator;
import org.rumbledb.runtime.typing.JSONiqValidateIterator;
import org.rumbledb.runtime.update.PendingUpdateList;
import org.rumbledb.spark.SparkSessionManager;
import org.rumbledb.types.SequenceType;
Expand Down Expand Up @@ -307,7 +307,7 @@ public HomogeneousItemDataFrame createNativeDataFrame(DynamicContext context) {
}

JavaRDD<Item> rdd = createNativeRDD(context);
return ValidateTypeIterator.convertRDDToValidDataFrame(
return JSONiqValidateIterator.convertRDDToValidDataFrame(
rdd,
this.expression.getRuntimeStaticContext().getStaticType().getItemType(),
context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
import org.rumbledb.runtime.plan.LocalRuntimePlan;
import org.rumbledb.runtime.plan.NativeQueryRuntimePlan;
import org.rumbledb.runtime.plan.RDDRuntimePlan;
import org.rumbledb.runtime.typing.ValidateTypeIterator;
import org.rumbledb.runtime.typing.JSONiqValidateIterator;
import org.rumbledb.spark.SparkSessionManager;

@Log4j2
Expand Down Expand Up @@ -181,7 +181,7 @@ public HomogeneousItemDataFrame createNativeDataFrame(DynamicContext context) {
if (nativeQuery == NativeClauseContext.NoNativeQuery) {
JavaRDD<Item> rdd = createNativeRDD(context);
JavaRDD<Row> rowRDD = rdd.map(i -> RowFactory.create(i.castToDecimalValue()));
StructType schema = ValidateTypeIterator.convertToDataFrameSchema(
StructType schema = JSONiqValidateIterator.convertToDataFrameSchema(
getStaticType().getItemType(), this.staticContext);
schema.printTreeString();
Dataset<Row> result =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
import org.rumbledb.runtime.plan.LocalRuntimePlan;
import org.rumbledb.runtime.plan.NativeQueryRuntimePlan;
import org.rumbledb.runtime.plan.RDDRuntimePlan;
import org.rumbledb.runtime.typing.JSONiqValidateIterator;
import org.rumbledb.runtime.typing.TypeInferrenceUtils;
import org.rumbledb.runtime.typing.ValidateTypeIterator;
import org.rumbledb.types.ItemType;

public class DistinctValuesFunctionIterator extends ItemRuntimePlan
Expand Down Expand Up @@ -95,7 +95,7 @@ public HomogeneousItemDataFrame createNativeDataFrame(DynamicContext dynamicCont
itemType = TypeInferrenceUtils.inferItemTypeOfRDDItems(
rdd, getMetadata(), TypeInferrenceUtils.TypeMergeMode.LAX);
}
return ValidateTypeIterator.convertRDDToValidDataFrame(
return JSONiqValidateIterator.convertRDDToValidDataFrame(
rdd, itemType, dynamicContext, true, getRuntimeStaticContext());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.rumbledb.runtime.plan.LocalRuntimePlan;
import org.rumbledb.runtime.plan.NativeQueryRuntimePlan;
import org.rumbledb.runtime.plan.RDDRuntimePlan;
import org.rumbledb.runtime.xml.BuiltinTypeValidator;
import org.rumbledb.spark.SparkSessionManager;
import org.rumbledb.types.BuiltinTypesCatalogue;
import org.rumbledb.types.FieldDescriptor;
Expand All @@ -53,7 +54,7 @@
import org.rumbledb.types.TypeMappings;

@Log4j2
public class ValidateTypeIterator extends ItemRuntimePlan
public class JSONiqValidateIterator extends ItemRuntimePlan
implements LocalRuntimePlan<Item>, RDDRuntimePlan<Item>, DataFrameRuntimePlan<Item>, NativeQueryRuntimePlan {

@Serial
Expand All @@ -64,7 +65,7 @@ public class ValidateTypeIterator extends ItemRuntimePlan
private final boolean isValidate;
private final ItemValidator validator;

public ValidateTypeIterator(
public JSONiqValidateIterator(
ItemRuntimePlan instance, ItemType itemType, boolean isValidate, RuntimeStaticContext staticContext) {
super(Collections.singletonList(instance), staticContext);
this.iterator = instance;
Expand Down Expand Up @@ -346,7 +347,7 @@ private static Object getRowColumnFromItemUsingDataType(Item item, DataType data
}

if (dataType instanceof StructType structType) {
return ValidateTypeIterator.convertLocalItemToRow(item, structType, context);
return JSONiqValidateIterator.convertLocalItemToRow(item, structType, context);
}

if (dataType.equals(DataTypes.BooleanType)) {
Expand Down Expand Up @@ -441,7 +442,7 @@ public Item call(Item item) {
private Item validate(Item item, ItemType itemType) {
if (itemType.isAtomicItemType()) {
if (item.isElementNode() || item.isDocumentNode()) {
return validateXmlNodeAgainstAtomicType(item, itemType);
return BuiltinTypeValidator.validate(item, itemType, this.metadata);
}
if (InstanceOfIterator.doesItemTypeMatchItem(itemType, item)) {
return item;
Expand Down Expand Up @@ -626,103 +627,6 @@ private Item validate(Item item, ItemType itemType) {
}
return item;
}

private Item validateXmlNodeAgainstAtomicType(Item item, ItemType itemType) {
Item copiedRoot;
Item validatedElement;
if (item.isDocumentNode()) {
validateAtomicDocumentShape(item);
copiedRoot = item.copy(false);
reattachXmlParents(copiedRoot, null);
Item copiedDocumentElement = getSingleElementChild(copiedRoot);
if (copiedDocumentElement == null) {
throw new InvalidInstanceException(
"Document validation requires exactly one element child for atomic type "
+ itemType.getIdentifierString(),
this.metadata);
}
validatedElement = copiedDocumentElement;
} else if (item.isElementNode()) {
validateAtomicElementShape(item);
copiedRoot = item.copy(false);
reattachXmlParents(copiedRoot, null);
validatedElement = copiedRoot;
} else {
throw new InvalidInstanceException(
"Atomic XML validation is only supported for document and element nodes.", this.metadata);
}

Item castType;
try {
castType = CastIterator.castItemToType(
ItemFactory.getInstance().createUntypedAtomicItem(validatedElement.getStringValue()),
itemType,
this.metadata,
this.staticContext);
} catch (Exception e) {
throw new InvalidInstanceException(
"Cannot cast " + item.serialize() + " to type " + itemType.getIdentifierString());
}
if (castType == null) {
throw new InvalidInstanceException(
"Cannot cast " + item.serialize() + " to type " + itemType.getIdentifierString());
}
validatedElement.setSchemaType(itemType);
return copiedRoot;
}

private static void validateAtomicDocumentShape(Item document) {
Item documentElement = getSingleElementChild(document);
if (documentElement == null) {
throw new InvalidInstanceException(
"Document validation requires exactly one element child for atomic type validation.");
}
for (Item child : document.children()) {
if (!child.isElementNode() && !child.isCommentNode() && !child.isProcessingInstructionNode()) {
throw new InvalidInstanceException(
"Document validation for atomic types only supports element, comment, and processing-instruction children.");
}
}
validateAtomicElementShape(documentElement);
}

private static void validateAtomicElementShape(Item element) {
if (!element.attributes().isEmpty()) {
throw new InvalidInstanceException("Element validation for atomic types does not support attributes.");
}
for (Item child : element.children()) {
if (child.isElementNode()) {
throw new InvalidInstanceException(
"Element validation for atomic types does not support nested element children.");
}
}
}

private static Item getSingleElementChild(Item document) {
Item elementChild = null;
for (Item child : document.children()) {
if (!child.isElementNode()) {
continue;
}
if (elementChild != null) {
return null;
}
elementChild = child;
}
return elementChild;
}

private static void reattachXmlParents(Item node, Item parent) {
if (parent != null) {
node.setParent(parent);
}
for (Item attribute : node.attributes()) {
attribute.setParent(node);
}
for (Item child : node.children()) {
reattachXmlParents(child, node);
}
}
}

@Override
Expand Down
Loading