提交 d48f308c authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Do not load the whole LOBs into memory for comparison operation

上级 1e462004
...@@ -29,6 +29,22 @@ public final class Bits { ...@@ -29,6 +29,22 @@ public final class Bits {
*/ */
private static final VarHandle LONG_VH = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); private static final VarHandle LONG_VH = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN);
/**
* Compare the contents of two char arrays. If the content or length of the
* first array is smaller than the second array, -1 is returned. If the content
* or length of the second array is smaller than the first array, 1 is returned.
* If the contents and lengths are the same, 0 is returned.
*
* @param data1
* the first char array (must not be null)
* @param data2
* the second char array (must not be null)
* @return the result of the comparison (-1, 1 or 0)
*/
public static int compareNotNull(char[] data1, char[] data2) {
return Integer.signum(Arrays.compare(data1, data2));
}
/** /**
* Compare the contents of two byte arrays. If the content or length of the * Compare the contents of two byte arrays. If the content or length of the
* first array is smaller than the second array, -1 is returned. If the content * first array is smaller than the second array, -1 is returned. If the content
......
...@@ -20,6 +20,33 @@ public final class Bits { ...@@ -20,6 +20,33 @@ public final class Bits {
* h2/src/java9/precompiled/org/h2/util/Bits.class. * h2/src/java9/precompiled/org/h2/util/Bits.class.
*/ */
/**
* Compare the contents of two char arrays. If the content or length of the
* first array is smaller than the second array, -1 is returned. If the content
* or length of the second array is smaller than the first array, 1 is returned.
* If the contents and lengths are the same, 0 is returned.
*
* @param data1
* the first char array (must not be null)
* @param data2
* the second char array (must not be null)
* @return the result of the comparison (-1, 1 or 0)
*/
public static int compareNotNull(char[] data1, char[] data2) {
if (data1 == data2) {
return 0;
}
int len = Math.min(data1.length, data2.length);
for (int i = 0; i < len; i++) {
char b = data1[i];
char b2 = data2[i];
if (b != b2) {
return b > b2 ? 1 : -1;
}
}
return Integer.signum(data1.length - data2.length);
}
/** /**
* Compare the contents of two byte arrays. If the content or length of the * Compare the contents of two byte arrays. If the content or length of the
* first array is smaller than the second array, -1 is returned. If the content * first array is smaller than the second array, -1 is returned. If the content
......
...@@ -35,6 +35,8 @@ import org.h2.util.Utils; ...@@ -35,6 +35,8 @@ import org.h2.util.Utils;
*/ */
public class ValueLob extends Value { public class ValueLob extends Value {
private static final int BLOCK_COMPARISON_SIZE = 512;
private static void rangeCheckUnknown(long zeroBasedOffset, long length) { private static void rangeCheckUnknown(long zeroBasedOffset, long length) {
if (zeroBasedOffset < 0) { if (zeroBasedOffset < 0) {
throw DbException.getInvalidValueException("offset", zeroBasedOffset + 1); throw DbException.getInvalidValueException("offset", zeroBasedOffset + 1);
...@@ -66,6 +68,87 @@ public class ValueLob extends Value { ...@@ -66,6 +68,87 @@ public class ValueLob extends Value {
} }
} }
/**
* Compares LOBs of the same type.
*
* @param valueType type
* @param v1 first LOB value
* @param v2 second LOB value
* @return result of comparison
*/
static int compare(int valueType, Value v1, Value v2) {
long prec1 = v1.getPrecision(), prec2 = v2.getPrecision();
if (Math.max(prec1, prec2) <= BLOCK_COMPARISON_SIZE) {
if (valueType == Value.BLOB) {
return Bits.compareNotNullSigned(v1.getBytesNoCopy(), v2.getBytesNoCopy());
} else {
return Integer.signum(v1.getString().compareTo(v2.getString()));
}
}
long minPrec = Math.min(prec1, prec2);
if (valueType == Value.BLOB) {
try (InputStream is1 = v1.getInputStream();
InputStream is2 = v2.getInputStream()) {
byte[] buf1 = new byte[BLOCK_COMPARISON_SIZE];
byte[] buf2 = new byte[BLOCK_COMPARISON_SIZE];
for (; minPrec >= BLOCK_COMPARISON_SIZE; minPrec -= BLOCK_COMPARISON_SIZE) {
if (IOUtils.readFully(is1, buf1, BLOCK_COMPARISON_SIZE) != BLOCK_COMPARISON_SIZE
|| IOUtils.readFully(is2, buf2, BLOCK_COMPARISON_SIZE) != BLOCK_COMPARISON_SIZE) {
throw DbException.getUnsupportedException("Invalid LOB");
}
int cmp = Bits.compareNotNullSigned(buf1, buf2);
if (cmp != 0) {
return cmp;
}
}
for (;;) {
int c1 = is1.read(), c2 = is2.read();
if (c1 < 0) {
return c2 < 0 ? 0 : -1;
}
if (c2 < 0) {
return 1;
}
if (c1 != c2) {
return Integer.compare(c1, c2);
}
}
} catch (IOException ex) {
throw DbException.convert(ex);
}
} else {
try (Reader reader1 = v1.getReader();
Reader reader2 = v2.getReader()) {
char[] buf1 = new char[BLOCK_COMPARISON_SIZE];
char[] buf2 = new char[BLOCK_COMPARISON_SIZE];
for (; minPrec >= BLOCK_COMPARISON_SIZE; minPrec -= BLOCK_COMPARISON_SIZE) {
if (IOUtils.readFully(reader1, buf1, BLOCK_COMPARISON_SIZE) != BLOCK_COMPARISON_SIZE
|| IOUtils.readFully(reader2, buf2, BLOCK_COMPARISON_SIZE) != BLOCK_COMPARISON_SIZE) {
throw DbException.getUnsupportedException("Invalid LOB");
}
int cmp = Bits.compareNotNull(buf1, buf2);
if (cmp != 0) {
return cmp;
}
}
for (;;) {
int c1 = reader1.read(), c2 = reader2.read();
if (c1 < 0) {
return c2 < 0 ? 0 : -1;
}
if (c2 < 0) {
return 1;
}
if (c1 != c2) {
return Integer.compare(c1, c2);
}
}
} catch (IOException ex) {
throw DbException.convert(ex);
}
}
}
/** /**
* Create a reader that is s subset of the given reader. * Create a reader that is s subset of the given reader.
* *
...@@ -430,11 +513,7 @@ public class ValueLob extends Value { ...@@ -430,11 +513,7 @@ public class ValueLob extends Value {
@Override @Override
protected int compareSecure(Value v, CompareMode mode) { protected int compareSecure(Value v, CompareMode mode) {
if (valueType == Value.CLOB) { return compare(valueType, this, v);
return Integer.signum(getString().compareTo(v.getString()));
}
byte[] v2 = v.getBytesNoCopy();
return Bits.compareNotNullSigned(getBytesNoCopy(), v2);
} }
@Override @Override
......
...@@ -26,7 +26,6 @@ import org.h2.store.LobStorageFrontend; ...@@ -26,7 +26,6 @@ import org.h2.store.LobStorageFrontend;
import org.h2.store.LobStorageInterface; import org.h2.store.LobStorageInterface;
import org.h2.store.RangeReader; import org.h2.store.RangeReader;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.util.Bits;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -386,11 +385,7 @@ public class ValueLobDb extends Value { ...@@ -386,11 +385,7 @@ public class ValueLobDb extends Value {
return 0; return 0;
} }
} }
if (valueType == Value.CLOB) { return ValueLob.compare(valueType, this, v);
return Integer.signum(getString().compareTo(v.getString()));
}
byte[] v2 = v.getBytesNoCopy();
return Bits.compareNotNullSigned(getBytesNoCopy(), v2);
} }
@Override @Override
...@@ -720,7 +715,7 @@ public class ValueLobDb extends Value { ...@@ -720,7 +715,7 @@ public class ValueLobDb extends Value {
* @param small the byte array * @param small the byte array
* @return the LOB * @return the LOB
*/ */
public static Value createSmallLob(int type, byte[] small) { public static ValueLobDb createSmallLob(int type, byte[] small) {
int precision; int precision;
if (type == Value.CLOB) { if (type == Value.CLOB) {
precision = new String(small, StandardCharsets.UTF_8).length(); precision = new String(small, StandardCharsets.UTF_8).length();
......
...@@ -70,6 +70,7 @@ public class TestValue extends TestDb { ...@@ -70,6 +70,7 @@ public class TestValue extends TestDb {
testModulusDouble(); testModulusDouble();
testModulusDecimal(); testModulusDecimal();
testModulusOperator(); testModulusOperator();
testLobComparison();
} }
private void testResultSetOperations() throws SQLException { private void testResultSetOperations() throws SQLException {
...@@ -415,4 +416,37 @@ public class TestValue extends TestDb { ...@@ -415,4 +416,37 @@ public class TestValue extends TestDb {
} }
} }
private void testLobComparison() {
assertEquals(0, testLobComparisonImpl(Value.BLOB, 0, 0, 0, 0));
assertEquals(0, testLobComparisonImpl(Value.CLOB, 0, 0, 0, 0));
assertEquals(-1, testLobComparisonImpl(Value.BLOB, 1, 1, 200, 210));
assertEquals(-1, testLobComparisonImpl(Value.CLOB, 1, 1, 'a', 'b'));
assertEquals(1, testLobComparisonImpl(Value.BLOB, 512, 512, 210, 200));
assertEquals(1, testLobComparisonImpl(Value.CLOB, 512, 512, 'B', 'A'));
assertEquals(1, testLobComparisonImpl(Value.BLOB, 1_024, 1_024, 210, 200));
assertEquals(1, testLobComparisonImpl(Value.CLOB, 1_024, 1_024, 'B', 'A'));
assertEquals(-1, testLobComparisonImpl(Value.BLOB, 10_000, 10_000, 200, 210));
assertEquals(-1, testLobComparisonImpl(Value.CLOB, 10_000, 10_000, 'a', 'b'));
assertEquals(0, testLobComparisonImpl(Value.BLOB, 10_000, 10_000, 0, 0));
assertEquals(0, testLobComparisonImpl(Value.CLOB, 10_000, 10_000, 0, 0));
assertEquals(-1, testLobComparisonImpl(Value.BLOB, 1_000, 10_000, 0, 0));
assertEquals(-1, testLobComparisonImpl(Value.CLOB, 1_000, 10_000, 0, 0));
assertEquals(1, testLobComparisonImpl(Value.BLOB, 10_000, 1_000, 0, 0));
assertEquals(1, testLobComparisonImpl(Value.CLOB, 10_000, 1_000, 0, 0));
}
private static int testLobComparisonImpl(int type, int size1, int size2, int suffix1, int suffix2) {
byte[] bytes1 = new byte[size1];
byte[] bytes2 = new byte[size2];
if (size1 > 0) {
bytes1[size1 - 1] = (byte) suffix1;
}
if (size2 > 0) {
bytes2[size2 - 1] = (byte) suffix2;
}
ValueLobDb lob1 = ValueLobDb.createSmallLob(type, bytes1);
ValueLobDb lob2 = ValueLobDb.createSmallLob(type, bytes2);
return lob1.compareTypeSafe(lob2, null);
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论