Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/test_head.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Test with CH HEAD
on:
schedule:
- cron: "55 10 * * *"
workflow_dispatch:
push:
branches:
- main
Expand Down Expand Up @@ -56,7 +57,7 @@ jobs:
strategy:
matrix:
protocol: ["http"]
r2dbc: ["1.0.0.RELEASE", "0.9.1.RELEASE"]
r2dbc: ["1.0.0.RELEASE"]
fail-fast: false
timeout-minutes: 10
name: R2DBC ${{ matrix.r2dbc }} + CH HEAD (${{ matrix.protocol }})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import com.clickhouse.data.ClickHouseFormat;
import com.clickhouse.data.ClickHouseRecord;
import com.clickhouse.data.ClickHouseUtils;
import com.clickhouse.data.value.UnsignedLong;
import com.clickhouse.logging.Logger;
import com.clickhouse.logging.LoggerFactory;

Expand All @@ -26,42 +27,60 @@
public final class ClickHouseTransaction implements Serializable {
/**
* This class encapsulates transaction ID, which is defined as
* {@code Tuple(snapshotVersion UInt64, localTxCounter UInt64, hostId UUID)}.
* {@code Tuple(snapshotVersion UInt64, localTxCounter UInt64, hostId UUID[, sessionNodeVersion Int64])}.
*/
public static class XID implements Serializable {
private static final long serialVersionUID = 4907177669971332404L;

public static final XID EMPTY = new XID(0L, 0L, new UUID(0L, 0L).toString());

private static long toLong(Object value) {
if (value instanceof Number) {
return ((Number) value).longValue();
} else if (value != null) {
return Long.parseLong(value.toString());
}
throw new IllegalArgumentException("Non-null number is required");
}

/**
* Creates transaction ID from the given tuple.
*
* @param list non-null tuple with 3 elements
* @param list non-null tuple with 3 or 4 elements
* @return non-null transaction ID
*/
public static XID of(List<?> list) {
if (list == null || list.size() != 3) {
if (list == null || (list.size() != 3 && list.size() != 4)) {
throw new IllegalArgumentException(
"Non-null tuple with 3 elements(long, long, String) is required");
"Non-null tuple with 3 or 4 elements(long, long, String[, long]) is required");
}
long snapshotVersion = ((UnsignedLong) list.get(0)).longValue();
long localTxCounter = ((UnsignedLong) list.get(1)).longValue();
long snapshotVersion = toLong(list.get(0));
long localTxCounter = toLong(list.get(1));
String hostId = String.valueOf(list.get(2));
Long sessionNodeVersion = list.size() == 4 && list.get(3) != null
? toLong(list.get(3))
: null;
if (EMPTY.snapshotVersion == snapshotVersion && EMPTY.localTxCounter == localTxCounter
&& EMPTY.hostId.equals(hostId)) {
return EMPTY;
}
return new XID(snapshotVersion, localTxCounter, hostId);
return new XID(snapshotVersion, localTxCounter, hostId, sessionNodeVersion);
}

private final long snapshotVersion;
private final long localTxCounter;
private final String hostId;
private final Long sessionNodeVersion;

protected XID(long snapshotVersion, long localTxCounter, String hostId) {
this(snapshotVersion, localTxCounter, hostId, null);
}

protected XID(long snapshotVersion, long localTxCounter, String hostId, Long sessionNodeVersion) {
this.snapshotVersion = snapshotVersion;
this.localTxCounter = localTxCounter;
this.hostId = hostId;
this.sessionNodeVersion = sessionNodeVersion;
}

public long getSnapshotVersion() {
Expand All @@ -76,9 +95,17 @@ public String getHostId() {
return hostId;
}

public Optional<Long> getSessionNodeVersion() {
return Optional.ofNullable(sessionNodeVersion);
}

public String asTupleString() {
return new StringBuilder().append('(').append(snapshotVersion).append(',').append(localTxCounter)
.append(",'").append(hostId).append("')").toString();
StringBuilder sb = new StringBuilder().append('(').append(snapshotVersion).append(',').append(localTxCounter)
.append(",'").append(hostId).append('\'');
if (sessionNodeVersion != null) {
sb.append(',').append(sessionNodeVersion);
}
return sb.append(')').toString();
}

@Override
Expand All @@ -87,6 +114,7 @@ public int hashCode() {
int result = prime + (int) (snapshotVersion ^ (snapshotVersion >>> 32));
result = prime * result + (int) (localTxCounter ^ (localTxCounter >>> 32));
result = prime * result + hostId.hashCode();
result = prime * result + (sessionNodeVersion != null ? sessionNodeVersion.hashCode() : 0);
return result;
}

Expand All @@ -100,14 +128,18 @@ public boolean equals(Object obj) {

XID other = (XID) obj;
return snapshotVersion == other.snapshotVersion && localTxCounter == other.localTxCounter
&& hostId.equals(other.hostId);
&& hostId.equals(other.hostId)
&& Objects.equals(sessionNodeVersion, other.sessionNodeVersion);
}

@Override
public String toString() {
return new StringBuilder().append("TransactionId [snapshotVersion=").append(snapshotVersion)
.append(", localTxCounter=").append(localTxCounter).append(", hostId=").append(hostId).append("]@")
.append(hashCode()).toString();
StringBuilder sb = new StringBuilder().append("TransactionId [snapshotVersion=").append(snapshotVersion)
.append(", localTxCounter=").append(localTxCounter).append(", hostId=").append(hostId);
if (sessionNodeVersion != null) {
sb.append(", sessionNodeVersion=").append(sessionNodeVersion);
}
return sb.append("]@").append(hashCode()).toString();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.clickhouse.client;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import com.clickhouse.client.ClickHouseTransaction.XID;
import com.clickhouse.data.value.UnsignedLong;

@SuppressWarnings("deprecation")
public class ClickHouseTransactionTest {
@DataProvider(name = "validTuples")
public Object[][] getValidTuples() {
return new Object[][] {
{ Arrays.asList(UnsignedLong.valueOf(10L), UnsignedLong.valueOf(20L), "host-uuid"), 10L, 20L,
"host-uuid", null, "(10,20,'host-uuid')" },
{ Arrays.asList(15L, 25L, UUID.fromString("00000000-0000-0000-0000-000000000001"), 5L), 15L, 25L,
"00000000-0000-0000-0000-000000000001", 5L,
"(15,25,'00000000-0000-0000-0000-000000000001',5)" },
{ Arrays.asList(UnsignedLong.valueOf(100L), UnsignedLong.valueOf(200L), "host-4", 0L), 100L, 200L,
"host-4", 0L, "(100,200,'host-4',0)" },
{ Arrays.asList(1L, 2L, "host-null-version", null), 1L, 2L, "host-null-version", null,
"(1,2,'host-null-version')" },
};
}

@Test(groups = "unit", dataProvider = "validTuples")
public void testParseValidXid(List<?> list, long expectedSnapshot, long expectedCounter, String expectedHost,
Long expectedSessionVersion, String expectedTupleString) {
XID xid = XID.of(list);
Assert.assertEquals(xid.getSnapshotVersion(), expectedSnapshot);
Assert.assertEquals(xid.getLocalTransactionCounter(), expectedCounter);
Assert.assertEquals(xid.getHostId(), expectedHost);
if (expectedSessionVersion != null) {
Assert.assertTrue(xid.getSessionNodeVersion().isPresent());
Assert.assertEquals(xid.getSessionNodeVersion().get(), expectedSessionVersion);
} else {
Assert.assertFalse(xid.getSessionNodeVersion().isPresent());
}
Assert.assertEquals(xid.asTupleString(), expectedTupleString);
}

@DataProvider(name = "emptyTuples")
public Object[][] getEmptyTuples() {
return new Object[][] {
{ Arrays.asList(UnsignedLong.valueOf(0L), UnsignedLong.valueOf(0L),
"00000000-0000-0000-0000-000000000000") },
{ Arrays.asList(0L, 0L, UUID.fromString("00000000-0000-0000-0000-000000000000"), 0L) },
{ Arrays.asList(0L, 0L, "00000000-0000-0000-0000-000000000000", 1L) },
};
}

@Test(groups = "unit", dataProvider = "emptyTuples")
public void testParseEmptyXid(List<?> list) {
XID xid = XID.of(list);
Assert.assertSame(xid, XID.EMPTY);
Assert.assertEquals(xid, XID.EMPTY);
}

@DataProvider(name = "invalidTuples")
public Object[][] getInvalidTuples() {
return new Object[][] {
{ null },
{ Collections.emptyList() },
{ Collections.singletonList(1L) },
{ Arrays.asList(1L, 2L) },
{ Arrays.asList(1L, 2L, "host", 0L, "extra") },
};
}

@Test(groups = "unit", dataProvider = "invalidTuples", expectedExceptions = IllegalArgumentException.class)
public void testParseInvalidXid(List<?> list) {
XID.of(list);
}

@Test(groups = "unit")
public void testEqualsAndHashCode() {
XID xid3a = XID.of(Arrays.asList(1L, 2L, "host1"));
XID xid3b = XID.of(Arrays.asList(1L, 2L, "host1"));
XID xid3c = XID.of(Arrays.asList(1L, 3L, "host1"));

XID xid4a = XID.of(Arrays.asList(1L, 2L, "host1", 10L));
XID xid4b = XID.of(Arrays.asList(1L, 2L, "host1", 10L));
XID xid4c = XID.of(Arrays.asList(1L, 2L, "host1", 20L));

Assert.assertEquals(xid3a, xid3a);
Assert.assertEquals(xid3a, xid3b);
Assert.assertEquals(xid3a.hashCode(), xid3b.hashCode());
Assert.assertNotEquals(xid3a, xid3c);

Assert.assertEquals(xid4a, xid4a);
Assert.assertEquals(xid4a, xid4b);
Assert.assertEquals(xid4a.hashCode(), xid4b.hashCode());
Assert.assertNotEquals(xid4a, xid4c);

Assert.assertNotEquals(xid3a, xid4a);
Assert.assertNotEquals(xid4a, xid3a);
Assert.assertNotEquals(xid4a, null);
Assert.assertNotEquals(xid4a, "(1,2,'host1',10)");
}

@Test(groups = "unit")
public void testToString() {
XID xid3 = XID.of(Arrays.asList(1L, 2L, "host1"));
Assert.assertTrue(xid3.toString().contains("snapshotVersion=1"));
Assert.assertTrue(xid3.toString().contains("localTxCounter=2"));
Assert.assertTrue(xid3.toString().contains("hostId=host1"));
Assert.assertFalse(xid3.toString().contains("sessionNodeVersion="));

XID xid4 = XID.of(Arrays.asList(1L, 2L, "host1", 5L));
Assert.assertTrue(xid4.toString().contains("snapshotVersion=1"));
Assert.assertTrue(xid4.toString().contains("localTxCounter=2"));
Assert.assertTrue(xid4.toString().contains("hostId=host1"));
Assert.assertTrue(xid4.toString().contains("sessionNodeVersion=5"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1906,7 +1906,7 @@ public void testArrayOfNestedType() throws SQLException {
+ "aan Array(Array(Nested(a Int8, b Nullable(String)))), "
+ "aat Array(Array(Tuple(a Int8, b Nullable(String)))), "
+ "tail Int32"
+ ") ENGINE = MergeTree ORDER BY (order)");
+ ") ORDER BY (order)");
runQuery("INSERT INTO test_array_of_nested_jdbc VALUES "
+ "(1, [[(1, 'x'), (2, NULL)], [], [(3, 'y')]], [[[(1, 'x'), (2, NULL)]], [], [[], [(3, 'y')]]], "
+ "[[(1, 'x'), (2, NULL)], [], [(3, 'y')]], 100), "
Expand Down
Loading