From 8c2cebaee95425581dd73307332ae7f251c79ed2 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 22 Feb 2023 13:04:19 +0100 Subject: [PATCH] [jOOQ/jOOQ#14671] Wrong column resolved by JoinTable.field(Field), when JoinTable contains aliased tables and lookup uses unaliased tables --- .../main/java/org/jooq/impl/FieldsImpl.java | 53 ++++++++++++++----- jOOQ/src/main/java/org/jooq/impl/Tools.java | 15 ++++++ 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java b/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java index 2100e35870..bc37fe2ddf 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldsImpl.java @@ -44,6 +44,7 @@ import static org.jooq.impl.Tools.converterOrFail; import static org.jooq.impl.Tools.indexOrFail; import static org.jooq.impl.Tools.map; import static org.jooq.impl.Tools.newRecord; +import static org.jooq.impl.Tools.unaliasTable; import java.sql.SQLWarning; import java.util.ArrayList; @@ -244,10 +245,16 @@ final class FieldsImpl extends AbstractQueryPart implements Re return result.result(f, i); } - // [#4283] table / column matches are better than only column matches - Field columnMatch = null; - Field columnMatch2 = null; - int indexMatch = -1; + // [#4283] table / column matches are better than column only matches + Field columnOnlyMatch = null; + Field columnOnlyMatch2 = null; + int columnOnlyIndexMatch = -1; + + // [#14671] column only matches might still match on the unaliased table + Field unaliased = null; + Field aliasMatch = null; + Field aliasMatch2 = null; + int aliasIndexMatch = -1; String tableName = tableName(field); String fieldName = field.getName(); @@ -265,26 +272,44 @@ final class FieldsImpl extends AbstractQueryPart implements Re // In case no exact match was found, return the first field with matching name if (fName.equals(fieldName)) { - if (columnMatch == null) { - columnMatch = f; - indexMatch = i; + + // [#14671] Prefer matches by unaliased tables, if applicable + if (unaliased == null) + unaliased = unaliasTable(field); + + if (unaliased != null && unaliased.equals(unaliasTable(f))) { + if (aliasMatch == null) { + aliasMatch = f; + aliasIndexMatch = i; + } + else + aliasMatch2 = f; + } + + if (columnOnlyMatch == null) { + columnOnlyMatch = f; + columnOnlyIndexMatch = i; } // [#4476] [#4477] This might be unintentional from a user // perspective, e.g. when ambiguous ID columns are present. // [#5578] Finish the loop, though, as we might have an exact match // despite some ambiguity - else { - columnMatch2 = f; - } + else + columnOnlyMatch2 = f; } } - if (columnMatch2 != null) - if (log.isInfoEnabled()) - log.info("Ambiguous match found for " + fieldName + ". Both " + columnMatch + " and " + columnMatch2 + " match.", new SQLWarning()); + if (aliasMatch2 != null && log.isInfoEnabled()) + log.info("Ambiguous match found for " + fieldName + ". Both " + aliasMatch + " and " + aliasMatch2 + " match.", new SQLWarning()); - return result.result(columnMatch, indexMatch); + if (aliasMatch != null) + return result.result(aliasMatch, aliasIndexMatch); + + if (columnOnlyMatch2 != null && log.isInfoEnabled()) + log.info("Ambiguous match found for " + fieldName + ". Both " + columnOnlyMatch + " and " + columnOnlyMatch2 + " match.", new SQLWarning()); + + return result.result(columnOnlyMatch, columnOnlyIndexMatch); } private final String tableName(Field field) { diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index ddccb17816..0bcc013c48 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -6132,6 +6132,21 @@ final class Tools { return result != null ? result : field; } + @SuppressWarnings("unchecked") + static final Field unaliasTable(Field field) { + if (field instanceof TableField tf) { + Table t = aliased(tf.getTable()); + + // [#14671] Use only the Field::getName for lookups to avoid: + // - StackOverflowError + // - Otherwise Field or Name related lookup efforts + if (t != null) + return (Field) t.field(field.getName()); + } + + return field; + } + static final Field aliased(Field field) { if (field instanceof FieldAlias f) return f.getAliasedField();