提交 583e6341 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Use shared code between HASH() and ORA_HASH() and make last argument of HASH() optional

上级 789d7322
...@@ -3426,7 +3426,7 @@ CALL TRIM(CHAR(0) FROM UTF8TOSTRING( ...@@ -3426,7 +3426,7 @@ CALL TRIM(CHAR(0) FROM UTF8TOSTRING(
" "
"Functions (Numeric)","HASH"," "Functions (Numeric)","HASH","
HASH(algorithmString, dataBytes, iterationInt) HASH(algorithmString, expression [, iterationInt])
"," ","
Calculate the hash value using an algorithm, and repeat this process for a number of iterations. Calculate the hash value using an algorithm, and repeat this process for a number of iterations.
Currently, the only algorithm supported is SHA256. Currently, the only algorithm supported is SHA256.
......
...@@ -23,6 +23,8 @@ Change Log ...@@ -23,6 +23,8 @@ Change Log
<ul> <ul>
<li>Issue #1038: ora_hash function implementation off by one <li>Issue #1038: ora_hash function implementation off by one
</li> </li>
<li>PR #1054: Introduce overflow bit in tx state
</li>
<li>Issue #1047: Support DISTINCT in custom aggregate functions <li>Issue #1047: Support DISTINCT in custom aggregate functions
</li> </li>
<li>PR #1051: Atomic change of transaction state <li>PR #1051: Atomic change of transaction state
......
...@@ -31,7 +31,6 @@ import org.h2.schema.Schema; ...@@ -31,7 +31,6 @@ import org.h2.schema.Schema;
import org.h2.schema.Sequence; import org.h2.schema.Sequence;
import org.h2.security.BlockCipher; import org.h2.security.BlockCipher;
import org.h2.security.CipherFactory; import org.h2.security.CipherFactory;
import org.h2.security.SHA256;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
...@@ -213,7 +212,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -213,7 +212,7 @@ public class Function extends Expression implements FunctionCall {
addFunction("TRUNCATE", TRUNCATE, VAR_ARGS, Value.NULL); addFunction("TRUNCATE", TRUNCATE, VAR_ARGS, Value.NULL);
// same as TRUNCATE // same as TRUNCATE
addFunction("TRUNC", TRUNCATE, VAR_ARGS, Value.NULL); addFunction("TRUNC", TRUNCATE, VAR_ARGS, Value.NULL);
addFunction("HASH", HASH, 3, Value.BYTES); addFunction("HASH", HASH, VAR_ARGS, Value.BYTES);
addFunction("ENCRYPT", ENCRYPT, 3, Value.BYTES); addFunction("ENCRYPT", ENCRYPT, 3, Value.BYTES);
addFunction("DECRYPT", DECRYPT, 3, Value.BYTES); addFunction("DECRYPT", DECRYPT, 3, Value.BYTES);
addFunctionNotDeterministic("SECURE_RAND", SECURE_RAND, 1, Value.BYTES); addFunctionNotDeterministic("SECURE_RAND", SECURE_RAND, 1, Value.BYTES);
...@@ -1203,8 +1202,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1203,8 +1202,7 @@ public class Function extends Expression implements FunctionCall {
break; break;
} }
case HASH: case HASH:
result = ValueBytes.getNoCopy(getHash(v0.getString(), result = getHash(v0.getString(), v1, v2 == null ? 1 : v2.getInt());
v1.getBytesNoCopy(), v2.getInt()));
break; break;
case ENCRYPT: case ENCRYPT:
result = ValueBytes.getNoCopy(encrypt(v0.getString(), result = ValueBytes.getNoCopy(encrypt(v0.getString(),
...@@ -1728,14 +1726,22 @@ public class Function extends Expression implements FunctionCall { ...@@ -1728,14 +1726,22 @@ public class Function extends Expression implements FunctionCall {
return newData; return newData;
} }
private static byte[] getHash(String algorithm, byte[] bytes, int iterations) { private static Value getHash(String algorithm, Value value, int iterations) {
if (!"SHA256".equalsIgnoreCase(algorithm)) { if (!"SHA256".equalsIgnoreCase(algorithm)) {
throw DbException.getInvalidValueException("algorithm", algorithm); throw DbException.getInvalidValueException("algorithm", algorithm);
} }
for (int i = 0; i < iterations; i++) { if (iterations <= 0) {
bytes = SHA256.getHash(bytes, false); throw DbException.getInvalidValueException("iterations", iterations);
} }
return bytes; MessageDigest md = hashImpl(value, "SHA-256");
if (md == null) {
return ValueNull.INSTANCE;
}
byte[] b = md.digest();
for (int i = 1; i < iterations; i++) {
b = md.digest(b);
}
return ValueBytes.getNoCopy(b);
} }
private static String substring(String s, int start, int length) { private static String substring(String s, int start, int length) {
...@@ -1951,15 +1957,30 @@ public class Function extends Expression implements FunctionCall { ...@@ -1951,15 +1957,30 @@ public class Function extends Expression implements FunctionCall {
if ((seed & 0xffff_ffff_0000_0000L) != 0L) { if ((seed & 0xffff_ffff_0000_0000L) != 0L) {
throw DbException.getInvalidValueException("seed", seed); throw DbException.getInvalidValueException("seed", seed);
} }
MessageDigest md = hashImpl(value, "SHA-1");
if (md == null) {
return ValueNull.INSTANCE;
}
if (seed != 0L) {
byte[] b = new byte[4];
Bits.writeInt(b, 0, (int) seed);
md.update(b);
}
long hc = Bits.readLong(md.digest(), 0);
// Strip sign and use modulo operation to get value from 0 to bucket inclusive
return ValueLong.get((hc & Long.MAX_VALUE) % (bucket + 1));
}
private static MessageDigest hashImpl(Value value, String algorithm) {
MessageDigest md; MessageDigest md;
switch (value.getType()) { switch (value.getType()) {
case Value.NULL: case Value.NULL:
return ValueNull.INSTANCE; return null;
case Value.STRING: case Value.STRING:
case Value.STRING_FIXED: case Value.STRING_FIXED:
case Value.STRING_IGNORECASE: case Value.STRING_IGNORECASE:
try { try {
md = MessageDigest.getInstance("SHA-1"); md = MessageDigest.getInstance(algorithm);
md.update(value.getString().getBytes(StandardCharsets.UTF_8)); md.update(value.getString().getBytes(StandardCharsets.UTF_8));
} catch (Exception ex) { } catch (Exception ex) {
throw DbException.convert(ex); throw DbException.convert(ex);
...@@ -1968,7 +1989,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1968,7 +1989,7 @@ public class Function extends Expression implements FunctionCall {
case Value.BLOB: case Value.BLOB:
case Value.CLOB: case Value.CLOB:
try { try {
md = MessageDigest.getInstance("SHA-1"); md = MessageDigest.getInstance(algorithm);
byte[] buf = new byte[4096]; byte[] buf = new byte[4096];
try (InputStream is = value.getInputStream()) { try (InputStream is = value.getInputStream()) {
for (int r; (r = is.read(buf)) > 0; ) { for (int r; (r = is.read(buf)) > 0; ) {
...@@ -1981,20 +2002,13 @@ public class Function extends Expression implements FunctionCall { ...@@ -1981,20 +2002,13 @@ public class Function extends Expression implements FunctionCall {
break; break;
default: default:
try { try {
md = MessageDigest.getInstance("SHA-1"); md = MessageDigest.getInstance(algorithm);
md.update(value.getBytesNoCopy()); md.update(value.getBytesNoCopy());
} catch (Exception ex) { } catch (Exception ex) {
throw DbException.convert(ex); throw DbException.convert(ex);
} }
} }
if (seed != 0L) { return md;
byte[] b = new byte[4];
Bits.writeInt(b, 0, (int) seed);
md.update(b);
}
long hc = Bits.readLong(md.digest(), 0);
// Strip sign and use modulo operation to get value from 0 to bucket inclusive
return ValueLong.get((hc & Long.MAX_VALUE) % (bucket + 1));
} }
private static int makeRegexpFlags(String stringFlags) { private static int makeRegexpFlags(String stringFlags) {
...@@ -2082,6 +2096,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -2082,6 +2096,7 @@ public class Function extends Expression implements FunctionCall {
min = 1; min = 1;
max = 3; max = 3;
break; break;
case HASH:
case REPLACE: case REPLACE:
case LOCATE: case LOCATE:
case INSTR: case INSTR:
......
...@@ -3,8 +3,23 @@ ...@@ -3,8 +3,23 @@
-- Initial Developer: H2 Group -- Initial Developer: H2 Group
-- --
call hash('SHA256', 'Hello', 0);
> exception
call hash('SHA256', 'Hello');
>> 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969
call hash('SHA256', 'Hello', 1);
>> 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969
call hash('SHA256', stringtoutf8('Hello'), 1); call hash('SHA256', stringtoutf8('Hello'), 1);
>> 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969 >> 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969
CALL HASH('SHA256', 'Password', 1000);
>> c644a176ce920bde361ac336089b06cc2f1514dfa95ba5aabfe33f9a22d577f0
CALL HASH('SHA256', STRINGTOUTF8('Password'), 1000); CALL HASH('SHA256', STRINGTOUTF8('Password'), 1000);
>> c644a176ce920bde361ac336089b06cc2f1514dfa95ba5aabfe33f9a22d577f0 >> c644a176ce920bde361ac336089b06cc2f1514dfa95ba5aabfe33f9a22d577f0
call hash('unknown', 'Hello', 1);
> exception
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论