mchades commented on code in PR #14318:
URL: https://github.com/apache/gravitino/pull/14318#discussion_r4408367185


##########
common/src/main/java/org/apache/gravitino/utils/WritableDataTypeValidator.java:
##########
@@ -0,0 +1,259 @@
+/*
+ * 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.gravitino.utils;
+
+import com.google.common.base.Preconditions;
+import javax.annotation.Nullable;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.function.FunctionColumn;
+import org.apache.gravitino.function.FunctionDefinition;
+import org.apache.gravitino.function.FunctionParam;
+import org.apache.gravitino.rel.Column;
+import org.apache.gravitino.rel.TableChange;
+import org.apache.gravitino.rel.expressions.Expression;
+import org.apache.gravitino.rel.expressions.FunctionExpression;
+import org.apache.gravitino.rel.expressions.distributions.Distribution;
+import org.apache.gravitino.rel.expressions.literals.Literal;
+import org.apache.gravitino.rel.expressions.sorts.SortOrder;
+import org.apache.gravitino.rel.expressions.transforms.Transform;
+import org.apache.gravitino.rel.partitions.IdentityPartition;
+import org.apache.gravitino.rel.partitions.ListPartition;
+import org.apache.gravitino.rel.partitions.Partition;
+import org.apache.gravitino.rel.partitions.RangePartition;
+import org.apache.gravitino.rel.types.Type;
+import org.apache.gravitino.rel.types.Types;
+
+/**
+ * Validates data types in newly written metadata. Read and import paths, and 
selectors identifying
+ * existing metadata, must retain support for {@link Types.UnparsedType}.
+ *
+ * <p>This checks the type representation, not whether a connector supports 
the type or whether a
+ * literal value matches its declared type.
+ */
+public final class WritableDataTypeValidator {
+
+  private WritableDataTypeValidator() {}
+
+  /**
+   * Validates a type, including all nested types.
+   *
+   * @param dataType The type to validate.
+   * @param fieldPath The request field path used in validation errors.
+   * @throws IllegalArgumentException If the type is unresolved or an external 
type is blank.
+   */
+  public static void validate(Type dataType, String fieldPath) {
+    Preconditions.checkArgument(
+        dataType != null, "\"%s\" field is required and cannot be null", 
fieldPath);
+    Preconditions.checkArgument(
+        !(dataType instanceof Types.UnparsedType),
+        "\"%s\" must be a Gravitino-recognized data type or an explicit 
ExternalType; "
+            + "UnparsedType is not allowed for new metadata",
+        fieldPath);
+
+    if (dataType instanceof Types.ExternalType) {
+      Types.ExternalType externalType = (Types.ExternalType) dataType;
+      Preconditions.checkArgument(
+          StringUtils.isNotBlank(externalType.catalogString()),
+          "\"%s.catalogString\" field is required and cannot be blank",
+          fieldPath);
+      return;
+    }
+
+    if (dataType instanceof Types.StructType) {
+      Types.StructType.Field[] fields = ((Types.StructType) dataType).fields();
+      Preconditions.checkArgument(
+          fields != null, "\"%s.fields\" field is required and cannot be 
null", fieldPath);
+      for (int i = 0; i < fields.length; i++) {
+        Preconditions.checkArgument(
+            fields[i] != null,
+            "\"%s.fields[%s]\" field is required and cannot be null",
+            fieldPath,
+            i);
+        validate(fields[i].type(), fieldPath + ".fields[" + i + "].type");
+      }
+      return;
+    }
+
+    if (dataType instanceof Types.ListType) {
+      validate(((Types.ListType) dataType).elementType(), fieldPath + 
".elementType");
+      return;
+    }
+
+    if (dataType instanceof Types.MapType) {
+      Types.MapType mapType = (Types.MapType) dataType;
+      validate(mapType.keyType(), fieldPath + ".keyType");
+      validate(mapType.valueType(), fieldPath + ".valueType");
+      return;
+    }
+
+    if (dataType instanceof Types.UnionType) {
+      Type[] types = ((Types.UnionType) dataType).types();
+      Preconditions.checkArgument(
+          types != null, "\"%s.types\" field is required and cannot be null", 
fieldPath);
+      for (int i = 0; i < types.length; i++) {
+        validate(types[i], fieldPath + ".types[" + i + "]");
+      }
+      return;
+    }
+
+    Preconditions.checkArgument(
+        dataType instanceof Type.PrimitiveType || dataType instanceof 
Types.NullType,
+        "\"%s\" must be a Gravitino-recognized data type or an explicit 
ExternalType",
+        fieldPath);
+  }
+
+  /**
+   * Validates literal types recursively within an expression and any 
partition assignments.
+   *
+   * @param expression The expression to validate, or null when absent.
+   * @param fieldPath The request field path used in validation errors.
+   * @throws IllegalArgumentException If a literal contains a non-writable 
type.
+   */
+  public static void validateExpression(@Nullable Expression expression, 
String fieldPath) {
+    if (expression == null) {
+      return;
+    }
+    if (expression instanceof Literal) {
+      validate(((Literal<?>) expression).dataType(), fieldPath + ".dataType");
+    }
+    if (expression instanceof Transform) {
+      Partition[] assignments = ((Transform) expression).assignments();
+      for (int i = 0; i < assignments.length; i++) {

Review Comment:
   Added path-aware `IllegalArgumentException` checks for null traversal arrays 
and null or unsupported partitions. Absent optional expressions and 
implementations of the three documented partition interfaces remain supported.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to