Skip to content

Commit 2e46a1c

Browse files
committed
Merge remote-tracking branch 'h2database/master'
# Conflicts: # h2/src/docsrc/html/changelog.html
2 parents 5badbf9 + c0696ef commit 2e46a1c

File tree

14 files changed

+93
-30
lines changed

14 files changed

+93
-30
lines changed

h2/src/docsrc/html/advanced.html

+19-3
Original file line numberDiff line numberDiff line change
@@ -1526,12 +1526,16 @@ <h2 id="uuid">Universally Unique Identifiers (UUID)</h2>
15261526
<p>
15271527
This database supports UUIDs. Also supported is a function to create new UUIDs using
15281528
a cryptographically strong pseudo random number generator.
1529+
</p>
1530+
<p>
15291531
With random UUIDs, the chance of two having the same value can be calculated
15301532
using the probability theory. See also 'Birthday Paradox'.
1531-
Standardized randomly generated UUIDs have 122 random bits.
1533+
</p>
1534+
<p>
1535+
RFC 9562-compliant randomly generated UUIDs with version 4 have 122 random bits.
15321536
4 bits are used for the version (Randomly generated UUID), and 2 bits for the variant (Leach-Salz).
1533-
This database supports generating such UUIDs using the built-in function
1534-
<code>RANDOM_UUID()</code> or <code>UUID()</code>.
1537+
This database supports generating such UUIDs using the built-in function <code>RANDOM_UUID(4)</code>.
1538+
Please note that indexes on UUIDs with this version may have a poor performance.
15351539
Here is a small program to estimate the probability of having two identical UUIDs
15361540
after generating a number of values:
15371541
</p>
@@ -1564,6 +1568,18 @@ <h2 id="uuid">Universally Unique Identifiers (UUID)</h2>
15641568
that means the probability is about 0.000'000'000'06.
15651569
</p>
15661570

1571+
<p>
1572+
RFC 9562-compliant time-ordered UUIDs with version 7 have layout optimized for database systems.
1573+
They contain 48-bit number of milliseconds seconds since midnight 1 Jan 1970 UTC with leap seconds excluded
1574+
and additional 12-bit sub-millisecond timestamp fraction plus 62 random bits or 74 random bits without this fraction
1575+
depending on implementation.
1576+
</p>
1577+
<p>
1578+
This database supports generating such UUIDs using the built-in function <code>RANDOM_UUID(7)</code>.
1579+
This function produces 12-bit sub-millisecond timestamp fraction if high resolution timestamps are available in JVM
1580+
and 62 pseudo random bits.
1581+
</p>
1582+
15671583
<h2 id="spatial_features">Spatial Features</h2>
15681584
<p>
15691585
H2 supports the geometry data type and spatial indexes.

h2/src/docsrc/html/changelog.html

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ <h2>Next Version (unreleased)</h2>
2727

2828
<h2>Version 2.3.232 (2024-08-11)</h2>
2929
<ul>
30+
<li>Issue #4005: Add optional version to RANDOM_UUID function and generator of version 7 in addition to existing version 4
31+
</li>
3032
<li>Issue #3945: Column not found in correlated subquery, when referencing outer column from LEFT JOIN .. ON clause
3133
</li>
3234
<li>Issue #4097: StackOverflowException when using multiple SELECT statements in one query (2.3.230)

h2/src/main/org/h2/command/Parser.java

+1
Original file line numberDiff line numberDiff line change
@@ -4276,6 +4276,7 @@ private Expression readBuiltinFunctionIf(String upperName) {
42764276
case "SECURE_RAND":
42774277
return new RandFunction(readSingleArgument(), RandFunction.SECURE_RAND);
42784278
case "RANDOM_UUID":
4279+
return new RandFunction(readIfSingleArgument(), RandFunction.RANDOM_UUID);
42794280
case "UUID":
42804281
read(CLOSE_PAREN);
42814282
return new RandFunction(null, RandFunction.RANDOM_UUID);

h2/src/main/org/h2/expression/function/RandFunction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public Value getValue(SessionLocal session) {
7474
v = ValueVarbinary.getNoCopy(MathUtils.secureRandomBytes(v.getInt()));
7575
break;
7676
case RANDOM_UUID:
77-
v = ValueUuid.getNewRandom();
77+
v = ValueUuid.getNewRandom(v != null ? v.getInt() : 4);
7878
break;
7979
default:
8080
throw DbException.getInternalError("function=" + function);

h2/src/main/org/h2/mode/FunctionsMSSQLServer.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.h2.engine.SessionLocal;
1212
import org.h2.expression.Expression;
1313
import org.h2.expression.TypedValueExpression;
14+
import org.h2.expression.ValueExpression;
1415
import org.h2.expression.function.CoalesceFunction;
1516
import org.h2.expression.function.CurrentDateTimeValueFunction;
1617
import org.h2.expression.function.RandFunction;
@@ -19,6 +20,7 @@
1920
import org.h2.value.TypeInfo;
2021
import org.h2.value.Value;
2122
import org.h2.value.ValueBigint;
23+
import org.h2.value.ValueInteger;
2224
import org.h2.value.ValueNull;
2325

2426
/**
@@ -131,8 +133,18 @@ public Expression optimize(SessionLocal session) {
131133
case ISNULL:
132134
return new CoalesceFunction(CoalesceFunction.COALESCE, args).optimize(session);
133135
case NEWID:
136+
/*
137+
* MS SQL Server uses version 4.
138+
*/
139+
return new RandFunction(ValueExpression.get(ValueInteger.get(4)), RandFunction.RANDOM_UUID)
140+
.optimize(session);
134141
case NEWSEQUENTIALID:
135-
return new RandFunction(null, RandFunction.RANDOM_UUID).optimize(session);
142+
/*
143+
* MS SQL Server uses something non-standard, use standard version 7
144+
* instead.
145+
*/
146+
return new RandFunction(ValueExpression.get(ValueInteger.get(7)), RandFunction.RANDOM_UUID)
147+
.optimize(session);
136148
case SCOPE_IDENTITY:
137149
type = SCOPE_IDENTITY_TYPE;
138150
break;

h2/src/main/org/h2/mode/FunctionsOracle.java

+8-7
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,9 @@ public final class FunctionsOracle extends ModeFunction {
3636
private static final HashMap<String, FunctionInfo> FUNCTIONS = new HashMap<>();
3737

3838
static {
39-
FUNCTIONS.put("ADD_MONTHS",
40-
new FunctionInfo("ADD_MONTHS", ADD_MONTHS, 2, Value.TIMESTAMP, true, true));
41-
FUNCTIONS.put("SYS_GUID",
42-
new FunctionInfo("SYS_GUID", SYS_GUID, 0, Value.VARBINARY, false, false));
43-
FUNCTIONS.put("TO_DATE",
44-
new FunctionInfo("TO_DATE", TO_DATE, VAR_ARGS, Value.TIMESTAMP, true, true));
39+
FUNCTIONS.put("ADD_MONTHS", new FunctionInfo("ADD_MONTHS", ADD_MONTHS, 2, Value.TIMESTAMP, true, true));
40+
FUNCTIONS.put("SYS_GUID", new FunctionInfo("SYS_GUID", SYS_GUID, 0, Value.VARBINARY, false, false));
41+
FUNCTIONS.put("TO_DATE", new FunctionInfo("TO_DATE", TO_DATE, VAR_ARGS, Value.TIMESTAMP, true, true));
4542
FUNCTIONS.put("TO_TIMESTAMP",
4643
new FunctionInfo("TO_TIMESTAMP", TO_TIMESTAMP, VAR_ARGS, Value.TIMESTAMP, true, true));
4744
FUNCTIONS.put("TO_TIMESTAMP_TZ",
@@ -115,7 +112,11 @@ public Value getValue(SessionLocal session) {
115112
result = DateTimeFunction.dateadd(session, DateTimeFunction.MONTH, v1.getInt(), v0);
116113
break;
117114
case SYS_GUID:
118-
result = ValueUuid.getNewRandom().convertTo(TypeInfo.TYPE_VARBINARY);
115+
/*
116+
* Oracle actually uses version 8 (vendor-specific). Standard
117+
* version 7 is more similar to it than default 4.
118+
*/
119+
result = ValueUuid.getNewRandom(7).convertTo(TypeInfo.TYPE_VARBINARY);
119120
break;
120121
case TO_DATE:
121122
result = ToDateParser.toDate(session, v0.getString(), v1 == null ? null : v1.getString());

h2/src/main/org/h2/mode/FunctionsPostgreSQL.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,11 @@ public Expression optimize(SessionLocal session) {
192192
return new CurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_CATALOG)
193193
.optimize(session);
194194
case GEN_RANDOM_UUID:
195-
return new RandFunction(null, RandFunction.RANDOM_UUID).optimize(session);
195+
/*
196+
* PostgresSQL uses version 4.
197+
*/
198+
return new RandFunction(ValueExpression.get(ValueInteger.get(4)), RandFunction.RANDOM_UUID)
199+
.optimize(session);
196200
default:
197201
boolean allConst = optimizeArguments(session);
198202
type = TypeInfo.getTypeInfo(info.returnDataType);

h2/src/main/org/h2/res/help.csv

+12-3
Original file line numberDiff line numberDiff line change
@@ -4611,7 +4611,7 @@ JSON
46114611
"Data Types","UUID Type","
46124612
@h2@ UUID
46134613
","
4614-
Universally unique identifier. This is a 128 bit value.
4614+
RFC 9562-compliant universally unique identifier. This is a 128 bit value.
46154615
To store values, use ""PreparedStatement.setBytes"",
46164616
""setString"", or ""setObject(uuid)"" (where ""uuid"" is a ""java.util.UUID"").
46174617
""ResultSet.getObject"" will return a ""java.util.UUID"".
@@ -4620,6 +4620,7 @@ Please note that using an index on randomly generated data will
46204620
result on poor performance once there are millions of rows in a table.
46214621
The reason is that the cache behavior is very bad with randomly distributed data.
46224622
This is a problem for any database system.
4623+
To avoid this problem use UUID version 7 values.
46234624

46244625
For details, see the documentation of ""java.util.UUID"".
46254626
","
@@ -5295,15 +5296,23 @@ RAND()
52955296
"
52965297

52975298
"Functions (Numeric)","RANDOM_UUID","
5298-
@h2@ { RANDOM_UUID | UUID } ()
5299+
@h2@ RANDOM_UUID([versionInt]) | UUID()
52995300
","
5300-
Returns a new UUID with 122 pseudo random bits.
5301+
Returns a new RFC 9562-compliant UUID with the specified version.
5302+
If version is not specified, a default version will be used.
5303+
Current default is 4, but it may be changed in future versions of H2.
53015304

5305+
Version 4 is a UUID with 122 pseudo random bits.
53025306
Please note that using an index on randomly generated data will
53035307
result on poor performance once there are millions of rows in a table.
53045308
The reason is that the cache behavior is very bad with randomly distributed data.
53055309
This is a problem for any database system.
5310+
5311+
Version 7 is a time-ordered UUID value with layout optimized for database systems.
5312+
It contains 48-bit number of milliseconds seconds since midnight 1 Jan 1970 UTC with leap seconds excluded,
5313+
additional 12-bit sub-millisecond timestamp fraction if available, and 62 pseudo random bits.
53065314
","
5315+
RANDOM_UUID(7)
53075316
RANDOM_UUID()
53085317
"
53095318

h2/src/main/org/h2/table/Column.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ public void initializeSequence(SessionLocal session, Schema schema, int id, bool
496496
String sequenceName;
497497
do {
498498
sequenceName = "SYSTEM_SEQUENCE_"
499-
+ StringUtils.toUpperEnglish(ValueUuid.getNewRandom().getString().replace('-', '_'));
499+
+ StringUtils.toUpperEnglish(ValueUuid.getNewRandom(4).getString().replace('-', '_'));
500500
} while (schema.findSequence(sequenceName) != null);
501501
identityOptions.setDataType(type);
502502
Sequence seq = new Sequence(session, schema, id, sequenceName, identityOptions, true);

h2/src/main/org/h2/util/StringUtils.java

-3
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,12 @@
1010
import java.net.URLEncoder;
1111
import java.nio.charset.StandardCharsets;
1212
import java.text.Collator;
13-
import java.text.Normalizer;
1413
import java.util.ArrayList;
1514
import java.util.Arrays;
1615
import java.util.HashSet;
1716
import java.util.Locale;
1817
import java.util.concurrent.TimeUnit;
1918
import java.util.function.IntPredicate;
20-
import java.util.regex.Matcher;
21-
import java.util.regex.Pattern;
2219

2320
import org.h2.api.ErrorCode;
2421
import org.h2.engine.SysProperties;

h2/src/main/org/h2/value/ValueUuid.java

+23-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import static org.h2.util.Bits.LONG_VH_BE;
99

10+
import java.time.Instant;
1011
import java.util.UUID;
1112

1213
import org.h2.api.ErrorCode;
@@ -47,16 +48,30 @@ public int hashCode() {
4748
/**
4849
* Create a new UUID using the pseudo random number generator.
4950
*
51+
* @param version
52+
* a version to use
5053
* @return the new UUID
5154
*/
52-
public static ValueUuid getNewRandom() {
53-
long high = MathUtils.secureRandomLong();
54-
long low = MathUtils.secureRandomLong();
55-
// version 4 (random)
56-
high = (high & ~0xf000L) | 0x4000L;
57-
// variant (Leach-Salz)
58-
low = (low & 0x3fff_ffff_ffff_ffffL) | 0x8000_0000_0000_0000L;
59-
return new ValueUuid(high, low);
55+
public static ValueUuid getNewRandom(int version) {
56+
long high, low;
57+
switch (version) {
58+
case 4:
59+
high = MathUtils.secureRandomLong();
60+
low = MathUtils.secureRandomLong();
61+
break;
62+
case 7: {
63+
Instant now = Instant.now();
64+
int nanos = now.getNano();
65+
int sub = nanos % 1_000_000 * 2_000 / 488_281;
66+
high = now.getEpochSecond() * 1_000L + nanos / 1_000_000 << 16 | sub;
67+
low = MathUtils.secureRandomLong();
68+
break;
69+
}
70+
default:
71+
throw DbException.getInvalidValueException("RANDOM_UUID version", version);
72+
}
73+
return new ValueUuid((high & ~0xf000L) | version << 12,
74+
/* variant 0b10 */ low & 0x3fff_ffff_ffff_ffffL | 0x8000_0000_0000_0000L);
6075
}
6176

6277
/**

h2/src/test/org/h2/test/scripts/functions/numeric/random-uuid.sql

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ SELECT CHAR_LENGTH(CAST(RANDOM_UUID() AS VARCHAR));
99
SELECT RANDOM_UUID() = RANDOM_UUID();
1010
>> FALSE
1111

12+
SELECT RANDOM_UUID(7) < RANDOM_UUID(7);
13+
>> TRUE
14+
15+
SELECT RANDOM_UUID(100);
16+
> exception INVALID_VALUE_2
17+
1218
SELECT NEWID();
1319
> exception FUNCTION_NOT_FOUND_1
1420

h2/src/test/org/h2/test/unit/TestValue.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ private void testArray() {
272272
private void testUUID() {
273273
long maxHigh = 0, maxLow = 0, minHigh = -1L, minLow = -1L;
274274
for (int i = 0; i < 100; i++) {
275-
ValueUuid uuid = ValueUuid.getNewRandom();
275+
ValueUuid uuid = ValueUuid.getNewRandom(4);
276276
maxHigh |= uuid.getHigh();
277277
maxLow |= uuid.getLow();
278278
minHigh &= uuid.getHigh();

h2/src/tools/org/h2/build/doc/dictionary.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -856,4 +856,4 @@ bck clo cur hwm materializedview udca vol connectionpooldatasource xadatasource
856856
ampm sssssff sstzh tzs yyyysssss newsequentialid solidus openjdk furthermore ssff secons nashorn fractions
857857
btrim underscores ffl decomposed decomposition subfield infinities retryable salted establish
858858
hatchet fis loom birthdate penrosed eve graalvm roberto polyglot truffle scriptengine unstarted conversation
859-
recompilations normalizer tablename tablenames coarse
859+
recompilations normalizer tablename tablenames coarse stale

0 commit comments

Comments
 (0)