Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance type-checked result effect during the call to createQuery() #8152

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
Expand Down Expand Up @@ -214,6 +215,7 @@
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind;
import org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper;
import org.hibernate.type.BasicPluralType;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaType;
Expand Down Expand Up @@ -1498,12 +1500,24 @@ public SqmDynamicInstantiation<?> visitInstantiation(HqlParser.InstantiationCont
}

if ( !dynamicInstantiation.checkInstantiation( creationContext.getTypeConfiguration() ) ) {
final String typeName = dynamicInstantiation.getJavaType().getSimpleName();
final Class<?> type = dynamicInstantiation.getJavaType();
if ( dynamicInstantiation.isFullyAliased() ) {
throw new SemanticException( "Missing constructor or attributes for injection into type '" + typeName + "'", query );
throw new SemanticException("Missing constructor or attributes for injection into type [" + type + "]." +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please back out this change and the other one like it.

  • we don't need punctuation at the end of exception messages, that's quite nonstandard, and
  • I have been gradually migrating to the use of single quotes for quoting the names of program elements, since square brackets have more visual weight than is needed for this purpose.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reminding me, I have finished the modification

"Expected arguments are: " + dynamicInstantiation.argumentTypes(), query);
}
else {
throw new SemanticException( "Missing constructor for type '" + typeName + "'", query );
List<InstantiationHelper.MismatchConstructorFieldPositionInfo> list = dynamicInstantiation.findMismatchConstructorFieldPositions(creationContext.getTypeConfiguration());

String conjecture = "";
if (list != null && list.isEmpty() == false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (list != null && list.isEmpty() == false) {
if ( list != null && !list.isEmpty() ) {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have finished the modification

conjecture = " The presumed mismatch field position is: " +
Copy link
Member

@gavinking gavinking Apr 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
conjecture = " The presumed mismatch field position is: " +
conjecture = "mismatch at position " +

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have finished the modification

list.stream()
.map(InstantiationHelper.MismatchConstructorFieldPositionInfo::toString)
.collect(Collectors.joining(","));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use StringHelper.join() for this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have finished the modification

}

throw new SemanticException("Unable to locate appropriate constructor on class [" + type + "]. " +
"Expected arguments are: " + dynamicInstantiation.argumentTypes() + conjecture, query);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
import static org.hibernate.query.sqm.DynamicInstantiationNature.CLASS;
import static org.hibernate.query.sqm.DynamicInstantiationNature.LIST;
import static org.hibernate.query.sqm.DynamicInstantiationNature.MAP;
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.isConstructorCompatible;
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.isInjectionCompatible;
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.*;

/**
* Represents a dynamic instantiation ({@code select new XYZ(...) ...}) as part of the SQM.
Expand Down Expand Up @@ -141,7 +140,23 @@ public boolean checkInstantiation(TypeConfiguration typeConfiguration) {
}
}

private List<Class<?>> argumentTypes() {
public List<MismatchConstructorFieldPositionInfo> findMismatchConstructorFieldPositions(TypeConfiguration typeConfiguration) {
if (getInstantiationTarget().getNature() == CLASS) {
if (getJavaType().isArray()) {
// hack to accommodate the needs of jpamodelgen
// where Class objects not available during build
return null;
}
final List<Class<?>> argTypes = argumentTypes();

return findMismatchConstructorFieldPosition(getJavaType(), argTypes, typeConfiguration);
} else {
// TODO: is there anything we need to check for list/map instantiation?
return null;
}
}

public List<Class<?>> argumentTypes() {
return getArguments().stream()
.map(arg -> arg.getNodeJavaType() == null ? Void.class : arg.getNodeJavaType().getJavaTypeClass())
.collect(toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import static org.hibernate.query.sqm.tree.expression.Compatibility.areAssignmentCompatible;
Expand Down Expand Up @@ -97,6 +98,59 @@ public static boolean isConstructorCompatible(
}
}

public static List<MismatchConstructorFieldPositionInfo> findMismatchConstructorFieldPosition(Class<?> javaClass, List<Class<?>> argTypes, TypeConfiguration typeConfiguration) {
List<MismatchConstructorFieldPositionInfo> list = new ArrayList<>();

for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) {
MismatchConstructorFieldPositionInfo positionInfo = findMismatchConstructorFieldPosition(constructor, argTypes, typeConfiguration);
if (positionInfo != null) {
list.add(positionInfo);
}
}
return list;
}

public static MismatchConstructorFieldPositionInfo findMismatchConstructorFieldPosition(
Constructor<?> constructor,
List<Class<?>> argumentTypes,
TypeConfiguration typeConfiguration) {
final Type[] genericParameterTypes = constructor.getGenericParameterTypes();
if (genericParameterTypes.length == argumentTypes.size()) {
for (int i = 0; i < argumentTypes.size(); i++) {
final Type parameterType = genericParameterTypes[i];
final Class<?> argumentType = argumentTypes.get(i);
final Class<?> type = parameterType instanceof Class<?>
? (Class<?>) parameterType
: typeConfiguration.getJavaTypeRegistry().resolveDescriptor(parameterType).getJavaTypeClass();

if (!areAssignmentCompatible(type, argumentType)) {
return new MismatchConstructorFieldPositionInfo(i, argumentType.getTypeName(),
parameterType.getTypeName());
}
}
return null;
} else {
return null;
}
}

public static class MismatchConstructorFieldPositionInfo {
private final int position;
private final String expectTypeName;
private final String actualTypeName;

public MismatchConstructorFieldPositionInfo(int index, String expectTypeName, String actualTypeName) {
this.position = index + 1;
this.expectTypeName = expectTypeName;
this.actualTypeName = actualTypeName;
}

@Override
public String toString() {
return String.format("[%s](%s -> %s)", position, expectTypeName, actualTypeName);
}
}

static Field findField(Class<?> declaringClass, String name, Class<?> javaType) {
try {
final Field field = declaringClass.getDeclaredField( name );
Expand Down