Skip to content

Commit

Permalink
[#5383] Add column commands to the Gravitino CLI (#5716)
Browse files Browse the repository at this point in the history
### 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.
  • Loading branch information
justinmclean authored Dec 3, 2024
1 parent 0e4a0dc commit 758cd2e
Show file tree
Hide file tree
Showing 28 changed files with 2,328 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.";
Expand Down
20 changes: 19 additions & 1 deletion clients/cli/src/main/java/org/apache/gravitino/cli/FullName.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,21 @@ public String getTopicName() {
/**
* 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.
*
Expand Down Expand Up @@ -193,4 +202,13 @@ public boolean hasSchemaName() {
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -487,9 +487,89 @@ private void handleColumnCommand() {
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();
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -68,11 +74,11 @@ public Options options() {
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"));
Expand All @@ -81,6 +87,11 @@ public Options options() {
"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"));

Expand Down Expand Up @@ -108,6 +119,17 @@ public Option createSimpleOption(String shortName, String longName, String descr
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.
*
Expand All @@ -120,6 +142,17 @@ public Option createArgOption(String shortName, String longName, String descript
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.
*
Expand All @@ -129,7 +162,6 @@ public Option createArgOption(String shortName, String longName, String descript
* @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();
}
}
73 changes: 73 additions & 0 deletions clients/cli/src/main/java/org/apache/gravitino/cli/ParseType.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit 758cd2e

Please sign in to comment.