Skip to content

Commit

Permalink
Merge remote-tracking branch 'h2database/master'
Browse files Browse the repository at this point in the history
# Conflicts:
#	h2/src/docsrc/html/changelog.html
  • Loading branch information
andreitokar committed Aug 10, 2024
2 parents 5badbf9 + c0696ef commit 2e46a1c
Show file tree
Hide file tree
Showing 14 changed files with 93 additions and 30 deletions.
22 changes: 19 additions & 3 deletions h2/src/docsrc/html/advanced.html
Original file line number Diff line number Diff line change
Expand Up @@ -1526,12 +1526,16 @@ <h2 id="uuid">Universally Unique Identifiers (UUID)</h2>
<p>
This database supports UUIDs. Also supported is a function to create new UUIDs using
a cryptographically strong pseudo random number generator.
</p>
<p>
With random UUIDs, the chance of two having the same value can be calculated
using the probability theory. See also 'Birthday Paradox'.
Standardized randomly generated UUIDs have 122 random bits.
</p>
<p>
RFC 9562-compliant randomly generated UUIDs with version 4 have 122 random bits.
4 bits are used for the version (Randomly generated UUID), and 2 bits for the variant (Leach-Salz).
This database supports generating such UUIDs using the built-in function
<code>RANDOM_UUID()</code> or <code>UUID()</code>.
This database supports generating such UUIDs using the built-in function <code>RANDOM_UUID(4)</code>.
Please note that indexes on UUIDs with this version may have a poor performance.
Here is a small program to estimate the probability of having two identical UUIDs
after generating a number of values:
</p>
Expand Down Expand Up @@ -1564,6 +1568,18 @@ <h2 id="uuid">Universally Unique Identifiers (UUID)</h2>
that means the probability is about 0.000'000'000'06.
</p>

<p>
RFC 9562-compliant time-ordered UUIDs with version 7 have layout optimized for database systems.
They contain 48-bit number of milliseconds seconds since midnight 1 Jan 1970 UTC with leap seconds excluded
and additional 12-bit sub-millisecond timestamp fraction plus 62 random bits or 74 random bits without this fraction
depending on implementation.
</p>
<p>
This database supports generating such UUIDs using the built-in function <code>RANDOM_UUID(7)</code>.
This function produces 12-bit sub-millisecond timestamp fraction if high resolution timestamps are available in JVM
and 62 pseudo random bits.
</p>

<h2 id="spatial_features">Spatial Features</h2>
<p>
H2 supports the geometry data type and spatial indexes.
Expand Down
2 changes: 2 additions & 0 deletions h2/src/docsrc/html/changelog.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ <h2>Next Version (unreleased)</h2>

<h2>Version 2.3.232 (2024-08-11)</h2>
<ul>
<li>Issue #4005: Add optional version to RANDOM_UUID function and generator of version 7 in addition to existing version 4
</li>
<li>Issue #3945: Column not found in correlated subquery, when referencing outer column from LEFT JOIN .. ON clause
</li>
<li>Issue #4097: StackOverflowException when using multiple SELECT statements in one query (2.3.230)
Expand Down
1 change: 1 addition & 0 deletions h2/src/main/org/h2/command/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -4276,6 +4276,7 @@ private Expression readBuiltinFunctionIf(String upperName) {
case "SECURE_RAND":
return new RandFunction(readSingleArgument(), RandFunction.SECURE_RAND);
case "RANDOM_UUID":
return new RandFunction(readIfSingleArgument(), RandFunction.RANDOM_UUID);
case "UUID":
read(CLOSE_PAREN);
return new RandFunction(null, RandFunction.RANDOM_UUID);
Expand Down
2 changes: 1 addition & 1 deletion h2/src/main/org/h2/expression/function/RandFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public Value getValue(SessionLocal session) {
v = ValueVarbinary.getNoCopy(MathUtils.secureRandomBytes(v.getInt()));
break;
case RANDOM_UUID:
v = ValueUuid.getNewRandom();
v = ValueUuid.getNewRandom(v != null ? v.getInt() : 4);
break;
default:
throw DbException.getInternalError("function=" + function);
Expand Down
14 changes: 13 additions & 1 deletion h2/src/main/org/h2/mode/FunctionsMSSQLServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.h2.engine.SessionLocal;
import org.h2.expression.Expression;
import org.h2.expression.TypedValueExpression;
import org.h2.expression.ValueExpression;
import org.h2.expression.function.CoalesceFunction;
import org.h2.expression.function.CurrentDateTimeValueFunction;
import org.h2.expression.function.RandFunction;
Expand All @@ -19,6 +20,7 @@
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueBigint;
import org.h2.value.ValueInteger;
import org.h2.value.ValueNull;

/**
Expand Down Expand Up @@ -131,8 +133,18 @@ public Expression optimize(SessionLocal session) {
case ISNULL:
return new CoalesceFunction(CoalesceFunction.COALESCE, args).optimize(session);
case NEWID:
/*
* MS SQL Server uses version 4.
*/
return new RandFunction(ValueExpression.get(ValueInteger.get(4)), RandFunction.RANDOM_UUID)
.optimize(session);
case NEWSEQUENTIALID:
return new RandFunction(null, RandFunction.RANDOM_UUID).optimize(session);
/*
* MS SQL Server uses something non-standard, use standard version 7
* instead.
*/
return new RandFunction(ValueExpression.get(ValueInteger.get(7)), RandFunction.RANDOM_UUID)
.optimize(session);
case SCOPE_IDENTITY:
type = SCOPE_IDENTITY_TYPE;
break;
Expand Down
15 changes: 8 additions & 7 deletions h2/src/main/org/h2/mode/FunctionsOracle.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,9 @@ public final class FunctionsOracle extends ModeFunction {
private static final HashMap<String, FunctionInfo> FUNCTIONS = new HashMap<>();

static {
FUNCTIONS.put("ADD_MONTHS",
new FunctionInfo("ADD_MONTHS", ADD_MONTHS, 2, Value.TIMESTAMP, true, true));
FUNCTIONS.put("SYS_GUID",
new FunctionInfo("SYS_GUID", SYS_GUID, 0, Value.VARBINARY, false, false));
FUNCTIONS.put("TO_DATE",
new FunctionInfo("TO_DATE", TO_DATE, VAR_ARGS, Value.TIMESTAMP, true, true));
FUNCTIONS.put("ADD_MONTHS", new FunctionInfo("ADD_MONTHS", ADD_MONTHS, 2, Value.TIMESTAMP, true, true));
FUNCTIONS.put("SYS_GUID", new FunctionInfo("SYS_GUID", SYS_GUID, 0, Value.VARBINARY, false, false));
FUNCTIONS.put("TO_DATE", new FunctionInfo("TO_DATE", TO_DATE, VAR_ARGS, Value.TIMESTAMP, true, true));
FUNCTIONS.put("TO_TIMESTAMP",
new FunctionInfo("TO_TIMESTAMP", TO_TIMESTAMP, VAR_ARGS, Value.TIMESTAMP, true, true));
FUNCTIONS.put("TO_TIMESTAMP_TZ",
Expand Down Expand Up @@ -115,7 +112,11 @@ public Value getValue(SessionLocal session) {
result = DateTimeFunction.dateadd(session, DateTimeFunction.MONTH, v1.getInt(), v0);
break;
case SYS_GUID:
result = ValueUuid.getNewRandom().convertTo(TypeInfo.TYPE_VARBINARY);
/*
* Oracle actually uses version 8 (vendor-specific). Standard
* version 7 is more similar to it than default 4.
*/
result = ValueUuid.getNewRandom(7).convertTo(TypeInfo.TYPE_VARBINARY);
break;
case TO_DATE:
result = ToDateParser.toDate(session, v0.getString(), v1 == null ? null : v1.getString());
Expand Down
6 changes: 5 additions & 1 deletion h2/src/main/org/h2/mode/FunctionsPostgreSQL.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,11 @@ public Expression optimize(SessionLocal session) {
return new CurrentGeneralValueSpecification(CurrentGeneralValueSpecification.CURRENT_CATALOG)
.optimize(session);
case GEN_RANDOM_UUID:
return new RandFunction(null, RandFunction.RANDOM_UUID).optimize(session);
/*
* PostgresSQL uses version 4.
*/
return new RandFunction(ValueExpression.get(ValueInteger.get(4)), RandFunction.RANDOM_UUID)
.optimize(session);
default:
boolean allConst = optimizeArguments(session);
type = TypeInfo.getTypeInfo(info.returnDataType);
Expand Down
15 changes: 12 additions & 3 deletions h2/src/main/org/h2/res/help.csv
Original file line number Diff line number Diff line change
Expand Up @@ -4611,7 +4611,7 @@ JSON
"Data Types","UUID Type","
@h2@ UUID
","
Universally unique identifier. This is a 128 bit value.
RFC 9562-compliant universally unique identifier. This is a 128 bit value.
To store values, use ""PreparedStatement.setBytes"",
""setString"", or ""setObject(uuid)"" (where ""uuid"" is a ""java.util.UUID"").
""ResultSet.getObject"" will return a ""java.util.UUID"".
Expand All @@ -4620,6 +4620,7 @@ Please note that using an index on randomly generated data will
result on poor performance once there are millions of rows in a table.
The reason is that the cache behavior is very bad with randomly distributed data.
This is a problem for any database system.
To avoid this problem use UUID version 7 values.

For details, see the documentation of ""java.util.UUID"".
","
Expand Down Expand Up @@ -5295,15 +5296,23 @@ RAND()
"

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

Version 4 is a UUID with 122 pseudo random bits.
Please note that using an index on randomly generated data will
result on poor performance once there are millions of rows in a table.
The reason is that the cache behavior is very bad with randomly distributed data.
This is a problem for any database system.

Version 7 is a time-ordered UUID value with layout optimized for database systems.
It contains 48-bit number of milliseconds seconds since midnight 1 Jan 1970 UTC with leap seconds excluded,
additional 12-bit sub-millisecond timestamp fraction if available, and 62 pseudo random bits.
","
RANDOM_UUID(7)
RANDOM_UUID()
"

Expand Down
2 changes: 1 addition & 1 deletion h2/src/main/org/h2/table/Column.java
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ public void initializeSequence(SessionLocal session, Schema schema, int id, bool
String sequenceName;
do {
sequenceName = "SYSTEM_SEQUENCE_"
+ StringUtils.toUpperEnglish(ValueUuid.getNewRandom().getString().replace('-', '_'));
+ StringUtils.toUpperEnglish(ValueUuid.getNewRandom(4).getString().replace('-', '_'));
} while (schema.findSequence(sequenceName) != null);
identityOptions.setDataType(type);
Sequence seq = new Sequence(session, schema, id, sequenceName, identityOptions, true);
Expand Down
3 changes: 0 additions & 3 deletions h2/src/main/org/h2/util/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,12 @@
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.Collator;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.function.IntPredicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.h2.api.ErrorCode;
import org.h2.engine.SysProperties;
Expand Down
31 changes: 23 additions & 8 deletions h2/src/main/org/h2/value/ValueUuid.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

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

import java.time.Instant;
import java.util.UUID;

import org.h2.api.ErrorCode;
Expand Down Expand Up @@ -47,16 +48,30 @@ public int hashCode() {
/**
* Create a new UUID using the pseudo random number generator.
*
* @param version
* a version to use
* @return the new UUID
*/
public static ValueUuid getNewRandom() {
long high = MathUtils.secureRandomLong();
long low = MathUtils.secureRandomLong();
// version 4 (random)
high = (high & ~0xf000L) | 0x4000L;
// variant (Leach-Salz)
low = (low & 0x3fff_ffff_ffff_ffffL) | 0x8000_0000_0000_0000L;
return new ValueUuid(high, low);
public static ValueUuid getNewRandom(int version) {
long high, low;
switch (version) {
case 4:
high = MathUtils.secureRandomLong();
low = MathUtils.secureRandomLong();
break;
case 7: {
Instant now = Instant.now();
int nanos = now.getNano();
int sub = nanos % 1_000_000 * 2_000 / 488_281;
high = now.getEpochSecond() * 1_000L + nanos / 1_000_000 << 16 | sub;
low = MathUtils.secureRandomLong();
break;
}
default:
throw DbException.getInvalidValueException("RANDOM_UUID version", version);
}
return new ValueUuid((high & ~0xf000L) | version << 12,
/* variant 0b10 */ low & 0x3fff_ffff_ffff_ffffL | 0x8000_0000_0000_0000L);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ SELECT CHAR_LENGTH(CAST(RANDOM_UUID() AS VARCHAR));
SELECT RANDOM_UUID() = RANDOM_UUID();
>> FALSE

SELECT RANDOM_UUID(7) < RANDOM_UUID(7);
>> TRUE

SELECT RANDOM_UUID(100);
> exception INVALID_VALUE_2

SELECT NEWID();
> exception FUNCTION_NOT_FOUND_1

Expand Down
2 changes: 1 addition & 1 deletion h2/src/test/org/h2/test/unit/TestValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ private void testArray() {
private void testUUID() {
long maxHigh = 0, maxLow = 0, minHigh = -1L, minLow = -1L;
for (int i = 0; i < 100; i++) {
ValueUuid uuid = ValueUuid.getNewRandom();
ValueUuid uuid = ValueUuid.getNewRandom(4);
maxHigh |= uuid.getHigh();
maxLow |= uuid.getLow();
minHigh &= uuid.getHigh();
Expand Down
2 changes: 1 addition & 1 deletion h2/src/tools/org/h2/build/doc/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -856,4 +856,4 @@ bck clo cur hwm materializedview udca vol connectionpooldatasource xadatasource
ampm sssssff sstzh tzs yyyysssss newsequentialid solidus openjdk furthermore ssff secons nashorn fractions
btrim underscores ffl decomposed decomposition subfield infinities retryable salted establish
hatchet fis loom birthdate penrosed eve graalvm roberto polyglot truffle scriptengine unstarted conversation
recompilations normalizer tablename tablenames coarse
recompilations normalizer tablename tablenames coarse stale

0 comments on commit 2e46a1c

Please sign in to comment.