This is an automated email from the ASF dual-hosted git repository. jshao pushed a commit to branch branch-gvfs-fuse-dev in repository https://gitbox.apache.org/repos/asf/gravitino.git
commit b38b9dd13d8036bb1bbfca5ca8b447172e4c3554 Author: Justin Mclean <[email protected]> AuthorDate: Tue Dec 3 20:09:18 2024 +1100 [#5383] Add column commands to the Gravitino CLI (#5716) ### What changes were proposed in this pull request? Add column commands to the Gravitino CLI ### Why are the changes needed? Expand Gravitino CLI. Fix: #5383 ### Does this PR introduce _any_ user-facing change? No, but add extra commands. ### How was this patch tested? locally. --- .../org/apache/gravitino/cli/DefaultConverter.java | 48 ++++ .../org/apache/gravitino/cli/ErrorMessages.java | 2 + .../java/org/apache/gravitino/cli/FullName.java | 20 +- .../apache/gravitino/cli/GravitinoCommandLine.java | 80 ++++++ .../org/apache/gravitino/cli/GravitinoOptions.java | 38 ++- .../java/org/apache/gravitino/cli/ParseType.java | 73 +++++ .../java/org/apache/gravitino/cli/ParsedType.java | 100 +++++++ .../apache/gravitino/cli/PositionConverter.java | 43 +++ .../apache/gravitino/cli/TestableCommandLine.java | 140 ++++++++++ .../org/apache/gravitino/cli/TypeConverter.java | 121 +++++++++ .../{UpdateTableComment.java => AddColumn.java} | 61 ++++- .../{UpdateTableComment.java => DeleteColumn.java} | 33 ++- ...Comment.java => UpdateColumnAutoIncrement.java} | 37 ++- ...eTableComment.java => UpdateColumnComment.java} | 31 ++- ...TableComment.java => UpdateColumnDatatype.java} | 40 ++- ...eTableComment.java => UpdateColumnDefault.java} | 44 ++- ...{UpdateTableName.java => UpdateColumnName.java} | 31 ++- ...leComment.java => UpdateColumnNullability.java} | 37 ++- ...TableComment.java => UpdateColumnPosition.java} | 40 ++- .../gravitino/cli/commands/UpdateTableComment.java | 1 + .../gravitino/cli/commands/UpdateTableName.java | 1 + .../apache/gravitino/cli/TestColumnCommands.java | 301 +++++++++++++++++++++ .../apache/gravitino/cli/TestDefaultConverter.java | 76 ++++++ .../org/apache/gravitino/cli/TestFulllName.java | 21 +- .../org/apache/gravitino/cli/TestParseType.java | 67 +++++ .../gravitino/cli/TestPositionConverter.java | 82 ++++++ .../apache/gravitino/cli/TestTypeConverter.java | 99 +++++++ docs/cli.md | 41 ++- 28 files changed, 1592 insertions(+), 116 deletions(-) diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/DefaultConverter.java b/clients/cli/src/main/java/org/apache/gravitino/cli/DefaultConverter.java new file mode 100644 index 000000000..29b0b3475 --- /dev/null +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/DefaultConverter.java @@ -0,0 +1,48 @@ +/* + * 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.cli; + +import org.apache.gravitino.rel.Column; +import org.apache.gravitino.rel.expressions.Expression; +import org.apache.gravitino.rel.expressions.literals.Literals; +import org.apache.gravitino.rel.types.Type; + +public class DefaultConverter { + + /** + * Converts a default value string to the appropriate internal default value. + * + * @param defaultValue The string representing the default value i.e. "current_timestamp" or a + * literal value. + * @param dataType The string representing the default type. + * @return An instance of the appropriate default value. + */ + public static Expression convert(String defaultValue, String dataType) { + Type convertedDatatype = ParseType.toType(dataType); + + if (defaultValue == null || defaultValue.isEmpty()) { + return Column.DEFAULT_VALUE_NOT_SET; + } else if (defaultValue.equalsIgnoreCase("current_timestamp")) { + return Column.DEFAULT_VALUE_OF_CURRENT_TIMESTAMP; + } else { + return Literals.of(defaultValue, convertedDatatype); + } + } +} diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java b/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java index 2a34e21cc..351a40fc4 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/ErrorMessages.java @@ -41,6 +41,8 @@ public class ErrorMessages { public static final String MULTIPLE_TAG_COMMAND_ERROR = "Error: The current command only supports one --tag option."; public static final String TAG_EXISTS = "Tag already exists."; + public static final String UNKNOWN_COLUMN = "Unknown column."; + public static final String COLUMN_EXISTS = "Column already exists."; public static final String UNKNOWN_TOPIC = "Unknown topic."; public static final String TOPIC_EXISTS = "Topic already exists."; public static final String UNKNOWN_FILESET = "Unknown fileset."; diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java b/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java index 959282485..c53a5adc8 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java @@ -118,12 +118,21 @@ public class FullName { /** * Retrieves the fileset name from the third part of the full name option. * - * @return The table name, or null if not found. + * @return The fileset name, or null if not found. */ public String getFilesetName() { return getNamePart(2); } + /** + * Retrieves the column name from the fourth part of the full name option. + * + * @return The column name, or null if not found. + */ + public String getColumnName() { + return getNamePart(3); + } + /** * Helper method to retrieve a specific part of the full name based on the position of the part. * @@ -193,4 +202,13 @@ public class FullName { public boolean hasTableName() { return hasNamePart(3); } + + /** + * Does the column name exist? + * + * @return True if the column name exists, or false if it does not. + */ + public boolean hasColumnName() { + return hasNamePart(4); + } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java index 33eca22b3..dd9091543 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoCommandLine.java @@ -487,9 +487,89 @@ public class GravitinoCommandLine extends TestableCommandLine { String catalog = name.getCatalogName(); String schema = name.getSchemaName(); String table = name.getTableName(); + String column = name.getColumnName(); if (CommandActions.LIST.equals(command)) { newListColumns(url, ignore, metalake, catalog, schema, table).handle(); + } else if (CommandActions.CREATE.equals(command)) { + String datatype = line.getOptionValue(GravitinoOptions.DATATYPE); + String comment = line.getOptionValue(GravitinoOptions.COMMENT); + String position = line.getOptionValue(GravitinoOptions.POSITION); + boolean nullable = true; + boolean autoIncrement = false; + String defaultValue = line.getOptionValue(GravitinoOptions.DEFAULT); + + if (line.hasOption(GravitinoOptions.NULL)) { + nullable = line.getOptionValue(GravitinoOptions.NULL).equals("true"); + } + + if (line.hasOption(GravitinoOptions.AUTO)) { + autoIncrement = line.getOptionValue(GravitinoOptions.AUTO).equals("true"); + } + + newAddColumn( + url, + ignore, + metalake, + catalog, + schema, + table, + column, + datatype, + comment, + position, + nullable, + autoIncrement, + defaultValue) + .handle(); + } else if (CommandActions.DELETE.equals(command)) { + newDeleteColumn(url, ignore, metalake, catalog, schema, table, column).handle(); + } else if (CommandActions.UPDATE.equals(command)) { + if (line.hasOption(GravitinoOptions.COMMENT)) { + String comment = line.getOptionValue(GravitinoOptions.COMMENT); + newUpdateColumnComment(url, ignore, metalake, catalog, schema, table, column, comment) + .handle(); + } + if (line.hasOption(GravitinoOptions.RENAME)) { + String newName = line.getOptionValue(GravitinoOptions.RENAME); + newUpdateColumnName(url, ignore, metalake, catalog, schema, table, column, newName) + .handle(); + } + if (line.hasOption(GravitinoOptions.DATATYPE)) { + String datatype = line.getOptionValue(GravitinoOptions.DATATYPE); + newUpdateColumnDatatype(url, ignore, metalake, catalog, schema, table, column, datatype) + .handle(); + } + if (line.hasOption(GravitinoOptions.POSITION)) { + String position = line.getOptionValue(GravitinoOptions.POSITION); + newUpdateColumnPosition(url, ignore, metalake, catalog, schema, table, column, position) + .handle(); + } + if (line.hasOption(GravitinoOptions.NULL)) { + if (line.getOptionValue(GravitinoOptions.NULL).equals("true")) { + newUpdateColumnNullability(url, ignore, metalake, catalog, schema, table, column, true) + .handle(); + } else if (line.getOptionValue(GravitinoOptions.NULL).equals("false")) { + newUpdateColumnNullability(url, ignore, metalake, catalog, schema, table, column, false) + .handle(); + } + } + if (line.hasOption(GravitinoOptions.AUTO)) { + if (line.getOptionValue(GravitinoOptions.AUTO).equals("true")) { + newUpdateColumnAutoIncrement(url, ignore, metalake, catalog, schema, table, column, true) + .handle(); + } else if (line.getOptionValue(GravitinoOptions.AUTO).equals("false")) { + newUpdateColumnAutoIncrement(url, ignore, metalake, catalog, schema, table, column, false) + .handle(); + } + } + if (line.hasOption(GravitinoOptions.DEFAULT)) { + String defaultValue = line.getOptionValue(GravitinoOptions.DEFAULT); + String dataType = line.getOptionValue(GravitinoOptions.DATATYPE); + newUpdateColumnDefault( + url, ignore, metalake, catalog, schema, table, column, defaultValue, dataType) + .handle(); + } } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java index 8cf5f0cc1..1fcc9fedf 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/GravitinoOptions.java @@ -40,6 +40,12 @@ public class GravitinoOptions { public static final String USER = "user"; public static final String GROUP = "group"; public static final String TAG = "tag"; + public static final String DATATYPE = "datatype"; + public static final String POSITION = "position"; + public static final String NULL = "null"; + public static final String AUTO = "auto"; + public static final String DEFAULT = "default"; + public static final String FILESET = "fileset"; public static final String OWNER = "owner"; public static final String ROLE = "role"; public static final String AUDIT = "audit"; @@ -68,11 +74,11 @@ public class GravitinoOptions { options.addOption(createSimpleOption("a", AUDIT, "display audit information")); options.addOption(createSimpleOption("x", INDEX, "display index information")); options.addOption(createSimpleOption("d", DISTRIBUTION, "display distribution information")); - options.addOption(createSimpleOption(null, PARTITION, "display partition information")); + options.addOption(createSimpleOption(PARTITION, "display partition information")); options.addOption(createSimpleOption("o", OWNER, "display entity owner")); // Create/update options - options.addOption(createArgOption(null, RENAME, "new entity name")); + options.addOption(createArgOption(RENAME, "new entity name")); options.addOption(createArgOption("c", COMMENT, "entity comment")); options.addOption(createArgOption("P", PROPERTY, "property name")); options.addOption(createArgOption("V", VALUE, "property value")); @@ -81,6 +87,11 @@ public class GravitinoOptions { "z", PROVIDER, "provider one of hadoop, hive, mysql, postgres, iceberg, kafka")); options.addOption(createArgOption("l", USER, "user name")); options.addOption(createArgOption("g", GROUP, "group name")); + options.addOption(createArgOption(DATATYPE, "column data type")); + options.addOption(createArgOption(POSITION, "position of column")); + options.addOption(createArgOption(NULL, "column value can be null (true/false)")); + options.addOption(createArgOption(AUTO, "column value auto-increments (true/false)")); + options.addOption(createArgOption(DEFAULT, "default column value")); options.addOption(createSimpleOption("o", OWNER, "display entity owner")); options.addOption(createArgOption("r", ROLE, "role name")); @@ -108,6 +119,17 @@ public class GravitinoOptions { return new Option(shortName, longName, false, description); } + /** + * Helper method to create an Option that does not require arguments. + * + * @param longName The long option name. + * @param description The option description. + * @return The Option object. + */ + public Option createSimpleOption(String longName, String description) { + return new Option(null, longName, false, description); + } + /** * Helper method to create an Option that requires an argument. * @@ -120,6 +142,17 @@ public class GravitinoOptions { return new Option(shortName, longName, true, description); } + /** + * Helper method to create an Option that requires an argument. + * + * @param longName The long option name. + * @param description The option description. + * @return The Option object. + */ + public Option createArgOption(String longName, String description) { + return new Option(null, longName, true, description); + } + /** * Helper method to create an Option that requires multiple argument. * @@ -129,7 +162,6 @@ public class GravitinoOptions { * @return The Option object. */ public Option createArgsOption(String shortName, String longName, String description) { - // Support multiple arguments return Option.builder().option(shortName).longOpt(longName).hasArgs().desc(description).build(); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/ParseType.java b/clients/cli/src/main/java/org/apache/gravitino/cli/ParseType.java new file mode 100644 index 000000000..e797d0552 --- /dev/null +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/ParseType.java @@ -0,0 +1,73 @@ +/* + * 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.cli; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.gravitino.rel.types.Type; + +public class ParseType { + + /** + * Parses a data type string and returns a {@link org.apache.gravitino.cli.ParsedType} object + * containing the type name and length or type name, precision, and scale if applicable. + * + * <p>This method supports SQL style types in the format of "typeName(length)" or + * "typeName(precision, scale)". For example, "varchar(10)" and "decimal(10,5)" are valid inputs. + * + * @param datatype The data type string to parse e.g. "varchar(10)" or "decimal(10,5)". + * @return a {@link org.apache.gravitino.cli.ParsedType} object representing the parsed type name. + * @throws IllegalArgumentException if the data type format is unsupported or malformed + */ + public static ParsedType parse(String datatype) { + Pattern pattern = Pattern.compile("^(\\w+)\\((\\d+)(?:,(\\d+))?\\)$"); + Matcher matcher = pattern.matcher(datatype); + + if (matcher.matches()) { + String typeName = matcher.group(1); + Integer lengthOrPrecision = Integer.parseInt(matcher.group(2)); + Integer scale = matcher.group(3) != null ? Integer.parseInt(matcher.group(3)) : null; + + if (lengthOrPrecision != null && scale != null) { + return new ParsedType(typeName, lengthOrPrecision, scale); + } else if (lengthOrPrecision != null) { + return new ParsedType(typeName, lengthOrPrecision); + } else { + throw new IllegalArgumentException("Unsupported/malformed data type: " + typeName); + } + } + + return null; + } + + public static Type toType(String datatype) { + ParsedType parsed = parse(datatype); + + if (parsed != null) { + if (parsed.getPrecision() != null && parsed.getScale() != null) { + return TypeConverter.convert(datatype, parsed.getPrecision(), parsed.getScale()); + } else if (parsed.getLength() != null) { + return TypeConverter.convert(datatype, parsed.getLength()); + } + } + + return TypeConverter.convert(datatype); + } +} diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/ParsedType.java b/clients/cli/src/main/java/org/apache/gravitino/cli/ParsedType.java new file mode 100644 index 000000000..6e30c6ed3 --- /dev/null +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/ParsedType.java @@ -0,0 +1,100 @@ +/* + * 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.cli; + +public class ParsedType { + private String typeName; + private Integer length; + private Integer precision; + private Integer scale; + + /** + * Constructs a ParsedType with specified type name, precision, and scale. + * + * @param typeName The name of the data type. + * @param precision The precision of the data type, which defines the total number of digits. + * @param scale The scale of the data type, which defines the number of digits to the right of the + * decimal point. + */ + public ParsedType(String typeName, Integer precision, Integer scale) { + this.typeName = typeName; + this.precision = precision; + this.scale = scale; + } + + /* + * Constructs a ParsedType with specified type name and length. + * + * @param typeName The name of the data type. + * @param length The length of the data type, which typically defines the maximum number of characters. + */ + public ParsedType(String typeName, Integer length) { + this.typeName = typeName; + this.length = length; + } + + /** + * Gets the type name (e.g., "varchar" or "decimal"). + * + * @return the type name + */ + public String getTypeName() { + return typeName; + } + + /** + * Gets the length for types like "varchar". + * + * @return the length, or null if not applicable + */ + public Integer getLength() { + return length; + } + + /** + * Gets the precision for types like "decimal". + * + * @return the precision, or null if not applicable + */ + public Integer getPrecision() { + return precision; + } + + /** + * Gets the scale for types like "decimal". + * + * @return the scale, or null if not applicable + */ + public Integer getScale() { + return scale; + } + + /** Returns a string representation of this {@code ParsedType} object. */ + @Override + public String toString() { + if (length != null) { + return String.format("Type: %s, Length: %d", typeName, length); + } else if (precision != null && scale != null) { + return String.format("Type: %s, Precision: %d, Scale: %d", typeName, precision, scale); + } else { + return "Unsupported type"; + } + } +} diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/PositionConverter.java b/clients/cli/src/main/java/org/apache/gravitino/cli/PositionConverter.java new file mode 100644 index 000000000..324e70f4b --- /dev/null +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/PositionConverter.java @@ -0,0 +1,43 @@ +/* + * 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.cli; + +import org.apache.gravitino.rel.TableChange; + +public class PositionConverter { + + /** + * Converts a position string to the appropriate position. + * + * @param position The string representing the position i.e. "first" or a column name. + * @return An instance of the appropriate position. + * @throws IllegalArgumentException if the type name is not recognized. + */ + public static TableChange.ColumnPosition convert(String position) { + + if (position == null || position.isEmpty()) { + return TableChange.ColumnPosition.defaultPos(); + } else if (position.equalsIgnoreCase("first")) { + return TableChange.ColumnPosition.first(); + } else { + return TableChange.ColumnPosition.after(position); + } + } +} diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java b/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java index 546c92e33..c6164107b 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/TestableCommandLine.java @@ -21,6 +21,7 @@ package org.apache.gravitino.cli; import java.util.Map; +import org.apache.gravitino.cli.commands.AddColumn; import org.apache.gravitino.cli.commands.AddRoleToGroup; import org.apache.gravitino.cli.commands.AddRoleToUser; import org.apache.gravitino.cli.commands.CatalogAudit; @@ -36,6 +37,7 @@ import org.apache.gravitino.cli.commands.CreateTag; import org.apache.gravitino.cli.commands.CreateTopic; import org.apache.gravitino.cli.commands.CreateUser; import org.apache.gravitino.cli.commands.DeleteCatalog; +import org.apache.gravitino.cli.commands.DeleteColumn; import org.apache.gravitino.cli.commands.DeleteFileset; import org.apache.gravitino.cli.commands.DeleteGroup; import org.apache.gravitino.cli.commands.DeleteMetalake; @@ -101,6 +103,13 @@ import org.apache.gravitino.cli.commands.TopicDetails; import org.apache.gravitino.cli.commands.UntagEntity; import org.apache.gravitino.cli.commands.UpdateCatalogComment; import org.apache.gravitino.cli.commands.UpdateCatalogName; +import org.apache.gravitino.cli.commands.UpdateColumnAutoIncrement; +import org.apache.gravitino.cli.commands.UpdateColumnComment; +import org.apache.gravitino.cli.commands.UpdateColumnDatatype; +import org.apache.gravitino.cli.commands.UpdateColumnDefault; +import org.apache.gravitino.cli.commands.UpdateColumnName; +import org.apache.gravitino.cli.commands.UpdateColumnNullability; +import org.apache.gravitino.cli.commands.UpdateColumnPosition; import org.apache.gravitino.cli.commands.UpdateFilesetComment; import org.apache.gravitino.cli.commands.UpdateFilesetName; import org.apache.gravitino.cli.commands.UpdateMetalakeComment; @@ -669,4 +678,135 @@ public class TestableCommandLine { String property) { return new RemoveFilesetProperty(url, ignore, metalake, catalog, schema, fileset, property); } + + protected AddColumn newAddColumn( + String url, + boolean ignore, + String metalake, + String catalog, + String schema, + String table, + String column, + String datatype, + String comment, + String position, + boolean nullable, + boolean autoIncrement, + String defaultValue) { + return new AddColumn( + url, + ignore, + metalake, + catalog, + schema, + table, + column, + datatype, + comment, + position, + nullable, + autoIncrement, + defaultValue); + } + + protected DeleteColumn newDeleteColumn( + String url, + boolean ignore, + String metalake, + String catalog, + String schema, + String table, + String column) { + return new DeleteColumn(url, ignore, metalake, catalog, schema, table, column); + } + + protected UpdateColumnComment newUpdateColumnComment( + String url, + boolean ignore, + String metalake, + String catalog, + String schema, + String table, + String column, + String comment) { + return new UpdateColumnComment(url, ignore, metalake, catalog, schema, table, column, comment); + } + + protected UpdateColumnName newUpdateColumnName( + String url, + boolean ignore, + String metalake, + String catalog, + String schema, + String table, + String column, + String rename) { + return new UpdateColumnName(url, ignore, metalake, catalog, schema, table, column, rename); + } + + protected UpdateColumnDatatype newUpdateColumnDatatype( + String url, + boolean ignore, + String metalake, + String catalog, + String schema, + String table, + String column, + String datatype) { + return new UpdateColumnDatatype( + url, ignore, metalake, catalog, schema, table, column, datatype); + } + + protected UpdateColumnPosition newUpdateColumnPosition( + String url, + boolean ignore, + String metalake, + String catalog, + String schema, + String table, + String column, + String position) { + return new UpdateColumnPosition( + url, ignore, metalake, catalog, schema, table, column, position); + } + + protected UpdateColumnNullability newUpdateColumnNullability( + String url, + boolean ignore, + String metalake, + String catalog, + String schema, + String table, + String column, + boolean nullable) { + return new UpdateColumnNullability( + url, ignore, metalake, catalog, schema, table, column, nullable); + } + + protected UpdateColumnAutoIncrement newUpdateColumnAutoIncrement( + String url, + boolean ignore, + String metalake, + String catalog, + String schema, + String table, + String column, + boolean autoIncrement) { + return new UpdateColumnAutoIncrement( + url, ignore, metalake, catalog, schema, table, column, autoIncrement); + } + + protected UpdateColumnDefault newUpdateColumnDefault( + String url, + boolean ignore, + String metalake, + String catalog, + String schema, + String table, + String column, + String defaultValue, + String dataType) { + return new UpdateColumnDefault( + url, ignore, metalake, catalog, schema, table, column, defaultValue, dataType); + } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/TypeConverter.java b/clients/cli/src/main/java/org/apache/gravitino/cli/TypeConverter.java new file mode 100644 index 000000000..c34560c2a --- /dev/null +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/TypeConverter.java @@ -0,0 +1,121 @@ +/* + * 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.cli; + +import org.apache.gravitino.rel.types.Type; +import org.apache.gravitino.rel.types.Types; + +public class TypeConverter { + + /** + * Converts a type name string to the appropriate Type. + * + * @param typeName The string representing the data type (e.g., "boolean", "byte", "string"). + * @return An instance of the appropriate Type. + * @throws IllegalArgumentException if the type name is not recognized. + */ + public static Type convert(String typeName) { + switch (typeName.toLowerCase()) { + case "null": + return Types.NullType.get(); + case "boolean": + return Types.BooleanType.get(); + case "byte": + return Types.ByteType.get(); + case "ubyte": + return Types.ByteType.unsigned(); + case "short": + return Types.ShortType.get(); + case "ushort": + return Types.ShortType.unsigned(); + case "integer": + return Types.IntegerType.get(); + case "uinteger": + return Types.IntegerType.unsigned(); + case "long": + return Types.LongType.get(); + case "ulong": + return Types.LongType.unsigned(); + case "float": + return Types.FloatType.get(); + case "double": + return Types.DoubleType.get(); + case "date": + return Types.DateType.get(); + case "time": + return Types.TimeType.get(); + case "timestamp": + return Types.TimestampType.withoutTimeZone(); + case "tztimestamp": + return Types.TimestampType.withTimeZone(); + case "intervalyear": + return Types.IntervalYearType.get(); + case "intervalday": + return Types.IntervalDayType.get(); + case "uuid": + return Types.UUIDType.get(); + case "string": + return Types.StringType.get(); + case "binary": + return Types.BinaryType.get(); + default: + throw new IllegalArgumentException("Unknown or unsupported type: " + typeName); + } + } + + /** + * Converts a type name string to the appropriate Type. + * + * @param typeName The string representing the data type (e.g., "fixed" or "varchar"). + * @param length Length of the data type. + * @return An instance of the appropriate Type. + * @throws IllegalArgumentException if the type name is not recognized. + */ + public static Type convert(String typeName, int length) { + if (typeName.toLowerCase().startsWith("fixed")) { + return Types.FixedType.of(length); + } else if (typeName.toLowerCase().startsWith("varchar")) { + return Types.VarCharType.of(length); + } else if (typeName.toLowerCase().startsWith("char")) { + return Types.FixedCharType.of(length); + } else { + throw new IllegalArgumentException( + "Unknown or unsupported variable length type: " + typeName); + } + } + + /** + * Converts a type name string to the appropriate Type. + * + * @param typeName The string representing the data type. Only "decimal" is supported. + * @param precision Precision of the decimal. + * @param scale Scale of the decimal. + * @return An instance of the appropriate Type. + * @throws IllegalArgumentException if the type name is not recognized. + */ + public static Type convert(String typeName, int precision, int scale) { + if (typeName.toLowerCase().startsWith("decimal")) { + return Types.DecimalType.of(precision, scale); + } else { + throw new IllegalArgumentException( + "Unknown or unsupported precision and scale type: " + typeName); + } + } +} diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/AddColumn.java similarity index 58% copy from clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java copy to clients/cli/src/main/java/org/apache/gravitino/cli/commands/AddColumn.java index ff0cfa760..39dc6eb9d 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/AddColumn.java @@ -20,25 +20,34 @@ package org.apache.gravitino.cli.commands; import org.apache.gravitino.NameIdentifier; +import org.apache.gravitino.cli.DefaultConverter; import org.apache.gravitino.cli.ErrorMessages; +import org.apache.gravitino.cli.ParseType; +import org.apache.gravitino.cli.PositionConverter; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; import org.apache.gravitino.rel.TableChange; +import org.apache.gravitino.rel.types.Type; -/** Update the comment of a table. */ -public class UpdateTableComment extends Command { +public class AddColumn extends Command { protected final String metalake; protected final String catalog; protected final String schema; protected final String table; + protected final String column; + protected final String datatype; protected final String comment; + protected final String position; + protected final boolean nullable; + protected final boolean autoIncrement; + protected final String defaultValue; /** - * Update the comment of a table. + * Adds an optional column to a table. * * @param url The URL of the Gravitino server. * @param ignoreVersions If true don't check the client/server versions match. @@ -46,32 +55,62 @@ public class UpdateTableComment extends Command { * @param catalog The name of the catalog. * @param schema The name of the schema. * @param table The name of the table. - * @param comment New metalake comment. + * @param column The name of the new column. + * @param datatype The data type of the new column. + * @param comment The comment for the column (optional). + * @param position The position of the column (optional). + * @param nullable True if the column can be null, false if it cannot be (optional). + * @param autoIncrement True if the column auto increments (optional). + * @param defaultValue Default value of the column (optional). */ - public UpdateTableComment( + public AddColumn( String url, boolean ignoreVersions, String metalake, String catalog, String schema, String table, - String comment) { + String column, + String datatype, + String comment, + String position, + boolean nullable, + boolean autoIncrement, + String defaultValue) { super(url, ignoreVersions); this.metalake = metalake; this.catalog = catalog; this.schema = schema; this.table = table; + this.column = column; + this.datatype = datatype; this.comment = comment; + this.position = position; + this.nullable = nullable; + this.autoIncrement = autoIncrement; + this.defaultValue = defaultValue; } - /** Update the comment of a table. */ + /** Adds an optional column to a table. */ + @Override public void handle() { + String[] columns = {column}; + Type convertedDatatype = ParseType.toType(datatype); + try { - NameIdentifier tableName = NameIdentifier.of(schema, table); GravitinoClient client = buildClient(metalake); - TableChange change = TableChange.updateComment(comment); + NameIdentifier name = NameIdentifier.of(schema, table); + TableChange change = + TableChange.addColumn( + columns, + convertedDatatype, + comment, + PositionConverter.convert(position), + nullable, + autoIncrement, + DefaultConverter.convert(defaultValue, datatype)); - client.loadCatalog(catalog).asTableCatalog().alterTable(tableName, change); + client.loadCatalog(catalog).asTableCatalog().alterTable(name, change); } catch (NoSuchMetalakeException err) { System.err.println(ErrorMessages.UNKNOWN_METALAKE); return; @@ -89,6 +128,6 @@ public class UpdateTableComment extends Command { return; } - System.out.println(table + " comment changed."); + System.out.println(column + " added to table " + table + "."); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteColumn.java similarity index 78% copy from clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java copy to clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteColumn.java index ff0cfa760..2e2bbd3f9 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/DeleteColumn.java @@ -23,22 +23,22 @@ import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; +import org.apache.gravitino.exceptions.NoSuchColumnException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; import org.apache.gravitino.rel.TableChange; -/** Update the comment of a table. */ -public class UpdateTableComment extends Command { +public class DeleteColumn extends Command { protected final String metalake; protected final String catalog; protected final String schema; protected final String table; - protected final String comment; + protected final String column; /** - * Update the comment of a table. + * Delete a column. * * @param url The URL of the Gravitino server. * @param ignoreVersions If true don't check the client/server versions match. @@ -46,32 +46,34 @@ public class UpdateTableComment extends Command { * @param catalog The name of the catalog. * @param schema The name of the schema. * @param table The name of the table. - * @param comment New metalake comment. + * @param column The name of the column. */ - public UpdateTableComment( + public DeleteColumn( String url, boolean ignoreVersions, String metalake, String catalog, String schema, String table, - String comment) { + String column) { super(url, ignoreVersions); this.metalake = metalake; this.catalog = catalog; this.schema = schema; this.table = table; - this.comment = comment; + this.column = column; } - /** Update the comment of a table. */ + /** Delete a column. */ + @Override public void handle() { + String[] columns = {column}; + try { - NameIdentifier tableName = NameIdentifier.of(schema, table); GravitinoClient client = buildClient(metalake); - TableChange change = TableChange.updateComment(comment); - - client.loadCatalog(catalog).asTableCatalog().alterTable(tableName, change); + TableChange change = TableChange.deleteColumn(columns, false); + NameIdentifier name = NameIdentifier.of(schema, table); + client.loadCatalog(catalog).asTableCatalog().alterTable(name, change); } catch (NoSuchMetalakeException err) { System.err.println(ErrorMessages.UNKNOWN_METALAKE); return; @@ -84,11 +86,14 @@ public class UpdateTableComment extends Command { } catch (NoSuchTableException err) { System.err.println(ErrorMessages.UNKNOWN_TABLE); return; + } catch (NoSuchColumnException err) { + System.err.println(ErrorMessages.UNKNOWN_COLUMN); + return; } catch (Exception exp) { System.err.println(exp.getMessage()); return; } - System.out.println(table + " comment changed."); + System.out.println(column + " deleted."); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnAutoIncrement.java similarity index 70% copy from clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java copy to clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnAutoIncrement.java index ff0cfa760..f848e6a68 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnAutoIncrement.java @@ -23,22 +23,24 @@ import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; +import org.apache.gravitino.exceptions.NoSuchColumnException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; import org.apache.gravitino.rel.TableChange; -/** Update the comment of a table. */ -public class UpdateTableComment extends Command { +/** Update the auto increment of a column. */ +public class UpdateColumnAutoIncrement extends Command { protected final String metalake; protected final String catalog; protected final String schema; protected final String table; - protected final String comment; + protected final String column; + protected final boolean autoincrement; /** - * Update the comment of a table. + * Update the auto increment of a column. * * @param url The URL of the Gravitino server. * @param ignoreVersions If true don't check the client/server versions match. @@ -46,32 +48,38 @@ public class UpdateTableComment extends Command { * @param catalog The name of the catalog. * @param schema The name of the schema. * @param table The name of the table. - * @param comment New metalake comment. + * @param column The name of the column. + * @param autoincrement True if the column can be null, false if it must have non-null values. */ - public UpdateTableComment( + public UpdateColumnAutoIncrement( String url, boolean ignoreVersions, String metalake, String catalog, String schema, String table, - String comment) { + String column, + boolean autoincrement) { super(url, ignoreVersions); this.metalake = metalake; this.catalog = catalog; this.schema = schema; this.table = table; - this.comment = comment; + this.column = column; + this.autoincrement = autoincrement; } - /** Update the comment of a table. */ + /** Update the auto increment of a column. */ + @Override public void handle() { + String[] columns = {column}; + try { - NameIdentifier tableName = NameIdentifier.of(schema, table); + NameIdentifier columnName = NameIdentifier.of(schema, table); GravitinoClient client = buildClient(metalake); - TableChange change = TableChange.updateComment(comment); + TableChange change = TableChange.updateColumnAutoIncrement(columns, autoincrement); - client.loadCatalog(catalog).asTableCatalog().alterTable(tableName, change); + client.loadCatalog(catalog).asTableCatalog().alterTable(columnName, change); } catch (NoSuchMetalakeException err) { System.err.println(ErrorMessages.UNKNOWN_METALAKE); return; @@ -84,11 +92,14 @@ public class UpdateTableComment extends Command { } catch (NoSuchTableException err) { System.err.println(ErrorMessages.UNKNOWN_TABLE); return; + } catch (NoSuchColumnException err) { + System.err.println(ErrorMessages.UNKNOWN_COLUMN); + return; } catch (Exception exp) { System.err.println(exp.getMessage()); return; } - System.out.println(table + " comment changed."); + System.out.println(column + " auto increment changed to " + autoincrement + "."); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnComment.java similarity index 76% copy from clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java copy to clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnComment.java index ff0cfa760..09d73ccb9 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnComment.java @@ -23,22 +23,24 @@ import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; +import org.apache.gravitino.exceptions.NoSuchColumnException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; import org.apache.gravitino.rel.TableChange; -/** Update the comment of a table. */ -public class UpdateTableComment extends Command { +/** Update the comment of a column. */ +public class UpdateColumnComment extends Command { protected final String metalake; protected final String catalog; protected final String schema; protected final String table; + protected final String column; protected final String comment; /** - * Update the comment of a table. + * Update the comment of a column. * * @param url The URL of the Gravitino server. * @param ignoreVersions If true don't check the client/server versions match. @@ -46,32 +48,38 @@ public class UpdateTableComment extends Command { * @param catalog The name of the catalog. * @param schema The name of the schema. * @param table The name of the table. - * @param comment New metalake comment. + * @param column The name of the column. + * @param comment New column comment. */ - public UpdateTableComment( + public UpdateColumnComment( String url, boolean ignoreVersions, String metalake, String catalog, String schema, String table, + String column, String comment) { super(url, ignoreVersions); this.metalake = metalake; this.catalog = catalog; this.schema = schema; this.table = table; + this.column = column; this.comment = comment; } - /** Update the comment of a table. */ + /** Update the comment of a column. */ + @Override public void handle() { + String[] columns = {column}; + try { - NameIdentifier tableName = NameIdentifier.of(schema, table); + NameIdentifier columnName = NameIdentifier.of(schema, table); GravitinoClient client = buildClient(metalake); - TableChange change = TableChange.updateComment(comment); + TableChange change = TableChange.updateColumnComment(columns, comment); - client.loadCatalog(catalog).asTableCatalog().alterTable(tableName, change); + client.loadCatalog(catalog).asTableCatalog().alterTable(columnName, change); } catch (NoSuchMetalakeException err) { System.err.println(ErrorMessages.UNKNOWN_METALAKE); return; @@ -84,11 +92,14 @@ public class UpdateTableComment extends Command { } catch (NoSuchTableException err) { System.err.println(ErrorMessages.UNKNOWN_TABLE); return; + } catch (NoSuchColumnException err) { + System.err.println(ErrorMessages.UNKNOWN_COLUMN); + return; } catch (Exception exp) { System.err.println(exp.getMessage()); return; } - System.out.println(table + " comment changed."); + System.out.println(column + " comment changed."); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnDatatype.java similarity index 70% copy from clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java copy to clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnDatatype.java index ff0cfa760..6a40be953 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnDatatype.java @@ -21,24 +21,28 @@ package org.apache.gravitino.cli.commands; import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.cli.ErrorMessages; +import org.apache.gravitino.cli.ParseType; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; +import org.apache.gravitino.exceptions.NoSuchColumnException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; import org.apache.gravitino.rel.TableChange; +import org.apache.gravitino.rel.types.Type; -/** Update the comment of a table. */ -public class UpdateTableComment extends Command { +/** Update the data type of a column. */ +public class UpdateColumnDatatype extends Command { protected final String metalake; protected final String catalog; protected final String schema; protected final String table; - protected final String comment; + protected final String column; + protected final String datatype; /** - * Update the comment of a table. + * Update the data type of a column. * * @param url The URL of the Gravitino server. * @param ignoreVersions If true don't check the client/server versions match. @@ -46,32 +50,39 @@ public class UpdateTableComment extends Command { * @param catalog The name of the catalog. * @param schema The name of the schema. * @param table The name of the table. - * @param comment New metalake comment. + * @param column The name of the column. + * @param datatype The new data type name. */ - public UpdateTableComment( + public UpdateColumnDatatype( String url, boolean ignoreVersions, String metalake, String catalog, String schema, String table, - String comment) { + String column, + String datatype) { super(url, ignoreVersions); this.metalake = metalake; this.catalog = catalog; this.schema = schema; this.table = table; - this.comment = comment; + this.column = column; + this.datatype = datatype; } - /** Update the comment of a table. */ + /** Update the data type of a column. */ + @Override public void handle() { + String[] columns = {column}; + Type convertedDatatype = ParseType.toType(datatype); + try { - NameIdentifier tableName = NameIdentifier.of(schema, table); + NameIdentifier columnName = NameIdentifier.of(schema, table); GravitinoClient client = buildClient(metalake); - TableChange change = TableChange.updateComment(comment); + TableChange change = TableChange.updateColumnType(columns, convertedDatatype); - client.loadCatalog(catalog).asTableCatalog().alterTable(tableName, change); + client.loadCatalog(catalog).asTableCatalog().alterTable(columnName, change); } catch (NoSuchMetalakeException err) { System.err.println(ErrorMessages.UNKNOWN_METALAKE); return; @@ -84,11 +95,14 @@ public class UpdateTableComment extends Command { } catch (NoSuchTableException err) { System.err.println(ErrorMessages.UNKNOWN_TABLE); return; + } catch (NoSuchColumnException err) { + System.err.println(ErrorMessages.UNKNOWN_COLUMN); + return; } catch (Exception exp) { System.err.println(exp.getMessage()); return; } - System.out.println(table + " comment changed."); + System.out.println(column + " datatype changed to " + datatype + "."); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnDefault.java similarity index 68% copy from clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java copy to clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnDefault.java index ff0cfa760..de5a00c1f 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnDefault.java @@ -20,25 +20,29 @@ package org.apache.gravitino.cli.commands; import org.apache.gravitino.NameIdentifier; +import org.apache.gravitino.cli.DefaultConverter; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; +import org.apache.gravitino.exceptions.NoSuchColumnException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; import org.apache.gravitino.rel.TableChange; -/** Update the comment of a table. */ -public class UpdateTableComment extends Command { +/** Update the default type of a column. */ +public class UpdateColumnDefault extends Command { protected final String metalake; protected final String catalog; protected final String schema; protected final String table; - protected final String comment; + protected final String column; + protected final String defaultValue; + protected final String dataType; /** - * Update the comment of a table. + * Update the default type of a column. * * @param url The URL of the Gravitino server. * @param ignoreVersions If true don't check the client/server versions match. @@ -46,32 +50,43 @@ public class UpdateTableComment extends Command { * @param catalog The name of the catalog. * @param schema The name of the schema. * @param table The name of the table. - * @param comment New metalake comment. + * @param column The name of the column. + * @param defaultValue The new default value. + * @param dataType The new default data type. */ - public UpdateTableComment( + public UpdateColumnDefault( String url, boolean ignoreVersions, String metalake, String catalog, String schema, String table, - String comment) { + String column, + String defaultValue, + String dataType) { super(url, ignoreVersions); this.metalake = metalake; this.catalog = catalog; this.schema = schema; this.table = table; - this.comment = comment; + this.column = column; + this.defaultValue = defaultValue; + this.dataType = dataType; } - /** Update the comment of a table. */ + /** Update the default type of a column. */ + @Override public void handle() { + String[] columns = {column}; + try { - NameIdentifier tableName = NameIdentifier.of(schema, table); + NameIdentifier columnName = NameIdentifier.of(schema, table); GravitinoClient client = buildClient(metalake); - TableChange change = TableChange.updateComment(comment); + TableChange change = + TableChange.updateColumnDefaultValue( + columns, DefaultConverter.convert(defaultValue, dataType)); - client.loadCatalog(catalog).asTableCatalog().alterTable(tableName, change); + client.loadCatalog(catalog).asTableCatalog().alterTable(columnName, change); } catch (NoSuchMetalakeException err) { System.err.println(ErrorMessages.UNKNOWN_METALAKE); return; @@ -84,11 +99,14 @@ public class UpdateTableComment extends Command { } catch (NoSuchTableException err) { System.err.println(ErrorMessages.UNKNOWN_TABLE); return; + } catch (NoSuchColumnException err) { + System.err.println(ErrorMessages.UNKNOWN_COLUMN); + return; } catch (Exception exp) { System.err.println(exp.getMessage()); return; } - System.out.println(table + " comment changed."); + System.out.println(column + " default changed."); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableName.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnName.java similarity index 76% copy from clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableName.java copy to clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnName.java index e2f4fae61..f03d0ce8d 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableName.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnName.java @@ -23,22 +23,24 @@ import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; +import org.apache.gravitino.exceptions.NoSuchColumnException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; import org.apache.gravitino.rel.TableChange; -/** Update the name of a table. */ -public class UpdateTableName extends Command { +/** Update the name of a column. */ +public class UpdateColumnName extends Command { protected final String metalake; protected final String catalog; protected final String schema; protected final String table; + protected final String column; protected final String name; /** - * Update the name of a table. + * Update the name of a column. * * @param url The URL of the Gravitino server. * @param ignoreVersions If true don't check the client/server versions match. @@ -46,15 +48,17 @@ public class UpdateTableName extends Command { * @param catalog The name of the catalog. * @param schema The name of the schema. * @param table The name of the table. - * @param name The new metalake name. + * @param column The name of the column. + * @param name The new column name. */ - public UpdateTableName( + public UpdateColumnName( String url, boolean ignoreVersions, String metalake, String catalog, String schema, String table, + String column, String name) { super(url, ignoreVersions); this.metalake = metalake; @@ -62,16 +66,20 @@ public class UpdateTableName extends Command { this.schema = schema; this.table = table; this.name = name; + this.column = column; } - /** Update the name of a table. */ + /** Update the name of a column. */ + @Override public void handle() { + String[] columns = {column}; + try { - NameIdentifier tableName = NameIdentifier.of(schema, table); + NameIdentifier columnName = NameIdentifier.of(schema, table); GravitinoClient client = buildClient(metalake); - TableChange change = TableChange.rename(name); + TableChange change = TableChange.renameColumn(columns, name); - client.loadCatalog(catalog).asTableCatalog().alterTable(tableName, change); + client.loadCatalog(catalog).asTableCatalog().alterTable(columnName, change); } catch (NoSuchMetalakeException err) { System.err.println(ErrorMessages.UNKNOWN_METALAKE); return; @@ -84,11 +92,14 @@ public class UpdateTableName extends Command { } catch (NoSuchTableException err) { System.err.println(ErrorMessages.UNKNOWN_TABLE); return; + } catch (NoSuchColumnException err) { + System.err.println(ErrorMessages.UNKNOWN_COLUMN); + return; } catch (Exception exp) { System.err.println(exp.getMessage()); return; } - System.out.println(table + " name changed."); + System.out.println(column + " name changed."); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnNullability.java similarity index 71% copy from clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java copy to clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnNullability.java index ff0cfa760..88f3634e0 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnNullability.java @@ -23,22 +23,24 @@ import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.cli.ErrorMessages; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; +import org.apache.gravitino.exceptions.NoSuchColumnException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; import org.apache.gravitino.rel.TableChange; -/** Update the comment of a table. */ -public class UpdateTableComment extends Command { +/** Update the nullability of a column. */ +public class UpdateColumnNullability extends Command { protected final String metalake; protected final String catalog; protected final String schema; protected final String table; - protected final String comment; + protected final String column; + protected final boolean nullability; /** - * Update the comment of a table. + * Update the nullability of a column. * * @param url The URL of the Gravitino server. * @param ignoreVersions If true don't check the client/server versions match. @@ -46,32 +48,38 @@ public class UpdateTableComment extends Command { * @param catalog The name of the catalog. * @param schema The name of the schema. * @param table The name of the table. - * @param comment New metalake comment. + * @param column The name of the column. + * @param nullability True if the column can be null, false if it must have non-null values. */ - public UpdateTableComment( + public UpdateColumnNullability( String url, boolean ignoreVersions, String metalake, String catalog, String schema, String table, - String comment) { + String column, + boolean nullability) { super(url, ignoreVersions); this.metalake = metalake; this.catalog = catalog; this.schema = schema; this.table = table; - this.comment = comment; + this.column = column; + this.nullability = nullability; } - /** Update the comment of a table. */ + /** Update the nullability of a column. */ + @Override public void handle() { + String[] columns = {column}; + try { - NameIdentifier tableName = NameIdentifier.of(schema, table); + NameIdentifier columnName = NameIdentifier.of(schema, table); GravitinoClient client = buildClient(metalake); - TableChange change = TableChange.updateComment(comment); + TableChange change = TableChange.updateColumnNullability(columns, nullability); - client.loadCatalog(catalog).asTableCatalog().alterTable(tableName, change); + client.loadCatalog(catalog).asTableCatalog().alterTable(columnName, change); } catch (NoSuchMetalakeException err) { System.err.println(ErrorMessages.UNKNOWN_METALAKE); return; @@ -84,11 +92,14 @@ public class UpdateTableComment extends Command { } catch (NoSuchTableException err) { System.err.println(ErrorMessages.UNKNOWN_TABLE); return; + } catch (NoSuchColumnException err) { + System.err.println(ErrorMessages.UNKNOWN_COLUMN); + return; } catch (Exception exp) { System.err.println(exp.getMessage()); return; } - System.out.println(table + " comment changed."); + System.out.println(column + " nullability changed to " + nullability + "."); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnPosition.java similarity index 71% copy from clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java copy to clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnPosition.java index ff0cfa760..460fe6ed1 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateColumnPosition.java @@ -21,24 +21,27 @@ package org.apache.gravitino.cli.commands; import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.cli.ErrorMessages; +import org.apache.gravitino.cli.PositionConverter; import org.apache.gravitino.client.GravitinoClient; import org.apache.gravitino.exceptions.NoSuchCatalogException; +import org.apache.gravitino.exceptions.NoSuchColumnException; import org.apache.gravitino.exceptions.NoSuchMetalakeException; import org.apache.gravitino.exceptions.NoSuchSchemaException; import org.apache.gravitino.exceptions.NoSuchTableException; import org.apache.gravitino.rel.TableChange; -/** Update the comment of a table. */ -public class UpdateTableComment extends Command { +/** Update the position of a column. */ +public class UpdateColumnPosition extends Command { protected final String metalake; protected final String catalog; protected final String schema; protected final String table; - protected final String comment; + protected final String column; + protected final String position; /** - * Update the comment of a table. + * Update the position of a column. * * @param url The URL of the Gravitino server. * @param ignoreVersions If true don't check the client/server versions match. @@ -46,32 +49,40 @@ public class UpdateTableComment extends Command { * @param catalog The name of the catalog. * @param schema The name of the schema. * @param table The name of the table. - * @param comment New metalake comment. + * @param column The name of the column. + * @param position The new position of the column. */ - public UpdateTableComment( + public UpdateColumnPosition( String url, boolean ignoreVersions, String metalake, String catalog, String schema, String table, - String comment) { + String column, + String position) { super(url, ignoreVersions); this.metalake = metalake; this.catalog = catalog; this.schema = schema; this.table = table; - this.comment = comment; + this.column = column; + this.position = position; } - /** Update the comment of a table. */ + /** Update the position of a column. */ + @Override public void handle() { + String[] columns = {column}; + try { - NameIdentifier tableName = NameIdentifier.of(schema, table); + NameIdentifier columnName = NameIdentifier.of(schema, table); GravitinoClient client = buildClient(metalake); - TableChange change = TableChange.updateComment(comment); - client.loadCatalog(catalog).asTableCatalog().alterTable(tableName, change); + TableChange change = + TableChange.updateColumnPosition(columns, PositionConverter.convert(position)); + + client.loadCatalog(catalog).asTableCatalog().alterTable(columnName, change); } catch (NoSuchMetalakeException err) { System.err.println(ErrorMessages.UNKNOWN_METALAKE); return; @@ -84,11 +95,14 @@ public class UpdateTableComment extends Command { } catch (NoSuchTableException err) { System.err.println(ErrorMessages.UNKNOWN_TABLE); return; + } catch (NoSuchColumnException err) { + System.err.println(ErrorMessages.UNKNOWN_COLUMN); + return; } catch (Exception exp) { System.err.println(exp.getMessage()); return; } - System.out.println(table + " comment changed."); + System.out.println(column + " position changed to " + position + "."); } } diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java index ff0cfa760..3edd73940 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableComment.java @@ -65,6 +65,7 @@ public class UpdateTableComment extends Command { } /** Update the comment of a table. */ + @Override public void handle() { try { NameIdentifier tableName = NameIdentifier.of(schema, table); diff --git a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableName.java b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableName.java index e2f4fae61..2346ee4ab 100644 --- a/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableName.java +++ b/clients/cli/src/main/java/org/apache/gravitino/cli/commands/UpdateTableName.java @@ -65,6 +65,7 @@ public class UpdateTableName extends Command { } /** Update the name of a table. */ + @Override public void handle() { try { NameIdentifier tableName = NameIdentifier.of(schema, table); diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestColumnCommands.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestColumnCommands.java index b7e091ebd..647e85353 100644 --- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestColumnCommands.java +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestColumnCommands.java @@ -27,7 +27,16 @@ import static org.mockito.Mockito.when; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; +import org.apache.gravitino.cli.commands.AddColumn; +import org.apache.gravitino.cli.commands.DeleteColumn; import org.apache.gravitino.cli.commands.ListColumns; +import org.apache.gravitino.cli.commands.UpdateColumnAutoIncrement; +import org.apache.gravitino.cli.commands.UpdateColumnComment; +import org.apache.gravitino.cli.commands.UpdateColumnDatatype; +import org.apache.gravitino.cli.commands.UpdateColumnDefault; +import org.apache.gravitino.cli.commands.UpdateColumnName; +import org.apache.gravitino.cli.commands.UpdateColumnNullability; +import org.apache.gravitino.cli.commands.UpdateColumnPosition; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -60,4 +69,296 @@ class TestColumnCommands { commandLine.handleCommandLine(); verify(mockList).handle(); } + + @Test + void testAddColumn() { + AddColumn mockAddColumn = mock(AddColumn.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)) + .thenReturn("catalog.schema.users.name"); + when(mockCommandLine.hasOption(GravitinoOptions.DATATYPE)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.DATATYPE)).thenReturn("varchar(100)"); + when(mockCommandLine.hasOption(GravitinoOptions.COMMENT)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.COMMENT)).thenReturn("comment"); + when(mockCommandLine.getOptionValue(GravitinoOptions.POSITION)).thenReturn(null); + when(mockCommandLine.getOptionValue(GravitinoOptions.DEFAULT)).thenReturn(null); + + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.COLUMN, CommandActions.CREATE)); + doReturn(mockAddColumn) + .when(commandLine) + .newAddColumn( + GravitinoCommandLine.DEFAULT_URL, + false, + "metalake_demo", + "catalog", + "schema", + "users", + "name", + "varchar(100)", + "comment", + null, + true, + false, + null); + commandLine.handleCommandLine(); + verify(mockAddColumn).handle(); + } + + @Test + void testDeleteColumn() { + DeleteColumn mockDelete = mock(DeleteColumn.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)) + .thenReturn("catalog.schema.users.name"); + + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.COLUMN, CommandActions.DELETE)); + doReturn(mockDelete) + .when(commandLine) + .newDeleteColumn( + GravitinoCommandLine.DEFAULT_URL, + false, + "metalake_demo", + "catalog", + "schema", + "users", + "name"); + commandLine.handleCommandLine(); + verify(mockDelete).handle(); + } + + @Test + void testUpdateColumnComment() { + UpdateColumnComment mockUpdateColumn = mock(UpdateColumnComment.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)) + .thenReturn("catalog.schema.users.name"); + when(mockCommandLine.hasOption(GravitinoOptions.COMMENT)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.COMMENT)).thenReturn("new comment"); + when(mockCommandLine.hasOption(GravitinoOptions.NULL)).thenReturn(false); + when(mockCommandLine.hasOption(GravitinoOptions.AUTO)).thenReturn(false); + + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.COLUMN, CommandActions.UPDATE)); + doReturn(mockUpdateColumn) + .when(commandLine) + .newUpdateColumnComment( + GravitinoCommandLine.DEFAULT_URL, + false, + "metalake_demo", + "catalog", + "schema", + "users", + "name", + "new comment"); + commandLine.handleCommandLine(); + verify(mockUpdateColumn).handle(); + } + + @Test + void testUpdateColumnName() { + UpdateColumnName mockUpdateName = mock(UpdateColumnName.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)) + .thenReturn("catalog.schema.users.name"); + when(mockCommandLine.hasOption(GravitinoOptions.RENAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.RENAME)).thenReturn("renamed"); + when(mockCommandLine.hasOption(GravitinoOptions.NULL)).thenReturn(false); + when(mockCommandLine.hasOption(GravitinoOptions.AUTO)).thenReturn(false); + + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.COLUMN, CommandActions.UPDATE)); + doReturn(mockUpdateName) + .when(commandLine) + .newUpdateColumnName( + GravitinoCommandLine.DEFAULT_URL, + false, + "metalake_demo", + "catalog", + "schema", + "users", + "name", + "renamed"); + commandLine.handleCommandLine(); + verify(mockUpdateName).handle(); + } + + @Test + void testUpdateColumnDatatype() { + UpdateColumnDatatype mockUpdateDatatype = mock(UpdateColumnDatatype.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)) + .thenReturn("catalog.schema.users.name"); + when(mockCommandLine.hasOption(GravitinoOptions.DATATYPE)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.DATATYPE)).thenReturn("varchar(250)"); + when(mockCommandLine.hasOption(GravitinoOptions.NULL)).thenReturn(false); + when(mockCommandLine.hasOption(GravitinoOptions.AUTO)).thenReturn(false); + + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.COLUMN, CommandActions.UPDATE)); + doReturn(mockUpdateDatatype) + .when(commandLine) + .newUpdateColumnDatatype( + GravitinoCommandLine.DEFAULT_URL, + false, + "metalake_demo", + "catalog", + "schema", + "users", + "name", + "varchar(250)"); + commandLine.handleCommandLine(); + verify(mockUpdateDatatype).handle(); + } + + @Test + void testUpdateColumnPosition() { + UpdateColumnPosition mockUpdatePosition = mock(UpdateColumnPosition.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)) + .thenReturn("catalog.schema.users.name"); + when(mockCommandLine.hasOption(GravitinoOptions.POSITION)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.POSITION)).thenReturn("first"); + when(mockCommandLine.hasOption(GravitinoOptions.NULL)).thenReturn(false); + when(mockCommandLine.hasOption(GravitinoOptions.AUTO)).thenReturn(false); + + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.COLUMN, CommandActions.UPDATE)); + doReturn(mockUpdatePosition) + .when(commandLine) + .newUpdateColumnPosition( + GravitinoCommandLine.DEFAULT_URL, + false, + "metalake_demo", + "catalog", + "schema", + "users", + "name", + "first"); + commandLine.handleCommandLine(); + verify(mockUpdatePosition).handle(); + } + + @Test + void testUpdateColumnNullability() { + UpdateColumnNullability mockUpdateNull = mock(UpdateColumnNullability.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)) + .thenReturn("catalog.schema.users.name"); + when(mockCommandLine.hasOption(GravitinoOptions.NULL)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NULL)).thenReturn("true"); + when(mockCommandLine.hasOption(GravitinoOptions.AUTO)).thenReturn(false); + + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.COLUMN, CommandActions.UPDATE)); + doReturn(mockUpdateNull) + .when(commandLine) + .newUpdateColumnNullability( + GravitinoCommandLine.DEFAULT_URL, + false, + "metalake_demo", + "catalog", + "schema", + "users", + "name", + true); + commandLine.handleCommandLine(); + verify(mockUpdateNull).handle(); + } + + @Test + void testUpdateColumnAutoIncrement() { + UpdateColumnAutoIncrement mockUpdateAuto = mock(UpdateColumnAutoIncrement.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)) + .thenReturn("catalog.schema.users.name"); + when(mockCommandLine.hasOption(GravitinoOptions.AUTO)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.AUTO)).thenReturn("true"); + when(mockCommandLine.hasOption(GravitinoOptions.NULL)).thenReturn(false); + + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.COLUMN, CommandActions.UPDATE)); + doReturn(mockUpdateAuto) + .when(commandLine) + .newUpdateColumnAutoIncrement( + GravitinoCommandLine.DEFAULT_URL, + false, + "metalake_demo", + "catalog", + "schema", + "users", + "name", + true); + commandLine.handleCommandLine(); + verify(mockUpdateAuto).handle(); + } + + @Test + void testUpdateColumnDefault() { + UpdateColumnDefault mockUpdateDefault = mock(UpdateColumnDefault.class); + when(mockCommandLine.hasOption(GravitinoOptions.METALAKE)).thenReturn(true); + when(mockCommandLine.getOptionValue(CommandEntities.METALAKE)).thenReturn("metalake_demo"); + when(mockCommandLine.hasOption(GravitinoOptions.NAME)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.NAME)) + .thenReturn("catalog.schema.users.name"); + when(mockCommandLine.hasOption(GravitinoOptions.DEFAULT)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.DEFAULT)).thenReturn("Fred Smith"); + when(mockCommandLine.hasOption(GravitinoOptions.DATATYPE)).thenReturn(true); + when(mockCommandLine.getOptionValue(GravitinoOptions.DATATYPE)).thenReturn("varchar(100)"); + + when(mockCommandLine.hasOption(GravitinoOptions.NULL)).thenReturn(false); + when(mockCommandLine.hasOption(GravitinoOptions.AUTO)).thenReturn(false); + + GravitinoCommandLine commandLine = + spy( + new GravitinoCommandLine( + mockCommandLine, mockOptions, CommandEntities.COLUMN, CommandActions.UPDATE)); + doReturn(mockUpdateDefault) + .when(commandLine) + .newUpdateColumnDefault( + GravitinoCommandLine.DEFAULT_URL, + false, + "metalake_demo", + "catalog", + "schema", + "users", + "name", + "Fred Smith", + "varchar(100)"); + commandLine.handleCommandLine(); + verify(mockUpdateDefault).handle(); + } } diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestDefaultConverter.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestDefaultConverter.java new file mode 100644 index 000000000..7aa20bd37 --- /dev/null +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestDefaultConverter.java @@ -0,0 +1,76 @@ +/* + * 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.cli; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.gravitino.rel.Column; +import org.apache.gravitino.rel.expressions.Expression; +import org.apache.gravitino.rel.expressions.literals.Literals; +import org.junit.jupiter.api.Test; + +class TestDefaultConverter { + + @Test + void testConvertNullDefaultValue() { + String defaultValue = null; + String dataType = "INTEGER"; + Expression result = DefaultConverter.convert(defaultValue, dataType); + + assertEquals( + Column.DEFAULT_VALUE_NOT_SET, + result, + "Expected DEFAULT_VALUE_NOT_SET for null defaultValue."); + } + + @Test + void testConvertEmptyDefaultValue() { + String defaultValue = ""; + String dataType = "INTEGER"; + Expression result = DefaultConverter.convert(defaultValue, dataType); + + assertEquals( + Column.DEFAULT_VALUE_NOT_SET, + result, + "Expected DEFAULT_VALUE_NOT_SET for empty defaultValue."); + } + + @Test + void testConvertCurrentTimestamp() { + String defaultValue = "current_timestamp"; + String dataType = "TIMESTAMP"; + Expression result = DefaultConverter.convert(defaultValue, dataType); + + assertEquals( + Column.DEFAULT_VALUE_OF_CURRENT_TIMESTAMP, + result, + "Expected DEFAULT_VALUE_OF_CURRENT_TIMESTAMP for 'current_timestamp' defaultValue."); + } + + @Test + void testConvertLiteralValue() { + String defaultValue = "42"; + String dataType = "INTEGER"; + Expression result = DefaultConverter.convert(defaultValue, dataType); + Expression expected = Literals.of(defaultValue, ParseType.toType(dataType)); + + assertEquals(expected, result, "Expected literal expression for non-special default value."); + } +} diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestFulllName.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestFulllName.java index 05e78b4c8..ecde923a3 100644 --- a/clients/cli/src/test/java/org/apache/gravitino/cli/TestFulllName.java +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestFulllName.java @@ -43,7 +43,7 @@ public class TestFulllName { @Test public void entityFromFullNameOption() throws Exception { - String[] args = {"--metalake", "metalakeA", "--name", "catalogB.schemaC.tableD"}; + String[] args = {"--metalake", "metalakeA", "--name", "catalogB.schemaC.tableD.columnE"}; CommandLine commandLine = new DefaultParser().parse(options, args); FullName fullName = new FullName(commandLine); @@ -55,6 +55,8 @@ public class TestFulllName { assertEquals("schemaC", schemaName); String tableName = fullName.getTableName(); assertEquals("tableD", tableName); + String columnName = fullName.getColumnName(); + assertEquals("columnE", columnName); } @Test @@ -100,6 +102,7 @@ public class TestFulllName { assertFalse(fullName.hasCatalogName()); assertFalse(fullName.hasSchemaName()); assertFalse(fullName.hasTableName()); + assertFalse(fullName.hasColumnName()); } @Test @@ -110,6 +113,7 @@ public class TestFulllName { assertTrue(fullName.hasCatalogName()); assertFalse(fullName.hasSchemaName()); assertFalse(fullName.hasTableName()); + assertFalse(fullName.hasColumnName()); } @Test @@ -120,6 +124,7 @@ public class TestFulllName { assertTrue(fullName.hasCatalogName()); assertTrue(fullName.hasSchemaName()); assertFalse(fullName.hasTableName()); + assertFalse(fullName.hasColumnName()); } @Test @@ -132,5 +137,19 @@ public class TestFulllName { assertTrue(fullName.hasCatalogName()); assertTrue(fullName.hasSchemaName()); assertTrue(fullName.hasTableName()); + assertFalse(fullName.hasColumnName()); + } + + @Test + public void hasPartNameColumn() throws Exception { + String[] args = { + "table", "details", "--metalake", "metalake", "--name", "catalog.schema.table.column" + }; + CommandLine commandLine = new DefaultParser().parse(options, args); + FullName fullName = new FullName(commandLine); + assertTrue(fullName.hasCatalogName()); + assertTrue(fullName.hasSchemaName()); + assertTrue(fullName.hasTableName()); + assertTrue(fullName.hasColumnName()); } } diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestParseType.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestParseType.java new file mode 100644 index 000000000..c53d3c2bd --- /dev/null +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestParseType.java @@ -0,0 +1,67 @@ +/* + * 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.cli; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +public class TestParseType { + + @Test + public void testParseVarcharWithLength() { + ParsedType parsed = ParseType.parse("varchar(10)"); + assertNotNull(parsed); + assertEquals("varchar", parsed.getTypeName()); + assertEquals(10, parsed.getLength()); + assertNull(parsed.getScale()); + assertNull(parsed.getPrecision()); + } + + @Test + public void testParseDecimalWithPrecisionAndScale() { + ParsedType parsed = ParseType.parse("decimal(10,5)"); + assertNotNull(parsed); + assertEquals("decimal", parsed.getTypeName()); + assertEquals(10, parsed.getPrecision()); + assertEquals(5, parsed.getScale()); + assertNull(parsed.getLength()); + } + + @Test + public void testParseIntegerWithoutParameters() { + ParsedType parsed = ParseType.parse("int()"); + assertNull(parsed); // Expect null because the format is unsupported + } + + @Test + public void testParseOrdinaryInput() { + assertNull(ParseType.parse("string")); + assertNull(ParseType.parse("int")); + } + + @Test + public void testParseMalformedInput() { + assertNull(ParseType.parse("varchar(-10)")); + assertNull(ParseType.parse("decimal(10,abc)")); + } +} diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestPositionConverter.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestPositionConverter.java new file mode 100644 index 000000000..1fc55097e --- /dev/null +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestPositionConverter.java @@ -0,0 +1,82 @@ +/* + * 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.cli; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.apache.gravitino.rel.TableChange; +import org.junit.jupiter.api.Test; + +public class TestPositionConverter { + + @Test + public void testConvertFirstPosition() { + TableChange.ColumnPosition position = PositionConverter.convert("first"); + assertNotNull(position); + assertEquals(TableChange.ColumnPosition.first(), position); + } + + @Test + public void testConvertNullPosition() { + TableChange.ColumnPosition position = PositionConverter.convert(null); + assertNotNull(position); + assertEquals(TableChange.ColumnPosition.defaultPos(), position); + } + + @Test + public void testConvertEmptyPosition() { + TableChange.ColumnPosition position = PositionConverter.convert(""); + assertNotNull(position); + assertEquals(TableChange.ColumnPosition.defaultPos(), position); + } + + @Test + public void testConvertValidColumnName() { + String columnName = "column1"; + TableChange.ColumnPosition position = PositionConverter.convert(columnName); + assertNotNull(position); + assertEquals(TableChange.ColumnPosition.after(columnName), position); + } + + @Test + public void testConvertCaseInsensitiveColumnName() { + String columnName = "COLUMN2"; + TableChange.ColumnPosition position = PositionConverter.convert(columnName); + assertNotNull(position); + assertEquals(TableChange.ColumnPosition.after(columnName), position); + } + + @Test + public void testConvertWhitespaceColumnName() { + String columnName = " column3 "; + TableChange.ColumnPosition position = PositionConverter.convert(columnName.trim()); + assertNotNull(position); + assertEquals(TableChange.ColumnPosition.after("column3"), position); + } + + @Test + public void testConvertUnrecognizedPosition() { + String unrecognized = "unrecognized"; + TableChange.ColumnPosition position = PositionConverter.convert(unrecognized); + assertNotNull(position); + assertEquals(TableChange.ColumnPosition.after(unrecognized), position); + } +} diff --git a/clients/cli/src/test/java/org/apache/gravitino/cli/TestTypeConverter.java b/clients/cli/src/test/java/org/apache/gravitino/cli/TestTypeConverter.java new file mode 100644 index 000000000..a4145b294 --- /dev/null +++ b/clients/cli/src/test/java/org/apache/gravitino/cli/TestTypeConverter.java @@ -0,0 +1,99 @@ +/* + * 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.cli; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.gravitino.rel.types.Types; +import org.junit.jupiter.api.Test; + +public class TestTypeConverter { + + @Test + public void testConvertBasicTypes() { + assertEquals(Types.NullType.get(), TypeConverter.convert("null")); + assertEquals(Types.BooleanType.get(), TypeConverter.convert("boolean")); + assertEquals(Types.ByteType.get(), TypeConverter.convert("byte")); + assertEquals(Types.ByteType.unsigned(), TypeConverter.convert("ubyte")); + assertEquals(Types.ShortType.get(), TypeConverter.convert("short")); + assertEquals(Types.ShortType.unsigned(), TypeConverter.convert("ushort")); + assertEquals(Types.IntegerType.get(), TypeConverter.convert("integer")); + assertEquals(Types.IntegerType.unsigned(), TypeConverter.convert("uinteger")); + assertEquals(Types.LongType.get(), TypeConverter.convert("long")); + assertEquals(Types.LongType.unsigned(), TypeConverter.convert("ulong")); + assertEquals(Types.FloatType.get(), TypeConverter.convert("float")); + assertEquals(Types.DoubleType.get(), TypeConverter.convert("double")); + assertEquals(Types.DateType.get(), TypeConverter.convert("date")); + assertEquals(Types.TimeType.get(), TypeConverter.convert("time")); + assertEquals(Types.TimestampType.withoutTimeZone(), TypeConverter.convert("timestamp")); + assertEquals(Types.TimestampType.withTimeZone(), TypeConverter.convert("tztimestamp")); + assertEquals(Types.IntervalYearType.get(), TypeConverter.convert("intervalyear")); + assertEquals(Types.IntervalDayType.get(), TypeConverter.convert("intervalday")); + assertEquals(Types.UUIDType.get(), TypeConverter.convert("uuid")); + assertEquals(Types.StringType.get(), TypeConverter.convert("string")); + assertEquals(Types.BinaryType.get(), TypeConverter.convert("binary")); + } + + @Test + public void testConvertBasicTypesCaseInsensitive() { + assertEquals(Types.BooleanType.get(), TypeConverter.convert("BOOLEAN")); + assertEquals(Types.StringType.get(), TypeConverter.convert("STRING")); + } + + @Test + public void testConvertUnsupportedType() { + assertThrows( + IllegalArgumentException.class, + () -> { + TypeConverter.convert("unsupportedType"); + }); + } + + @Test + public void testConvertFixedVarcharAndChar() { + assertEquals(Types.FixedType.of(10), TypeConverter.convert("fixed", 10)); + assertEquals(Types.VarCharType.of(20), TypeConverter.convert("varchar", 20)); + assertEquals(Types.FixedCharType.of(30), TypeConverter.convert("char", 30)); + } + + @Test + public void testConvertFixedVarcharAndCharUnsupportedType() { + assertThrows( + IllegalArgumentException.class, + () -> { + TypeConverter.convert("unsupportedType", 10); + }); + } + + @Test + public void testConvertDecimal() { + assertEquals(Types.DecimalType.of(10, 5), TypeConverter.convert("decimal", 10, 5)); + } + + @Test + public void testConvertDecimalUnsupportedType() { + assertThrows( + IllegalArgumentException.class, + () -> { + TypeConverter.convert("unsupportedType", 10, 5); + }); + } +} diff --git a/docs/cli.md b/docs/cli.md index ff4f94ab9..211e16bd5 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -29,10 +29,13 @@ The general structure for running commands with the Gravitino CLI is `gcli entit ```bash usage: gcli [metalake|catalog|schema|table|column|user|group|tag|topic|fileset] [list|details|create|delete|update|set|remove|properties|revoke|grant] [options] Options - [options] + usage: gcli -a,--audit display audit information + --auto <arg> column value auto-increments (true/false) -c,--comment <arg> entity comment -d,--distribution display distribution information + --datatype <arg> column data type + --default <arg> default column value -f,--force force operation -g,--group <arg> group name -h,--help command help information @@ -40,10 +43,13 @@ The general structure for running commands with the Gravitino CLI is `gcli entit -l,--user <arg> user name -m,--metalake <arg> metalake name -n,--name <arg> full entity name (dot separated) + --null <arg> column value can be null (true/false) -o,--owner display entity owner + --output <arg> output format (plain/table) -P,--property <arg> property name -p,--properties <arg> property name/value pairs --partition display partition information + --position <arg> position of column -r,--role <arg> role name --rename <arg> new entity name -s,--server Gravitino server version @@ -721,3 +727,36 @@ gcli fileset set --name hadoop.schema.fileset --property test --value value ```bash gcli fileset remove --name hadoop.schema.fileset --property test ``` + +### column commands + +Note that some commands are not supported depending on what the database supports. + +When setting the datatype of a column the following basic types are currently supported: +null, boolean, byte, ubyte, short, ushort, integer, uinteger, long, ulong, float, double, date, time, timestamp, tztimestamp, intervalyear, intervalday, uuid, string, binary + +In addition decimal(precision,scale) and varchar(length). + +#### Add a column + +```bash +gcli column create --name catalog_postgres.hr.departments.value --datatype long +gcli column create --name catalog_postgres.hr.departments.money --datatype "decimal(10,2)" +gcli column create --name catalog_postgres.hr.departments.name --datatype "varchar(100)" +gcli column create --name catalog_postgres.hr.departments.fullname --datatype "varchar(250)" --default "Fred Smith" --null=false +``` + +#### Delete a column + +```bash +gcli column delete --name catalog_postgres.hr.departments.money +``` + +#### Update a column + +```bash +gcli column update --name catalog_postgres.hr.departments.value --rename values +gcli column update --name catalog_postgres.hr.departments.values --datatype "varchar(500)" +gcli column update --name catalog_postgres.hr.departments.values --position name +gcli column update --name catalog_postgres.hr.departments.name --null=true +``` \ No newline at end of file
