[#8040] Prevent excess loop in Fields.indexOf() methods

This commit is contained in:
lukaseder 2018-11-13 16:13:56 +01:00
parent 936da988ab
commit 5ff097fcbd

View File

@ -84,46 +84,62 @@ final class Fields<R extends Record> extends AbstractQueryPart implements Record
@Override
@SuppressWarnings("unchecked")
public final <T> Field<T> field(Field<T> field) {
return (Field<T>) field0(field, RETURN_FIELD);
}
private final <U> U field0(Field<?> field, FieldOrIndex<U> result) {
if (field == null)
return null;
return result.resultNull();
// [#4540] Try finding a match by identity
for (Field<?> f : fields)
for (int i = 0; i < fields.length; i++) {
Field<?> f = fields[i];
if (f == field)
return (Field<T>) f;
return result.result(f, i);
}
// [#1802] Try finding an exact match (e.g. exact matching qualified name)
for (Field<?> f : fields)
for (int i = 0; i < fields.length; i++) {
Field<?> f = fields[i];
if (f.equals(field))
return (Field<T>) f;
return result.result(f, i);
}
// [#4283] table / column matches are better than only column matches
Field<?> columnMatch = null;
Field<?> columnMatch2 = null;
int indexMatch = -1;
String tableName = tableName(field);
String fieldName = field.getName();
for (Field<?> f : fields) {
for (int i = 0; i < fields.length; i++) {
Field<?> f = fields[i];
String fName = f.getName();
if (tableName != null) {
String tName = tableName(f);
if (tName != null && tableName.equals(tName) && fName.equals(fieldName))
return (Field<T>) f;
return result.result(f, i);
}
// In case no exact match was found, return the first field with matching name
if (fName.equals(fieldName)) {
if (columnMatch == null)
if (columnMatch == null) {
columnMatch = f;
else
// [#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
indexMatch = 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;
}
}
}
@ -131,7 +147,7 @@ final class Fields<R extends Record> extends AbstractQueryPart implements Record
if (log.isInfoEnabled())
log.info("Ambiguous match found for " + fieldName + ". Both " + columnMatch + " and " + columnMatch2 + " match.", new SQLWarning());
return (Field<T>) columnMatch;
return result.result(columnMatch, indexMatch);
}
private final String tableName(Field<?> field) {
@ -147,21 +163,34 @@ final class Fields<R extends Record> extends AbstractQueryPart implements Record
@Override
public final Field<?> field(String fieldName) {
return field0(fieldName, RETURN_FIELD);
}
private final <U> U field0(String fieldName, FieldOrIndex<U> result) {
if (fieldName == null)
return null;
return result.resultNull();
Field<?> columnMatch = null;
int indexMatch = -1;
for (Field<?> f : fields)
if (f.getName().equals(fieldName))
if (columnMatch == null)
for (int i = 0; i < fields.length; i++) {
Field<?> f = fields[i];
if (f.getName().equals(fieldName)) {
if (columnMatch == null) {
columnMatch = f;
else
// [#4476] [#4477] [#5046] This might be unintentional from a user
// perspective, e.g. when ambiguous ID columns are present.
log.info("Ambiguous match found for " + fieldName + ". Both " + columnMatch + " and " + f + " match.", new SQLWarning());
indexMatch = i;
}
return columnMatch;
// [#4476] [#4477] [#5046] This might be unintentional from a user
// perspective, e.g. when ambiguous ID columns are present.
else {
log.info("Ambiguous match found for " + fieldName + ". Both " + columnMatch + " and " + f + " match.", new SQLWarning());
}
}
}
return result.result(columnMatch, indexMatch);
}
@Override
@ -178,10 +207,14 @@ final class Fields<R extends Record> extends AbstractQueryPart implements Record
@Override
public final Field<?> field(Name name) {
if (name == null)
return null;
return field0(name, RETURN_FIELD);
}
return field(DSL.field(name));
private final <U> U field0(Name name, FieldOrIndex<U> result) {
if (name == null)
return result.resultNull();
return field0(DSL.field(name), result);
}
@Override
@ -263,34 +296,17 @@ final class Fields<R extends Record> extends AbstractQueryPart implements Record
@Override
public final int indexOf(Field<?> field) {
// Get an exact match, or a field with a similar name
Field<?> compareWith = field(field);
if (compareWith != null) {
int size = fields.length;
// [#4540] Match by identity first
for (int i = 0; i < size; i++)
if (fields[i] == compareWith)
return i;
for (int i = 0; i < size; i++)
if (fields[i].equals(compareWith))
return i;
}
return -1;
return field0(field, RETURN_INDEX);
}
@Override
public final int indexOf(String fieldName) {
return indexOf(field(fieldName));
return field0(fieldName, RETURN_INDEX);
}
@Override
public final int indexOf(Name fieldName) {
return indexOf(field(fieldName));
return field0(fieldName, RETURN_INDEX);
}
@Override
@ -397,6 +413,39 @@ final class Fields<R extends Record> extends AbstractQueryPart implements Record
fields = result;
}
// -------------------------------------------------------------------------
// XXX: [#8040] An abstraction over two possible return types.
// -------------------------------------------------------------------------
private static interface FieldOrIndex<U> {
U result(Field<?> field, int index);
U resultNull();
}
private static final FieldOrIndex<Field<?>> RETURN_FIELD = new FieldOrIndex<Field<?>>() {
@Override
public Field<?> result(Field<?> field, int index) {
return field;
}
@Override
public Field<?> resultNull() {
return null;
}
};
private static final FieldOrIndex<Integer> RETURN_INDEX = new FieldOrIndex<Integer>() {
@Override
public Integer result(Field<?> field, int index) {
return index;
}
@Override
public Integer resultNull() {
return -1;
}
};
// -------------------------------------------------------------------------
// XXX: Object API
// -------------------------------------------------------------------------