[jOOQ/jOOQ#10614] Avoid ClassCastException when comparing two column

values without natural order in Record::compareTo
This commit is contained in:
Lukas Eder 2020-09-10 14:38:30 +02:00
parent 373e725e9c
commit 4b8fcd9eff
3 changed files with 37 additions and 41 deletions

View File

@ -98,7 +98,7 @@ import org.jetbrains.annotations.Nullable;
* <p>
* jOOQ records have a natural ordering implemented in the same way as this is
* defined in the SQL standard. For more details, see the
* {@link #compareTo(Record)} method
* {@link #compareTo(Record)} method.
*
* @author Lukas Eder
* @see Result

View File

@ -1080,19 +1080,18 @@ abstract class AbstractRecord extends AbstractStore implements Record {
public int compareTo(Record that) {
// Note: keep this implementation in-sync with AbstractStore.equals()!
if (that == null) {
if (that == this)
return 0;
if (that == null)
throw new NullPointerException();
}
if (size() != that.size()) {
if (size() != that.size())
throw new ClassCastException(String.format("Trying to compare incomparable records (wrong degree):\n%s\n%s", this, that));
}
Class<?>[] thisTypes = this.fieldsRow().types();
Class<?>[] thatTypes = that.fieldsRow().types();
if (!asList(thisTypes).equals(asList(thatTypes))) {
if (!asList(thisTypes).equals(asList(thatTypes)))
throw new ClassCastException(String.format("Trying to compare incomparable records (type mismatch):\n%s\n%s", this, that));
}
for (int i = 0; i < size(); i++) {
final Object thisValue = get(i);
@ -1100,18 +1099,15 @@ abstract class AbstractRecord extends AbstractStore implements Record {
// [#1850] Only return -1/+1 early. In all other cases,
// continue checking the remaining fields
if (thisValue == null && thatValue == null) {
if (thisValue == null && thatValue == null)
continue;
}
// Order column values in a SQL NULLS LAST manner
else if (thisValue == null) {
else if (thisValue == null)
return 1;
}
else if (thatValue == null) {
else if (thatValue == null)
return -1;
}
// [#985] Compare arrays too.
else if (thisValue.getClass().isArray() && thatValue.getClass().isArray()) {
@ -1120,30 +1116,26 @@ abstract class AbstractRecord extends AbstractStore implements Record {
if (thisValue.getClass() == byte[].class) {
int compare = compare((byte[]) thisValue, (byte[]) thatValue);
if (compare != 0) {
if (compare != 0)
return compare;
}
}
// Other primitive types are not expected
else if (!thisValue.getClass().getComponentType().isPrimitive()) {
int compare = compare((Object[]) thisValue, (Object[]) thatValue);
if (compare != 0) {
if (compare != 0)
return compare;
}
}
else {
else
throw new ClassCastException(String.format("Unsupported data type in natural ordering: %s", thisValue.getClass()));
}
}
else {
int compare = ((Comparable) thisValue).compareTo(thatValue);
int compare = compare0(thisValue, thatValue);
if (compare != 0) {
if (compare != 0)
return compare;
}
}
}
@ -1161,9 +1153,8 @@ abstract class AbstractRecord extends AbstractStore implements Record {
int v1 = (array1[i] & 0xff);
int v2 = (array2[i] & 0xff);
if (v1 != v2) {
if (v1 != v2)
return v1 < v2 ? -1 : 1;
}
}
return array1.length - array2.length;
@ -1176,16 +1167,28 @@ abstract class AbstractRecord extends AbstractStore implements Record {
int length = Math.min(array1.length, array2.length);
for (int i = 0; i < length; i++) {
int compare = ((Comparable) array1[i]).compareTo(array2[i]);
int compare = compare0(array1[i], array2[i]);
if (compare != 0) {
if (compare != 0)
return compare;
}
}
return array1.length - array2.length;
}
/**
* Compare two uncomparable objects
*/
final int compare0(Object object1, Object object2) {
return object1 == object2
? 0
: object1 == null
? -1
: object2 == null
? 1
: object1.hashCode() - object2.hashCode();
}
// -------------------------------------------------------------------------
// XXX: Deprecated and discouraged methods
// -------------------------------------------------------------------------

View File

@ -155,9 +155,8 @@ abstract class AbstractStore extends AbstractFormattable implements Attachable {
@Override
public boolean equals(Object obj) {
if (this == obj) {
if (this == obj)
return true;
}
// Note: keep this implementation in-sync with AbstractRecord.compareTo()!
if (obj instanceof AbstractStore) {
@ -170,39 +169,33 @@ abstract class AbstractStore extends AbstractFormattable implements Attachable {
// [#1850] Only return false early. In all other cases,
// continue checking the remaining fields
if (thisValue == null && thatValue == null) {
if (thisValue == null && thatValue == null)
continue;
}
else if (thisValue == null || thatValue == null) {
else if (thisValue == null || thatValue == null)
return false;
}
// [#985] Compare arrays too.
else if (thisValue.getClass().isArray() && thatValue.getClass().isArray()) {
// Might be byte[]
if (thisValue.getClass() == byte[].class && thatValue.getClass() == byte[].class) {
if (!Arrays.equals((byte[]) thisValue, (byte[]) thatValue)) {
if (!Arrays.equals((byte[]) thisValue, (byte[]) thatValue))
return false;
}
}
// Other primitive types are not expected
else if (!thisValue.getClass().getComponentType().isPrimitive() &&
!thatValue.getClass().getComponentType().isPrimitive()) {
if (!Arrays.equals((Object[]) thisValue, (Object[]) thatValue)) {
if (!Arrays.equals((Object[]) thisValue, (Object[]) thatValue))
return false;
}
}
else {
else
return false;
}
}
else if (!thisValue.equals(thatValue)) {
else if (!thisValue.equals(thatValue))
return false;
}
}
// If we got through the above loop, the two records are equal