[jOOQ/jOOQ#19247] Compilation error in generated Record POJO

constructors when table uses a table type reference in PostgreSQL
This commit is contained in:
Lukas Eder 2025-11-18 16:39:57 +01:00
parent 7c40f49e5e
commit 5094618d52
3 changed files with 76 additions and 44 deletions

View File

@ -2613,18 +2613,18 @@ public class JavaGenerator extends AbstractGenerator {
final TypedElementDefinition<?> t = (TypedElementDefinition<?>) column;
final JavaTypeResolver r = resolver(out, Mode.RECORD);
final boolean isUDT = t.getType(r).isUDT();
final boolean isUDTOrTable = t.getType(r).isUDT() || t.getType(r).isTable();
final boolean isArray = t.getType(r).isArray();
final boolean isUDTArray = t.getType(r).isUDTArray();
final boolean isUDTOrTableArray = t.getType(r).isUDTArray() || t.getType(r).isTableArray();
final ArrayDefinition array = database.getArray(t.getType(r).getSchema(), t.getType(r).getQualifiedUserType());
final String indexTypeFull = array == null || array.getIndexType() == null ? null : getJavaType(array.getIndexType(resolver(out)), out);
final boolean isArrayOfUDTs = isArrayOfUDTs(t, r, Mode.RECORD);
final boolean isConverted = t.getType(r).getConverter() != null || t.getType(r).getBinding() != null;
final String udtType = (isUDT || isArray)
final String udtType = (isUDTOrTable || isArray)
? out.ref(getJavaType(t.getType(r), out, Mode.RECORD))
: "";
final String udtArrayElementType = isUDTArray
final String udtArrayElementType = isUDTOrTableArray
? out.ref(database.getArray(t.getType(r).getSchema(), t.getType(r).getQualifiedUserType()).getElementType(r).getJavaType(r))
: isArrayOfUDTs
? out.ref(getArrayBaseType(t.getType(r).getJavaType(r)))
@ -2632,7 +2632,7 @@ public class JavaGenerator extends AbstractGenerator {
if (kotlin) {
if (pojoArgument)
if (isUDTArray)
if (isUDTOrTableArray)
out.println("this.%s = value.%s?.let { %s(it.map { it?.let { %s(it) } }) }",
getStrategy().getJavaMemberName(column, Mode.POJO),
getStrategy().getJavaMemberName(column, Mode.POJO),
@ -2643,7 +2643,7 @@ public class JavaGenerator extends AbstractGenerator {
getStrategy().getJavaMemberName(column, Mode.POJO),
getStrategy().getJavaMemberName(column, Mode.POJO),
udtArrayElementType);
else if (isUDT && !isConverted || isArray)
else if (isUDTOrTable && !isConverted || isArray)
out.println("this.%s = value.%s?.let { %s(it) }",
getStrategy().getJavaMemberName(column, Mode.POJO),
getStrategy().getJavaMemberName(column, Mode.POJO),
@ -2665,7 +2665,7 @@ public class JavaGenerator extends AbstractGenerator {
// In Scala, the setter call can be ambiguous, e.g. when using KeepNamesGeneratorStrategy
else if (scala) {
if (pojoArgument)
if (isUDTArray)
if (isUDTOrTableArray)
out.println("this.%s(if (value.%s == null) null else new %s(value.%s.stream().map { it => new %s(it) }.collect(%s.toList())))",
getStrategy().getJavaSetterName(column, Mode.POJO),
getStrategy().getJavaGetterName(column, Mode.POJO),
@ -2679,7 +2679,7 @@ public class JavaGenerator extends AbstractGenerator {
getStrategy().getJavaGetterName(column, Mode.POJO),
getStrategy().getJavaGetterName(column, Mode.POJO),
udtArrayElementType);
else if (isUDT && !isConverted || isArray)
else if (isUDTOrTable && !isConverted || isArray)
out.println("this.%s(if (value.%s == null) null else new %s(value.%s))",
getStrategy().getJavaSetterName(column, Mode.RECORD),
getStrategy().getJavaGetterName(column, Mode.POJO),
@ -2702,7 +2702,7 @@ public class JavaGenerator extends AbstractGenerator {
? getStrategy().getJavaMemberName(column, Mode.POJO)
: getStrategy().getJavaGetterName(column, Mode.POJO);
if (isUDTArray) {
if (isUDTOrTableArray) {
if (indexTypeFull == null) {
out.println("%s(value.%s() == null ? null : new %s(value.%s().stream().map(%s::new).collect(%s.toList())));",
getStrategy().getJavaSetterName(column, Mode.RECORD),
@ -2741,7 +2741,7 @@ public class JavaGenerator extends AbstractGenerator {
brackets
);
}
else if (isUDT && !isConverted || isArray) {
else if (isUDTOrTable && !isConverted || isArray) {
out.println("%s(value.%s() == null ? null : new %s(value.%s()));",
getStrategy().getJavaSetterName(column, Mode.RECORD),
getterName,
@ -2842,15 +2842,15 @@ public class JavaGenerator extends AbstractGenerator {
final String typeFull = getJavaType(column.getType(resolver(out)), out);
final String type = out.ref(typeFull);
final String name = column.getQualifiedOutputName();
final boolean isUDT = column.getType(resolver(out)).isUDT();
final boolean isUDTOrTable = column.getType(resolver(out)).isUDT() || column.getType(resolver(out)).isTable();
final boolean isArray = column.getType(resolver(out)).isArray();
final boolean override = generateInterfaces() && !generateImmutableInterfaces() && !isUDT
final boolean override = generateInterfaces() && !generateImmutableInterfaces() && !isUDTOrTable
|| column instanceof AttributeDefinition && ((AttributeDefinition) column).getContainer().isInTypeHierarchy()
|| !kotlin && getStrategy().getJavaSetterOverride(column, Mode.RECORD)
|| kotlin && getStrategy().getJavaMemberOverride(column, Mode.RECORD);
// We cannot have covariant setters for arrays because of type erasure
if (!(generateInterfaces() && (isArray || isUDT))) {
if (!(generateInterfaces() && (isArray || isUDTOrTable))) {
if (!kotlin && !printDeprecationIfUnknownType(out, typeFull))
out.javadoc("Setter for <code>%s</code>.[[before= ][%s]]", name, list(escapeEntities(comment(column))));
@ -2890,7 +2890,7 @@ public class JavaGenerator extends AbstractGenerator {
}
// [#3117] Avoid covariant setters for UDTs when generating interfaces
if (generateInterfaces() && (isUDT || isArray)) {
if (generateInterfaces() && (isUDTOrTable || isArray)) {
final String columnTypeFull = getJavaType(column.getType(resolver(out, Mode.DEFAULT)), out, Mode.DEFAULT);
final String columnTypeInterface = out.ref(getJavaType(column.getType(resolver(out, Mode.INTERFACE)), out, Mode.INTERFACE));
final UDTDefinition udt = column.getDatabase().getUDT(column.getSchema(), column.getType().getUserType());
@ -2898,7 +2898,7 @@ public class JavaGenerator extends AbstractGenerator {
if (!printDeprecationIfUnknownType(out, columnTypeFull))
out.javadoc("Setter for <code>%s</code>.[[before= ][%s]]", name, list(escapeEntities(comment(column))));
if (!generateImmutableInterfaces() || udt.isInTypeHierarchy())
if (!generateImmutableInterfaces() || udt != null && udt.isInTypeHierarchy())
out.override();
if (scala) {
@ -2938,12 +2938,12 @@ public class JavaGenerator extends AbstractGenerator {
final String typeFull = getJavaType(column.getType(resolver(out)), out);
final String type = out.ref(typeFull);
final String recordTypeFull = getJavaType(column.getType(resolver(out, Mode.RECORD)), out, Mode.RECORD);
final boolean isUDT = column.getType(resolver(out)).isUDT();
final boolean isUDTArray = column.getType(resolver(out)).isUDTArray();
final boolean isUDTOrTable = column.getType(resolver(out)).isUDT() || column.getType(resolver(out)).isTable();
final boolean isUDTOrTableArray = column.getType(resolver(out)).isUDTArray() || column.getType(resolver(out)).isTableArray();
final boolean isArray = column.getType(resolver(out)).isArray();
final boolean isArrayOfUDTs = isArrayOfUDTs(column, resolver(out, RECORD), RECORD);
if (generateInterfaces() && (isUDT || isArray)) {
if (generateInterfaces() && (isUDTOrTable || isArray)) {
final String typeInterface = out.ref(getJavaType(column.getType(resolver(out, Mode.INTERFACE)), out, Mode.INTERFACE));
final UDTDefinition udt = column.getDatabase().getUDT(column.getSchema(), column.getType().getUserType());
@ -2956,7 +2956,7 @@ public class JavaGenerator extends AbstractGenerator {
out.println("else if (%s.isInstanceOf[%s])", member, type);
out.println("return %s.asInstanceOf[%s]", member, type);
if (isUDT && udt != null)
if (isUDTOrTable && udt != null)
generateRecordSetterSubtypeChecks0(out, index, column, udt);
}
@ -2968,7 +2968,7 @@ public class JavaGenerator extends AbstractGenerator {
out.println("return %s.map(u => new %s(%s))", member, columnBaseTypeName, udtInterfaceGetterCalls(udt, "u"));
}
else
out.println("return new %s(%s)", out.ref(recordTypeFull), udtInterfaceGetterCalls(udt, member));
out.println("return new %s(%s)", out.ref(recordTypeFull), udt != null ? udtInterfaceGetterCalls(udt, member) : member);
out.println("}");
}
@ -2981,7 +2981,7 @@ public class JavaGenerator extends AbstractGenerator {
out.println("if (%s == null)", member);
out.println("return null;");
if (isUDT) {
if (isUDTOrTable) {
if (!isArrayOfUDTs) {
out.println("else if (%s instanceof %s)", member, type);
out.println("return (%s) %s;", type, member);
@ -2998,7 +2998,7 @@ public class JavaGenerator extends AbstractGenerator {
out.println("return %s.of(%s).map(u -> new %s(%s)).toArray(%s[]::new);", Stream.class, member, columnBaseTypeName, udtInterfaceGetterCalls(udt, "u"), columnBaseTypeName);
}
else
out.println("return new %s(%s);", out.ref(recordTypeFull), udtInterfaceGetterCalls(udt, member));
out.println("return new %s(%s);", out.ref(recordTypeFull), udt != null ? udtInterfaceGetterCalls(udt, member) : member);
}
else if (isArray) {
final ArrayDefinition array = database.getArray(column.getType(resolver(out)).getSchema(), column.getType(resolver(out)).getQualifiedUserType());
@ -3010,7 +3010,7 @@ public class JavaGenerator extends AbstractGenerator {
out.println();
out.println("for (%s i : %s)", componentTypeInterface, member);
if (isUDTArray)
if (isUDTOrTableArray)
out.println("a.add(i.into(new %s()));", componentType);
else
out.println("a.add(i);", componentType);
@ -6167,10 +6167,10 @@ public class JavaGenerator extends AbstractGenerator {
final Definition column = embeddablesAndUnreplacedColumns.get(i);
if (column instanceof TypedElementDefinition<?> t) {
final boolean isUDT = t.getType(resolver(out)).isUDT();
final boolean isUDTArray = t.getType(resolver(out)).isUDTArray();
final boolean isUDTOrTable = t.getType(resolver(out)).isUDT() || t.getType(resolver(out)).isTable();
final boolean isUDTOrTableArray = t.getType(resolver(out)).isUDTArray() || t.getType(resolver(out)).isTableArray();
if (isUDT || isUDTArray) {
if (isUDTOrTable || isUDTOrTableArray) {
if (!hasUdts) {
hasUdts = true;
out.println();
@ -6278,6 +6278,8 @@ public class JavaGenerator extends AbstractGenerator {
if (column instanceof TypedElementDefinition<?> e) {
if (e.getType().isUDT())
return e.getDatabase().getUDT(e.getSchema(), e.getType().getQualifiedUserType());
else if (e.getType().isTable())
return e.getDatabase().getTable(e.getSchema(), e.getType().getQualifiedUserType());
}
@ -6692,13 +6694,14 @@ public class JavaGenerator extends AbstractGenerator {
final String columnTypeFull = getJavaType(column.getType(resolver(out, Mode.POJO)), out, Mode.POJO);
final String columnType = out.ref(columnTypeFull);
final String columnMember = getStrategy().getJavaMemberName(column, Mode.POJO);
final boolean isUDT = column.getType(resolver(out)).isUDT();
final boolean isUDTArray = column.getType(resolver(out)).isUDTArray();
final boolean isUDTOrTable = column.getType(resolver(out)).isUDT() || column.getType(resolver(out)).isTable();
final boolean isUDTOrTableArray = column.getType(resolver(out)).isUDTArray() || column.getType(resolver(out)).isTableArray();
final boolean isArrayOfUDTs = isArrayOfUDTs(column, resolver(out, POJO), POJO);
if (generateInterfaces() && (isUDT || isUDTArray)) {
if (generateInterfaces() && (isUDTOrTable || isUDTOrTableArray)) {
final String columnTypeInterface = out.ref(getJavaType(column.getType(resolver(out, Mode.INTERFACE)), out, Mode.INTERFACE));
final UDTDefinition udt = column.getDatabase().getUDT(column.getSchema(), column.getType().getUserType());
final Definition udtOrTable = udt != null ? udt : column.getDatabase().getTable(column.getSchema(), column.getType().getUserType());
if (scala) {
out.println();
@ -6706,13 +6709,13 @@ public class JavaGenerator extends AbstractGenerator {
out.println("if (%s == null)", columnMember);
out.println("return null");
if (isUDT && supportPojoInheritance())
if (isUDTOrTable && supportPojoInheritance() && udt != null)
generatePojoSetterSubtypeChecks0(out, index, column, udt);
out.println("else");
if (isArrayOfUDTs) {
final String columnBaseTypeName = out.ref(getStrategy().getFullJavaClassName(udt, Mode.POJO));
final String columnBaseTypeName = out.ref(getStrategy().getFullJavaClassName(udtOrTable, Mode.POJO));
out.println("return %s.map(u => new %s(u))", columnMember, columnBaseTypeName);
}
@ -6733,21 +6736,21 @@ public class JavaGenerator extends AbstractGenerator {
out.println("if (%s == null)", columnMember);
out.println("return null;");
if (isUDT) {
if (supportPojoInheritance())
if (isUDTOrTable) {
if (supportPojoInheritance() && udt != null)
generatePojoSetterSubtypeChecks0(out, index, column, udt);
out.println("else");
if (isArrayOfUDTs) {
final String columnBaseTypeName = out.ref(getStrategy().getFullJavaClassName(udt, Mode.POJO));
final String columnBaseTypeName = out.ref(getStrategy().getFullJavaClassName(udtOrTable, Mode.POJO));
out.println("return %s.of(%s).map(u -> new %s(%s)).toArray(%s[]::new);", Stream.class, columnMember, columnBaseTypeName, udtInterfaceGetterCalls(udt, "u"), columnBaseTypeName);
}
else
out.println("return new %s(%s);", columnType, udtInterfaceGetterCalls(udt, columnMember));
out.println("return new %s(%s);", columnType, udt != null ? udtInterfaceGetterCalls(udt, columnMember) : columnMember);
}
else if (isUDTArray) {
else if (isUDTOrTableArray) {
final ArrayDefinition array = database.getArray(column.getType(resolver(out)).getSchema(), column.getType(resolver(out)).getQualifiedUserType());
final String componentType = out.ref(getJavaType(array.getElementType(resolver(out, Mode.POJO)), out, Mode.POJO));
final String componentTypeInterface = out.ref(getJavaType(array.getElementType(resolver(out, Mode.INTERFACE)), out, Mode.INTERFACE));
@ -6781,13 +6784,13 @@ public class JavaGenerator extends AbstractGenerator {
final String columnSetterReturnType = generateFluentSetters() ? className : tokenVoid;
final String columnSetter = getStrategy().getJavaSetterName(column, Mode.POJO);
final String columnMember = getStrategy().getJavaMemberName(column, Mode.POJO);
final boolean isUDT = column.getType(resolver(out)).isUDT();
final boolean isUDTOrTable = column.getType(resolver(out)).isUDT() || column.getType(resolver(out)).isTable();
final boolean isArray = column.getType(resolver(out)).isArray();
final String name = column.getQualifiedOutputName();
final boolean override = generateInterfaces() && !generateImmutableInterfaces() && !isUDT;
final boolean override = generateInterfaces() && !generateImmutableInterfaces() && !isUDTOrTable;
// We cannot have covariant setters for arrays because of type erasure
if (!(generateInterfaces() && (isArray || isUDT))) {
if (!(generateInterfaces() && (isArray || isUDTOrTable))) {
if (!printDeprecationIfUnknownType(out, columnTypeFull))
out.javadoc("Setter for <code>%s</code>.[[before= ][%s]]", name, list(escapeEntities(comment(column))));
@ -6815,7 +6818,7 @@ public class JavaGenerator extends AbstractGenerator {
}
// [#3117] To avoid covariant setters on POJOs, we need to generate two setter overloads
if (generateInterfaces() && (isUDT || isArray)) {
if (generateInterfaces() && (isUDTOrTable || isArray)) {
final String columnTypeInterface = out.ref(getJavaType(column.getType(resolver(out, Mode.INTERFACE)), out, Mode.INTERFACE));
out.println();
@ -11032,12 +11035,14 @@ public class JavaGenerator extends AbstractGenerator {
final String getter = parameter == procedure.getReturnValue()
? "getReturnValue"
: getStrategy().getJavaGetterName(parameter, Mode.DEFAULT);
final boolean isUDT = parameter.getType(resolver(out)).isUDT();
final boolean isUDTOrTable =
parameter.getType(resolver(out)).isUDT()
|| parameter.getType(resolver(out)).isTable();
if (instance) {
// [#3117] Avoid funny call-site ambiguity if this is a UDT that is implemented by an interface
if (generateInterfaces() && isUDT) {
if (generateInterfaces() && isUDTOrTable) {
final String columnTypeInterface = out.ref(getJavaType(parameter.getType(resolver(out, Mode.INTERFACE)), out, Mode.INTERFACE));
if (scala)
@ -11792,9 +11797,11 @@ public class JavaGenerator extends AbstractGenerator {
// Check for Oracle-style VARRAY types
else if (db.getArray(schema, u) != null) {
boolean udtArray = db.getArray(schema, u).getElementType(resolver(out)).isUDT();
boolean udtOrTableArray =
db.getArray(schema, u).getElementType(resolver(out)).isUDT()
|| db.getArray(schema, u).getElementType(resolver(out)).isTable();
if (udtMode == Mode.POJO || (udtMode == Mode.INTERFACE && !udtArray)) {
if (udtMode == Mode.POJO || (udtMode == Mode.INTERFACE && !udtOrTableArray)) {
if (scala)
type = "java.util.List[" + getJavaType(db.getArray(schema, u).getElementType(resolver(out, udtMode)), out, udtMode) + "]";
else

View File

@ -178,6 +178,11 @@ public interface DataTypeDefinition {
*/
boolean isUDT();
/**
* Whether this data type represents a table type.
*/
boolean isTable();
/**
* Whether this data type represents an array producing an
* {@link ArrayRecord}.
@ -190,6 +195,12 @@ public interface DataTypeDefinition {
*/
boolean isUDTArray();
/**
* Whether this data type represents an array producing an
* {@link ArrayRecord} of table types.
*/
boolean isTableArray();
/**
* Whether this data type is a NUMBER type without precision and scale.
*/

View File

@ -297,6 +297,15 @@ public class DefaultDataTypeDefinition implements DataTypeDefinition {
return getDatabase().getUDT(schema, userType) != null;
}
@Override
public final boolean isTable() {
if (userType == null)
return false;
// [#19247] In PostgreSQL, tables expose types that can be used as UDTs as well in SQL
return getDatabase().getTable(schema, userType) != null;
}
@Override
public final boolean isArray() {
if (userType == null)
@ -310,6 +319,11 @@ public class DefaultDataTypeDefinition implements DataTypeDefinition {
return isArray() && getDatabase().getArray(schema, userType).getElementType(new DefaultJavaTypeResolver()).isUDT();
}
@Override
public final boolean isTableArray() {
return isArray() && getDatabase().getArray(schema, userType).getElementType(new DefaultJavaTypeResolver()).isTable();
}
@Override
public final String getType() {
return type;