Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -1548,3 +1548,10 @@ log.tools.csv_read_error = Error reading CSV file: %1$s

# CsvSourceReader — error closing CSV reader
log.tools.csv_close_reader_error = Error closing CSV reader

# TsFileTablePointCountTool
error.utils.table_point_count_tool_usage = Usage: TsFileTablePointCountTool <file.tsfile>
error.utils.table_point_count_tool_file_not_found = TsFile does not exist: %1$s
error.utils.table_point_count_tool_incomplete_file = TsFile is incomplete: %1$s
error.utils.table_point_count_tool_copy_failed = Failed to copy the TsFile data and metadata prefix
info.utils.table_point_count_tool_result = Processed %1$s: %2$s
Original file line number Diff line number Diff line change
Expand Up @@ -1548,3 +1548,10 @@ log.tools.csv_read_error = 读取 CSV 文件出错: %1$s

# CsvSourceReader — error closing CSV reader
log.tools.csv_close_reader_error = 关闭 CSV reader 出错

# TsFileTablePointCountTool
error.utils.table_point_count_tool_usage = 用法:TsFileTablePointCountTool <file.tsfile>
error.utils.table_point_count_tool_file_not_found = TsFile 不存在:%1$s
error.utils.table_point_count_tool_incomplete_file = TsFile 不完整:%1$s
error.utils.table_point_count_tool_copy_failed = 复制 TsFile 数据和元数据前缀失败
info.utils.table_point_count_tool_result = 已处理 %1$s:%2$s
21 changes: 21 additions & 0 deletions java/tools/README-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,24 @@ arrow2tsfile.bat --source .\data\arrow --target .\output --fail_dir .\failed
- 多批次:`{源文件名}_1.tsfile`、`{源文件名}_2.tsfile`、...
- 表名与输出文件名相互独立——表名来自 schema 或 `--table_name`,文件名来自源文件。

## 表点数统计工具

`tools/tsfile-table-point-count.sh`(Linux/macOS)和 `tools\tsfile-table-point-count.bat`(Windows)用于检查完整 TsFile 是否包含表级点数统计属性,并在属性缺失时补写。每张表的点数是其所有 FIELD 列中非空值的总数,不统计 TAG 列和时间列。

设置 `JAVA_HOME` 后,传入且仅传入一个 TsFile 路径:

# Linux/macOS
tools/tsfile-table-point-count.sh /data/example.tsfile

:: Windows
tools\tsfile-table-point-count.bat C:\data\example.tsfile

工具会输出以下状态之一:

| 状态 | 说明 |
|------|------|
| `UPDATED` | 已计算缺失的点数统计属性并写回文件。 |
| `ALREADY_PRESENT` | 每张表都已有有效的点数统计属性,未修改文件。 |
| `NO_TABLE` | 文件不包含表 Schema,例如仅含树模型数据的 TsFile,未修改文件。 |

仅当状态为 `UPDATED` 时,工具才会原地修改输入文件。工具先在源文件所在目录生成完整的临时文件,确保重写后的元数据落盘后再替换源文件。运行前请确保目录可写、磁盘空间足以存放临时副本,并备份重要文件。不存在的文件和未写完整的 TsFile 会被拒绝处理。
26 changes: 26 additions & 0 deletions java/tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,29 @@ arrow2tsfile.bat --source .\data\arrow --target .\output --fail_dir .\failed
- Multiple batches: `{source_basename}_1.tsfile`, `{source_basename}_2.tsfile`, ...
- Table name and output filename are independent — table name comes from schema or `--table_name`, filename comes from source file.

## Table Point Count Tool

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section documents the tool point-count semantics, invocation, result statuses, in-place replacement behavior, and disk-space and backup considerations. The matching Chinese README and message bundles keep the user experience consistent with the project localization convention.


`tools/tsfile-table-point-count.sh` (Linux/macOS) and `tools\tsfile-table-point-count.bat` (Windows) check whether a complete TsFile contains table-level
point-count properties and backfills them when they are missing. For each table, the point count is
the total number of non-null values in its FIELD columns; TAG and time columns are not counted.

Set `JAVA_HOME`, then run the tool with exactly one TsFile path:

# Linux/macOS
tools/tsfile-table-point-count.sh /data/example.tsfile

:: Windows
tools\tsfile-table-point-count.bat C:\data\example.tsfile

The tool prints one of the following statuses:

| Status | Description |
|--------|-------------|
| `UPDATED` | Missing point-count properties were calculated and written to the file. |
| `ALREADY_PRESENT` | Every table already had a valid point-count property; the file was not modified. |
| `NO_TABLE` | The file contains no table schema, for example a tree-model-only TsFile; the file was not modified. |

The tool modifies the input file in place only for `UPDATED`. It writes a complete temporary copy
next to the source file and replaces the source after the rewritten metadata has been flushed. Make
sure the directory is writable, leave enough free space for a temporary copy, and back up important
files before running the tool. Missing files and incomplete TsFiles are rejected.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@REM
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM

@echo off
setlocal enabledelayedexpansion

if "%OS%" == "Windows_NT" setlocal

pushd %~dp0..
if NOT DEFINED TSFILE_HOME set TSFILE_HOME=%CD%
popd

set JAVA_OPTS=-ea^
-DTSFILE_HOME="%TSFILE_HOME%"

if NOT DEFINED JAVA_HOME goto :err

echo ------------------------------------------
echo Starting TsFile Table Point Count Tool
echo ------------------------------------------

set CLASSPATH="%TSFILE_HOME%\lib\*"
set MAIN_CLASS=org.apache.tsfile.utils.TsFileTablePointCountTool

@REM The argument is the TsFile to inspect. The tool rewrites its properties only when table-level
@REM point-count statistics are missing.
"%JAVA_HOME%\bin\java" -DTSFILE_HOME=!TSFILE_HOME! !JAVA_OPTS! -cp !CLASSPATH! !MAIN_CLASS! %*
exit /b %ERRORLEVEL%

:err
echo JAVA_HOME environment variable must be set!
set ret_code=1
exit /b %ret_code%
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/bin/bash
#
# 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.
#

echo ------------------------------------------
echo Starting TsFile Table Point Count Tool
echo ------------------------------------------

if [ -z "${TSFILE_HOME}" ]; then
export TSFILE_HOME="$(cd "`dirname "$0"`"/..; pwd)"
fi

if [ -n "$JAVA_HOME" ]; then
for java in "$JAVA_HOME"/bin/amd64/java "$JAVA_HOME"/bin/java; do
if [ -x "$java" ]; then
JAVA="$java"
break
fi
done
else
JAVA=java
fi

if [ -z $JAVA ] ; then
echo Unable to find java executable. Check JAVA_HOME and PATH environment variables. > /dev/stderr
exit 1;
fi

CLASSPATH=${TSFILE_HOME}/lib/*
MAIN_CLASS=org.apache.tsfile.utils.TsFileTablePointCountTool

# The argument is the TsFile to inspect. The tool rewrites its properties only when table-level
# point-count statistics are missing.
exec "$JAVA" -DTSFILE_HOME=${TSFILE_HOME} -cp "$CLASSPATH" "$MAIN_CLASS" "$@"
9 changes: 9 additions & 0 deletions java/tools/src/assembly/tools.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,14 @@
<source>${maven.multiModuleProjectDirectory}/java/tools/src/assembly/resources/tools/arrow2tsfile.bat</source>
<destName>tools/arrow2tsfile.bat</destName>
</file>
<file>
<source>${maven.multiModuleProjectDirectory}/java/tools/src/assembly/resources/tools/tsfile-table-point-count.sh</source>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Packaging both launchers makes the repair utility directly available from the assembled tools distribution. The shell entry is explicitly marked executable, and the Windows launcher is placed beside it using the same tool name.

<destName>tools/tsfile-table-point-count.sh</destName>
<fileMode>0755</fileMode>
</file>
<file>
<source>${maven.multiModuleProjectDirectory}/java/tools/src/assembly/resources/tools/tsfile-table-point-count.bat</source>
<destName>tools/tsfile-table-point-count.bat</destName>
</file>
</files>
</assembly>
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package org.apache.tsfile.file.metadata;

import org.apache.tsfile.common.conf.TSFileConfig;
import org.apache.tsfile.compatibility.DeserializeConfig;
import org.apache.tsfile.encrypt.EncryptUtils;
import org.apache.tsfile.exception.encrypt.EncryptException;
Expand All @@ -45,7 +46,7 @@ public class TsFileMetadata {
private Map<String, MetadataIndexNode> tableMetadataIndexNodeMap;
private Map<String, TableSchema> tableSchemaMap;
private boolean hasTableSchemaMapCache;
private Map<String, String> tsFileProperties;
private Map<String, byte[]> tsFileProperties;

// offset of MetaMarker.SEPARATOR
private long metaOffset;
Expand Down Expand Up @@ -117,21 +118,27 @@ public static TsFileMetadata deserializeFrom(

if (buffer.hasRemaining()) {
int propertiesSize = ReadWriteForEncodingUtils.readVarInt(buffer);
Map<String, String> propertiesMap = new HashMap<>();
Map<String, byte[]> propertiesMap = new HashMap<>();
for (int i = 0; i < propertiesSize; i++) {
String key = ReadWriteIOUtils.readVarIntString(buffer);
String value = ReadWriteIOUtils.readVarIntString(buffer);
int valueSize = ReadWriteForEncodingUtils.readVarInt(buffer);
byte[] value = null;
if (valueSize >= 0) {
value = new byte[valueSize];
buffer.get(value);
}
propertiesMap.put(key, value);
}
String encryptLevel = getPropertyAsString(propertiesMap, "encryptLevel");
// if the file is not encrypted, set the default value(for compatible reason)
if (!propertiesMap.containsKey("encryptLevel") || propertiesMap.get("encryptLevel") == null) {
propertiesMap.put("encryptLevel", "0");
propertiesMap.put("encryptType", "org.apache.tsfile.encrypt.UNENCRYPTED");
propertiesMap.put("encryptKey", "");
} else if (propertiesMap.get("encryptLevel").equals("0")) {
propertiesMap.put("encryptType", "org.apache.tsfile.encrypt.UNENCRYPTED");
propertiesMap.put("encryptKey", "");
} else if (propertiesMap.get("encryptLevel").equals("1")) {
if (!propertiesMap.containsKey("encryptLevel") || encryptLevel == null) {
propertiesMap.put("encryptLevel", stringToBytes("0"));
propertiesMap.put("encryptType", stringToBytes("org.apache.tsfile.encrypt.UNENCRYPTED"));
propertiesMap.put("encryptKey", stringToBytes(""));
} else if (encryptLevel.equals("0")) {
propertiesMap.put("encryptType", stringToBytes("org.apache.tsfile.encrypt.UNENCRYPTED"));
propertiesMap.put("encryptKey", stringToBytes(""));
} else if (encryptLevel.equals("1")) {
if (!propertiesMap.containsKey("encryptType")) {
throw new EncryptException(
Messages.format("error.file.tsfile_metadata_no_encrypt_type", 1));
Expand All @@ -140,15 +147,15 @@ public static TsFileMetadata deserializeFrom(
throw new EncryptException(
Messages.format("error.file.tsfile_metadata_no_encrypt_key", 1));
}
if (propertiesMap.get("encryptKey") == null || propertiesMap.get("encryptKey").isEmpty()) {
String encryptKey = getPropertyAsString(propertiesMap, "encryptKey");
if (encryptKey == null || encryptKey.isEmpty()) {
throw new EncryptException(
Messages.format("error.file.tsfile_metadata_null_encrypt_key", 1));
}
String str = propertiesMap.get("encryptKey");
fileMetaData.encryptLevel = 1;
fileMetaData.secondKey = EncryptUtils.getSecondKeyFromStr(str);
fileMetaData.encryptType = propertiesMap.get("encryptType");
} else if (propertiesMap.get("encryptLevel").equals("2")) {
fileMetaData.secondKey = EncryptUtils.getSecondKeyFromStr(encryptKey);
fileMetaData.encryptType = getPropertyAsString(propertiesMap, "encryptType");
} else if (encryptLevel.equals("2")) {
if (!propertiesMap.containsKey("encryptType")) {
throw new EncryptException(
Messages.format("error.file.tsfile_metadata_no_encrypt_type", 2));
Expand All @@ -157,27 +164,25 @@ public static TsFileMetadata deserializeFrom(
throw new EncryptException(
Messages.format("error.file.tsfile_metadata_no_encrypt_key", 2));
}
if (propertiesMap.get("encryptKey") == null || propertiesMap.get("encryptKey").isEmpty()) {
String encryptKey = getPropertyAsString(propertiesMap, "encryptKey");
if (encryptKey == null || encryptKey.isEmpty()) {
throw new EncryptException(
Messages.format("error.file.tsfile_metadata_null_encrypt_key", 2));
}
fileMetaData.encryptLevel = 2;
String str = propertiesMap.get("encryptKey");
fileMetaData.secondKey = EncryptUtils.getSecondKeyFromStr(str);
fileMetaData.encryptType = propertiesMap.get("encryptType");
fileMetaData.secondKey = EncryptUtils.getSecondKeyFromStr(encryptKey);
fileMetaData.encryptType = getPropertyAsString(propertiesMap, "encryptType");
} else {
throw new EncryptException(
Messages.format(
"error.file.tsfile_metadata_unsupported_encrypt_level",
propertiesMap.get("encryptLevel")));
Messages.format("error.file.tsfile_metadata_unsupported_encrypt_level", encryptLevel));
}
fileMetaData.tsFileProperties = propertiesMap;
}

return fileMetaData;
}

public void addProperty(String key, String value) {
public void addProperty(String key, byte[] value) {
if (tsFileProperties == null) {
tsFileProperties = new HashMap<>();
}
Expand Down Expand Up @@ -248,9 +253,15 @@ public int serializeTo(OutputStream outputStream) throws IOException {
ReadWriteForEncodingUtils.writeVarInt(
tsFileProperties != null ? tsFileProperties.size() : 0, outputStream);
if (tsFileProperties != null) {
for (Entry<String, String> entry : tsFileProperties.entrySet()) {
for (Entry<String, byte[]> entry : tsFileProperties.entrySet()) {
byteLen += ReadWriteIOUtils.writeVar(entry.getKey(), outputStream);
byteLen += ReadWriteIOUtils.writeVar(entry.getValue(), outputStream);
byte[] value = entry.getValue();
byteLen +=
ReadWriteForEncodingUtils.writeVarInt(value == null ? -1 : value.length, outputStream);
if (value != null) {
outputStream.write(value);
byteLen += value.length;
}
}
}

Expand Down Expand Up @@ -295,7 +306,16 @@ public Map<String, TableSchema> getTableSchemaMap() {
return tableSchemaMap;
}

public Map<String, String> getTsFileProperties() {
public Map<String, byte[]> getTsFileProperties() {
return tsFileProperties;
}

private static String getPropertyAsString(Map<String, byte[]> properties, String key) {
byte[] value = properties.get(key);
return value == null ? null : new String(value, TSFileConfig.STRING_CHARSET);
}

private static byte[] stringToBytes(String value) {
return value.getBytes(TSFileConfig.STRING_CHARSET);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,11 @@ public Map<String, TableSchema> getTableSchemaMap() throws IOException {
return getTableSchemaMap(null);
}

/** Get the properties stored in the TsFile metadata. */
public Map<String, byte[]> getTsFileProperties() throws IOException {
return readFileMetadata().getTsFileProperties();
}

public Map<String, TableSchema> getTableSchemaMap(LongConsumer ioSizeRecorder)
throws IOException {
if (tsFileMetaData != null && tsFileMetaData.hasTableSchemaMapCache()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ public Optional<TableSchema> getTableSchemas(String tableName) throws IOExceptio
return Optional.ofNullable(tableSchemaMap.get(tableName.toLowerCase()));
}

@Override
@TsFileApi
public Map<String, byte[]> getTsFileProperties() throws IOException {
return fileReader.getTsFileProperties();
}

@TsFileApi
public ResultSet query(String tableName, List<String> columnNames, long startTime, long endTime)
throws IOException, NoTableException, NoMeasurementException, ReadProcessException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public interface ITsFileReader extends AutoCloseable {
Expand All @@ -48,6 +49,9 @@ ResultSet query(
@TsFileApi
List<TableSchema> getAllTableSchema() throws IOException;

@TsFileApi
Map<String, byte[]> getTsFileProperties() throws IOException;

@TsFileApi
void close();
}
Loading
Loading