blob: f38d3290775d2fbb2f9e5bc22e804fe371048eae [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.iotdb.db.metadata.schemaregion.rocksdb;
import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.commons.exception.IllegalPathException;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.utils.PathUtils;
import org.apache.iotdb.db.metadata.mnode.IMNode;
import org.apache.iotdb.db.metadata.schemaregion.rocksdb.mnode.REntityMNode;
import org.apache.iotdb.db.metadata.schemaregion.rocksdb.mnode.RMNodeType;
import org.apache.iotdb.db.metadata.schemaregion.rocksdb.mnode.RMNodeValueType;
import org.apache.iotdb.db.metadata.schemaregion.rocksdb.mnode.RMeasurementMNode;
import org.apache.iotdb.db.metadata.schemaregion.rocksdb.mnode.RStorageGroupMNode;
import org.apache.iotdb.tsfile.utils.BytesUtils;
import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
import org.apache.iotdb.tsfile.write.schema.IMeasurementSchema;
import org.apache.iotdb.tsfile.write.schema.MeasurementSchema;
import org.apache.commons.lang3.ArrayUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.DATA_BLOCK_TYPE_ALIAS;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.DATA_BLOCK_TYPE_ATTRIBUTES;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.DATA_BLOCK_TYPE_ORIGIN_KEY;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.DATA_BLOCK_TYPE_SCHEMA;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.DATA_BLOCK_TYPE_TAGS;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.DATA_BLOCK_TYPE_TTL;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.DATA_VERSION;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.DEFAULT_FLAG;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.ESCAPE_PATH_SEPARATOR;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.FLAG_HAS_ALIAS;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.FLAG_HAS_ATTRIBUTES;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.FLAG_HAS_SCHEMA;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.FLAG_HAS_TAGS;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.FLAG_IS_ALIGNED;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.FLAG_SET_TTL;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.NODE_TYPE_ALIAS;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.NODE_TYPE_ENTITY;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.NODE_TYPE_INTERNAL;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.NODE_TYPE_MEASUREMENT;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.NODE_TYPE_SG;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.PATH_SEPARATOR;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.ROOT;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.ROOT_CHAR;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.ROOT_STRING;
import static org.apache.iotdb.db.metadata.schemaregion.rocksdb.RSchemaConstants.ZERO;
public class RSchemaUtils {
public static final RMNodeType[] NODE_TYPE_ARRAY = new RMNodeType[NODE_TYPE_ALIAS + 1];
static {
NODE_TYPE_ARRAY[NODE_TYPE_INTERNAL] = RMNodeType.INTERNAL;
NODE_TYPE_ARRAY[NODE_TYPE_SG] = RMNodeType.STORAGE_GROUP;
NODE_TYPE_ARRAY[NODE_TYPE_ENTITY] = RMNodeType.ENTITY;
NODE_TYPE_ARRAY[NODE_TYPE_MEASUREMENT] = RMNodeType.MEASUREMENT;
NODE_TYPE_ARRAY[NODE_TYPE_ALIAS] = RMNodeType.ALISA;
}
protected static byte[] toInternalNodeKey(String levelPath) {
return toRocksDBKey(levelPath, NODE_TYPE_INTERNAL);
}
protected static byte[] toStorageNodeKey(String levelPath) {
return toRocksDBKey(levelPath, NODE_TYPE_SG);
}
protected static byte[] toEntityNodeKey(String levelPath) {
return toRocksDBKey(levelPath, NODE_TYPE_ENTITY);
}
protected static byte[] toMeasurementNodeKey(String levelPath) {
return toRocksDBKey(levelPath, NODE_TYPE_MEASUREMENT);
}
protected static byte[] toAliasNodeKey(String levelPath) {
return toRocksDBKey(levelPath, NODE_TYPE_ALIAS);
}
protected static byte[] toRocksDBKey(String levelPath, char type) {
return (type + levelPath).getBytes();
}
public static String getLevelPathPrefix(String[] nodes, int end, int level) {
StringBuilder builder = new StringBuilder();
char depth = (char) (ZERO + level);
builder.append(ROOT).append(PATH_SEPARATOR).append(depth);
for (int i = 1; i <= end; i++) {
builder.append(nodes[i]).append(PATH_SEPARATOR).append(depth);
}
return builder.toString();
}
public static String getLevelPath(String[] nodes, int end) {
return getLevelPath(nodes, end, end);
}
public static String getLevelPath(String[] nodes, int end, int level) {
StringBuilder builder = new StringBuilder();
builder.append(ROOT);
char depth = (char) (ZERO + level);
for (int i = 1; i <= end; i++) {
builder.append(PATH_SEPARATOR).append(depth).append(nodes[i]);
}
return builder.toString();
}
public static String getMeasurementLevelPath(String[] prefixPath, String measurement) {
String[] nodes = ArrayUtils.add(prefixPath, measurement);
return getLevelPath(nodes, nodes.length - 1);
}
public static List<PartialPath> convertToPartialPath(Collection<String> paths, int level) {
return paths
.parallelStream()
.map(x -> getPartialPathFromInnerPath(x, level))
.collect(Collectors.toList());
}
public static String getNextLevelOfPath(String innerPath, int currentLevel) {
char levelChar = (char) (ZERO + currentLevel);
String old = PATH_SEPARATOR + levelChar;
String target = PATH_SEPARATOR + (char) (levelChar + 1);
return innerPath.replace(old, target);
}
public static String getNextLevelOfPath(String innerPath, char currentLevel) {
String old = PATH_SEPARATOR + currentLevel;
String target = PATH_SEPARATOR + (char) (currentLevel + 1);
return innerPath.replace(old, target);
}
public static PartialPath getPartialPathFromInnerPath(String path, int level) {
char charLevel = (char) (ZERO + level);
return getPartialPathFromInnerPath(path, charLevel);
}
public static PartialPath getPartialPathFromInnerPath(String path, char level) {
String pathWithoutLevel = path.replace(PATH_SEPARATOR + level, PATH_SEPARATOR);
String[] nodes = pathWithoutLevel.split(ESCAPE_PATH_SEPARATOR);
nodes[0] = IoTDBConstant.PATH_ROOT;
return new PartialPath(nodes);
}
public static RMNodeType typeOfMNode(IMNode mNode) {
// order sensitive
if (mNode instanceof REntityMNode) {
return RMNodeType.ENTITY;
}
if (mNode instanceof RStorageGroupMNode) {
return RMNodeType.STORAGE_GROUP;
}
if (mNode instanceof RMeasurementMNode) {
return RMNodeType.MEASUREMENT;
}
return RMNodeType.INTERNAL;
}
public static byte[] buildMeasurementNodeValue(
IMeasurementSchema schema,
String alias,
Map<String, String> tags,
Map<String, String> attributes)
throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ReadWriteIOUtils.write(DATA_VERSION, outputStream);
byte flag = DEFAULT_FLAG;
if (alias != null) {
flag = (byte) (flag | FLAG_HAS_ALIAS);
}
if (tags != null && tags.size() > 0) {
flag = (byte) (flag | FLAG_HAS_TAGS);
}
if (attributes != null && attributes.size() > 0) {
flag = (byte) (flag | FLAG_HAS_ATTRIBUTES);
}
if (schema != null) {
flag = (byte) (flag | FLAG_HAS_SCHEMA);
}
ReadWriteIOUtils.write(flag, outputStream);
if (schema != null) {
ReadWriteIOUtils.write(DATA_BLOCK_TYPE_SCHEMA, outputStream);
schema.serializeTo(outputStream);
}
if (alias != null) {
ReadWriteIOUtils.write(DATA_BLOCK_TYPE_ALIAS, outputStream);
ReadWriteIOUtils.write(alias, outputStream);
}
if (tags != null && tags.size() > 0) {
ReadWriteIOUtils.write(DATA_BLOCK_TYPE_TAGS, outputStream);
ReadWriteIOUtils.write(tags, outputStream);
}
if (attributes != null && attributes.size() > 0) {
ReadWriteIOUtils.write(DATA_BLOCK_TYPE_TAGS, outputStream);
ReadWriteIOUtils.write(tags, outputStream);
}
return outputStream.toByteArray();
}
public static byte[] buildAliasNodeValue(byte[] originKey) {
byte[] prefix = new byte[] {DATA_VERSION, DEFAULT_FLAG, DATA_BLOCK_TYPE_ORIGIN_KEY};
byte[] len = BytesUtils.intToBytes(originKey.length);
return BytesUtils.concatByteArray(BytesUtils.concatByteArray(prefix, len), originKey);
}
public static byte[] readOriginKey(ByteBuffer buffer) {
int len = ReadWriteIOUtils.readInt(buffer);
return ReadWriteIOUtils.readBytes(buffer, len);
}
public static int indexOfDataBlockType(byte[] data, RMNodeValueType valueType) {
if (valueType.getFlag() != null && (data[1] & valueType.getFlag()) == 0) {
return -1;
}
int index = -1;
boolean typeExist = false;
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
// skip the data version and filter byte
ReadWriteIOUtils.readBytes(byteBuffer, 2);
while (byteBuffer.hasRemaining()) {
byte blockType = ReadWriteIOUtils.readByte(byteBuffer);
index = byteBuffer.position();
switch (blockType) {
case DATA_BLOCK_TYPE_TTL:
ReadWriteIOUtils.readLong(byteBuffer);
break;
case DATA_BLOCK_TYPE_ALIAS:
ReadWriteIOUtils.readString(byteBuffer);
break;
case DATA_BLOCK_TYPE_ORIGIN_KEY:
readOriginKey(byteBuffer);
break;
case DATA_BLOCK_TYPE_SCHEMA:
MeasurementSchema.deserializeFrom(byteBuffer);
break;
case DATA_BLOCK_TYPE_TAGS:
case DATA_BLOCK_TYPE_ATTRIBUTES:
ReadWriteIOUtils.readMap(byteBuffer);
break;
default:
break;
}
// got the data we need,don't need to read any more
if (valueType.getType() == blockType) {
typeExist = true;
break;
}
}
return typeExist ? index : -1;
}
public static byte[] updateTTL(byte[] origin, long ttl) {
int index = indexOfDataBlockType(origin, RMNodeValueType.TTL);
if (index < 1) {
byte[] ttlBlock = new byte[Long.BYTES + 1];
ttlBlock[0] = DATA_BLOCK_TYPE_TTL;
BytesUtils.longToBytes(ttl, ttlBlock, 1);
origin[1] = (byte) (origin[1] | FLAG_SET_TTL);
return BytesUtils.concatByteArray(origin, ttlBlock);
} else {
BytesUtils.longToBytes(ttl, origin, index);
return origin;
}
}
private static final char START_FLAG = '\u0019';
private static final char SPLIT_FLAG = '.';
/**
* parse value and return a specified type. if no data is required, null is returned.
*
* @param value value written in default table
* @param valueType the type of value to obtain
*/
public static Object parseNodeValue(byte[] value, RMNodeValueType valueType) {
ByteBuffer byteBuffer = ByteBuffer.wrap(value);
// skip the version flag and node type flag
ReadWriteIOUtils.readByte(byteBuffer);
// get block type
byte filter = ReadWriteIOUtils.readByte(byteBuffer);
Object obj = null;
if (valueType.getFlag() != null && (filter & valueType.getFlag()) == 0) {
return obj;
}
// this means that the following data contains the information we need
while (byteBuffer.hasRemaining()) {
byte blockType = ReadWriteIOUtils.readByte(byteBuffer);
switch (blockType) {
case DATA_BLOCK_TYPE_TTL:
obj = ReadWriteIOUtils.readLong(byteBuffer);
break;
case DATA_BLOCK_TYPE_ALIAS:
obj = ReadWriteIOUtils.readString(byteBuffer);
break;
case DATA_BLOCK_TYPE_ORIGIN_KEY:
obj = readOriginKey(byteBuffer);
break;
case DATA_BLOCK_TYPE_SCHEMA:
obj = MeasurementSchema.deserializeFrom(byteBuffer);
break;
case DATA_BLOCK_TYPE_TAGS:
case DATA_BLOCK_TYPE_ATTRIBUTES:
obj = ReadWriteIOUtils.readMap(byteBuffer);
break;
default:
break;
}
// got the data we need,don't need to read any more
if (valueType.getType() == blockType) {
break;
}
}
return obj;
}
public static boolean isAligned(byte[] value) {
ByteBuffer byteBuffer = ByteBuffer.wrap(value);
// skip the version flag and node type flag
ReadWriteIOUtils.readByte(byteBuffer);
// get block type
byte flag = ReadWriteIOUtils.readByte(byteBuffer);
return (flag & FLAG_IS_ALIGNED) > 0;
}
/**
* get inner name by converting partial path.
*
* @param partialPath the path needed to be converted.
* @param level the level needed to be added.
* @param nodeType specified type
* @return inner name
*/
public static String convertPartialPathToInner(String partialPath, int level, char nodeType) {
StringBuilder stringBuilder = new StringBuilder(nodeType + ROOT);
for (int i = partialPath.indexOf(PATH_SEPARATOR); i < partialPath.length() && i >= 0; i++) {
char currentChar = partialPath.charAt(i);
stringBuilder.append(currentChar);
if (currentChar == SPLIT_FLAG) {
stringBuilder.append(level);
}
}
return stringBuilder.toString();
}
public static String convertPartialPathToInnerByNodes(String[] nodes, int level, char nodeType) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(nodeType).append(ROOT);
for (int i = 0; i < nodes.length; i++) {
if (i == 0 && nodes[i].equals(ROOT_STRING)) {
continue;
}
stringBuilder.append(SPLIT_FLAG).append(level).append(nodes[i]);
}
return stringBuilder.toString();
}
public static int getLevelByPartialPath(String partialPath) {
int levelCount = 0;
for (char c : partialPath.toCharArray()) {
if (SPLIT_FLAG == c) {
levelCount++;
}
}
return levelCount;
}
public static byte[] getSuffixOfLevelPath(String[] nodes, int level) {
StringBuilder stringBuilder = new StringBuilder();
for (String str : nodes) {
stringBuilder.append(PATH_SEPARATOR).append(level).append(str);
}
return stringBuilder.toString().getBytes();
}
public static boolean suffixMatch(byte[] key, byte[] suffix) {
if (key.length < suffix.length) {
return false;
}
for (int i = key.length - 1, j = suffix.length - 1; i >= 0 && j >= 0; i--, j--) {
if ((key[i] ^ suffix[j]) != 0) {
return false;
}
}
return true;
}
public static boolean prefixMatch(byte[] key, byte[] prefix) {
if (key.length < prefix.length) {
return false;
}
for (int i = 0, j = 0; i < key.length && j < prefix.length; i++, j++) {
if ((key[i] ^ prefix[j]) != 0) {
return false;
}
}
return true;
}
public static String[] toMetaNodes(byte[] rocksdbKey) {
String rawKey =
new String(
Objects.requireNonNull(BytesUtils.subBytes(rocksdbKey, 1, rocksdbKey.length - 1)));
String[] nodes = rawKey.split(ESCAPE_PATH_SEPARATOR);
nodes[0] = ROOT_STRING;
for (int i = 1; i < nodes.length; i++) {
nodes[i] = nodes[i].substring(1);
}
return nodes;
}
public static String getPathByInnerName(String innerName) {
char[] keyConvertToCharArray = innerName.toCharArray();
StringBuilder stringBuilder = new StringBuilder();
char lastChar = START_FLAG;
boolean replaceFlag = true;
for (char c : keyConvertToCharArray) {
if (SPLIT_FLAG == lastChar || START_FLAG == lastChar) {
lastChar = c;
continue;
}
if (ROOT_CHAR == c && replaceFlag) {
lastChar = c;
stringBuilder.append(ROOT_STRING);
replaceFlag = false;
continue;
}
stringBuilder.append(c);
lastChar = c;
}
return stringBuilder.toString();
}
public static String getPathByLevelPath(String innerName) {
char[] keyConvertToCharArray = innerName.toCharArray();
StringBuilder stringBuilder = new StringBuilder();
char lastChar = START_FLAG;
boolean replaceFlag = true;
for (char c : keyConvertToCharArray) {
if (SPLIT_FLAG == lastChar) {
lastChar = c;
continue;
}
if (ROOT_CHAR == c && replaceFlag) {
lastChar = c;
stringBuilder.append(ROOT_STRING);
replaceFlag = false;
continue;
}
stringBuilder.append(c);
lastChar = c;
}
return stringBuilder.toString();
}
public static String[] newStringArray(String[] oldArray) throws IllegalPathException {
StringBuilder stringBuilder = new StringBuilder();
for (String str : oldArray) {
stringBuilder.append(PATH_SEPARATOR).append(str);
}
return PathUtils.splitPathToDetachedNodes(stringBuilder.substring(1));
}
public static String replaceWildcard(int num) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < num; i++) {
stringBuilder
.append(RSchemaConstants.PATH_SEPARATOR)
.append(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD);
}
return stringBuilder.substring(1);
}
public static List<int[]> getAllCompoundMode(int sum, int n) {
if (n <= 2) {
List<int[]> result = new ArrayList<>();
for (int i = 1; i < sum; i++) {
result.add(new int[] {i, sum - i});
}
return result;
}
List<int[]> allResult = new ArrayList<>();
for (int i = 1; i <= sum - n + 1; i++) {
List<int[]> temp = getAllCompoundMode(sum - i, n - 1);
for (int[] value : temp) {
int[] result = new int[value.length + 1];
result[0] = i;
System.arraycopy(value, 0, result, 1, value.length);
allResult.add(result);
}
}
return allResult;
}
// eg. root.a.*.**.b.**.c
public static List<String[]> replaceMultiWildcardToSingle(String[] nodes, int maxLevel)
throws IllegalPathException {
List<String[]> allNodesArray = new ArrayList<>();
List<Integer> multiWildcardPosition = new ArrayList<>();
for (int i = 0; i < nodes.length; i++) {
if (IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD.equals(nodes[i])) {
multiWildcardPosition.add(i);
}
}
if (multiWildcardPosition.isEmpty()) {
allNodesArray.add(nodes);
} else if (multiWildcardPosition.size() == 1) {
for (int i = 1; i <= maxLevel - nodes.length + 2; i++) {
String[] clone = nodes.clone();
clone[multiWildcardPosition.get(0)] = replaceWildcard(i);
allNodesArray.add(RSchemaUtils.newStringArray(clone));
}
} else {
for (int sum = multiWildcardPosition.size();
sum <= maxLevel - (nodes.length - multiWildcardPosition.size() - 1);
sum++) {
List<int[]> result = getAllCompoundMode(sum, multiWildcardPosition.size());
for (int[] value : result) {
String[] clone = nodes.clone();
for (int i = 0; i < value.length; i++) {
clone[multiWildcardPosition.get(i)] = replaceWildcard(value[i]);
}
allNodesArray.add(RSchemaUtils.newStringArray(clone));
}
}
}
return allNodesArray;
}
public static String concatNodesName(String[] nodes, int startIdx, int endIdx) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = startIdx; i <= endIdx; i++) {
stringBuilder.append(PATH_SEPARATOR).append(nodes[i]);
}
return stringBuilder.substring(1);
}
public static boolean startWith(byte[] a, byte[] b) {
if (a.length < b.length) {
return false;
}
for (int i = 0; i < b.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
}