diff --git a/dev/proto.sh b/dev/proto.sh index a988ce8cd51..9c0e6cf433e 100755 --- a/dev/proto.sh +++ b/dev/proto.sh @@ -21,7 +21,7 @@ if [ -d $proto_dir ]; then fi repos=("https://github.com/pingcap/kvproto" "https://github.com/pingcap/raft-rs" "https://github.com/pingcap/tipb") -commits=(3056ca36e6f2a71a9fc7ba7135e6b119fd977553 b9891b673573fad77ebcf9bbe0969cf945841926 c4d518eb1d60c21f05b028b36729e64610346dac) +commits=(a0e3fbb1eeee6b8e04e2bec6d1ca65dcd9172662 7c21f8d12f2043c43004fca48861568db7c76bd9 2fb8289108131ccd5faf86d3e92fb05d4ff3b326) for i in "${!repos[@]}"; do repo_name=$(basename ${repos[$i]}) diff --git a/pom.xml b/pom.xml index 9d95527e976..3b9c769a88f 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ 3.18.0 1.2.17 1.7.16 - 1.60.0 + 1.69.0 2.0.34.Final 2.8.9 1.6.6 @@ -164,6 +164,11 @@ netty-tcnative-boringssl-static ${netty.tcnative.version} + + io.netty + netty-all + 4.1.100.Final + io.grpc grpc-testing diff --git a/pom.xml.versionsBackup b/pom.xml.versionsBackup new file mode 100644 index 00000000000..80c3de7d078 --- /dev/null +++ b/pom.xml.versionsBackup @@ -0,0 +1,684 @@ + + + 4.0.0 + org.tikv + tikv-client-java + 3.2.1-SNAPSHOT + jar + TiKV Java Client + A Java Client for TiKV + http://github.com/tikv/client-java + + + Apache 2.0 License + http://www.apache.org/licenses/LICENSE-2.0.html + repo + + + + PingCAP + + + + Xiaoyu Ma + maxiaoyu@pingcap.com + PingCAP + https://www.pingcap.com + + + Yifei Wu + birdstorm@pingcap.com + PingCAP + https://www.pingcap.com + + + Zhexuan Yang + yangzhexuan@pingcap.com + PingCAP + https://www.pingcap.com + + + Liangliang Gu + guliangliang@pingcap.com + PingCAP + https://www.pingcap.com + + + + scm:git:git://github.com/tikv/client-java.git + scm:git:ssh://github.com:tikv/client-java.git + https://github.com/tikv/client-java/tree/master + + + 1.8 + 1.8 + UTF-8 + UTF-8 + 6.22.1.1 + 3.5.1 + 1.2.17 + 1.7.16 + 1.38.0 + 2.0.34.Final + 2.8.9 + 1.6.6 + 2.13.2 + 2.13.2.2 + 3.0.1 + 0.4.1 + 2.9.9 + 1.9.2 + ${basedir}/proto + fake gpg key name + true + + + + com.google.protobuf + protobuf-java + 3.16.1 + + + com.google.protobuf + protobuf-java-util + 3.16.1 + + + io.perfmark + perfmark-api + 0.24.0 + + + io.perfmark + perfmark-traceviewer + 0.24.0 + + + org.rocksdb + rocksdbjni + ${rocksdb.version} + + + org.antlr + antlr4-runtime + 4.7.1 + + + org.slf4j + slf4j-api + ${slf4j.version} + provided + + + org.slf4j + slf4j-log4j12 + ${slf4j.version} + provided + + + org.slf4j + jul-to-slf4j + ${slf4j.version} + provided + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + provided + + + com.sangupta + murmur + 1.0.0 + + + + io.grpc + grpc-netty + ${grpc.version} + + + io.grpc + grpc-protobuf + ${grpc.version} + + + com.google.protobuf + protobuf-java + + + + + io.grpc + grpc-stub + ${grpc.version} + + + io.grpc + grpc-services + ${grpc.version} + + + com.google.protobuf + protobuf-java-util + + + + + io.netty + netty-tcnative-boringssl-static + ${netty.tcnative.version} + + + io.grpc + grpc-testing + ${grpc.version} + test + + + com.google.code.gson + gson + ${gson.version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-annotations.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + io.etcd + jetcd-core + + + io.etcd + jetcd-resolver + + + io.etcd + jetcd-common + + + io.grpc + grpc-grpclb + + + ${jetcd.version} + + + joda-time + joda-time + ${joda-time.version} + + + org.joda + joda-convert + ${joda-convert.version} + + + + net.sf.trove4j + trove4j + ${trove4j.version} + + + org.apache.commons + commons-lang3 + 3.10 + test + + + org.apache.commons + commons-lang3 + 3.9 + compile + + + org.apache.httpcomponents + httpclient + 4.5.13 + + + io.prometheus + simpleclient + 0.10.0 + + + + io.prometheus + simpleclient_hotspot + 0.10.0 + + + + io.prometheus + simpleclient_httpserver + 0.10.0 + + + + io.prometheus + simpleclient_pushgateway + 0.10.0 + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.3.1 + + + com.github.spotbugs + spotbugs-maven-plugin + 4.5.2.0 + + true + + target/site + dev/spotbugs-include.xml + + + + org.apache.maven.plugins + maven-jxr-plugin + 3.1.1 + + + + + + + src/main/resources + + + + + kr.motd.maven + os-maven-plugin + 1.4.1.Final + + + + + org.antlr + antlr4-maven-plugin + 4.7.1 + + + + antlr4 + + + + + + -package + org.tikv.common.parser + + true + ./src/main/java/org/tikv/common/parser + ./target/generated-sources/antlr4/java/org/tikv/common/parser + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + clone proto files + + ${basedir}/scripts/proto.sh + + validate + + exec + + + + + + maven-resources-plugin + 2.5 + + + copy-resources + validate + + copy-resources + + + ${proto.folder} + + + ${basedir}/kvproto/include + + **/gogoproto/** + + + + ${basedir}/kvproto/include + + *.proto + + + + ${basedir}/kvproto/proto + true + + + ${basedir}/raft-rs/proto/proto + true + + + ${basedir}/tipb/proto + true + + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + ${proto.folder} + + **/*.proto + + grpc-java + io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} + + + + + compile + compile-custom + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + UTF-8 + true + true + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + false + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + + ${proto.folder} + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + package + + shade + + + + + + + + io.grpc + org.tikv.shade.io.grpc + + + com.google + org.tikv.shade.com.google + + + io.netty + org.tikv.shade.io.netty + + + io.opencensus + org.tikv.shade.io.opencensus + + + io.prometheus + org.tikv.shade.io.prometheus + + + com.fasterxml.jackson + org.tikv.shade.com.fasterxml.jackson + + + + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.3 + + + jacoco-initialize + + prepare-agent + + + + jacoco-site + test + + report + + + + + + + com.coveo + fmt-maven-plugin + 2.6.0 + + src/main/java + src/test/java + true + .*\.java + false + false + + + + + validate + + format + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + ${gpg.keyname} + ${gpg.skip} + + + + sign-artifacts + + sign + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + io.github.git-commit-id + git-commit-id-maven-plugin + 4.9.9 + + + get-the-git-infos + + revision + + initialize + + + validate-the-git-infos + + validateRevision + + package + + + + true + true + ${project.build.outputDirectory}/git.properties + + ^git.build.(time|version)$ + ^git.commit.id.(abbrev|full)$ + + full + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + always + + + + org.apache.maven.plugins + maven-site-plugin + 3.7.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.0.0 + + + com.github.spotbugs + spotbugs-maven-plugin + 4.5.2.0 + + dev/spotbugs-include.xml + true + false + + + + + com.github.spotbugs + spotbugs + 4.5.3 + + + + + au.com.acegi + xml-format-maven-plugin + 3.2.0 + + + xml-format + validate + + xml-format + + + + + 4 + + + + + + + jdk9plus + + + !1.8 + + + + javax.annotation + javax.annotation-api + 1.3.2 + provided + + + + + diff --git a/src/main/java/org/tikv/common/Snapshot.java b/src/main/java/org/tikv/common/Snapshot.java index 7012bc749ec..19f1d433026 100644 --- a/src/main/java/org/tikv/common/Snapshot.java +++ b/src/main/java/org/tikv/common/Snapshot.java @@ -28,6 +28,7 @@ import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.tikv.common.columnar.TiChunk; +import org.tikv.common.handle.Handle; import org.tikv.common.key.Key; import org.tikv.common.meta.TiDAGRequest; import org.tikv.common.meta.TiTimestamp; @@ -102,6 +103,7 @@ public Iterator tableReadChunk( return getTiChunkIterator(dagRequest, tasks, getSession(), numOfRows); } } + /** * Issue a table read request * @@ -126,7 +128,7 @@ public Iterator tableReadRow(TiDAGRequest dagRequest, long physicalId) { */ private Iterator tableReadRow(TiDAGRequest dagRequest, List tasks) { if (dagRequest.isDoubleRead()) { - Iterator iter = getHandleIterator(dagRequest, tasks, getSession()); + Iterator iter = getHandleIterator(dagRequest, tasks, getSession()); return new IndexScanIterator(this, dagRequest, iter); } else { return getRowIterator(dagRequest, tasks, getSession()); @@ -141,7 +143,7 @@ private Iterator tableReadRow(TiDAGRequest dagRequest, List tas * @param tasks RegionTask of the coprocessor request to send * @return Row iterator to iterate over resulting rows */ - public Iterator indexHandleRead(TiDAGRequest dagRequest, List tasks) { + public Iterator indexHandleRead(TiDAGRequest dagRequest, List tasks) { return getHandleIterator(dagRequest, tasks, session); } diff --git a/src/main/java/org/tikv/common/codec/Codec.java b/src/main/java/org/tikv/common/codec/Codec.java index 642b6fc8c70..933bd312211 100644 --- a/src/main/java/org/tikv/common/codec/Codec.java +++ b/src/main/java/org/tikv/common/codec/Codec.java @@ -32,10 +32,18 @@ import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.tikv.common.ExtendedDateTime; +import org.tikv.common.exception.CodecException; import org.tikv.common.exception.ConvertOverflowException; import org.tikv.common.exception.InvalidCodecFormatException; import org.tikv.common.exception.TypeException; import org.tikv.common.exception.UnsupportedSyntaxException; +import org.tikv.common.types.BytesType; +import org.tikv.common.types.DataType; +import org.tikv.common.types.DecimalType; +import org.tikv.common.types.IntegerType; +import org.tikv.common.types.JsonType; +import org.tikv.common.types.RealType; +import org.tikv.common.types.TimeType; public class Codec { @@ -57,6 +65,41 @@ public static boolean isNullFlag(int flag) { return flag == NULL_FLAG; } + public static Object decodeOne(byte[] colData) { + if (colData.length <= 1) { + throw new CodecException("invalid encoded column data, length <=1"); + } + int flag = colData[0]; + DataType tp; + switch (flag) { + case INT_FLAG: + case UINT_FLAG: + case VARINT_FLAG: + case UVARINT_FLAG: + tp = IntegerType.BIGINT; + break; + case FLOATING_FLAG: + tp = RealType.DOUBLE; + break; + case BYTES_FLAG: + case COMPACT_BYTES_FLAG: + tp = BytesType.TEXT; + break; + case DECIMAL_FLAG: + tp = DecimalType.DECIMAL; + break; + case DURATION_FLAG: + tp = TimeType.TIME; + break; + case JSON_FLAG: + tp = JsonType.JSON; + break; + default: + throw new CodecException("Unknown type"); + } + return tp.decode(new CodecDataInput(colData)); + } + public static class IntegerCodec { private static long flipSignBit(long v) { @@ -603,10 +646,10 @@ public static void writeDateTimeProto( * Read datetime from packed Long encoded as unsigned var-len integer converting into specified * timezone * - * @see DateTimeCodec#fromPackedLong(long, DateTimeZone) * @param cdi codec buffer input * @param tz timezone to interpret datetime parts * @return decoded ExtendedDateTime using provided timezone + * @see DateTimeCodec#fromPackedLong(long, DateTimeZone) */ public static ExtendedDateTime readFromUVarInt(CodecDataInput cdi, DateTimeZone tz) { return DateTimeCodec.fromPackedLong(IntegerCodec.readUVarLong(cdi), tz); @@ -615,10 +658,10 @@ public static ExtendedDateTime readFromUVarInt(CodecDataInput cdi, DateTimeZone /** * Read datetime from packed Long as unsigned fixed-len integer * - * @see DateTimeCodec#fromPackedLong(long, DateTimeZone) * @param cdi codec buffer input * @param tz timezone to interpret datetime parts * @return decoded ExtendedDateTime using provided timezone + * @see DateTimeCodec#fromPackedLong(long, DateTimeZone) */ public static ExtendedDateTime readFromUInt(CodecDataInput cdi, DateTimeZone tz) { return DateTimeCodec.fromPackedLong(IntegerCodec.readULong(cdi), tz); @@ -731,9 +774,9 @@ public static void writeDateProto(CodecDataOutput cdo, Date date, DateTimeZone t * Read date from packed Long encoded as unsigned var-len integer converting into specified * timezone * - * @see DateCodec#fromPackedLong(long) * @param cdi codec buffer input * @return decoded DateTime using provided timezone + * @see DateCodec#fromPackedLong(long) */ public static LocalDate readFromUVarInt(CodecDataInput cdi) { return DateCodec.fromPackedLong(IntegerCodec.readUVarLong(cdi)); @@ -742,9 +785,9 @@ public static LocalDate readFromUVarInt(CodecDataInput cdi) { /** * Read date from packed Long as unsigned fixed-len integer * - * @see DateCodec#fromPackedLong(long) * @param cdi codec buffer input * @return decoded DateTime using provided timezone + * @see DateCodec#fromPackedLong(long) */ public static LocalDate readFromUInt(CodecDataInput cdi) { return DateCodec.fromPackedLong(IntegerCodec.readULong(cdi)); diff --git a/src/main/java/org/tikv/common/codec/CodecDataInput.java b/src/main/java/org/tikv/common/codec/CodecDataInput.java index 3035c5fbf15..f128579c0a9 100644 --- a/src/main/java/org/tikv/common/codec/CodecDataInput.java +++ b/src/main/java/org/tikv/common/codec/CodecDataInput.java @@ -17,9 +17,23 @@ package org.tikv.common.codec; +import static org.tikv.common.codec.Codec.BYTES_FLAG; +import static org.tikv.common.codec.Codec.COMPACT_BYTES_FLAG; +import static org.tikv.common.codec.Codec.DECIMAL_FLAG; +import static org.tikv.common.codec.Codec.DURATION_FLAG; +import static org.tikv.common.codec.Codec.FLOATING_FLAG; +import static org.tikv.common.codec.Codec.INT_FLAG; +import static org.tikv.common.codec.Codec.NULL_FLAG; +import static org.tikv.common.codec.Codec.UINT_FLAG; + import com.google.protobuf.ByteString; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; import javax.annotation.Nonnull; +import org.tikv.common.exception.CodecException; public class CodecDataInput implements DataInput { protected final DataInputStream inputStream; @@ -191,6 +205,48 @@ public String readUTF() { } } + /** + * peek the first encoded value and return its length + * + * @return first encoded value + */ + public int cutOne() { + if (available() < 1) { + throw new CodecException("invalid encoded key"); + } + int flag = readByte(); + int a1 = this.available(); + + switch (flag) { + case NULL_FLAG: + case INT_FLAG: + case UINT_FLAG: + case FLOATING_FLAG: + case DURATION_FLAG: + Codec.RealCodec.readDouble(this); + break; + case BYTES_FLAG: + Codec.BytesCodec.readBytes(this); + break; + case COMPACT_BYTES_FLAG: + Codec.BytesCodec.readCompactBytes(this); + break; + case DECIMAL_FLAG: + Codec.DecimalCodec.readDecimal(this); + break; + // case VARINT_FLAG: + // l = peekVarint(b); + // case UVARINT_FLAG: + // l = peekUvarint(b); + // case JSON_FLAG: + // l = json.PeekBytesAsJSON(b); + default: + throw new CodecException("invalid encoded key flag " + flag); + } + int a2 = this.available(); + return a1 - a2 + 1; + } + public int peekByte() { mark(currentPos()); int b = readByte() & 0xFF; diff --git a/src/main/java/org/tikv/common/codec/TableCodec.java b/src/main/java/org/tikv/common/codec/TableCodec.java index c0d141d94d2..ec334e6cec3 100644 --- a/src/main/java/org/tikv/common/codec/TableCodec.java +++ b/src/main/java/org/tikv/common/codec/TableCodec.java @@ -19,6 +19,9 @@ import java.util.List; import org.tikv.common.exception.CodecException; +import org.tikv.common.handle.CommonHandle; +import org.tikv.common.handle.Handle; +import org.tikv.common.handle.IntHandle; import org.tikv.common.meta.TiColumnInfo; import org.tikv.common.meta.TiTableInfo; import org.tikv.common.row.Row; @@ -42,7 +45,7 @@ public static byte[] encodeRow( return TableCodecV1.encodeRow(columnInfos, values, isPkHandle); } - public static Object[] decodeObjects(byte[] value, Long handle, TiTableInfo tableInfo) { + public static Object[] decodeObjects(byte[] value, Handle handle, TiTableInfo tableInfo) { if (value.length == 0) { throw new CodecException("Decode fails: value length is zero"); } @@ -52,7 +55,7 @@ public static Object[] decodeObjects(byte[] value, Long handle, TiTableInfo tabl return TableCodecV1.decodeObjects(value, handle, tableInfo); } - public static Row decodeRow(byte[] value, Long handle, TiTableInfo tableInfo) { + public static Row decodeRow(byte[] value, Handle handle, TiTableInfo tableInfo) { if (value.length == 0) { throw new CodecException("Decode fails: value length is zero"); } @@ -62,7 +65,10 @@ public static Row decodeRow(byte[] value, Long handle, TiTableInfo tableInfo) { return TableCodecV1.decodeRow(value, handle, tableInfo); } - public static long decodeHandle(byte[] value) { - return new CodecDataInput(value).readLong(); + public static Handle decodeHandle(byte[] value, boolean isCommonHandle) { + if (isCommonHandle) { + return new CommonHandle(value); + } + return new IntHandle(new CodecDataInput(value).readLong()); } } diff --git a/src/main/java/org/tikv/common/codec/TableCodecV1.java b/src/main/java/org/tikv/common/codec/TableCodecV1.java index b0a1811c054..4a5928126b1 100644 --- a/src/main/java/org/tikv/common/codec/TableCodecV1.java +++ b/src/main/java/org/tikv/common/codec/TableCodecV1.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.List; import org.tikv.common.codec.Codec.IntegerCodec; +import org.tikv.common.handle.Handle; import org.tikv.common.meta.TiColumnInfo; import org.tikv.common.meta.TiTableInfo; import org.tikv.common.row.ObjectRowImpl; @@ -51,7 +52,7 @@ protected static byte[] encodeRow( return cdo.toBytes(); } - protected static Object[] decodeObjects(byte[] value, Long handle, TiTableInfo tableInfo) { + protected static Object[] decodeObjects(byte[] value, Handle handle, TiTableInfo tableInfo) { if (handle == null && tableInfo.isPkHandle()) { throw new IllegalArgumentException("when pk is handle, handle cannot be null"); } @@ -85,7 +86,7 @@ protected static Object[] decodeObjects(byte[] value, Long handle, TiTableInfo t return res; } - protected static Row decodeRow(byte[] value, Long handle, TiTableInfo tableInfo) { + protected static Row decodeRow(byte[] value, Handle handle, TiTableInfo tableInfo) { return ObjectRowImpl.create(decodeObjects(value, handle, tableInfo)); } } diff --git a/src/main/java/org/tikv/common/codec/TableCodecV2.java b/src/main/java/org/tikv/common/codec/TableCodecV2.java index 1d90737fc43..ef0dd2b12ea 100644 --- a/src/main/java/org/tikv/common/codec/TableCodecV2.java +++ b/src/main/java/org/tikv/common/codec/TableCodecV2.java @@ -17,80 +17,104 @@ package org.tikv.common.codec; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import org.tikv.common.handle.Handle; import org.tikv.common.meta.TiColumnInfo; +import org.tikv.common.meta.TiIndexColumn; +import org.tikv.common.meta.TiIndexInfo; import org.tikv.common.meta.TiTableInfo; import org.tikv.common.row.ObjectRowImpl; import org.tikv.common.row.Row; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + public class TableCodecV2 { - /** - * New Row Format: Reference - * https://github.com/pingcap/tidb/blob/952d1d7541a8e86be0af58f5b7e3d5e982bab34e/docs/design/2018-07-19-row-format.md - * - *

- version, flag, numOfNotNullCols, numOfNullCols, notNullCols, nullCols, notNullOffsets, - * notNullValues - */ - protected static byte[] encodeRow( - List columnInfos, Object[] values, boolean isPkHandle) { - RowEncoderV2 encoder = new RowEncoderV2(); - List columnInfoList = new ArrayList<>(); - List valueList = new ArrayList<>(); - for (int i = 0; i < columnInfos.size(); i++) { - TiColumnInfo col = columnInfos.get(i); - // skip pk is handle case - if (col.isPrimaryKey() && isPkHandle) { - continue; - } - columnInfoList.add(col); - valueList.add(values[i]); + /** + * New Row Format: Reference + * https://github.com/pingcap/tidb/blob/952d1d7541a8e86be0af58f5b7e3d5e982bab34e/docs/design/2018-07-19-row-format.md + * + *

- version, flag, numOfNotNullCols, numOfNullCols, notNullCols, nullCols, notNullOffsets, + * notNullValues + */ + protected static byte[] encodeRow( + List columnInfos, Object[] values, boolean isPkHandle) { + RowEncoderV2 encoder = new RowEncoderV2(); + List columnInfoList = new ArrayList<>(); + List valueList = new ArrayList<>(); + for (int i = 0; i < columnInfos.size(); i++) { + TiColumnInfo col = columnInfos.get(i); + // skip pk is handle case + if (col.isPrimaryKey() && isPkHandle) { + continue; + } + columnInfoList.add(col); + valueList.add(values[i]); + } + return encoder.encode(columnInfoList, valueList); } - return encoder.encode(columnInfoList, valueList); - } - protected static Object[] decodeObjects(byte[] value, Long handle, TiTableInfo tableInfo) { - if (handle == null && tableInfo.isPkHandle()) { - throw new IllegalArgumentException("when pk is handle, handle cannot be null"); - } - int colSize = tableInfo.getColumns().size(); - // decode bytes to Map - HashMap decodedDataMap = new HashMap<>(colSize); - org.tikv.common.codec.RowV2 rowV2 = org.tikv.common.codec.RowV2.createNew(value); + protected static Object[] decodeObjects(byte[] value, Handle handle, TiTableInfo tableInfo) { + if (handle == null && tableInfo.isPkHandle()) { + throw new IllegalArgumentException("when pk is handle, handle cannot be null"); + } + int colSize = tableInfo.getColumns().size(); + // decode bytes to Map + HashMap decodedDataMap = new HashMap<>(colSize); + org.tikv.common.codec.RowV2 rowV2 = org.tikv.common.codec.RowV2.createNew(value); - for (TiColumnInfo col : tableInfo.getColumns()) { - if (col.isPrimaryKey() && tableInfo.isPkHandle()) { - decodedDataMap.put(col.getId(), handle); - continue; - } - org.tikv.common.codec.RowV2.ColIDSearchResult searchResult = rowV2.findColID(col.getId()); - if (searchResult.isNull) { - // current col is null, nothing should be added to decodedMap - continue; - } - if (!searchResult.notFound) { - // corresponding column should be found - assert (searchResult.idx != -1); - byte[] colData = rowV2.getData(searchResult.idx); - Object d = RowDecoderV2.decodeCol(colData, col.getType()); - decodedDataMap.put(col.getId(), d); - } - } + TiIndexInfo pk = tableInfo.getPrimaryKey(); - Object[] res = new Object[colSize]; + if (pk != null) { + List cols = new ArrayList<>(); + for (TiIndexColumn indexColumn : pk.getIndexColumns()) { + TiColumnInfo col = tableInfo.getColumn(indexColumn.getOffset()); + cols.add(col); + } + if (tableInfo.isPkHandle()) { + assert cols.size() == 1; + decodedDataMap.put(cols.get(0).getId(), handle.data()[0]); + } + if (tableInfo.isCommonHandle()) { + for (int i = 0; i < cols.size(); i++) { + decodedDataMap.put(cols.get(i).getId(), handle.data()[i]); + } + } + } - // construct Row with Map & handle - for (int i = 0; i < colSize; i++) { - // skip pk is handle case - TiColumnInfo col = tableInfo.getColumn(i); - res[i] = decodedDataMap.get(col.getId()); + for (TiColumnInfo col : tableInfo.getColumns()) { + if (decodedDataMap.containsKey(col.getId())) { + continue; + } else if (col.isPrimaryKey() && tableInfo.isPkHandle()) { + decodedDataMap.put(col.getId(), handle.data()[0]); + continue; + } + RowV2.ColIDSearchResult searchResult = rowV2.findColID(col.getId()); + if (searchResult.isNull) { + // current col is null, nothing should be added to decodedMap + continue; + } + if (!searchResult.notFound) { + // corresponding column should be found + assert (searchResult.idx != -1); + byte[] colData = rowV2.getData(searchResult.idx); + Object d = RowDecoderV2.decodeCol(colData, col.getType()); + decodedDataMap.put(col.getId(), d); + } + } + Object[] res = new Object[colSize]; + + // construct Row with Map & handle + for (int i = 0; i < colSize; i++) { + // skip pk is handle case + TiColumnInfo col = tableInfo.getColumn(i); + res[i] = decodedDataMap.get(col.getId()); + } + return res; } - return res; - } - protected static Row decodeRow(byte[] value, Long handle, TiTableInfo tableInfo) { - return ObjectRowImpl.create(decodeObjects(value, handle, tableInfo)); - } + protected static Row decodeRow(byte[] value, Handle handle, TiTableInfo tableInfo) { + return ObjectRowImpl.create(decodeObjects(value, handle, tableInfo)); + } } diff --git a/src/main/java/org/tikv/common/handle/CommonHandle.java b/src/main/java/org/tikv/common/handle/CommonHandle.java new file mode 100644 index 00000000000..8cc57f63faf --- /dev/null +++ b/src/main/java/org/tikv/common/handle/CommonHandle.java @@ -0,0 +1,191 @@ +package org.tikv.common.handle; + +import java.sql.Date; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.TimeZone; +import java.util.stream.Collectors; +import org.joda.time.Days; +import org.joda.time.LocalDate; +import org.tikv.common.codec.Codec; +import org.tikv.common.codec.CodecDataInput; +import org.tikv.common.codec.CodecDataOutput; +import org.tikv.common.exception.CodecException; +import org.tikv.common.key.Key; +import org.tikv.common.types.DataType; +import org.tikv.common.types.MySQLType; +import org.tikv.common.util.FastByteComparisons; + +public class CommonHandle implements Handle { + private final byte[] encoded; + private final int[] colEndOffsets; + + private static final int MS_OF_ONE_DAY = 24 * 3600 * 1000; + private static final int MIN_ENCODE_LEN = 9; + + public static CommonHandle newCommonHandle(DataType[] dataTypes, Object[] data) { + long[] prefixLengthes = new long[dataTypes.length]; + for (int i = 0; i < dataTypes.length; i++) { + prefixLengthes[i] = -1; + } + return newCommonHandle(dataTypes, data, prefixLengthes); + } + + public static CommonHandle newCommonHandle( + DataType[] dataTypes, Object[] data, long[] prefixLengthes) { + CodecDataOutput cdo = new CodecDataOutput(); + for (int i = 0; i < data.length; i++) { + if (dataTypes[i].getType().equals(MySQLType.TypeTimestamp)) { + // When writing `Timestamp`, it will pass `Timestamp` object. + // When indexScan or tableScan, it will pass `Long` object. + // It's a compromise here since we don't have a good way to make them consistent. + long milliseconds; + if (data[i] instanceof Timestamp) { + milliseconds = ((Timestamp) data[i]).getTime(); + } else { + milliseconds = ((long) data[i]) / 1000; + } + + dataTypes[i].encode(cdo, DataType.EncodeType.KEY, milliseconds); + } else if (dataTypes[i].getType().equals(MySQLType.TypeDate)) { + long days; + // When writing `Date`, it will pass `Date` object. + // When indexScan or tableScan, it will pass `Long` object. + // It's a compromise here since we don't have a good way to make them consistent. + if (data[i] instanceof Date) { + days = Days.daysBetween(new LocalDate(1970, 1, 1), new LocalDate(data[i])).getDays(); + } else { + days = (long) data[i]; + } + + SimpleDateFormat utcFmt = new SimpleDateFormat("yyyy-MM-dd"); + utcFmt.setTimeZone(TimeZone.getTimeZone("UTC")); + + dataTypes[i].encode( + cdo, DataType.EncodeType.KEY, Date.valueOf(utcFmt.format(days * MS_OF_ONE_DAY))); + } else { + if (prefixLengthes[i] > 0 && data[i] instanceof String) { + String source = (String) data[i]; + String dest = source; + if (source.length() > prefixLengthes[i]) { + dest = source.substring(0, (int) prefixLengthes[i]); + } + dataTypes[i].encode(cdo, DataType.EncodeType.KEY, dest); + } else { + dataTypes[i].encode(cdo, DataType.EncodeType.KEY, data[i]); + } + } + } + return new CommonHandle(cdo.toBytes()); + } + + public CommonHandle(byte[] encoded) { + if (encoded.length < MIN_ENCODE_LEN) { + this.encoded = Arrays.copyOf(encoded, MIN_ENCODE_LEN); + } else { + this.encoded = encoded; + } + + int endOffset = 0; + CodecDataInput cdi = new CodecDataInput(encoded); + List offsets = new ArrayList<>(); + while (!cdi.eof()) { + if (cdi.peekByte() == 0) { + // padded data + break; + } + endOffset += cdi.cutOne(); + offsets.add(endOffset); + } + this.colEndOffsets = offsets.stream().mapToInt(i -> i).toArray(); + } + + public CommonHandle(byte[] encoded, int[] colEndOffsets) { + if (encoded.length < MIN_ENCODE_LEN) { + this.encoded = Arrays.copyOf(encoded, MIN_ENCODE_LEN); + } else { + this.encoded = encoded; + } + this.colEndOffsets = colEndOffsets; + } + + @Override + public boolean isInt() { + return false; + } + + @Override + public long intValue() { + throw new CodecException("not supported in CommonHandle"); + } + + @Override + public Handle next() { + return new CommonHandle(new Key(encoded).nextPrefix().getBytes(), colEndOffsets); + } + + @Override + public boolean equals(Object other) { + if (other instanceof CommonHandle) { + return Arrays.equals(encoded, ((CommonHandle) other).encoded()); + } + return false; + } + + @Override + public int compare(Handle h) { + if (h.isInt()) { + throw new RuntimeException("CommonHandle compares to IntHandle"); + } + return FastByteComparisons.compareTo(encoded, h.encoded()); + } + + @Override + public byte[] encoded() { + return this.encoded; + } + + @Override + public byte[] encodedAsKey() { + return this.encoded; + } + + @Override + public int len() { + return this.encoded.length; + } + + @Override + public int numCols() { + return this.colEndOffsets.length; + } + + @Override + public byte[] encodedCol(int idx) { + int start = 0, end = colEndOffsets[idx]; + if (idx > 0) { + start = colEndOffsets[idx - 1]; + } + return Arrays.copyOfRange(encoded, start, end + 1); + } + + @Override + public Object[] data() { + int len = numCols(); + Object[] data = new Object[len]; + for (int i = 0; i < len; i++) { + byte[] col = encodedCol(i); + data[i] = Codec.decodeOne(col); + } + return data; + } + + @Override + public String toString() { + Object[] data = data(); + return Arrays.stream(data).map(Object::toString).collect(Collectors.joining("},{", "{", "}")); + } +} diff --git a/src/main/java/org/tikv/common/handle/Handle.java b/src/main/java/org/tikv/common/handle/Handle.java new file mode 100644 index 00000000000..d699a25e111 --- /dev/null +++ b/src/main/java/org/tikv/common/handle/Handle.java @@ -0,0 +1,42 @@ +/* + * Copyright 2020 PingCAP, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.tikv.common.handle; + +import java.io.Serializable; + +public interface Handle extends Serializable { + + boolean isInt(); + + long intValue(); + + Handle next(); + + int compare(Handle h); + + byte[] encoded(); + + byte[] encodedAsKey(); + + int len(); + + int numCols(); + + byte[] encodedCol(int idx); + + Object[] data(); +} diff --git a/src/main/java/org/tikv/common/handle/IntHandle.java b/src/main/java/org/tikv/common/handle/IntHandle.java new file mode 100644 index 00000000000..1677d5f1db5 --- /dev/null +++ b/src/main/java/org/tikv/common/handle/IntHandle.java @@ -0,0 +1,122 @@ +package org.tikv.common.handle; + +import java.util.Arrays; +import org.tikv.common.codec.Codec; +import org.tikv.common.codec.CodecDataOutput; +import org.tikv.common.exception.CodecException; +import org.tikv.common.types.DataType; +import org.tikv.common.types.IntegerType; + +public class IntHandle implements Handle { + private final long handle; + private final int infFlag; + + public IntHandle(long handle) { + this.handle = handle; + this.infFlag = 0; + } + + private IntHandle(long handle, int infFlag) { + this.handle = handle; + this.infFlag = infFlag; + } + + @Override + public boolean isInt() { + return true; + } + + @Override + public long intValue() { + return handle; + } + + @Override + public Handle next() { + if (handle != Long.MAX_VALUE) { + return new IntHandle(handle + 1); + } + return new IntHandle(handle, 1); + } + + @Override + public boolean equals(Object other) { + if (other instanceof IntHandle) { + return ((IntHandle) other).intValue() == handle; + } + return false; + } + + @Override + public int compare(Handle h) { + if (!h.isInt()) { + throw new RuntimeException("IntHandle compares to CommonHandle"); + } + if (infFlag != ((IntHandle) h).infFlag) { + return infFlag - ((IntHandle) h).infFlag; + } + long val = intValue(); + long hVal = h.intValue(); + if (val > hVal) { + return 1; + } else if (val < hVal) { + return -1; + } + return 0; + } + + @Override + public byte[] encoded() { + CodecDataOutput cdo = new CodecDataOutput(); + Codec.IntegerCodec.writeLong(cdo, handle); + byte[] encoded = cdo.toBytes(); + if (infFlag == 1) { + return Arrays.copyOf(encoded, encoded.length + 1); + } + return encoded; + } + + @Override + public byte[] encodedAsKey() { + CodecDataOutput cdo = new CodecDataOutput(); + IntegerType.BIGINT.encode(cdo, DataType.EncodeType.KEY, handle); + byte[] encoded = cdo.toBytes(); + if (infFlag == 1) { + return Arrays.copyOf(encoded, encoded.length + 1); + } + return encoded; + } + + @Override + public int len() { + if (infFlag == 1) { + return 9; + } + return 8; + } + + @Override + public int numCols() { + throw new CodecException("not supported in IntHandle"); + } + + @Override + public byte[] encodedCol(int idx) { + throw new CodecException("not supported in IntHandle"); + } + + @Override + public Object[] data() { + return new Object[] {handle}; + } + + @Override + public String toString() { + if (infFlag == -1) { + return "-inf"; + } else if (infFlag == 1) { + return "+inf"; + } + return String.valueOf(handle); + } +} diff --git a/src/main/java/org/tikv/common/key/Key.java b/src/main/java/org/tikv/common/key/Key.java index 6da4bd8acf9..daac57a3549 100644 --- a/src/main/java/org/tikv/common/key/Key.java +++ b/src/main/java/org/tikv/common/key/Key.java @@ -43,7 +43,7 @@ private Key(byte[] value, boolean negative) { this.infFlag = (value.length == 0 ? 1 : 0) * (negative ? -1 : 1); } - protected Key(byte[] value) { + public Key(byte[] value) { this(value, false); } diff --git a/src/main/java/org/tikv/common/key/RowKey.java b/src/main/java/org/tikv/common/key/RowKey.java index d752d7e2147..07f4a776c0b 100644 --- a/src/main/java/org/tikv/common/key/RowKey.java +++ b/src/main/java/org/tikv/common/key/RowKey.java @@ -25,15 +25,19 @@ import org.tikv.common.codec.CodecDataOutput; import org.tikv.common.exception.TiClientInternalException; import org.tikv.common.exception.TiExpressionException; +import org.tikv.common.handle.CommonHandle; +import org.tikv.common.handle.Handle; +import org.tikv.common.handle.IntHandle; public class RowKey extends Key implements Serializable { private static final byte[] REC_PREFIX_SEP = new byte[] {'_', 'r'}; - + private static final int INT_HANDLE_SIZE = 19; + private static final int HANDLE_PREFIX_SIZE = 11; private final long tableId; - private final long handle; + private final Handle handle; private final boolean maxHandleFlag; - private RowKey(long tableId, long handle) { + private RowKey(long tableId, Handle handle) { super(encode(tableId, handle)); this.tableId = tableId; this.handle = handle; @@ -48,24 +52,24 @@ private RowKey(long tableId, long handle) { private RowKey(long tableId) { super(encodeBeyondMaxHandle(tableId)); this.tableId = tableId; - this.handle = Long.MAX_VALUE; + this.handle = new IntHandle(Long.MAX_VALUE); this.maxHandleFlag = true; } - public static RowKey toRowKey(long tableId, long handle) { + public static RowKey toRowKey(long tableId, Handle handle) { return new RowKey(tableId, handle); } public static RowKey toRowKey(long tableId, org.tikv.common.key.TypedKey handle) { Object obj = handle.getValue(); if (obj instanceof Long) { - return new RowKey(tableId, (long) obj); + return new RowKey(tableId, new IntHandle((Long) obj)); } throw new TiExpressionException("Cannot encode row key with non-long type"); } public static RowKey createMin(long tableId) { - return toRowKey(tableId, Long.MIN_VALUE); + return toRowKey(tableId, new IntHandle(Long.MIN_VALUE)); } public static RowKey createBeyondMax(long tableId) { @@ -75,22 +79,35 @@ public static RowKey createBeyondMax(long tableId) { public static RowKey decode(byte[] value) { CodecDataInput cdi = new CodecDataInput(value); cdi.readByte(); - long tableId = IntegerCodec.readLong(cdi); // tableId + long tableId = IntegerCodec.readLong(cdi); cdi.readByte(); cdi.readByte(); - long handle = IntegerCodec.readLong(cdi); // handle + + Handle handle; + if (value.length == INT_HANDLE_SIZE) { + handle = new IntHandle(IntegerCodec.readLong(cdi)); + } else { + byte[] buffer = new byte[value.length - HANDLE_PREFIX_SIZE]; + cdi.readFully(buffer); + handle = new CommonHandle(buffer); + } + return toRowKey(tableId, handle); } - private static byte[] encode(long tableId, long handle) { + private static byte[] encode(long tableId, Handle handle) { + return encode(tableId, handle.encoded()); + } + + public static byte[] encode(long tableId, byte[] key) { CodecDataOutput cdo = new CodecDataOutput(); encodePrefix(cdo, tableId); - writeLong(cdo, handle); + cdo.write(key); return cdo.toBytes(); } private static byte[] encodeBeyondMaxHandle(long tableId) { - return prefixNext(encode(tableId, Long.MAX_VALUE)); + return prefixNext(encode(tableId, new IntHandle(Long.MAX_VALUE))); } private static void encodePrefix(CodecDataOutput cdo, long tableId) { @@ -101,22 +118,22 @@ private static void encodePrefix(CodecDataOutput cdo, long tableId) { @Override public RowKey next() { - long handle = getHandle(); + Handle handle = getHandle(); boolean maxHandleFlag = getMaxHandleFlag(); if (maxHandleFlag) { throw new TiClientInternalException("Handle overflow for Long MAX"); } - if (handle == Long.MAX_VALUE) { + if (handle.isInt() && handle.intValue() == Long.MAX_VALUE) { return createBeyondMax(tableId); } - return new RowKey(tableId, handle + 1); + return new RowKey(tableId, handle.next()); } public long getTableId() { return tableId; } - public long getHandle() { + public Handle getHandle() { return handle; } @@ -126,7 +143,7 @@ private boolean getMaxHandleFlag() { @Override public String toString() { - return Long.toString(handle); + return "handle" + handle.toString(); } public static class DecodeResult { diff --git a/src/main/java/org/tikv/common/meta/TiColumnInfo.java b/src/main/java/org/tikv/common/meta/TiColumnInfo.java index 20227b89f3f..6b0702d64df 100644 --- a/src/main/java/org/tikv/common/meta/TiColumnInfo.java +++ b/src/main/java/org/tikv/common/meta/TiColumnInfo.java @@ -53,6 +53,7 @@ public class TiColumnInfo implements Serializable { // if version is 1 then timestamp's default value will be read and decoded as utc. private final long version; private final String generatedExprString; + private final boolean hidden; @JsonCreator public TiColumnInfo( @@ -66,7 +67,8 @@ public TiColumnInfo( @JsonProperty("default_bit") String defaultValueBit, @JsonProperty("comment") String comment, @JsonProperty("version") long version, - @JsonProperty("generated_expr_string") String generatedExprString) { + @JsonProperty("generated_expr_string") String generatedExprString, + @JsonProperty("hidden") boolean hidden) { this.id = id; this.name = requireNonNull(name, "column name is null").getL(); this.offset = offset; @@ -81,6 +83,7 @@ public TiColumnInfo( this.isPrimaryKey = (type.getFlag() & PK_MASK) > 0; this.version = version; this.generatedExprString = generatedExprString; + this.hidden = hidden; } public TiColumnInfo( @@ -94,7 +97,8 @@ public TiColumnInfo( String defaultValueBit, String comment, long version, - String generatedExprString) { + String generatedExprString, + boolean hidden) { this.id = id; this.name = requireNonNull(name, "column name is null").toLowerCase(); this.offset = offset; @@ -107,6 +111,7 @@ public TiColumnInfo( this.isPrimaryKey = (type.getFlag() & PK_MASK) > 0; this.version = version; this.generatedExprString = generatedExprString; + this.hidden = hidden; } @VisibleForTesting @@ -123,6 +128,7 @@ public TiColumnInfo(long id, String name, int offset, DataType type, boolean isP this.defaultValueBit = null; this.version = DataType.COLUMN_VERSION_FLAG; this.generatedExprString = ""; + this.hidden = false; } static TiColumnInfo getRowIdColumn(int offset) { @@ -144,7 +150,8 @@ TiColumnInfo copyWithoutPrimaryKey() { this.defaultValueBit, this.comment, this.version, - this.generatedExprString); + this.generatedExprString, + this.hidden); } public long getId() { @@ -223,6 +230,10 @@ public boolean isAutoIncrement() { return this.type.isAutoIncrement(); } + public boolean isHidden() { + return hidden; + } + TiIndexColumn toFakeIndexColumn() { // we don't use original length of column since for a clustered index column // it always full index instead of prefix index diff --git a/src/main/java/org/tikv/common/meta/TiDAGRequest.java b/src/main/java/org/tikv/common/meta/TiDAGRequest.java index 5200bdef835..69bb8a4c033 100644 --- a/src/main/java/org/tikv/common/meta/TiDAGRequest.java +++ b/src/main/java/org/tikv/common/meta/TiDAGRequest.java @@ -26,9 +26,27 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.pingcap.tidb.tipb.*; -import java.io.*; -import java.util.*; +import com.pingcap.tidb.tipb.Aggregation; +import com.pingcap.tidb.tipb.ColumnInfo; +import com.pingcap.tidb.tipb.DAGRequest; +import com.pingcap.tidb.tipb.EncodeType; +import com.pingcap.tidb.tipb.ExecType; +import com.pingcap.tidb.tipb.Executor; +import com.pingcap.tidb.tipb.IndexScan; +import com.pingcap.tidb.tipb.Limit; +import com.pingcap.tidb.tipb.Selection; +import com.pingcap.tidb.tipb.TableScan; +import com.pingcap.tidb.tipb.TopN; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.tikv.common.codec.KeyUtils; @@ -176,6 +194,10 @@ public void setEncodeType(EncodeType encodeType) { this.encodeType = encodeType; } + public boolean isCommonHandle() { + return tableInfo.isCommonHandle(); + } + public DAGRequest buildIndexScan() { List outputOffsets = new ArrayList<>(); DAGRequest.Builder builder = buildScan(true, outputOffsets); diff --git a/src/main/java/org/tikv/common/meta/TiIndexInfo.java b/src/main/java/org/tikv/common/meta/TiIndexInfo.java index 0f0c70b22cf..673e40cad4b 100644 --- a/src/main/java/org/tikv/common/meta/TiIndexInfo.java +++ b/src/main/java/org/tikv/common/meta/TiIndexInfo.java @@ -44,6 +44,7 @@ public class TiIndexInfo implements Serializable { private final String comment; private final org.tikv.common.meta.IndexType indexType; private final boolean isFakePrimaryKey; + private final boolean isInvisible; // default index column size (TypeFlag + Int64) private long indexColumnSize = 9; @@ -62,7 +63,8 @@ public TiIndexInfo( @JsonProperty("index_type") int indexType, // This is a fake property and added JsonProperty only to // to bypass Jackson frameworks's check - @JsonProperty("___isFakePrimaryKey") boolean isFakePrimaryKey) { + @JsonProperty("___isFakePrimaryKey") boolean isFakePrimaryKey, + @JsonProperty("is_invisible") boolean isInvisible) { this.id = id; this.name = requireNonNull(name, "index name is null").getL(); this.tableName = requireNonNull(tableName, "table name is null").getL(); @@ -73,6 +75,7 @@ public TiIndexInfo( this.comment = comment; this.indexType = org.tikv.common.meta.IndexType.fromValue(indexType); this.isFakePrimaryKey = isFakePrimaryKey; + this.isInvisible = isInvisible; } public static TiIndexInfo generateFakePrimaryKeyIndex(TiTableInfo table) { @@ -88,7 +91,8 @@ public static TiIndexInfo generateFakePrimaryKeyIndex(TiTableInfo table) { org.tikv.common.meta.SchemaState.StatePublic.getStateCode(), "Fake Column", org.tikv.common.meta.IndexType.IndexTypeHash.getTypeCode(), - true); + true, + false); } return null; } @@ -186,6 +190,10 @@ public boolean isFakePrimaryKey() { return isFakePrimaryKey; } + public boolean isInvisible() { + return isInvisible; + } + @Override public String toString() { return String.format( diff --git a/src/main/java/org/tikv/common/meta/TiTableInfo.java b/src/main/java/org/tikv/common/meta/TiTableInfo.java index 344cf5d9647..bb9c2d8da35 100644 --- a/src/main/java/org/tikv/common/meta/TiTableInfo.java +++ b/src/main/java/org/tikv/common/meta/TiTableInfo.java @@ -44,6 +44,11 @@ public class TiTableInfo implements Serializable { private final Map columnsMap; private final List indices; private final boolean pkIsHandle; + private final boolean isCommonHandle; + // CommonHandleVersion is the version of the clustered index. + // 0 for the clustered index created == 5.0.0 RC. + // 1 for the clustered index created > 5.0.0 RC. + private final int commonHandleVersion; private final String comment; private final long autoIncId; private final long maxColumnId; @@ -58,6 +63,12 @@ public class TiTableInfo implements Serializable { private final long updateTimestamp; private final long maxShardRowIDBits; private final org.tikv.common.meta.TiSequenceInfo sequenceInfo; + private final long autoRandomBits; + + /** without hiden column */ + private final List columnsWithoutHidden; + + private final List indicesWithoutHiddenAndInvisible; @JsonCreator @JsonIgnoreProperties(ignoreUnknown = true) @@ -67,6 +78,8 @@ public TiTableInfo( @JsonProperty("charset") String charset, @JsonProperty("collate") String collate, @JsonProperty("pk_is_handle") boolean pkIsHandle, + @JsonProperty("is_common_handle") boolean isCommonHandle, + @JsonProperty("common_handle_version") int commonHandleVersion, @JsonProperty("cols") List columns, @JsonProperty("index_info") List indices, @JsonProperty("comment") String comment, @@ -80,27 +93,49 @@ public TiTableInfo( @JsonProperty("version") long version, @JsonProperty("update_timestamp") long updateTimestamp, @JsonProperty("max_shard_row_id_bits") long maxShardRowIDBits, - @JsonProperty("sequence") org.tikv.common.meta.TiSequenceInfo sequenceInfo) { + @JsonProperty("sequence") org.tikv.common.meta.TiSequenceInfo sequenceInfo, + @JsonProperty("auto_random_bits") long autoRandomBits) { this.id = id; this.name = name.getL(); this.charset = charset; this.collate = collate; if (sequenceInfo == null) { this.columns = ImmutableList.copyOf(requireNonNull(columns, "columns is null")); + this.columnsWithoutHidden = + columns.stream().filter(col -> !col.isHidden()).collect(Collectors.toList()); this.columnsMap = new HashMap<>(); - for (org.tikv.common.meta.TiColumnInfo col : this.columns) { + for (TiColumnInfo col : this.columns) { this.columnsMap.put(col.getName(), col); } - this.rowSize = columns.stream().mapToLong(org.tikv.common.meta.TiColumnInfo::getSize).sum(); + this.rowSize = columns.stream().mapToLong(TiColumnInfo::getSize).sum(); } else { this.columns = null; + this.columnsWithoutHidden = null; this.columnsMap = null; // 9 is the rowSize for type bigint this.rowSize = 9; } // TODO: Use more precise predication according to types this.pkIsHandle = pkIsHandle; + this.isCommonHandle = isCommonHandle; + this.commonHandleVersion = commonHandleVersion; this.indices = indices != null ? ImmutableList.copyOf(indices) : ImmutableList.of(); + this.indicesWithoutHiddenAndInvisible = + this.indices + .stream() + .filter( + idx -> { + if (idx.isInvisible()) { + return false; + } + for (TiIndexColumn idc : idx.getIndexColumns()) { + if (getColumn(idc.getName()).isHidden()) { + return false; + } + } + return true; + }) + .collect(Collectors.toList()); if (this.columns != null) { this.indices.forEach(x -> x.calculateIndexSize(columns)); } @@ -116,10 +151,11 @@ public TiTableInfo( this.updateTimestamp = updateTimestamp; this.maxShardRowIDBits = maxShardRowIDBits; this.sequenceInfo = sequenceInfo; + this.autoRandomBits = autoRandomBits; - org.tikv.common.meta.TiColumnInfo primaryKey = null; + TiColumnInfo primaryKey = null; if (sequenceInfo == null) { - for (org.tikv.common.meta.TiColumnInfo col : this.columns) { + for (TiColumnInfo col : this.columns) { if (col.isPrimaryKey()) { primaryKey = col; break; @@ -203,7 +239,15 @@ public boolean isPkHandle() { } public List getIndices() { - return indices; + return getIndices(false); + } + + public List getIndices(boolean includingAll) { + if (includingAll) { + return this.indices; + } else { + return this.indicesWithoutHiddenAndInvisible; + } } public String getComment() { @@ -226,6 +270,10 @@ private long getOldSchemaId() { return oldSchemaId; } + public boolean isCommonHandle() { + return isCommonHandle; + } + public org.tikv.common.meta.TiPartitionInfo getPartitionInfo() { return partitionInfo; } @@ -246,6 +294,15 @@ public boolean hasPrimaryKey() { return primaryKeyColumn != null; } + public TiIndexInfo getPrimaryKey() { + for (TiIndexInfo index : getIndices()) { + if (index.isPrimary()) { + return index; + } + } + return null; + } + // Only Integer Column will be a PK column // and there exists only one PK column public org.tikv.common.meta.TiColumnInfo getPKIsHandleColumn() { @@ -275,7 +332,8 @@ private org.tikv.common.meta.TiColumnInfo copyColumn(org.tikv.common.meta.TiColu col.getDefaultValueBit(), col.getComment(), col.getVersion(), - col.getGeneratedExprString()) + col.getGeneratedExprString(), + col.isHidden()) .copyWithoutPrimaryKey(); } @@ -292,6 +350,8 @@ public TiTableInfo copyTableWithRowId() { getCharset(), getCollate(), true, + isCommonHandle, + commonHandleVersion, newColumns.build(), getIndices(), getComment(), @@ -305,7 +365,8 @@ public TiTableInfo copyTableWithRowId() { version, updateTimestamp, maxShardRowIDBits, - null); + null, + autoRandomBits); } else { return this; } diff --git a/src/main/java/org/tikv/common/operation/iterator/CoprocessorIterator.java b/src/main/java/org/tikv/common/operation/iterator/CoprocessorIterator.java index 5208878dd18..063096d4e39 100644 --- a/src/main/java/org/tikv/common/operation/iterator/CoprocessorIterator.java +++ b/src/main/java/org/tikv/common/operation/iterator/CoprocessorIterator.java @@ -35,6 +35,8 @@ import org.tikv.common.columnar.TiColumnVector; import org.tikv.common.columnar.TiRowColumnVector; import org.tikv.common.columnar.datatypes.CHType; +import org.tikv.common.handle.Handle; +import org.tikv.common.handle.IntHandle; import org.tikv.common.meta.TiDAGRequest; import org.tikv.common.operation.SchemaInfer; import org.tikv.common.row.Row; @@ -204,13 +206,13 @@ public TiChunk next() { * @param session TiSession * @return a DAGIterator to be processed */ - public static CoprocessorIterator getHandleIterator( + public static CoprocessorIterator getHandleIterator( TiDAGRequest req, List regionTasks, TiSession session) { TiDAGRequest dagRequest = req.copy(); // set encode type to TypeDefault because currently, only // CoprocessorIterator support TypeChunk and TypeCHBlock encode type dagRequest.setEncodeType(EncodeType.TypeDefault); - return new DAGIterator( + return new DAGIterator( dagRequest.buildIndexScan(), regionTasks, session, @@ -219,8 +221,13 @@ public static CoprocessorIterator getHandleIterator( dagRequest.getStoreType(), dagRequest.getStartTs().getVersion()) { @Override - public Long next() { - return rowReader.readRow(handleTypes).getLong(handleTypes.length - 1); + public Handle next() { + Row row = rowReader.readRow(handleTypes); + Object[] data = new Object[handleTypes.length]; + for (int i = 0; i < handleTypes.length; i++) { + data[i] = row.get(i, handleTypes[i]); + } + return new IntHandle((long) data[handleTypes.length - 1]); } }; } diff --git a/src/main/java/org/tikv/common/operation/iterator/IndexScanIterator.java b/src/main/java/org/tikv/common/operation/iterator/IndexScanIterator.java index b8fcab5ac43..b5e4cb2c8a5 100644 --- a/src/main/java/org/tikv/common/operation/iterator/IndexScanIterator.java +++ b/src/main/java/org/tikv/common/operation/iterator/IndexScanIterator.java @@ -17,7 +17,6 @@ package org.tikv.common.operation.iterator; -import gnu.trove.list.array.TLongArrayList; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -27,13 +26,14 @@ import org.tikv.common.TiConfiguration; import org.tikv.common.TiSession; import org.tikv.common.exception.TiClientInternalException; +import org.tikv.common.handle.Handle; import org.tikv.common.meta.TiDAGRequest; import org.tikv.common.row.Row; import org.tikv.common.util.RangeSplitter; import org.tikv.common.util.RangeSplitter.RegionTask; public class IndexScanIterator implements Iterator { - private final Iterator handleIterator; + private final Iterator handleIterator; private final TiDAGRequest dagReq; private final Snapshot snapshot; private final ExecutorCompletionService> completionService; @@ -41,7 +41,7 @@ public class IndexScanIterator implements Iterator { private Iterator rowIterator; private int batchCount = 0; - public IndexScanIterator(Snapshot snapshot, TiDAGRequest req, Iterator handleIterator) { + public IndexScanIterator(Snapshot snapshot, TiDAGRequest req, Iterator handleIterator) { TiSession session = snapshot.getSession(); TiConfiguration conf = session.getConf(); this.dagReq = req; @@ -51,8 +51,8 @@ public IndexScanIterator(Snapshot snapshot, TiDAGRequest req, Iterator han this.completionService = new ExecutorCompletionService<>(session.getThreadPoolForIndexScan()); } - private TLongArrayList feedBatch() { - TLongArrayList handles = new TLongArrayList(512); + private List feedBatch() { + List handles = new ArrayList<>(512); while (handleIterator.hasNext()) { handles.add(handleIterator.next()); if (batchSize <= handles.size()) { @@ -68,7 +68,7 @@ public boolean hasNext() { if (rowIterator == null) { TiSession session = snapshot.getSession(); while (handleIterator.hasNext()) { - TLongArrayList handles = feedBatch(); + List handles = feedBatch(); batchCount++; completionService.submit( () -> { diff --git a/src/main/java/org/tikv/common/types/Converter.java b/src/main/java/org/tikv/common/types/Converter.java index 51d6762a5ac..a23fda73d18 100644 --- a/src/main/java/org/tikv/common/types/Converter.java +++ b/src/main/java/org/tikv/common/types/Converter.java @@ -38,6 +38,7 @@ import org.tikv.common.exception.ConvertNotSupportException; import org.tikv.common.exception.ConvertOverflowException; import org.tikv.common.exception.TypeException; +import org.tikv.common.handle.Handle; public class Converter { @@ -72,6 +73,8 @@ public static Long safeConvertToSigned(Object value, Long lowerBound, Long upper result = doubleToLong((Double) value); } else if (value instanceof String) { result = stringToLong((String) value); + } else if (value instanceof Handle) { + result = ((Handle) value).intValue(); } else { throw new ConvertNotSupportException(value.getClass().getName(), "SIGNED"); } diff --git a/src/main/java/org/tikv/common/util/RangeSplitter.java b/src/main/java/org/tikv/common/util/RangeSplitter.java index cd1098eccc7..ff129a0e1f9 100644 --- a/src/main/java/org/tikv/common/util/RangeSplitter.java +++ b/src/main/java/org/tikv/common/util/RangeSplitter.java @@ -23,10 +23,15 @@ import com.google.common.collect.ImmutableList; import com.google.protobuf.ByteString; -import gnu.trove.list.array.TLongArrayList; import gnu.trove.map.hash.TLongObjectHashMap; import java.io.Serializable; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.tikv.common.handle.Handle; import org.tikv.common.key.RowKey; import org.tikv.common.pd.PDUtils; import org.tikv.common.region.RegionManager; @@ -53,24 +58,23 @@ public static RangeSplitter newSplitter(RegionManager mgr) { * @param handles Handle list * @return {@code } map */ - public Map, TLongArrayList> groupByAndSortHandlesByRegionId( - long tableId, TLongArrayList handles) { - TLongObjectHashMap regionHandles = new TLongObjectHashMap<>(); + public Map, List> groupByAndSortHandlesByRegionId( + long tableId, List handles) { + TLongObjectHashMap> regionHandles = new TLongObjectHashMap<>(); TLongObjectHashMap> idToRegionStorePair = new TLongObjectHashMap<>(); - Map, TLongArrayList> result = new HashMap<>(); - handles.sort(); + Map, List> result = new HashMap<>(); + handles.sort(Handle::compare); byte[] endKey = null; TiRegion curRegion = null; - TLongArrayList handlesInCurRegion = new TLongArrayList(); - for (int i = 0; i < handles.size(); i++) { - long curHandle = handles.get(i); + List handlesInCurRegion = new ArrayList<>(); + for (Handle curHandle : handles) { RowKey key = RowKey.toRowKey(tableId, curHandle); if (endKey == null || (endKey.length != 0 && FastByteComparisons.compareTo(key.getBytes(), endKey) >= 0)) { if (curRegion != null) { regionHandles.put(curRegion.getId(), handlesInCurRegion); - handlesInCurRegion = new TLongArrayList(); + handlesInCurRegion = new ArrayList<>(); } Pair regionStorePair = regionManager.getRegionStorePairByKey(ByteString.copyFrom(key.getBytes())); @@ -80,7 +84,7 @@ public Map, TLongArrayList> groupByAndSortHandlesByRegio } handlesInCurRegion.add(curHandle); } - if (!handlesInCurRegion.isEmpty() && curRegion != null) { + if (!handlesInCurRegion.isEmpty()) { regionHandles.put(curRegion.getId(), handlesInCurRegion); } regionHandles.forEachEntry( @@ -92,7 +96,7 @@ public Map, TLongArrayList> groupByAndSortHandlesByRegio return result; } - public List splitAndSortHandlesByRegion(List ids, TLongArrayList handles) { + public List splitAndSortHandlesByRegion(List ids, List handles) { Set regionTasks = new HashSet<>(); for (Long id : ids) { regionTasks.addAll(splitAndSortHandlesByRegion(id, handles)); @@ -107,11 +111,11 @@ public List splitAndSortHandlesByRegion(List ids, TLongArrayLi * @param handles Handle list * @return A list of region tasks */ - private List splitAndSortHandlesByRegion(long tableId, TLongArrayList handles) { + private List splitAndSortHandlesByRegion(long tableId, List handles) { // Max value for current index handle range ImmutableList.Builder regionTasks = ImmutableList.builder(); - Map, TLongArrayList> regionHandlesMap = + Map, List> regionHandlesMap = groupByAndSortHandlesByRegionId(tableId, handles); regionHandlesMap.forEach((k, v) -> createTask(0, v.size(), tableId, v, k, regionTasks)); @@ -123,21 +127,21 @@ private void createTask( int startPos, int endPos, long tableId, - TLongArrayList handles, + List handles, Pair regionStorePair, ImmutableList.Builder regionTasks) { List newKeyRanges = new ArrayList<>(endPos - startPos + 1); - long startHandle = handles.get(startPos); - long endHandle = startHandle; + Handle startHandle = handles.get(startPos); + Handle endHandle = startHandle; for (int i = startPos + 1; i < endPos; i++) { - long curHandle = handles.get(i); - if (endHandle + 1 == curHandle) { + Handle curHandle = handles.get(i); + if (endHandle.next().equals(curHandle)) { endHandle = curHandle; } else { newKeyRanges.add( makeCoprocRange( RowKey.toRowKey(tableId, startHandle).toByteString(), - RowKey.toRowKey(tableId, endHandle + 1).toByteString())); + RowKey.toRowKey(tableId, endHandle.next()).toByteString())); startHandle = curHandle; endHandle = startHandle; } @@ -145,8 +149,9 @@ private void createTask( newKeyRanges.add( makeCoprocRange( RowKey.toRowKey(tableId, startHandle).toByteString(), - RowKey.toRowKey(tableId, endHandle + 1).toByteString())); - regionTasks.add(new RegionTask(regionStorePair.first, regionStorePair.second, newKeyRanges)); + RowKey.toRowKey(tableId, endHandle.next()).toByteString())); + regionTasks.add( + RegionTask.newInstance(regionStorePair.first, regionStorePair.second, newKeyRanges)); } /** diff --git a/src/test/java/org/tikv/util/RangeSplitterTest.java b/src/test/java/org/tikv/util/RangeSplitterTest.java index 7207f959d43..2db6f94ba89 100644 --- a/src/test/java/org/tikv/util/RangeSplitterTest.java +++ b/src/test/java/org/tikv/util/RangeSplitterTest.java @@ -21,7 +21,6 @@ import com.google.common.collect.ImmutableList; import com.google.protobuf.ByteString; -import gnu.trove.list.array.TLongArrayList; import gnu.trove.map.hash.TLongObjectHashMap; import java.util.ArrayList; import java.util.List; @@ -29,6 +28,8 @@ import org.tikv.common.MockRegionManager; import org.tikv.common.codec.Codec.IntegerCodec; import org.tikv.common.codec.CodecDataOutput; +import org.tikv.common.handle.Handle; +import org.tikv.common.handle.IntHandle; import org.tikv.common.key.RowKey; import org.tikv.common.key.RowKey.DecodeResult.Status; import org.tikv.common.util.RangeSplitter; @@ -80,7 +81,7 @@ private static ByteString shiftByStatus(ByteString v, Status s) { private static ByteString handleToByteString(long tableId, Long k) { if (k != null) { - return RowKey.toRowKey(tableId, k).toByteString(); + return RowKey.toRowKey(tableId, new IntHandle(k)).toByteString(); } return ByteString.EMPTY; } @@ -115,27 +116,27 @@ public void splitRangeByRegionTest() { @Test public void splitAndSortHandlesByRegionTest() { final long tableId = 1; - List handles = new ArrayList<>(); - handles.add(1L); - handles.add(5L); - handles.add(4L); - handles.add(3L); - handles.add(10L); - handles.add(2L); - handles.add(100L); - handles.add(101L); - handles.add(99L); - handles.add(88L); - handles.add(-1L); - handles.add(-255L); - handles.add(-100L); - handles.add(-99L); - handles.add(-98L); - handles.add(Long.MIN_VALUE); - handles.add(8960L); - handles.add(8959L); - handles.add(19999L); - handles.add(15001L); + List handles = new ArrayList<>(); + handles.add(new IntHandle(1L)); + handles.add(new IntHandle(5L)); + handles.add(new IntHandle(4L)); + handles.add(new IntHandle(3L)); + handles.add(new IntHandle(10L)); + handles.add(new IntHandle(2L)); + handles.add(new IntHandle(100L)); + handles.add(new IntHandle(101L)); + handles.add(new IntHandle(99L)); + handles.add(new IntHandle(88L)); + handles.add(new IntHandle(-1L)); + handles.add(new IntHandle(-255L)); + handles.add(new IntHandle(-100L)); + handles.add(new IntHandle(-99L)); + handles.add(new IntHandle(-98L)); + handles.add(new IntHandle(Long.MIN_VALUE)); + handles.add(new IntHandle(8960L)); + handles.add(new IntHandle(8959L)); + handles.add(new IntHandle(19999L)); + handles.add(new IntHandle(15001L)); MockRegionManager mgr = new MockRegionManager( @@ -150,10 +151,7 @@ public void splitAndSortHandlesByRegionTest() { RangeSplitter s = RangeSplitter.newSplitter(mgr); List tasks = - new ArrayList<>( - s.splitAndSortHandlesByRegion( - ImmutableList.of(tableId), - new TLongArrayList(handles.stream().mapToLong(t -> t).toArray()))); + new ArrayList<>(s.splitAndSortHandlesByRegion(ImmutableList.of(tableId), handles)); tasks.sort( (l, r) -> { Long regionIdLeft = l.getRegion().getId(); @@ -205,31 +203,30 @@ public void splitAndSortHandlesByRegionTest() { @Test public void groupByAndSortHandlesByRegionIdTest() { final long tableId = 1; - List handles = new ArrayList<>(); - handles.add(1L); - handles.add(5L); - handles.add(4L); - handles.add(3L); - handles.add(10L); - handles.add(11L); - handles.add(12L); - handles.add(2L); - handles.add(100L); - handles.add(101L); - handles.add(99L); - handles.add(88L); - handles.add(-1L); - handles.add(-255L); - handles.add(-100L); - handles.add(-99L); - handles.add(-98L); - handles.add(Long.MIN_VALUE); - handles.add(8960L); - handles.add(8959L); - handles.add(19999L); - handles.add(15001L); - handles.add(99999999999L); - handles.add(Long.MAX_VALUE); + List handles = new ArrayList<>(); + handles.add(new IntHandle(1L)); + handles.add(new IntHandle(5L)); + handles.add(new IntHandle(4L)); + handles.add(new IntHandle(3L)); + handles.add(new IntHandle(10L)); + handles.add(new IntHandle(2L)); + handles.add(new IntHandle(100L)); + handles.add(new IntHandle(101L)); + handles.add(new IntHandle(99L)); + handles.add(new IntHandle(88L)); + handles.add(new IntHandle(-1L)); + handles.add(new IntHandle(-255L)); + handles.add(new IntHandle(-100L)); + handles.add(new IntHandle(-99L)); + handles.add(new IntHandle(-98L)); + handles.add(new IntHandle(Long.MIN_VALUE)); + handles.add(new IntHandle(8960L)); + handles.add(new IntHandle(8959L)); + handles.add(new IntHandle(19999L)); + handles.add(new IntHandle(15001L)); + handles.add(new IntHandle(15001L)); + handles.add(new IntHandle(99999999999L)); + handles.add(new IntHandle(Long.MAX_VALUE)); MockRegionManager mgr = new MockRegionManager( @@ -242,10 +239,9 @@ public void groupByAndSortHandlesByRegionIdTest() { keyRangeByHandle(tableId, 0x2300L /*8960*/, Status.LESS, 16000L, Status.EQUAL), keyRangeByHandle(tableId, 16000L, Status.EQUAL, null, Status.EQUAL))); - TLongObjectHashMap result = new TLongObjectHashMap<>(); + TLongObjectHashMap> result = new TLongObjectHashMap<>(); RangeSplitter.newSplitter(mgr) - .groupByAndSortHandlesByRegionId( - tableId, new TLongArrayList(handles.stream().mapToLong(t -> t).toArray())) + .groupByAndSortHandlesByRegionId(tableId, handles) .forEach((k, v) -> result.put(k.first.getId(), v)); assertEquals(2, result.get(0).size()); assertEquals(10, result.get(1).size());