diff --git a/core/trino-main/src/main/java/io/trino/execution/SetColumnTypeTask.java b/core/trino-main/src/main/java/io/trino/execution/SetColumnTypeTask.java index fab3a71a84eb..29598537002f 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SetColumnTypeTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/SetColumnTypeTask.java @@ -93,7 +93,7 @@ else if (metadata.getView(session, qualifiedObjectName).isPresent()) { exceptionMessage += ", but a view with that name exists."; } if (!statement.isTableExists()) { - throw semanticException(TABLE_NOT_FOUND, statement, exceptionMessage); + throw semanticException(TABLE_NOT_FOUND, statement, "%s", exceptionMessage); } return immediateVoidFuture(); } diff --git a/core/trino-main/src/main/java/io/trino/execution/SetSessionTask.java b/core/trino-main/src/main/java/io/trino/execution/SetSessionTask.java index 87b01124a2d1..41d1e19c7148 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SetSessionTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/SetSessionTask.java @@ -109,7 +109,7 @@ public ListenableFuture execute( propertyMetadata.decode(objectValue); } catch (RuntimeException e) { - throw semanticException(INVALID_SESSION_PROPERTY, statement, e.getMessage()); + throw semanticException(INVALID_SESSION_PROPERTY, statement, "Invalid value of session property '%s': %s", propertyName.toString(), e.getMessage()); } stateMachine.addSetSessionProperties(propertyName.toString(), value); diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/AggregationAnalyzer.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/AggregationAnalyzer.java index 4e68a3a1a593..d18f627569a2 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/AggregationAnalyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/AggregationAnalyzer.java @@ -449,8 +449,7 @@ protected Boolean visitFunctionCall(FunctionCall node, Void context) if (node.getFilter().isPresent()) { throw semanticException(FUNCTION_NOT_AGGREGATE, node, - "Filter is only valid for aggregation functions", - node); + "Filter is only valid for aggregation functions"); } if (node.getOrderBy().isPresent()) { throw semanticException(FUNCTION_NOT_AGGREGATE, node, "ORDER BY is only valid for aggregation functions"); @@ -803,7 +802,7 @@ private void verifyNoOrderByReferencesToOutputColumns(Node node, StandardErrorCo getReferencesToScope(node, analysis, orderByScope.get()) .findFirst() .ifPresent(expression -> { - throw semanticException(errorCode, expression, errorString); + throw semanticException(errorCode, expression, "%s", errorString); }); } } diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/ExpressionAnalyzer.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/ExpressionAnalyzer.java index 16c53150c043..8c301980b3fc 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/ExpressionAnalyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/ExpressionAnalyzer.java @@ -22,6 +22,8 @@ import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Streams; +import com.google.errorprone.annotations.FormatMethod; +import com.google.errorprone.annotations.FormatString; import io.trino.Session; import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.FunctionResolver; @@ -761,7 +763,7 @@ protected Type visitDereferenceExpression(DereferenceExpression node, StackableA for (RowType.Field rowField : rowType.getFields()) { if (fieldName.equalsIgnoreCase(rowField.getName().orElse(null))) { if (foundFieldName) { - throw semanticException(AMBIGUOUS_NAME, field, "Ambiguous row field reference: " + fieldName); + throw semanticException(AMBIGUOUS_NAME, field, "Ambiguous row field reference: %s", fieldName); } foundFieldName = true; rowFieldType = rowField.getType(); @@ -1248,7 +1250,7 @@ protected Type visitFunctionCall(FunctionCall node, StackableAstVisitorContext fields = ImmutableList.builder(); @@ -2550,7 +2552,7 @@ public Type visitJsonValue(JsonValue node, StackableAstVisitorContext c !isDateTimeType(returnedType) || returnedType.equals(INTERVAL_DAY_TIME) || returnedType.equals(INTERVAL_YEAR_MONTH)) { - throw semanticException(TYPE_MISMATCH, node, "Invalid return type of function JSON_VALUE: " + node.getReturnedType().get()); + throw semanticException(TYPE_MISMATCH, node, "Invalid return type of function JSON_VALUE: %s", node.getReturnedType().get()); } JsonPathAnalysis pathAnalysis = jsonPathAnalyses.get(NodeRef.of(node)); @@ -2797,7 +2799,7 @@ private ResolvedFunction getInputFunction(Type type, JsonFormat format, Node nod if (isStringType(type)) { yield VARBINARY_TO_JSON; } - throw semanticException(TYPE_MISMATCH, node, format("Cannot read input of type %s as JSON using formatting %s", type, format)); + throw semanticException(TYPE_MISMATCH, node, "Cannot read input of type %s as JSON using formatting %s", type, format); } case UTF8 -> VARBINARY_UTF8_TO_JSON; case UTF16 -> VARBINARY_UTF16_TO_JSON; @@ -2822,23 +2824,23 @@ private ResolvedFunction getOutputFunction(Type type, JsonFormat format, Node no if (isStringType(type)) { yield JSON_TO_VARBINARY; } - throw semanticException(TYPE_MISMATCH, node, format("Cannot output JSON value as %s using formatting %s", type, format)); + throw semanticException(TYPE_MISMATCH, node, "Cannot output JSON value as %s using formatting %s", type, format); } case UTF8 -> { if (!VARBINARY.equals(type)) { - throw semanticException(TYPE_MISMATCH, node, format("Cannot output JSON value as %s using formatting %s", type, format)); + throw semanticException(TYPE_MISMATCH, node, "Cannot output JSON value as %s using formatting %s", type, format); } yield JSON_TO_VARBINARY_UTF8; } case UTF16 -> { if (!VARBINARY.equals(type)) { - throw semanticException(TYPE_MISMATCH, node, format("Cannot output JSON value as %s using formatting %s", type, format)); + throw semanticException(TYPE_MISMATCH, node, "Cannot output JSON value as %s using formatting %s", type, format); } yield JSON_TO_VARBINARY_UTF16; } case UTF32 -> { if (!VARBINARY.equals(type)) { - throw semanticException(TYPE_MISMATCH, node, format("Cannot output JSON value as %s using formatting %s", type, format)); + throw semanticException(TYPE_MISMATCH, node, "Cannot output JSON value as %s using formatting %s", type, format); } yield JSON_TO_VARBINARY_UTF32; } @@ -3129,7 +3131,8 @@ private void coerceType(StackableAstVisitorContext context, Expression coerceType(expression, actualType, expectedType, message); } - private Type coerceToSingleType(StackableAstVisitorContext context, Node node, String message, Expression first, Expression second) + @FormatMethod + private Type coerceToSingleType(StackableAstVisitorContext context, Node node, @FormatString String message, Expression first, Expression second) { Type firstType = UNKNOWN; if (first != null) { @@ -3155,7 +3158,7 @@ private Type coerceToSingleType(StackableAstVisitorContext context, Nod return superType; } - throw semanticException(TYPE_MISMATCH, node, message, firstType, secondType); + throw semanticException(TYPE_MISMATCH, node, "%s", format(message, firstType, secondType)); } private Type coerceToSingleType(StackableAstVisitorContext context, String description, List expressions) @@ -3176,12 +3179,12 @@ private Type coerceToSingleType(StackableAstVisitorContext context, Str for (Type type : types) { Optional newSuperType = typeCoercion.getCommonSuperType(superType, type); if (newSuperType.isEmpty()) { - throw semanticException(TYPE_MISMATCH, Iterables.get(typeExpressions.get(type), 0).getNode(), format( + throw semanticException(TYPE_MISMATCH, Iterables.get(typeExpressions.get(type), 0).getNode(), "%s must be the same type or coercible to a common type. Cannot find common type between %s and %s, all types (without duplicates): %s", description, superType, type, - typeExpressions.keySet())); + typeExpressions.keySet()); } superType = newSuperType.get(); } @@ -3192,12 +3195,12 @@ private Type coerceToSingleType(StackableAstVisitorContext context, Str if (!type.equals(superType)) { if (!typeCoercion.canCoerce(type, superType)) { - throw semanticException(TYPE_MISMATCH, Iterables.get(coercionCandidates, 0).getNode(), format( + throw semanticException(TYPE_MISMATCH, Iterables.get(coercionCandidates, 0).getNode(), "%s must be the same type or coercible to a common type. Cannot find common type between %s and %s, all types (without duplicates): %s", description, superType, type, - typeExpressions.keySet())); + typeExpressions.keySet()); } addOrReplaceExpressionsCoercion(coercionCandidates, type, superType); } @@ -3460,7 +3463,7 @@ public static void analyzeExpressionWithoutSubqueries( plannerContext, accessControl, (node, ignored) -> { - throw semanticException(errorCode, node, message); + throw semanticException(errorCode, node, "%s", message); }, session, TypeProvider.empty(), @@ -3582,7 +3585,7 @@ public static ExpressionAnalyzer createWithoutSubqueries( session, TypeProvider.empty(), parameters, - node -> semanticException(errorCode, node, message), + node -> semanticException(errorCode, node, "%s", message), warningCollector, isDescribe); } diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/JsonPathAnalyzer.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/JsonPathAnalyzer.java index edabd1f19bfe..093d9f016cbe 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/JsonPathAnalyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/JsonPathAnalyzer.java @@ -83,7 +83,6 @@ import static io.trino.sql.analyzer.TypeSignatureProvider.fromTypes; import static io.trino.sql.jsonpath.tree.ArithmeticUnary.Sign.PLUS; import static io.trino.type.Json2016Type.JSON_2016; -import static java.lang.String.format; import static java.util.Objects.requireNonNull; public class JsonPathAnalyzer @@ -368,9 +367,9 @@ protected Type visitNamedVariable(NamedVariable node, Void context) .filter(name -> name.equalsIgnoreCase(node.getName())) .findFirst(); if (similarName.isPresent()) { - throw semanticException(INVALID_PATH, pathNode, format("no value passed for parameter %s. Try quoting \"%s\" in the PASSING clause to match case", node.getName(), node.getName())); + throw semanticException(INVALID_PATH, pathNode, "no value passed for parameter %s. Try quoting \"%s\" in the PASSING clause to match case", node.getName(), node.getName()); } - throw semanticException(INVALID_PATH, pathNode, "no value passed for parameter " + node.getName()); + throw semanticException(INVALID_PATH, pathNode, "no value passed for parameter %s", node.getName()); } if (parameterType.equals(JSON_2016)) { diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/SemanticExceptions.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/SemanticExceptions.java index 49f4f57fd9d9..4a11129a1cce 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/SemanticExceptions.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/SemanticExceptions.java @@ -13,6 +13,7 @@ */ package io.trino.sql.analyzer; +import com.google.errorprone.annotations.FormatMethod; import io.trino.spi.ErrorCodeSupplier; import io.trino.spi.TrinoException; import io.trino.sql.tree.Expression; @@ -38,11 +39,13 @@ public static TrinoException ambiguousAttributeException(Expression node, Qualif throw semanticException(AMBIGUOUS_NAME, node, "Column '%s' is ambiguous", name); } + @FormatMethod public static TrinoException semanticException(ErrorCodeSupplier code, Node node, String format, Object... args) { return semanticException(code, node, null, format, args); } + @FormatMethod public static TrinoException semanticException(ErrorCodeSupplier code, Node node, Throwable cause, String format, Object... args) { throw new TrinoException(code, extractLocation(node), format(format, args), cause); diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java index e6771a68affb..2e423e577813 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java @@ -1520,7 +1520,7 @@ protected Scope visitQuery(Query node, Optional scope) verify(isTopLevel || node.getFunctions().isEmpty(), "Inline functions must be at the top level"); for (FunctionSpecification function : node.getFunctions()) { if (function.getName().getPrefix().isPresent()) { - throw semanticException(SYNTAX_ERROR, function, "Inline function names cannot be qualified: " + function.getName()); + throw semanticException(SYNTAX_ERROR, function, "Inline function names cannot be qualified: %s", function.getName()); } function.getRoutineCharacteristics().stream() .filter(SecurityCharacteristic.class::isInstance) @@ -2501,7 +2501,7 @@ private Scope createScopeForView( Query query = parseView(originalSql, name, table); if (!query.getFunctions().isEmpty()) { - throw semanticException(NOT_SUPPORTED, table, "View contains inline function: " + name); + throw semanticException(NOT_SUPPORTED, table, "View contains inline function: %s", name); } analysis.registerTableForView(table); diff --git a/core/trino-main/src/main/java/io/trino/sql/routine/SqlRoutineAnalyzer.java b/core/trino-main/src/main/java/io/trino/sql/routine/SqlRoutineAnalyzer.java index ab6280785dae..a01b20968db7 100644 --- a/core/trino-main/src/main/java/io/trino/sql/routine/SqlRoutineAnalyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/routine/SqlRoutineAnalyzer.java @@ -192,7 +192,7 @@ private Type getType(Node node, DataType type) return plannerContext.getTypeManager().getType(toTypeSignature(type)); } catch (TypeNotFoundException e) { - throw semanticException(TYPE_MISMATCH, node, "Unknown type: " + type); + throw semanticException(TYPE_MISMATCH, node, "Unknown type: %s", type); } } @@ -218,7 +218,7 @@ private static void validateArguments(FunctionSpecification function) } String name = identifierValue(parameter.getName().get()); if (!argumentNames.add(name)) { - throw semanticException(INVALID_ARGUMENTS, parameter, "Duplicate function parameter name: " + name); + throw semanticException(INVALID_ARGUMENTS, parameter, "Duplicate function parameter name: %s", name); } } } @@ -514,7 +514,7 @@ private void analyzeExpression(Context context, Expression expression, Type expe return; } if (!typeCoercion.canCoerce(actualType, expectedType)) { - throw semanticException(TYPE_MISMATCH, expression, message + " must evaluate to %s (actual: %s)", expectedType, actualType); + throw semanticException(TYPE_MISMATCH, expression, "%s must evaluate to %s (actual: %s)", message, expectedType, actualType); } addCoercion(expression, actualType, expectedType); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestSetSessionTask.java b/core/trino-main/src/test/java/io/trino/execution/TestSetSessionTask.java index f00380ce2289..4d9a9f6471c2 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestSetSessionTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestSetSessionTask.java @@ -169,7 +169,7 @@ public void testSetSessionWithValidation() assertThatThrownBy(() -> testSetSession("positive_property", new LongLiteral("-1"), "-1")) .isInstanceOf(TrinoException.class) - .hasMessage(MUST_BE_POSITIVE); + .hasMessage("Invalid value of session property 'my_catalog.positive_property': " + MUST_BE_POSITIVE); } @Test @@ -177,7 +177,7 @@ public void testSetSessionWithInvalidEnum() { assertThatThrownBy(() -> testSetSession("size_property", new StringLiteral("XL"), "XL")) .isInstanceOf(TrinoException.class) - .hasMessage("Invalid value [XL]. Valid values: [SMALL, MEDIUM, LARGE]") + .hasMessage("Invalid value of session property 'my_catalog.size_property': Invalid value [XL]. Valid values: [SMALL, MEDIUM, LARGE]") .matches(throwable -> ((TrinoException) throwable).getErrorCode() == INVALID_SESSION_PROPERTY.toErrorCode()); } diff --git a/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java b/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java index 5b9360f4a176..469578eb078c 100644 --- a/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java +++ b/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java @@ -533,11 +533,11 @@ public void testInvalidValueForQueryPartitionFilterRequiredSchemas() { assertQueryFails( "SET SESSION hive.query_partition_filter_required_schemas = ARRAY['tpch', null]", - "line 1:1: Invalid null or empty value in query_partition_filter_required_schemas property"); + "line 1:1: Invalid value of session property 'hive.query_partition_filter_required_schemas': Invalid null or empty value in query_partition_filter_required_schemas property"); assertQueryFails( "SET SESSION hive.query_partition_filter_required_schemas = ARRAY['tpch', '']", - "line 1:1: Invalid null or empty value in query_partition_filter_required_schemas property"); + "line 1:1: Invalid value of session property 'hive.query_partition_filter_required_schemas': Invalid null or empty value in query_partition_filter_required_schemas property"); } @Test