[jOOQ/jOOQ#228] Enhance API to provide access to UDT members - WIP

This commit is contained in:
Lukas Eder 2023-08-25 11:32:29 +02:00
parent 301b9749f4
commit c466d68a85
20 changed files with 891 additions and 26 deletions

View File

@ -71,6 +71,7 @@ abstract class AbstractGenerator implements Generator {
boolean generateDeprecationOnUnknownTypes = true;
boolean generateIndexes = true;
boolean generateRelations = true;
boolean generateUDTPaths = true;
boolean generateImplicitJoinPathsToOne = true;
boolean generateImplicitJoinPathsToMany = true;
boolean generateImplicitJoinPathTableSubtypes = true;
@ -306,6 +307,16 @@ abstract class AbstractGenerator implements Generator {
this.generateRelations = generateRelations;
}
@Override
public boolean generateUDTPaths() {
return generateUDTPaths;
}
@Override
public void setGenerateUDTPaths(boolean generateUDTPaths) {
this.generateUDTPaths = generateUDTPaths;
}
@Override
public boolean generateImplicitJoinPathsToOne() {
return generateImplicitJoinPathsToOne && generateRelations();

View File

@ -66,6 +66,7 @@ import org.jooq.impl.SchemaImpl;
import org.jooq.impl.TableImpl;
import org.jooq.impl.TableRecordImpl;
import org.jooq.impl.UDTImpl;
import org.jooq.impl.UDTPathTableFieldImpl;
import org.jooq.impl.UDTRecordImpl;
import org.jooq.impl.UpdatableRecordImpl;
import org.jooq.meta.ArrayDefinition;
@ -335,14 +336,15 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
}
else if (definition instanceof TableDefinition t) {
switch (mode) {
case DAO: return DAOImpl.class.getName();
case RECORD: return (t.getPrimaryKey() != null ? UpdatableRecordImpl.class : TableRecordImpl.class).getName();
case DAO: return DAOImpl.class.getName();
case RECORD: return (t.getPrimaryKey() != null ? UpdatableRecordImpl.class : TableRecordImpl.class).getName();
case DEFAULT: return TableImpl.class.getName();
}
}
else if (definition instanceof UDTDefinition) {
switch (mode) {
case RECORD: return UDTRecordImpl.class.getName();
case PATH: return UDTPathTableFieldImpl.class.getName();
case RECORD: return UDTRecordImpl.class.getName();
case DEFAULT: return UDTImpl.class.getName();
}
}
@ -464,7 +466,7 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
if (!(definition instanceof SchemaDefinition)) {
// Some definitions have their dedicated subpackages, e.g. "tables", "routines"
String subPackage = getSubPackage(definition);
String subPackage = getSubPackage(definition, mode);
if (!StringUtils.isBlank(subPackage)) {
sb.append(".");
sb.append(subPackage);
@ -547,7 +549,7 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
return result.toString();
}
private String getSubPackage(Definition definition) {
private String getSubPackage(Definition definition, Mode mode) {
if (definition instanceof TableDefinition)
return "tables";
@ -561,6 +563,8 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
// [#330] [#6529] A UDT inside of a package is a PL/SQL RECORD type
if (u.getPackage() != null)
return "packages." + getJavaIdentifier(u.getPackage()).toLowerCase(targetLocale) + ".udt";
else if (mode == Mode.PATH)
return "udt.paths";
else
return "udt";
}

View File

@ -736,9 +736,11 @@ public class GenerationTool {
generator.setGenerateIndexes(g.getGenerate().isIndexes());
if (g.getGenerate().isRelations() != null)
generator.setGenerateRelations(g.getGenerate().isRelations());
if (g.getGenerate().isImplicitJoinPathsToMany() != null)
generator.setGenerateImplicitJoinPathsToOne(g.getGenerate().isImplicitJoinPathsToOne());
if (g.getGenerate().isUdtPaths() != null)
generator.setGenerateUDTPaths(g.getGenerate().isUdtPaths());
if (g.getGenerate().isImplicitJoinPathsToOne() != null)
generator.setGenerateImplicitJoinPathsToOne(g.getGenerate().isImplicitJoinPathsToOne());
if (g.getGenerate().isImplicitJoinPathsToMany() != null)
generator.setGenerateImplicitJoinPathsToMany(g.getGenerate().isImplicitJoinPathsToMany());
if (g.getGenerate().isImplicitJoinPathTableSubtypes() != null)
generator.setGenerateImplicitJoinPathTableSubtypes(g.getGenerate().isImplicitJoinPathTableSubtypes());

View File

@ -124,6 +124,16 @@ public interface Generator {
*/
void setGenerateRelations(boolean generateRelations);
/**
* Whether to generate UDT path expressions on tables and UDTs.
*/
boolean generateUDTPaths();
/**
* Whether to generate UDT path expressions on tables and UDTs.
*/
void setGenerateUDTPaths(boolean generateUDTPaths);
/**
* Whether implicit join path constructors on generated tables for outgoing
* foreign key relationships (to-one relationships) should be generated.

View File

@ -567,9 +567,9 @@ public interface GeneratorStrategy {
DOMAIN,
/**
* The path mode. This is used when a {@link ForeignKeyDefinition} or an
* {@link InverseForeignKeyDefinition} is used to generate a path
* expression.
* The path mode. This is used when a {@link ForeignKeyDefinition}, an
* {@link InverseForeignKeyDefinition}, or a {@link UDTDefinition} is
* used to generate a path expression.
*/
PATH

View File

@ -95,8 +95,10 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jooq.AggregateFunction;
import org.jooq.Binding;
import org.jooq.Catalog;
import org.jooq.Check;
import org.jooq.Comment;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Constants;
@ -122,6 +124,7 @@ import org.jooq.PlainSQL;
import org.jooq.Query;
import org.jooq.QueryPart;
import org.jooq.Record;
import org.jooq.RecordQualifier;
import org.jooq.Records;
import org.jooq.Result;
import org.jooq.Row;
@ -3171,6 +3174,10 @@ public class JavaGenerator extends AbstractGenerator {
for (UDTDefinition udt : database.getUDTs(schema)) {
try {
generateUDT(schema, udt);
// [#228] Package UDTs can't be used as path expressions in SQL
if (generateUDTPaths() && udt.getPackage() == null)
generateUDTPath(schema, udt);
}
catch (Exception e) {
log.error("Error while generating udt " + udt, e);
@ -3349,6 +3356,92 @@ public class JavaGenerator extends AbstractGenerator {
printClassJavadoc(out, "The udt <code>" + udt.getQualifiedInputName() + "</code>.");
}
@SuppressWarnings("unused")
protected void generateUDTPath(SchemaDefinition schema, UDTDefinition udt) {
JavaWriter out = newJavaWriter(getFile(udt, Mode.PATH));
out.refConflicts(getStrategy().getJavaIdentifiers(udt.getAttributes()));
log.info("Generating UDT Path", out.file().getName());
if (log.isDebugEnabled())
for (AttributeDefinition attribute : udt.getAttributes())
log.debug("With attribute", "name=" + attribute.getOutputName() + ", matching type names=" + attribute.getDefinedType().getMatchNames());
generateUDTPath(udt, out);
closeJavaWriter(out);
}
protected void generateUDTPath(UDTDefinition udt, JavaWriter out) {
final String className = getStrategy().getJavaClassName(udt, Mode.PATH);
final String recordType = out.ref(getStrategy().getFullJavaClassName(udt, Mode.RECORD));
final String classExtends = out.ref(getStrategy().getJavaClassExtends(udt, Mode.PATH));
final List<String> interfaces = out.ref(getStrategy().getJavaClassImplements(udt, Mode.PATH));
final String udtId = out.ref(getStrategy().getFullJavaIdentifier(udt), 2);
printPackage(out, udt, Mode.PATH);
generateUDTPathClassJavadoc(udt, out);
printClassAnnotations(out, udt, Mode.PATH);
out.println("%sclass %s<R extends %s, T> extends %s<R, %s, T>[[before= implements ][%s]] {",
visibility(), className, Record.class, classExtends, recordType, interfaces);
out.printSerial();
for (AttributeDefinition attribute : udt.getAttributes()) {
final String attrTypeFull = getJavaType(attribute.getType(resolver(out)), out);
final String attrType = out.ref(attrTypeFull);
final String attrTypeRef = getJavaTypeReference(attribute.getDatabase(), attribute.getType(resolver(out)), out);
final String attrId = out.ref(getStrategy().getJavaIdentifier(attribute), 2);
final String attrName = attribute.getName();
final List<String> converter = out.ref(list(attribute.getType(resolver(out)).getConverter()));
final List<String> binding = out.ref(list(attribute.getType(resolver(out)).getBinding()));
if (!printDeprecationIfUnknownType(out, attrTypeFull))
out.javadoc("The attribute <code>%s</code>.[[before= ][%s]]", attribute.getQualifiedOutputName(), list(escapeEntities(comment(attribute))));
if (attribute.getType().isUDT() && !attribute.getDatabase().isArrayType(attrTypeFull)) {
final SchemaDefinition attrUdtSchema = attribute.getDatabase().getSchema(attribute.getType().getQualifiedUserType().qualifier().last());
final UDTDefinition attrUdt = attribute.getDatabase().getUDT(attrUdtSchema, attribute.getType().getUserType());
final String attrPathType = out.ref(getStrategy().getFullJavaClassName(attrUdt, Mode.PATH));
out.println("%sfinal %s<%s, %s> %s = %s.createUDTPathField(%s.name(\"%s\"), %s, this, \"%s\", %s.class" + converterTemplate(converter) + converterTemplate(binding) + ");",
visibility(), attrPathType, recordType, attrType, attrId, Internal.class, DSL.class, escapeString(attrName), attrTypeRef, escapeString(""), attrPathType, converter, binding);
}
else {
final String attrPathType = out.ref(UDTField.class);
out.println("%sfinal %s<%s, %s> %s = %s.createUDTPathField(%s.name(\"%s\"), %s, this, \"%s\", %s.class" + converterTemplate(converter) + converterTemplate(binding) + ");",
visibility(), attrPathType, recordType, attrType, attrId, Internal.class, DSL.class, escapeString(attrName), attrTypeRef, escapeString(""), attrPathType, converter, binding);
}
}
out.println();
out.println("%s%s(%s name, %s<T> type, %s<R> qualifier, %s comment, %s<?, T> binding) {",
visibility(), className, Name.class, DataType.class, RecordQualifier.class, Comment.class, Binding.class);
out.println("super(name, type, qualifier, %s, comment, binding);", udtId);
out.println("}");
generateUDTPathClassFooter(udt, out);
out.println("}");
closeJavaWriter(out);
}
/**
* Subclasses may override this method to provide udt class footer code.
*/
@SuppressWarnings("unused")
protected void generateUDTPathClassFooter(UDTDefinition udt, JavaWriter out) {}
/**
* Subclasses may override this method to provide their own Javadoc.
*/
protected void generateUDTPathClassJavadoc(UDTDefinition udt, JavaWriter out) {
if (generateCommentsOnUDTs())
printClassJavadoc(out, udt);
else
printClassJavadoc(out, "The udt path for <code>" + udt.getQualifiedInputName() + "</code>.");
}
protected void generateUDTPojos(SchemaDefinition schema) {
log.info("Generating UDT POJOs");
@ -6316,8 +6409,21 @@ public class JavaGenerator extends AbstractGenerator {
String isStatic = generateInstanceFields() ? "" : "static ";
String tableRef = generateInstanceFields() ? "this" : out.ref(getStrategy().getJavaIdentifier(table), 2);
out.println("%s%sfinal %s<%s, %s> %s = createField(%s.name(\"%s\"), %s, %s, \"%s\"" + converterTemplate(converter) + converterTemplate(binding) + converterTemplate(generator) + ");",
columnVisibility, isStatic, TableField.class, recordType, columnType, columnId, DSL.class, columnName, columnTypeRef, tableRef, escapeString(comment(column)), converter, binding, generator);
if (generateInstanceFields()
&& generateUDTPaths()
&& column.getType().isUDT()
&& !column.getDatabase().isArrayType(columnTypeFull)
) {
final SchemaDefinition columnUdtSchema = column.getDatabase().getSchema(column.getType().getQualifiedUserType().qualifier().last());
final UDTDefinition columnUdt = column.getDatabase().getUDT(columnUdtSchema, column.getType().getUserType());
final String columnPathType = out.ref(getStrategy().getFullJavaClassName(columnUdt, Mode.PATH));
out.println("%sfinal %s<%s, %s> %s = %s.createUDTPathTableField(%s.name(\"%s\"), %s, this, \"%s\", %s.class" + converterTemplate(converter) + converterTemplate(binding) + ");",
visibility(), columnPathType, recordType, columnType, columnId, Internal.class, DSL.class, escapeString(columnName), columnTypeRef, escapeString(comment(column)), columnPathType, converter, binding);
}
else
out.println("%s%sfinal %s<%s, %s> %s = createField(%s.name(\"%s\"), %s, %s, \"%s\"" + converterTemplate(converter) + converterTemplate(binding) + converterTemplate(generator) + ");",
columnVisibility, isStatic, TableField.class, recordType, columnType, columnId, DSL.class, columnName, columnTypeRef, tableRef, escapeString(comment(column)), converter, binding, generator);
}
}

View File

@ -703,7 +703,7 @@ fun InformationSchema.indexes(block: MutableList<org.jooq.util.xml.jaxb.Index>.(
}
@JvmName("mutableListIndex")
fun MutableList<org.jooq.util.xml.jaxb.Index>.indexe(block: org.jooq.util.xml.jaxb.Index.() -> Unit) {
fun MutableList<org.jooq.util.xml.jaxb.Index>.index(block: org.jooq.util.xml.jaxb.Index.() -> Unit) {
val e = org.jooq.util.xml.jaxb.Index()
block(e)
add(e)

View File

@ -99,7 +99,7 @@ fun Matchers.indexes(block: MutableList<MatchersIndexType>.() -> Unit) {
}
@JvmName("mutableListMatchersIndexType")
fun MutableList<MatchersIndexType>.indexe(block: MatchersIndexType.() -> Unit) {
fun MutableList<MatchersIndexType>.index(block: MatchersIndexType.() -> Unit) {
val e = MatchersIndexType()
block(e)
add(e)

View File

@ -37,6 +37,8 @@ public class Generate implements Serializable, XMLAppendable
@XmlElement(defaultValue = "true")
protected Boolean sequenceFlags = true;
@XmlElement(defaultValue = "true")
protected Boolean udtPaths = true;
@XmlElement(defaultValue = "true")
protected Boolean implicitJoinPathsToOne = true;
@XmlElement(defaultValue = "true")
protected Boolean implicitJoinPathsToMany = true;
@ -332,6 +334,30 @@ public class Generate implements Serializable, XMLAppendable
this.sequenceFlags = value;
}
/**
* Generate UDT path expressions on tables and on UDTs.
*
* @return
* possible object is
* {@link Boolean }
*
*/
public Boolean isUdtPaths() {
return udtPaths;
}
/**
* Sets the value of the udtPaths property.
*
* @param value
* allowed object is
* {@link Boolean }
*
*/
public void setUdtPaths(Boolean value) {
this.udtPaths = value;
}
/**
* Generate implicit join path constructors on generated tables for outgoing foreign key relationships (to-one relationships)
*
@ -2892,6 +2918,11 @@ public class Generate implements Serializable, XMLAppendable
return this;
}
public Generate withUdtPaths(Boolean value) {
setUdtPaths(value);
return this;
}
public Generate withImplicitJoinPathsToOne(Boolean value) {
setImplicitJoinPathsToOne(value);
return this;
@ -3490,6 +3521,7 @@ public class Generate implements Serializable, XMLAppendable
builder.append("indexes", indexes);
builder.append("relations", relations);
builder.append("sequenceFlags", sequenceFlags);
builder.append("udtPaths", udtPaths);
builder.append("implicitJoinPathsToOne", implicitJoinPathsToOne);
builder.append("implicitJoinPathsToMany", implicitJoinPathsToMany);
builder.append("implicitJoinPathTableSubtypes", implicitJoinPathTableSubtypes);
@ -3646,6 +3678,15 @@ public class Generate implements Serializable, XMLAppendable
return false;
}
}
if (udtPaths == null) {
if (other.udtPaths!= null) {
return false;
}
} else {
if (!udtPaths.equals(other.udtPaths)) {
return false;
}
}
if (implicitJoinPathsToOne == null) {
if (other.implicitJoinPathsToOne!= null) {
return false;
@ -4628,6 +4669,7 @@ public class Generate implements Serializable, XMLAppendable
result = ((prime*result)+((indexes == null)? 0 :indexes.hashCode()));
result = ((prime*result)+((relations == null)? 0 :relations.hashCode()));
result = ((prime*result)+((sequenceFlags == null)? 0 :sequenceFlags.hashCode()));
result = ((prime*result)+((udtPaths == null)? 0 :udtPaths.hashCode()));
result = ((prime*result)+((implicitJoinPathsToOne == null)? 0 :implicitJoinPathsToOne.hashCode()));
result = ((prime*result)+((implicitJoinPathsToMany == null)? 0 :implicitJoinPathsToMany.hashCode()));
result = ((prime*result)+((implicitJoinPathTableSubtypes == null)? 0 :implicitJoinPathTableSubtypes.hashCode()));

View File

@ -2166,6 +2166,10 @@ This is a prerequisite for various advanced features]]></jxb:javadoc></jxb:prope
<element name="sequenceFlags" type="boolean" default="true" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Sequence flags should be generated and used.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="udtPaths" type="boolean" default="true" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Generate UDT path expressions on tables and on UDTs.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="implicitJoinPathsToOne" type="boolean" default="true" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Generate implicit join path constructors on generated tables for outgoing foreign key relationships (to-one relationships)]]></jxb:javadoc></jxb:property></appinfo></annotation>

View File

@ -38,6 +38,7 @@
package org.jooq;
import org.jetbrains.annotations.Nullable;
/**
* A field contained in a UDT.
@ -54,5 +55,6 @@ public interface UDTField<R extends UDTRecord<R>, T> extends Field<T> {
/**
* @return The UDT this field is contained in
*/
@Nullable
UDT<R> getUDT();
}

View File

@ -0,0 +1,67 @@
/*
* Licensed 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
*
* https://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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq;
import org.jetbrains.annotations.NotNull;
/**
* A field dereferenced from a UDT path.
* <p>
* Instances of this type cannot be created directly. They are available from
* generated code.
*
* @param <R> The record type
* @param <T> The field type
* @author Lukas Eder
*/
public interface UDTPathField<R extends Record, U extends UDTRecord<U>, T> extends UDTField<U, T> {
/**
* @return The UDT path this field is contained in.
*/
@NotNull
RecordQualifier<R> getQualifier();
/**
* @return The UDT path represented by this field.
*/
@NotNull
RecordQualifier<U> asQualifier();
}

View File

@ -0,0 +1,61 @@
/*
* Licensed 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
*
* https://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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq;
import org.jetbrains.annotations.NotNull;
/**
* A field dereferenced from a UDT path.
* <p>
* Instances of this type cannot be created directly. They are available from
* generated code.
*
* @param <R> The record type
* @param <T> The field type
* @author Lukas Eder
*/
public interface UDTPathTableField<R extends Record, U extends UDTRecord<U>, T> extends TableField<R, T>, UDTPathField<R, U, T> {
/**
* @return The UDT path this field is contained in.
*/
@Override
@NotNull
RecordQualifier<R> getQualifier();
}

View File

@ -52,7 +52,6 @@ import static org.jooq.conf.WriteIfReadonly.IGNORE;
import static org.jooq.conf.WriteIfReadonly.THROW;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.FieldMapsForInsert.toSQLInsertSelect;
import static org.jooq.impl.Keywords.K_DEFAULT_VALUES;
import static org.jooq.impl.Keywords.K_VALUES;
import static org.jooq.impl.QueryPartCollectionView.wrap;
@ -64,6 +63,7 @@ import static org.jooq.impl.Tools.flattenCollection;
import static org.jooq.impl.Tools.flattenFieldOrRows;
import static org.jooq.impl.Tools.lazy;
import static org.jooq.impl.Tools.row0;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_STORE_ASSIGNMENT;
import java.util.AbstractList;
import java.util.AbstractMap;
@ -754,7 +754,7 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple
fields = keysFlattened(ctx, GeneratorStatementType.INSERT);
if (!fields.isEmpty())
ctx.sql(" (").visit(wrap(fields).qualify(false)).sql(')');
ctx.data(DATA_STORE_ASSIGNMENT, true, c -> c.sql(" (").visit(wrap(fields).qualify(false)).sql(')'));
return fields;
}

View File

@ -67,6 +67,7 @@ import org.jooq.Domain;
import org.jooq.EmbeddableRecord;
import org.jooq.Field;
import org.jooq.ForeignKey;
import org.jooq.Generator;
import org.jooq.Identity;
import org.jooq.Index;
import org.jooq.InverseForeignKey;
@ -79,6 +80,7 @@ import org.jooq.Queries;
import org.jooq.Query;
import org.jooq.QueryPart;
import org.jooq.Record;
import org.jooq.RecordQualifier;
// ...
// ...
import org.jooq.Result;
@ -95,11 +97,16 @@ import org.jooq.TableField;
// ...
// ...
import org.jooq.UDT;
import org.jooq.UDTField;
import org.jooq.UDTPathField;
import org.jooq.UDTPathTableField;
import org.jooq.UDTRecord;
import org.jooq.UniqueKey;
// ...
import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataTypeException;
import org.jooq.impl.QOM.CreateTable;
import org.jooq.impl.QOM.GenerationLocation;
// ...
// ...
@ -118,6 +125,222 @@ import org.reactivestreams.Subscription;
@org.jooq.Internal
public final class Internal {
public static final <R extends Record, UR extends UDTRecord<UR>, T, P extends UDTPathTableField<R, UR, T>> P createUDTPathTableField(
Name name,
DataType<T> type,
Table<R> table,
Class<P> returnType
) {
return createUDTPathTableField(name, type, table, null, returnType, null, null, null);
}
public static final <R extends Record, UR extends UDTRecord<UR>, T, P extends UDTPathTableField<R, UR, T>> P createUDTPathTableField(
Name name,
DataType<T> type,
Table<R> table,
String comment,
Class<P> returnType
) {
return createUDTPathTableField(name, type, table, comment, returnType, null, null, null);
}
public static final <R extends Record, UR extends UDTRecord<UR>, T, U, P extends UDTPathTableField<R, UR, U>> P createUDTPathTableField(
Name name,
DataType<T> type,
Table<R> table,
String comment,
Class<P> returnType,
Converter<T, U> converter
) {
return createUDTPathTableField(name, type, table, comment, returnType, converter, null, null);
}
public static final <R extends Record, UR extends UDTRecord<UR>, T, U, P extends UDTPathTableField<R, UR, U>> P createUDTPathTableField(
Name name,
DataType<T> type,
Table<R> table,
String comment,
Class<P> returnType,
Binding<T, U> binding
) {
return createUDTPathTableField(name, type, table, comment, returnType, null, binding, null);
}
public static final <R extends Record, UR extends UDTRecord<UR>, T, X, U, P extends UDTPathTableField<R, UR, U>> P createUDTPathTableField(
Name name,
DataType<T> type,
Table<R> table,
String comment,
Class<P> returnType,
Converter<X, U> converter,
Binding<T, X> binding
) {
return createUDTPathTableField(name, type, table, comment, returnType, converter, binding, null);
}
public static final <R extends Record, TR extends Table<R>, UR extends UDTRecord<UR>, T, P extends UDTPathTableField<R, UR, T>> P createUDTPathTableField(
Name name,
DataType<T> type,
TR table,
String comment,
Class<P> returnType,
Generator<R, TR, T> generator
) {
return createUDTPathTableField(name, type, table, comment, returnType, null, null, generator);
}
public static final <R extends Record, TR extends Table<R>, UR extends UDTRecord<UR>, T, U, P extends UDTPathTableField<R, UR, U>> P createUDTPathTableField(
Name name,
DataType<T> type,
TR table,
String comment,
Class<P> returnType,
Converter<T, U> converter,
Generator<R, TR, U> generator
) {
return createUDTPathTableField(name, type, table, comment, returnType, converter, null, generator);
}
public static final <R extends Record, TR extends Table<R>, UR extends UDTRecord<UR>, T, U, P extends UDTPathTableField<R, UR, U>> P createUDTPathTableField(
Name name,
DataType<T> type,
TR table,
String comment,
Class<P> returnType,
Binding<T, U> binding,
Generator<R, TR, U> generator
) {
return createUDTPathTableField(name, type, table, comment, returnType, null, binding, generator);
}
@SuppressWarnings("unchecked")
public static final <R extends Record, TR extends Table<R>, UR extends UDTRecord<UR>, T, X, U, P extends UDTPathTableField<R, UR, U>> P createUDTPathTableField(
Name name,
DataType<T> type,
TR table,
String comment,
Class<P> returnType,
Converter<X, U> converter,
Binding<T, X> binding,
Generator<R, TR, U> generator
) {
Binding<T, U> actualBinding = DefaultBinding.newBinding(converter, type, binding);
DataType<U> actualType =
converter == null && binding == null
? (DataType<U>) type
: type.asConvertedDataType(actualBinding);
if (generator != null)
actualType = actualType.generatedAlwaysAs(generator).generationLocation(GenerationLocation.CLIENT);
// [#5999] TODO: Allow for user-defined Names
try {
P tableField = newInstance(name, table, null, comment, returnType, actualBinding, actualType);
// [#1199] The public API of Table returns immutable field lists
if (table instanceof TableImpl<?> t)
t.fields.add(tableField);
return tableField;
}
catch (Exception e) {
throw new DataTypeException("Cannot instantiate " + returnType + ".", e);
}
}
public static final <R extends Record, UR extends UDTRecord<UR>, T, P extends UDTField<UR, T>> P createUDTPathField(
Name name,
DataType<T> type,
UDTPathField<R, UR, ?> qualifier,
Class<P> returnType
) {
return createUDTPathField(name, type, qualifier, null, returnType, null, null);
}
public static final <R extends Record, UR extends UDTRecord<UR>, T, P extends UDTField<UR, T>> P createUDTPathField(
Name name,
DataType<T> type,
UDTPathField<R, UR, ?> qualifier,
String comment,
Class<P> returnType
) {
return createUDTPathField(name, type, qualifier, comment, returnType, null, null);
}
public static final <R extends Record, UR extends UDTRecord<UR>, T, U, P extends UDTField<UR, U>> P createUDTPathField(
Name name,
DataType<T> type,
UDTPathField<R, UR, ?> qualifier,
String comment,
Class<P> returnType,
Converter<T, U> converter
) {
return createUDTPathField(name, type, qualifier, comment, returnType, converter, null);
}
public static final <R extends Record, UR extends UDTRecord<UR>, T, U, P extends UDTField<UR, U>> P createUDTPathField(
Name name,
DataType<T> type,
UDTPathField<R, UR, ?> qualifier,
String comment,
Class<P> returnType,
Binding<T, U> binding
) {
return createUDTPathField(name, type, qualifier, comment, returnType, null, binding);
}
public static final <R extends Record, UR extends UDTRecord<UR>, T, X, U, P extends UDTField<UR, U>> P createUDTPathField(
Name name,
DataType<T> type,
UDTPathField<R, UR, ?> qualifier,
String comment,
Class<P> returnType,
Converter<X, U> converter,
Binding<T, X> binding
) {
Binding<T, U> actualBinding = DefaultBinding.newBinding(converter, type, binding);
DataType<U> actualType =
converter == null && binding == null
? (DataType<U>) type
: type.asConvertedDataType(actualBinding);
// [#5999] TODO: Allow for user-defined Names
try {
// [#228] While it would be cleaner to pass around a Function5 constructor reference,
// chances are that the cost on compilation speed is significantly higher than
// if we just use reflection
return newInstance(name, null, qualifier, comment, returnType, actualBinding, actualType);
}
catch (Exception e) {
throw new DataTypeException("Cannot instantiate " + returnType + ".", e);
}
}
@SuppressWarnings("unchecked")
private static <R extends Record, UR extends UDTRecord<UR>, T, X, U, P extends UDTField<UR, U>> P newInstance(
Name name,
RecordQualifier<R> qualifier,
UDTPathField<R, ?, ?> path,
String comment,
Class<P> returnType,
Binding<T, U> actualBinding,
DataType<U> actualType
) throws Exception {
// [#228] This case only happens in generated UDTPath types, never in generated Table types
if (returnType == UDTField.class)
return (P) new UDTPathFieldImpl<>(name, actualType, path.asQualifier(), path.getUDT(), DSL.comment(comment), actualBinding);
// [#228] While it would be cleaner to pass around a Function5 constructor reference,
// chances are that the cost on compilation speed is significantly higher than
// if we just use reflection
else
return returnType
.getConstructor(Name.class, DataType.class, RecordQualifier.class, Comment.class, Binding.class)
.newInstance(name, actualType, qualifier == null ? path.asQualifier() : qualifier, DSL.comment(comment), actualBinding);
}
/**
* Factory method for embeddable types.
*/

View File

@ -78,7 +78,7 @@ import org.jooq.tools.StringUtils;
*/
final class TableFieldImpl<R extends Record, T>
extends
AbstractField<T>
AbstractField<T>
implements
TableField<R, T>,
SimpleQueryPart,
@ -201,11 +201,15 @@ implements
}
private final void accept1(Context<?> ctx) {
ctx.data(DATA_OMIT_CLAUSE_EVENT_EMISSION, true, c -> {
if (c.qualify() && getTable() != null && !FALSE.equals(ctx.data(DATA_RENDER_TABLE)))
c.visit(getTable()).sql('.');
accept2(ctx, getTable(), getUnqualifiedName());
}
c.visit(getUnqualifiedName());
static final void accept2(Context<?> ctx, Table<?> table, Name unqualifiedName) {
ctx.data(DATA_OMIT_CLAUSE_EVENT_EMISSION, true, c -> {
if (c.qualify() && table != null && !FALSE.equals(ctx.data(DATA_RENDER_TABLE)))
c.visit(table).sql('.');
c.visit(unqualifiedName);
});
}

View File

@ -658,8 +658,14 @@ final class Tools {
DATA_PARSE_ON_CONFLICT,
/**
* [#13808] We're in a store assignment context (e.g.
* <code>UPDATE</code> or assignment statement).
* [#228] [#13808] We're in a store assignment context.
* <p>
* This includes e.g.
* <ul>
* <li><code>INSERT</code> columns list.</li>
* <li><code>UPDATE SET</code> clause.</li>
* <li>The procedural assignment statement.</li>
* </ul>
*/
DATA_STORE_ASSIGNMENT,
@ -668,7 +674,6 @@ final class Tools {
* not the query itself.
*/
DATA_RENDER_IMPLICIT_JOIN,
;
private final boolean resetInSubqueryScope;

View File

@ -0,0 +1,248 @@
/*
* Licensed 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
*
* https://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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import static java.lang.Boolean.TRUE;
import static org.jooq.impl.SchemaImpl.DEFAULT_SCHEMA;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_STORE_ASSIGNMENT;
import static org.jooq.tools.StringUtils.defaultIfNull;
import java.util.stream.Stream;
import org.jooq.Binding;
import org.jooq.Catalog;
import org.jooq.Clause;
import org.jooq.Comment;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.Name;
import org.jooq.Package;
import org.jooq.QueryPart;
import org.jooq.Record;
import org.jooq.RecordQualifier;
// ...
import org.jooq.Row;
import org.jooq.Schema;
import org.jooq.Table;
// ...
import org.jooq.UDT;
import org.jooq.UDTPathField;
import org.jooq.UDTRecord;
import org.jooq.impl.QOM.UEmpty;
import org.jooq.impl.QOM.UNotYetImplemented;
import org.jooq.impl.Tools.BooleanDataKey;
import org.jooq.tools.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ApiStatus.Internal;
/**
* A common base type for UDT path fields.
*
* @author Lukas Eder
*/
@Internal
public /* non-final */ class UDTPathFieldImpl<R extends Record, U extends UDTRecord<U>, T>
extends
AbstractField<T>
implements
UDTPathField<R, U, T>,
SimpleQueryPart,
TypedReference<T>,
ScopeMappable,
UEmpty
{
private final RecordQualifier<R> qualifier;
private final UDT<U> udt;
UDTPathFieldImpl(Name name, DataType<T> type, RecordQualifier<R> qualifier, UDT<U> udt, Comment comment, Binding<?, T> binding) {
super(qualify(qualifier, name), type, comment, binding);
this.qualifier = qualifier;
this.udt = udt;
qualifier.$name();
}
@Override
public final RecordQualifier<R> getQualifier() {
return qualifier;
}
@Override
public final RecordQualifier<U> asQualifier() {
return new UDTPathFieldImplAsQualifier();
}
// [#228] TODO Refactor this logic into UDTImpl
private class UDTPathFieldImplAsQualifier
extends AbstractNamed
implements
RecordQualifier<U>,
FieldsTrait,
UNotYetImplemented
{
UDTPathFieldImplAsQualifier() {
super(UDTPathFieldImpl.this.getQualifiedName(), UDTPathFieldImpl.this.getCommentPart());
}
RecordQualifier<R> getQualifier() {
return UDTPathFieldImpl.this.getQualifier();
}
@Override
public final Catalog getCatalog() {
return null;
}
@Override
public final Schema getSchema() {
return null;
}
@Override
public final Schema $schema() {
return null;
}
@Override
public final Row fieldsRow() {
return getUDT().fieldsRow();
}
@Override
public final Package getPackage() {
return getUDT().getPackage();
}
@Override
public final Class<? extends U> getRecordType() {
return getUDT().getRecordType();
}
@Override
public final DataType<U> getDataType() {
return getUDT().getDataType();
}
@Override
public final U newRecord() {
return getUDT().newRecord();
}
@Override
public final void accept(Context<?> ctx) {
UDTPathFieldImpl.this.accept(ctx);
}
}
@Override
public final UDT<U> getUDT() {
return udt;
}
// ------------------------------------------------------------------------
// XXX: QueryPart API
// ------------------------------------------------------------------------
@Override
final boolean parenthesised(Context<?> ctx) {
return true;
}
@Override
public boolean declaresFields() {
return super.declaresFields();
}
@Override
public final Clause[] clauses(Context<?> ctx) {
return null;
}
@Override
public final void accept(Context<?> ctx) {
RecordQualifier<R> q = getQualifier();
// [#228] The disambiguating wrapping in parentheses is only required in references to
// a UDT path identifier expression, not in assignments (where it is disallowed)
if (!TRUE.equals(ctx.data(DATA_STORE_ASSIGNMENT)) && q instanceof UDTPathFieldImpl.UDTPathFieldImplAsQualifier && ((UDTPathFieldImpl<?, ?, ?>.UDTPathFieldImplAsQualifier) q).getQualifier() instanceof Table)
ctx.sql('(').visit(q).sql(").").visit(getUnqualifiedName());
else if (q instanceof Table<?> t)
TableFieldImpl.accept2(ctx, t, getUnqualifiedName());
else
ctx.visit(q).sql('.').visit(getUnqualifiedName());
}
// -------------------------------------------------------------------------
// XXX: Query Object Model
// -------------------------------------------------------------------------
// ------------------------------------------------------------------------
// XXX: Object API
// ------------------------------------------------------------------------
@Override
public int hashCode() {
return defaultIfNull(getQualifier().getSchema(), DEFAULT_SCHEMA.get()).getQualifiedName()
.append(getQualifier().getUnqualifiedName())
.append(getUDT().getUnqualifiedName())
.append(getUnqualifiedName()).hashCode();
}
@Override
public boolean equals(Object that) {
if (this == that)
return true;
// [#2144] UDTPathFieldImpl equality can be decided without executing the
// rather expensive implementation of AbstractQueryPart.equals()
if (that instanceof UDTPathField<?, ?, ?> other) {
return
StringUtils.equals(getQualifier(), other.getQualifier()) &&
StringUtils.equals(getUDT(), other.getUDT()) &&
StringUtils.equals(getName(), other.getName());
}
return super.equals(that);
}
}

View File

@ -0,0 +1,76 @@
/*
* Licensed 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
*
* https://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.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import org.jooq.Binding;
import org.jooq.Comment;
import org.jooq.DataType;
import org.jooq.Name;
import org.jooq.Record;
import org.jooq.RecordQualifier;
import org.jooq.Table;
import org.jooq.UDT;
import org.jooq.UDTPathField;
import org.jooq.UDTPathTableField;
import org.jooq.UDTRecord;
import org.jetbrains.annotations.ApiStatus.Internal;
/**
* A common base type for table fields that are also {@link UDTPathField}.
*
* @author Lukas Eder
*/
@Internal
public /* non-final */ class UDTPathTableFieldImpl<R extends Record, U extends UDTRecord<U>, T>
extends
UDTPathFieldImpl<R, U, T>
implements
UDTPathTableField<R, U, T>
{
public UDTPathTableFieldImpl(Name name, DataType<T> type, RecordQualifier<R> qualifier, UDT<U> udt, Comment comment, Binding<?, T> binding) {
super(name, type, qualifier, udt, comment, binding);
}
@Override
public final Table<R> getTable() {
return getQualifier() instanceof Table<R> t ? t : null;
}
}

View File

@ -118,7 +118,7 @@ public class Reflect {
* <pre><code>
* Supplier&lt;String&gt; supplier = Reflect.compile("org.joor.Test", """
* package org.joor;
* @MyAnnotation
* &#64;MyAnnotation
* class Test implements java.util.function.Supplier&lt;String&gt; {
* public String get() {
* return "Hello World!";
@ -146,7 +146,7 @@ public class Reflect {
* <pre><code>
* Supplier&lt;String&gt; supplier = Reflect.compile("org.joor.Test", """
* package org.joor;
* @MyAnnotation
* &#64;MyAnnotation
* class Test implements java.util.function.Supplier&lt;String&gt; {
* public String get() {
* return "Hello World!";