diff --git a/jOOQ/src/main/java/org/jooq/conf/Settings.java b/jOOQ/src/main/java/org/jooq/conf/Settings.java index f40b998c49..fad547693f 100644 --- a/jOOQ/src/main/java/org/jooq/conf/Settings.java +++ b/jOOQ/src/main/java/org/jooq/conf/Settings.java @@ -95,6 +95,8 @@ public class Settings protected Boolean renderOutputForSQLServerReturningClause = true; @XmlElement(defaultValue = "false") protected Boolean renderParenthesisAroundSetOperationQueries = false; + @XmlElement(defaultValue = ".") + protected String namePathSeparator = "."; @XmlElement(defaultValue = "false") protected Boolean bindOffsetDateTimeType = false; @XmlElement(defaultValue = "false") @@ -867,6 +869,44 @@ public class Settings this.renderParenthesisAroundSetOperationQueries = value; } + /** + * The character(s) to be used as a separator in paths encoded in a {@link Name} + *

+ * A few hierarchical mapping features work with paths encoded in names (specifically field aliases), such as the reflective mapping of nested values when aliasing fields as: + *

+ *

+     * SELECT 
+     *   a.first_name AS "book.author.firstName"
+     *   a.last_name AS "book.author.lastName"
+     * FROM ...
+     * 
+ *

+ * Not all dialects support "." in identifiers. This setting allows for specifying an alternative String to use as separator, e.g. "__". + * + */ + public String getNamePathSeparator() { + return namePathSeparator; + } + + /** + * The character(s) to be used as a separator in paths encoded in a {@link Name} + *

+ * A few hierarchical mapping features work with paths encoded in names (specifically field aliases), such as the reflective mapping of nested values when aliasing fields as: + *

+ *

+     * SELECT 
+     *   a.first_name AS "book.author.firstName"
+     *   a.last_name AS "book.author.lastName"
+     * FROM ...
+     * 
+ *

+ * Not all dialects support "." in identifiers. This setting allows for specifying an alternative String to use as separator, e.g. "__". + * + */ + public void setNamePathSeparator(String value) { + this.namePathSeparator = value; + } + /** * Whether the java.time (JSR 310) type {@link java.time.OffsetDateTime} should be bound natively to JDBC. *

@@ -2987,6 +3027,26 @@ public class Settings return this; } + /** + * The character(s) to be used as a separator in paths encoded in a {@link Name} + *

+ * A few hierarchical mapping features work with paths encoded in names (specifically field aliases), such as the reflective mapping of nested values when aliasing fields as: + *

+ *

+     * SELECT 
+     *   a.first_name AS "book.author.firstName"
+     *   a.last_name AS "book.author.lastName"
+     * FROM ...
+     * 
+ *

+ * Not all dialects support "." in identifiers. This setting allows for specifying an alternative String to use as separator, e.g. "__". + * + */ + public Settings withNamePathSeparator(String value) { + setNamePathSeparator(value); + return this; + } + public Settings withBindOffsetDateTimeType(Boolean value) { setBindOffsetDateTimeType(value); return this; @@ -3752,6 +3812,7 @@ public class Settings builder.append("renderOrderByRownumberForEmulatedPagination", renderOrderByRownumberForEmulatedPagination); builder.append("renderOutputForSQLServerReturningClause", renderOutputForSQLServerReturningClause); builder.append("renderParenthesisAroundSetOperationQueries", renderParenthesisAroundSetOperationQueries); + builder.append("namePathSeparator", namePathSeparator); builder.append("bindOffsetDateTimeType", bindOffsetDateTimeType); builder.append("bindOffsetTimeType", bindOffsetTimeType); builder.append("fetchTriggerValuesAfterSQLServerOutput", fetchTriggerValuesAfterSQLServerOutput); @@ -4080,6 +4141,15 @@ public class Settings return false; } } + if (namePathSeparator == null) { + if (other.namePathSeparator!= null) { + return false; + } + } else { + if (!namePathSeparator.equals(other.namePathSeparator)) { + return false; + } + } if (bindOffsetDateTimeType == null) { if (other.bindOffsetDateTimeType!= null) { return false; @@ -4930,6 +5000,7 @@ public class Settings result = ((prime*result)+((renderOrderByRownumberForEmulatedPagination == null)? 0 :renderOrderByRownumberForEmulatedPagination.hashCode())); result = ((prime*result)+((renderOutputForSQLServerReturningClause == null)? 0 :renderOutputForSQLServerReturningClause.hashCode())); result = ((prime*result)+((renderParenthesisAroundSetOperationQueries == null)? 0 :renderParenthesisAroundSetOperationQueries.hashCode())); + result = ((prime*result)+((namePathSeparator == null)? 0 :namePathSeparator.hashCode())); result = ((prime*result)+((bindOffsetDateTimeType == null)? 0 :bindOffsetDateTimeType.hashCode())); result = ((prime*result)+((bindOffsetTimeType == null)? 0 :bindOffsetTimeType.hashCode())); result = ((prime*result)+((fetchTriggerValuesAfterSQLServerOutput == null)? 0 :fetchTriggerValuesAfterSQLServerOutput.hashCode())); diff --git a/jOOQ/src/main/java/org/jooq/impl/ConstantSortField.java b/jOOQ/src/main/java/org/jooq/impl/ConstantSortField.java index 684df3bdf4..169de689f7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ConstantSortField.java +++ b/jOOQ/src/main/java/org/jooq/impl/ConstantSortField.java @@ -69,6 +69,7 @@ final class ConstantSortField extends CustomField { + case DERBY: case HSQLDB: case POSTGRES: diff --git a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java index 042c230a3d..f6558e64e7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CursorImpl.java @@ -1524,7 +1524,7 @@ final class CursorImpl extends AbstractCursor { Field f = field instanceof Coerce ? ((Coerce) field).field : field; if (f instanceof RowField && NO_NATIVE_SUPPORT.contains(ctx.dialect())) { - nested = ((RowField) f).emulatedFields(); + nested = ((RowField) f).emulatedFields(configuration); recordType = Tools.recordType(nested.size()); } else if (f.getDataType().isEmbeddable()) { diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java index 94a2122fe2..2b2ed21e0c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java @@ -316,6 +316,7 @@ public class DefaultRecordMapper implements RecordMappertype. @@ -351,6 +352,7 @@ public class DefaultRecordMapper implements RecordMapper implements RecordMapper -1) { - String prefix = name.substring(0, dot); + if (separator > -1) { + String prefix = name.substring(0, separator); if (nestedMappedFields == null) nestedMappedFields = new HashMap<>(); @@ -1026,8 +1028,8 @@ public class DefaultRecordMapper implements RecordMapper field = fields[i]; String name = field.getName(); - int dot = name.indexOf('.'); - propertyIndexes[i] = prefixes().get(dot > -1 ? name.substring(0, dot) : name); + int separator = name.indexOf(namePathSeparator); + propertyIndexes[i] = prefixes().get(separator > -1 ? name.substring(0, separator) : name); } } } @@ -1061,7 +1063,7 @@ public class DefaultRecordMapper implements RecordMapper implements RecordMapper implements RecordMapper field : fields) { String name = field.getName(); - int dot = name.indexOf('.'); - prefixes.computeIfAbsent(dot > -1 ? name.substring(0, dot) : name, k -> i[0]++); + int separator = name.indexOf(namePathSeparator); + prefixes.computeIfAbsent(separator > -1 ? name.substring(0, separator) : name, k -> i[0]++); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java index c4255bafc9..c591bc404a 100755 --- a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java @@ -76,6 +76,7 @@ import static org.jooq.JoinType.RIGHT_OUTER_JOIN; // ... // ... // ... +// ... import static org.jooq.SQLDialect.CUBRID; // ... // ... diff --git a/jOOQ/src/main/java/org/jooq/impl/Limit.java b/jOOQ/src/main/java/org/jooq/impl/Limit.java index bc4f5cd60f..cc24dbc8b1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Limit.java +++ b/jOOQ/src/main/java/org/jooq/impl/Limit.java @@ -301,12 +301,6 @@ final class Limit extends AbstractQueryPart { - - - - - - default: { acceptDefault(ctx, castMode); break; diff --git a/jOOQ/src/main/java/org/jooq/impl/RowField.java b/jOOQ/src/main/java/org/jooq/impl/RowField.java index bb477ea56e..89d25f249f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowField.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowField.java @@ -75,6 +75,7 @@ import static org.jooq.impl.Tools.BooleanDataKey.DATA_LIST_ALREADY_INDENTED; import java.util.Set; +import org.jooq.Configuration; import org.jooq.Context; import org.jooq.Field; import org.jooq.Name; @@ -90,7 +91,6 @@ final class RowField extends AbstractField< static final Set NO_NATIVE_SUPPORT = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, IGNITE, MARIADB, MYSQL, SQLITE); private final ROW row; - private final AbstractRow emulatedFields; RowField(ROW row) { this(row, N_ROW); @@ -113,11 +113,11 @@ final class RowField extends AbstractField< ))); this.row = row; - this.emulatedFields = (AbstractRow) row0(map(row.fields(), x -> x.as(as.unquotedName() + "." + x.getName()), Field[]::new)); } - AbstractRow emulatedFields() { - return emulatedFields; + @SuppressWarnings("unchecked") + AbstractRow emulatedFields(Configuration configuration) { + return (AbstractRow) row0(map(row.fields(), x -> x.as(getUnqualifiedName().unquotedName() + configuration.settings().getNamePathSeparator() + x.getName()), Field[]::new)); } ROW row() { @@ -127,7 +127,7 @@ final class RowField extends AbstractField< @Override public final void accept(Context ctx) { if (NO_NATIVE_SUPPORT.contains(ctx.dialect())) - ctx.data(DATA_LIST_ALREADY_INDENTED, true, c -> c.visit(new SelectFieldList<>(emulatedFields.fields.fields))); + ctx.data(DATA_LIST_ALREADY_INDENTED, true, c -> c.visit(new SelectFieldList<>(emulatedFields(ctx.configuration()).fields.fields))); diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index b4e9615a21..d8d883161c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -1761,6 +1761,7 @@ final class SelectQueryImpl extends AbstractResultQuery imp + case CUBRID: diff --git a/jOOQ/src/main/resources/xsd/jooq-runtime-3.15.0.xsd b/jOOQ/src/main/resources/xsd/jooq-runtime-3.15.0.xsd index e7fd2ba3b1..ab7b273fed 100644 --- a/jOOQ/src/main/resources/xsd/jooq-runtime-3.15.0.xsd +++ b/jOOQ/src/main/resources/xsd/jooq-runtime-3.15.0.xsd @@ -195,6 +195,21 @@ When this setting is set to true the queries combined with set oper For details, see https://github.com/jOOQ/jOOQ/issues/3676 and https://github.com/jOOQ/jOOQ/issues/9751.]]> + + +A few hierarchical mapping features work with paths encoded in names (specifically field aliases), such as the reflective mapping of nested values when aliasing fields as: +

+

+SELECT 
+  a.first_name AS "book.author.firstName"
+  a.last_name AS "book.author.lastName"
+FROM ...
+
+

+Not all dialects support "." in identifiers. This setting allows for specifying an alternative String to use as separator, e.g. "__".]]> + + java.time (JSR 310) type {@link java.time.OffsetDateTime} should be bound natively to JDBC.