Merge branch '3.0' into fix/3_liaohj
|
@ -159,4 +159,6 @@ pcre2.h
|
|||
zconf.h
|
||||
version.h
|
||||
geos_c.h
|
||||
|
||||
source/libs/parser/src/sql.c
|
||||
include/common/ttokenauto.h
|
||||
!packaging/smokeTest/pytest_require.txt
|
||||
|
|
169
Jenkinsfile2
|
@ -1,9 +1,11 @@
|
|||
import hudson.model.Result
|
||||
import hudson.model.*;
|
||||
import jenkins.model.CauseOfInterruption
|
||||
docs_only=0
|
||||
node {
|
||||
}
|
||||
file_zh_changed = ''
|
||||
file_en_changed = ''
|
||||
file_no_doc_changed = ''
|
||||
def abortPreviousBuilds() {
|
||||
def currentJobName = env.JOB_NAME
|
||||
def currentBuildNumber = env.BUILD_NUMBER.toInteger()
|
||||
|
@ -29,7 +31,7 @@ def abort_previous(){
|
|||
if (buildNumber > 1) milestone(buildNumber - 1)
|
||||
milestone(buildNumber)
|
||||
}
|
||||
def check_docs() {
|
||||
def check_docs(){
|
||||
if (env.CHANGE_URL =~ /\/TDengine\//) {
|
||||
sh '''
|
||||
hostname
|
||||
|
@ -40,39 +42,94 @@ def check_docs() {
|
|||
cd ${WKC}
|
||||
git reset --hard
|
||||
git clean -f
|
||||
rm -rf examples/rust/
|
||||
git remote prune origin
|
||||
git fetch
|
||||
'''
|
||||
script {
|
||||
sh '''
|
||||
cd ${WKC}
|
||||
git checkout ''' + env.CHANGE_TARGET + '''
|
||||
'''
|
||||
}
|
||||
sh '''
|
||||
cd ${WKC}
|
||||
git remote prune origin
|
||||
git checkout ''' + env.CHANGE_TARGET + '''
|
||||
git pull >/dev/null
|
||||
git fetch origin +refs/pull/${CHANGE_ID}/merge
|
||||
git checkout -qf FETCH_HEAD
|
||||
git checkout -qf FETCH_HEAD
|
||||
'''
|
||||
def file_changed = sh (
|
||||
|
||||
file_zh_changed = sh (
|
||||
script: '''
|
||||
cd ${WKC}
|
||||
git --no-pager diff --name-only FETCH_HEAD `git merge-base FETCH_HEAD ${CHANGE_TARGET}`|grep -v "^docs/en/"|grep -v "^docs/zh/" || :
|
||||
git --no-pager diff --name-only FETCH_HEAD `git merge-base FETCH_HEAD ${CHANGE_TARGET}`|grep "^docs/zh/" || :
|
||||
''',
|
||||
returnStdout: true
|
||||
)
|
||||
|
||||
file_en_changed = sh (
|
||||
script: '''
|
||||
cd ${WKC}
|
||||
git --no-pager diff --name-only FETCH_HEAD `git merge-base FETCH_HEAD ${CHANGE_TARGET}`|grep "^docs/en/" || :
|
||||
''',
|
||||
returnStdout: true
|
||||
)
|
||||
|
||||
file_no_doc_changed = sh (
|
||||
script: '''
|
||||
cd ${WKC}
|
||||
git --no-pager diff --name-only FETCH_HEAD `git merge-base FETCH_HEAD ${CHANGE_TARGET}`|grep -v "^docs/en/"|grep -v "^docs/zh/"|grep -v "*.md" || :
|
||||
''',
|
||||
returnStdout: true
|
||||
).trim()
|
||||
if (file_changed == '') {
|
||||
echo "docs PR"
|
||||
docs_only=1
|
||||
} else {
|
||||
echo file_changed
|
||||
}
|
||||
env.FILE_CHANGED = file_changed
|
||||
echo "file_zh_changed: ${file_zh_changed}"
|
||||
echo "file_en_changed: ${file_en_changed}"
|
||||
echo "file_no_doc_changed: ${file_no_doc_changed}"
|
||||
}
|
||||
}
|
||||
|
||||
def build_pre_docs(){
|
||||
if (env.CHANGE_URL =~ /\/TDengine\//) {
|
||||
sh '''
|
||||
hostname
|
||||
date
|
||||
env
|
||||
'''
|
||||
|
||||
sh '''
|
||||
cd ${DOC_WKC}/${td_repo}
|
||||
git reset --hard
|
||||
git clean -f
|
||||
git remote prune origin
|
||||
git fetch
|
||||
git checkout ''' + env.CHANGE_TARGET + '''
|
||||
git pull >/dev/null
|
||||
git fetch origin +refs/pull/${CHANGE_ID}/merge
|
||||
git checkout -qf FETCH_HEAD
|
||||
'''
|
||||
|
||||
sh '''
|
||||
cd ${DOC_WKC}/${tools_repo}
|
||||
git reset --hard
|
||||
git clean -f
|
||||
git fetch
|
||||
git remote prune origin
|
||||
git checkout ''' + env.CHANGE_TARGET + '''
|
||||
git pull >/dev/null
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
def build_zh_docs(){
|
||||
sh '''
|
||||
cd ${DOC_WKC}/${zh_doc_repo}
|
||||
# git pull
|
||||
yarn ass local
|
||||
yarn build
|
||||
'''
|
||||
}
|
||||
|
||||
def build_en_docs(){
|
||||
sh '''
|
||||
cd ${DOC_WKC}/${en_doc_repo}
|
||||
# git pull
|
||||
yarn ass local
|
||||
yarn build
|
||||
'''
|
||||
}
|
||||
|
||||
|
||||
def pre_test(){
|
||||
sh '''
|
||||
hostname
|
||||
|
@ -153,6 +210,7 @@ def pre_test(){
|
|||
'''
|
||||
return 1
|
||||
}
|
||||
|
||||
def pre_test_build_mac() {
|
||||
sh '''
|
||||
hostname
|
||||
|
@ -173,6 +231,7 @@ def pre_test_build_mac() {
|
|||
date
|
||||
'''
|
||||
}
|
||||
|
||||
def pre_test_win(){
|
||||
bat '''
|
||||
hostname
|
||||
|
@ -284,6 +343,7 @@ def pre_test_win(){
|
|||
git log -5
|
||||
'''
|
||||
}
|
||||
|
||||
def pre_test_build_win() {
|
||||
bat '''
|
||||
echo "building ..."
|
||||
|
@ -313,6 +373,7 @@ def pre_test_build_win() {
|
|||
'''
|
||||
return 1
|
||||
}
|
||||
|
||||
def run_win_ctest() {
|
||||
bat '''
|
||||
echo "windows ctest ..."
|
||||
|
@ -322,6 +383,7 @@ def run_win_ctest() {
|
|||
time /t
|
||||
'''
|
||||
}
|
||||
|
||||
def run_win_test() {
|
||||
bat '''
|
||||
echo "windows test ..."
|
||||
|
@ -344,28 +406,67 @@ pipeline {
|
|||
WK = '/var/lib/jenkins/workspace/TDinternal'
|
||||
WKC = '/var/lib/jenkins/workspace/TDinternal/community'
|
||||
WKPY = '/var/lib/jenkins/workspace/taos-connector-python'
|
||||
DOC_WKC = '/root/doc_ci_work'
|
||||
td_repo = 'TDengine'
|
||||
zh_doc_repo = 'docs.taosdata.com'
|
||||
en_doc_repo = 'docs.tdengine.com'
|
||||
tools_repo = 'taos-tools'
|
||||
}
|
||||
stages {
|
||||
stage('check') {
|
||||
stage ('check doc file changed') {
|
||||
agent{label " slave1_47 || slave1_48 || slave1_49 || slave1_50 || slave1_52 || slave1_59 || slave1_63 || worker03 || slave215 || slave217 || slave219 || Mac_catalina "}
|
||||
steps {
|
||||
check_docs()
|
||||
}
|
||||
}
|
||||
|
||||
stage ('pre for build docs') {
|
||||
when {
|
||||
allOf {
|
||||
not { expression { env.CHANGE_BRANCH =~ /docs\// }}
|
||||
}
|
||||
beforeAgent true
|
||||
expression { env.CHANGE_BRANCH =~ /(?i)doc.*/ || file_zh_changed != '' || file_en_changed != '' }
|
||||
}
|
||||
agent{label "doc_build_0_30"}
|
||||
steps {
|
||||
build_pre_docs()
|
||||
}
|
||||
}
|
||||
|
||||
stage('build Docs') {
|
||||
when {
|
||||
beforeAgent true
|
||||
expression { env.CHANGE_BRANCH =~ /(?i)doc.*/ || file_zh_changed != '' || file_en_changed != '' }
|
||||
}
|
||||
parallel {
|
||||
stage('check docs') {
|
||||
agent{label " slave1_47 || slave1_48 || slave1_49 || slave1_50 || slave1_52 || slave1_59 || slave1_63 || worker03 || slave215 || slave217 || slave219 || Mac_catalina "}
|
||||
stage('build zh docs') {
|
||||
agent{label "doc_build_0_30"}
|
||||
when {
|
||||
expression { file_zh_changed != '' }
|
||||
}
|
||||
steps {
|
||||
check_docs()
|
||||
build_zh_docs()
|
||||
}
|
||||
}
|
||||
stage('build en docs') {
|
||||
agent{label "doc_build_0_30"}
|
||||
when {
|
||||
expression { file_en_changed != '' }
|
||||
}
|
||||
steps {
|
||||
build_en_docs()
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
unsuccessful {
|
||||
error('build docs stage failed, terminating pipeline.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('run test') {
|
||||
when {
|
||||
allOf {
|
||||
not { expression { env.CHANGE_BRANCH =~ /docs\// }}
|
||||
expression { docs_only == 0 }
|
||||
not { expression { file_no_doc_changed == '' }}
|
||||
}
|
||||
}
|
||||
parallel {
|
||||
|
@ -420,7 +521,7 @@ pipeline {
|
|||
script {
|
||||
sh '''
|
||||
mkdir -p ${WKDIR}/tmp/${BRANCH_NAME}_${BUILD_ID}
|
||||
echo "''' + env.FILE_CHANGED + '''" > ${WKDIR}/tmp/${BRANCH_NAME}_${BUILD_ID}/docs_changed.txt
|
||||
echo "''' + file_no_doc_changed + '''" > ${WKDIR}/tmp/${BRANCH_NAME}_${BUILD_ID}/docs_changed.txt
|
||||
'''
|
||||
sh '''
|
||||
cd ${WKC}/tests/parallel_test
|
||||
|
@ -570,4 +671,4 @@ pipeline {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
# lemon
|
||||
|
||||
ExternalProject_Add(
|
||||
lemon
|
||||
SOURCE_DIR ${TD_CONTRIB_DIR}/lemon
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND "${C_COMPILER_LEMON}" -o ${TD_CONTRIB_DIR}/lemon/lemon ${TD_CONTRIB_DIR}/lemon/lemon.c
|
||||
INSTALL_COMMAND ""
|
||||
BUILD_IN_SOURCE 1
|
||||
BUILD_ALWAYS 1
|
||||
)
|
|
@ -184,6 +184,17 @@ if(${BUILD_PCRE2})
|
|||
cat("${TD_SUPPORT_DIR}/pcre2_CMakeLists.txt.in" ${CONTRIB_TMP_FILE})
|
||||
endif()
|
||||
|
||||
find_program(C_COMPILER_LEMON NAMES gcc)
|
||||
if(C_COMPILER_LEMON)
|
||||
message(STATUS "LEMON C compiler: ${C_COMPILER_LEMON}")
|
||||
else()
|
||||
set(C_COMPILER_LEMON ${CMAKE_C_COMPILER})
|
||||
message(STATUS "LEMON C compiler: ${C_COMPILER_LEMON}")
|
||||
endif()
|
||||
|
||||
# lemon
|
||||
cat("${TD_SUPPORT_DIR}/lemon_CMakeLists.txt.in" ${CONTRIB_TMP_FILE})
|
||||
|
||||
# download dependencies
|
||||
configure_file(${CONTRIB_TMP_FILE} "${TD_CONTRIB_DIR}/deps-download/CMakeLists.txt")
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
|
||||
|
|
|
@ -22,19 +22,19 @@
|
|||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>5.2.8.RELEASE</version>
|
||||
<version>5.3.39</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
<version>5.1.9.RELEASE</version>
|
||||
<version>5.3.39</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>5.1.9.RELEASE</version>
|
||||
<version>5.3.39</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -47,7 +47,7 @@
|
|||
<dependency>
|
||||
<groupId>com.taosdata.jdbc</groupId>
|
||||
<artifactId>taos-jdbcdriver</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>3.4.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.1.RELEASE</version>
|
||||
<version>2.6.15</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.taosdata.example</groupId>
|
||||
|
@ -65,6 +65,8 @@
|
|||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.taosdata.jdbc</groupId>
|
||||
<artifactId>taos-jdbcdriver</artifactId>
|
||||
|
|
|
@ -3,9 +3,10 @@ package com.taosdata.example.springbootdemo;
|
|||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration;
|
||||
|
||||
@MapperScan(basePackages = {"com.taosdata.example.springbootdemo"})
|
||||
@SpringBootApplication
|
||||
@SpringBootApplication(exclude = {JdbcRepositoriesAutoConfiguration.class})
|
||||
public class SpringbootdemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -15,6 +15,8 @@ spring.datasource.druid.max-wait=30000
|
|||
spring.datasource.druid.validation-query=select SERVER_VERSION();
|
||||
spring.aop.auto=true
|
||||
spring.aop.proxy-target-class=true
|
||||
|
||||
spring.jooq.sql-dialect=
|
||||
#mybatis
|
||||
mybatis.mapper-locations=classpath:mapper/*.xml
|
||||
logging.level.com.taosdata.jdbc.springbootdemo.dao=debug
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<description>Demo project for TDengine</description>
|
||||
|
||||
<properties>
|
||||
<spring.version>5.3.27</spring.version>
|
||||
<spring.version>5.3.39</spring.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -130,6 +130,7 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<target>8</target>
|
||||
|
|
|
@ -37,7 +37,7 @@ public class QueryService {
|
|||
stmt.execute("use " + dbName);
|
||||
ResultSet rs = stmt.executeQuery("show stables");
|
||||
while (rs.next()) {
|
||||
String name = rs.getString("name");
|
||||
String name = rs.getString("stable_name");
|
||||
sqls.add("select count(*) from " + dbName + "." + name);
|
||||
sqls.add("select first(*) from " + dbName + "." + name);
|
||||
sqls.add("select last(*) from " + dbName + "." + name);
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package com.taosdata.taosdemo.service;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class DatabaseServiceTest {
|
||||
private DatabaseService service;
|
||||
|
||||
private static DatabaseService service;
|
||||
|
||||
@Test
|
||||
public void testCreateDatabase1() {
|
||||
|
@ -20,4 +24,16 @@ public class DatabaseServiceTest {
|
|||
public void useDatabase() {
|
||||
service.useDatabase("test");
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws ClassNotFoundException {
|
||||
Class.forName("com.taosdata.jdbc.TSDBDriver");
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8");
|
||||
config.setUsername("root");
|
||||
config.setPassword("taosdata");
|
||||
HikariDataSource dataSource = new HikariDataSource(config);
|
||||
service = new DatabaseService(dataSource);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,7 @@ public class QueryServiceTest {
|
|||
|
||||
@Test
|
||||
public void generateSuperTableQueries() {
|
||||
String[] sqls = queryService.generateSuperTableQueries("restful_test");
|
||||
String[] sqls = queryService.generateSuperTableQueries("test");
|
||||
for (String sql : sqls) {
|
||||
System.out.println(sql);
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ public class QueryServiceTest {
|
|||
|
||||
@Test
|
||||
public void querySuperTable() {
|
||||
String[] sqls = queryService.generateSuperTableQueries("restful_test");
|
||||
queryService.querySuperTable(sqls, 1000, 10, 10);
|
||||
String[] sqls = queryService.generateSuperTableQueries("test");
|
||||
queryService.querySuperTable(sqls, 100, 3, 3);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
|
|
|
@ -3,6 +3,9 @@ package com.taosdata.taosdemo.service;
|
|||
import com.taosdata.taosdemo.domain.FieldMeta;
|
||||
import com.taosdata.taosdemo.domain.SuperTableMeta;
|
||||
import com.taosdata.taosdemo.domain.TagMeta;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -10,7 +13,7 @@ import java.util.List;
|
|||
|
||||
public class SuperTableServiceTest {
|
||||
|
||||
private SuperTableService service;
|
||||
private static SuperTableService service;
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
|
@ -29,4 +32,15 @@ public class SuperTableServiceTest {
|
|||
service.create(superTableMeta);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws ClassNotFoundException {
|
||||
Class.forName("com.taosdata.jdbc.TSDBDriver");
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8");
|
||||
config.setUsername("root");
|
||||
config.setPassword("taosdata");
|
||||
HikariDataSource dataSource = new HikariDataSource(config);
|
||||
service = new SuperTableService(dataSource);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.taosdata.example;
|
||||
package com.taos.example;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
|
||||
|
@ -8,11 +8,11 @@ import java.sql.Statement;
|
|||
public class DruidDemo {
|
||||
// ANCHOR: connection_pool
|
||||
public static void main(String[] args) throws Exception {
|
||||
String url = "jdbc:TAOS://127.0.0.1:6030/log";
|
||||
String url = "jdbc:TAOS-WS://127.0.0.1:6041/log";
|
||||
|
||||
DruidDataSource dataSource = new DruidDataSource();
|
||||
// jdbc properties
|
||||
dataSource.setDriverClassName("com.taosdata.jdbc.TSDBDriver");
|
||||
dataSource.setDriverClassName("com.taosdata.jdbc.ws.WebSocketDriver");
|
||||
dataSource.setUrl(url);
|
||||
dataSource.setUsername("root");
|
||||
dataSource.setPassword("taosdata");
|
||||
|
|
|
@ -144,8 +144,9 @@ public class GeometryDemo {
|
|||
|
||||
private void executeQuery(String sql) {
|
||||
long start = System.currentTimeMillis();
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
ResultSet resultSet = statement.executeQuery(sql);
|
||||
try (Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery(sql)) {
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
printSql(sql, true, (end - start));
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.taosdata.example;
|
||||
package com.taos.example;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
@ -11,7 +11,7 @@ public class HikariDemo {
|
|||
public static void main(String[] args) throws Exception {
|
||||
HikariConfig config = new HikariConfig();
|
||||
// jdbc properties
|
||||
config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/log");
|
||||
config.setJdbcUrl("jdbc:TAOS-WS://127.0.0.1:6041/log");
|
||||
config.setUsername("root");
|
||||
config.setPassword("taosdata");
|
||||
// connection pool configurations
|
||||
|
|
|
@ -39,6 +39,7 @@ public class TelnetLineProtocolExample {
|
|||
createDatabase(conn);
|
||||
SchemalessWriter writer = new SchemalessWriter(conn);
|
||||
writer.write(lines, SchemalessProtocolType.TELNET, SchemalessTimestampType.NOT_CONFIGURED);
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -116,10 +116,11 @@ create stream if not exists count_history_s fill_history 1 into count_history as
|
|||
|
||||
### 流计算的触发模式
|
||||
|
||||
在创建流时,可以通过 TRIGGER 指令指定流计算的触发模式。对于非窗口计算,流计算的触发是实时的,对于窗口计算,目前提供 3 种触发模式,默认为 WINDOW_CLOSE。
|
||||
在创建流时,可以通过 TRIGGER 指令指定流计算的触发模式。对于非窗口计算,流计算的触发是实时的,对于窗口计算,目前提供 4 种触发模式,默认为 WINDOW_CLOSE。
|
||||
1. AT_ONCE:写入立即触发。
|
||||
2. WINDOW_CLOSE:窗口关闭时触发(窗口关闭由事件时间决定,可配合 watermark 使用)。
|
||||
3. MAX_DELAY time:若窗口关闭,则触发计算。若窗口未关闭,且未关闭时长超过 max delay 指定的时间,则触发计算。
|
||||
4. FORCE_WINDOW_CLOSE:以操作系统当前时间为准,只计算当前关闭窗口的结果,并推送出去。窗口只会在被关闭的时刻计算一次,后续不会再重复计算。该模式当前只支持 INTERVAL 窗口(不支持滑动);FILL_HISTORY必须为 0,IGNORE EXPIRED 必须为 1,IGNORE UPDATE 必须为 1;FILL 只支持 PREV 、NULL、 NONE、VALUE。
|
||||
|
||||
窗口关闭是由事件时间决定的,如事件流中断、或持续延迟,此时事件时间无法更新,可能导致无法得到最新的计算结果。
|
||||
|
||||
|
@ -227,4 +228,35 @@ PAUSE STREAM [IF EXISTS] stream_name;
|
|||
RESUME STREAM [IF EXISTS] [IGNORE UNTREATED] stream_name;
|
||||
```
|
||||
|
||||
没有指定 IF EXISTS,如果该 stream 不存在,则报错。如果存在,则恢复流计算。指定了 IF EXISTS,如果 stream 不存在,则返回成功。如果存在,则恢复流计算。如果指定 IGNORE UNTREATED,则恢复流计算时,忽略流计算暂停期间写入的数据。
|
||||
没有指定 IF EXISTS,如果该 stream 不存在,则报错。如果存在,则恢复流计算。指定了 IF EXISTS,如果 stream 不存在,则返回成功。如果存在,则恢复流计算。如果指定 IGNORE UNTREATED,则恢复流计算时,忽略流计算暂停期间写入的数据。
|
||||
|
||||
### 流计算升级故障恢复
|
||||
|
||||
升级 TDengine 后,如果流计算不兼容,需要删除流计算,然后重新创建流计算。步骤如下:
|
||||
|
||||
1.修改 taos.cfg,添加 disableStream 1
|
||||
|
||||
2.重启 taosd。如果启动失败,修改 stream 目录的名称,避免 taosd 启动的时候尝试加载 stream 目录下的流计算数据信息。不使用删除操作避免误操作导致的风险。需要修改的文件夹:$dataDir/vnode/vnode*/tq/stream,$dataDir 指 TDengine 存储数据的目录,在 $dataDir/vnode/ 目录下会有多个类似 vnode1 、vnode2...vnode* 的目录,全部需要修改里面的 tq/stream 目录的名字,改为 tq/stream.bk
|
||||
|
||||
3.启动 taos
|
||||
|
||||
```sql
|
||||
drop stream xxxx; ---- xxx 指stream name
|
||||
flush database stream_source_db; ---- 流计算读取数据的超级表所在的 database
|
||||
flush database stream_dest_db; ---- 流计算写入数据的超级表所在的 database
|
||||
```
|
||||
|
||||
举例:
|
||||
|
||||
```sql
|
||||
create stream streams1 into test1.streamst as select _wstart, count(a) c1 from test.st interval(1s) ;
|
||||
drop database streams1;
|
||||
flush database test;
|
||||
flush database test1;
|
||||
```
|
||||
|
||||
4.关闭 taosd
|
||||
|
||||
5.修改 taos.cfg,去掉 disableStream 1,或将 disableStream 改为 0
|
||||
|
||||
6.启动 taosd
|
|
@ -2,86 +2,123 @@
|
|||
title: "CSV"
|
||||
sidebar_label: "CSV"
|
||||
---
|
||||
本节讲述如何通过 Explorer 界面创建数据迁移任务, 从 CSV 迁移数据到当前 TDengine 集群。
|
||||
本节讲述如何通过 Explorer 界面创建数据迁移任务,从 CSV 迁移数据到当前 TDengine 集群。
|
||||
|
||||
## 功能概述
|
||||
导入一个或多个 CSV 文件数据到 TDengine。
|
||||
|
||||
## 创建任务
|
||||
### 1. 新增数据源
|
||||
在数据写入页面中,点击 **+新增数据源** 按钮,进入新增数据源页面。
|
||||
在数据写入任务列表页面中,点击 **+新建任务** 按钮,进入新建任务页面。
|
||||
|
||||

|
||||
|
||||
### 2. 配置基本信息
|
||||
在 **名称** 中输入任务名称,如:“test_csv”;
|
||||
在 **名称** 中输入任务名称,如:“test_csv”。
|
||||
|
||||
在 **类型** 下拉列表中选择 **CSV**。
|
||||
|
||||
在 **目标数据库** 下拉列表中选择一个目标数据库,也可以先点击右侧的 **+创建数据库** 按钮
|
||||
在 **目标数据库** 下拉列表中选择一个目标数据库,也可以先点击右侧的 **+创建数据库** 按钮。
|
||||
|
||||

|
||||
|
||||
### 3. 配置 CSV 选项
|
||||
在 **包含表头** 区域点击开启或关闭,如果包含表头,则第一行将被视为列信息。
|
||||
在 **包含表头** 区域点击开启或关闭,如果包含表头,则 CSV 文件内容第一行将被视为列信息。
|
||||
|
||||
在 **忽略前 N 行** 区域填写 N,表示忽略 CSV 文件的前 N 行。
|
||||
在 **忽略前 N 行** 区域填写数字 N,表示忽略 CSV 文件的前 N 行。
|
||||
|
||||
在 **字段分隔符** 区域进行选择,CSV 字段之间的分隔符,默认是 “,” 。
|
||||
在 **字段分隔符** 区域选择 CSV 字段分隔符,用于分隔行内容为多个字段,默认是 `,`。
|
||||
|
||||
在 **字段引用符** 区域进行选择,当 CSV 字段中包含分隔符或换行符时,用于包围字段内容,以确保整个字段被正确识别,默认是 "“"。
|
||||
在 **字段引用符** 区域选择 CSV 字段引用符,当 CSV 字段中包含分隔符或换行符时,用于包围字段内容,以确保整个字段被正确识别,默认是 `"`。
|
||||
|
||||
在 **注释前缀符** 区域进行选择,当 CSV 文件中某行以此处指定的字符开头,则忽略该行默认是 “#”。
|
||||
在 **注释前缀符** 区域选择 CSV 行注释前缀符,当 CSV 文件中某行以此处指定的字符开头,则忽略该行,默认是 `#`。
|
||||
|
||||

|
||||
|
||||
### 4. 配置解析 CSV 文件
|
||||
在本地上传 CSV 文件,例如:test-json.csv,之后会使用这条示例 csv 文件来配置提取和过滤条件。
|
||||
|
||||
#### 4.1 解析
|
||||
#### 4.1 配置数据源
|
||||
|
||||
点击 **选取文件** 后,选择 test-json.csv,点击 **解析** 预览识别的列。
|
||||
包含“上传 CSV 文件”与“监听文件目录”两种方式,“上传 CSV 文件”是指将本地文件通过浏览器上传到 taosx 所在服务器作为数据源,“监听文件目录”是指配置一个 taosx 所在服务器的绝对路径作为数据源,以下将分别进行介绍:
|
||||
|
||||
##### 4.1.1 上传 CSV 文件
|
||||
|
||||
在“上传 CSV 文件”标签页中:
|
||||
|
||||
点击 **选取文件** 按钮,选取一个或多个本地文件,上传到服务器作为数据源。
|
||||
|
||||
在 **保留已处理文件** 区域点击开启或关闭,如果开启,则文件被处理完成后仍会保留在服务器中,如果关闭,则将被删除。
|
||||
|
||||

|
||||
|
||||
**预览解析结果**
|
||||
##### 4.1.2 监听文件目录
|
||||
|
||||
在“监听文件目录”标签页中:
|
||||
|
||||
在 **文件监听目录** 中输入一个 taosx 所在服务器的绝对路径,路径中包含的文件及子目录文件将作为数据源。
|
||||
|
||||
在 **匹配模式** 中输入一个正则表达式,用于筛选过滤目录中的文件。
|
||||
|
||||
在 **监听新文件** 区域点击开启或关闭,如果开启,则任务永不停止,且持续处理目录中新增的文件,如果关闭,则不处理新增文件,且初始文件处理结束后任务变为完成状态。
|
||||
|
||||
在 **监听间隔** 中输入一个数字,用于配置监听新文件的时间间隔。
|
||||
|
||||
在 **文件处理顺序** 区域选择“正序”或“倒序”,用于指定文件列表的处理先后顺序,“正序”将按照文件名的字典序正序处理,“倒序”将按照文件名的字典序倒序处理,与此同时,程序总是保持先处理文件后处理同级子目录的顺序。
|
||||
|
||||

|
||||
|
||||
#### 4.2 字段拆分
|
||||
#### 4.2 解析
|
||||
|
||||
在 **从列中提取或拆分** 中填写从消息体中提取或拆分的字段,例如:将 message 字段拆分成 `text_0` 和 `text_1` 这2个字段,选择 split 提取器,seperator 填写 -, number 填写2。
|
||||
点击 **删除**,可以删除当前提取规则。
|
||||
点击 **新增**,可以添加更多提取规则。
|
||||
上传文件或配置监听目录后,点击解析按钮,页面将获取文件中的示例数据,同时得到识别的列与示例数据解析结果:
|
||||
|
||||

|
||||

|
||||
|
||||
点击 **放大镜图标** 可预览提取或拆分结果。
|
||||
#### 4.2 从列中提取或拆分
|
||||
|
||||
在 **从列中提取或拆分** 中填写从消息体中提取或拆分规则,例如:将 `desc` 字段拆分为 `desc_0` 与 `desc_1` 两个字段,可以选择 split 规则,separator 填写 `,`,number 填写 2 即可。
|
||||
|
||||
点击 **删除** 可以删除当前提取规则。
|
||||
|
||||
点击 **预览** 可以预览提取或拆分结果。
|
||||
|
||||
点击 **新增提取/拆分** 可以添加更多提取规则。
|
||||
|
||||

|
||||
|
||||
<!-- 在 **过滤** 中,填写过滤条件,例如:填写 `id != 1`,则只有 id 不为 1 的数据才会被写入 TDengine。
|
||||
点击 **删除**,可以删除当前过滤规则。
|
||||
#### 4.3 过滤
|
||||
|
||||
在 **过滤** 中填写过滤条件,例如:填写 `id != "1"`,则只有 id 不为 1 的数据才会被处理。
|
||||
|
||||
点击 **删除** 可以删除当前过滤规则。
|
||||
|
||||
点击 **预览** 可以预览过滤结果。
|
||||
|
||||
点击 **新增过滤** 可以添加更多过滤规则。
|
||||
|
||||

|
||||
|
||||
点击 **放大镜图标** 可查看预览过滤结果。
|
||||
|
||||
 -->
|
||||
|
||||
#### 4.3 表映射
|
||||
#### 4.4 映射
|
||||
|
||||
在 **目标超级表** 的下拉列表中选择一个目标超级表,也可以先点击右侧的 **创建超级表** 按钮
|
||||
|
||||
在 **映射** 中,填写目标超级表中的子表名称,例如:`t_${groupid}`。
|
||||
在映射规则中,填写目标超级表中的子表名称,例如:`csv_meter_${id}`,同时配置映射到超级表的列。
|
||||
|
||||

|
||||
|
||||
点击 **预览**,可以预览映射的结果。
|
||||
|
||||

|
||||
点击 **预览** 可以预览映射的结果。
|
||||
|
||||

|
||||
|
||||
### 5. 创建完成
|
||||
|
||||
点击 **提交** 按钮,完成创建 CSV 到 TDengine 的数据同步任务,回到**数据源列表**页面可查看任务执行情况。
|
||||
点击 **提交** 按钮,完成创建 CSV 到 TDengine 的数据同步任务,回到数据写入任务列表页面,可查看任务执行情况,也可以进行任务的“启动/停止”操作与“查看/编辑/删除/复制”操作。
|
||||
|
||||

|
||||
|
||||
### 6. 查看运行指标
|
||||
|
||||
点击 **查看** 按钮,查看任务的运行指标,同时也可以查看任务中所有文件的处理情况。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 177 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 192 KiB |
Before Width: | Height: | Size: 363 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 118 KiB |
|
@ -0,0 +1,122 @@
|
|||
---
|
||||
title: "安装部署"
|
||||
sidebar_label: "安装部署"
|
||||
---
|
||||
|
||||
### 环境准备
|
||||
为了使用 TDgpt 的高级时序数据分析功能功能,需要在 TDengine 集群中安装部署 AI node(Anode)。ANode 可以运行在 Linux/Windows/Mac 等操作系统之上。请确保安装部署 Anode之前,系统中已经具备 3.10 及以上版本的Python环境,以及相应的 Python 包自动安装组件 Pip,否则无法正常安装 Anode。
|
||||
|
||||
### 安装及卸载
|
||||
不同操作系统上安装及部署操作有细微的差异,主要是安装/卸载操作、安装路径、Anode服务的启停等几个方面。下面将以 Linux 系统为例,说明安装部署的整个流程。使用 Linux 环境下的安装包 TDengine-enterprise-anode-1.x.x.tar.gz 可进行 ANode 的安装部署工作,使用如下命令:
|
||||
|
||||
```bash
|
||||
tar -xzvf TDengine-enterprise-anode-1.0.0.tar.gz
|
||||
cd TDengine-enterprise-anode-1.0.0
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
在安装完成 ANode 之后,执行命令 `rmtaosanode` 即可已经安装的 Anode。
|
||||
ANode 使用 Python 虚拟环境运行,避免影响安装环境中现有的 Python 库。安装后的默认 Python 虚拟环境目录位于 `/var/lib/taos/taosanode/venv/`。为了避免反复安装虚拟环境带来的开销,卸载 ANode 执行的命令 `rmtaosanode` 并不会自动删除该虚拟环境,如果您确认不需要 Python 的虚拟环境,手动删除即可。
|
||||
|
||||
### 启停服务
|
||||
在 Linux 系统中,安装 ANode 以后可以使用 `systemd` 来管理 ANode 服务。使用如下命令可以启动/停止/检查状态。
|
||||
|
||||
```bash
|
||||
systemctl start taosanoded
|
||||
systemctl stop taosanoded
|
||||
systemctl status taosanoded
|
||||
```
|
||||
|
||||
### 目录及配置说明
|
||||
|目录/文件|说明|
|
||||
|---------------|------|
|
||||
|/usr/local/taos/taosanode/bin|可执行文件目录|
|
||||
|/usr/local/taos/taosanode/resource|资源文件目录,链接到文件夹 /var/lib/taos/taosanode/resource/|
|
||||
|/usr/local/taos/taosanode/lib|库文件目录|
|
||||
|/var/lib/taos/taosanode/model/|模型文件目录,链接到文件夹 /var/lib/taos/taosanode/model|
|
||||
|/var/log/taos/taosanode/|日志文件目录|
|
||||
|/etc/taos/taosanode.ini|配置文件|
|
||||
|
||||
#### 配置说明
|
||||
|
||||
Anode 提供的服务使用 uWSGI 驱动,因此 ANode 和 uWSGI 的配置信息共同存放在相同的配置文件 `taosanode.ini`,该配置文件默认位于 `/etc/taos/`目录下,其具体内容及说明如下:
|
||||
|
||||
```ini
|
||||
[uwsgi]
|
||||
|
||||
# Anode HTTP service ip:port
|
||||
http = 127.0.0.1:6050
|
||||
|
||||
# base directory for Anode python files, do NOT modified this
|
||||
chdir = /usr/local/taos/taosanode/lib
|
||||
|
||||
# initialize Anode python file
|
||||
wsgi-file = /usr/local/taos/taosanode/lib/taos/app.py
|
||||
|
||||
# pid file
|
||||
pidfile = /usr/local/taos/taosanode/taosanode.pid
|
||||
|
||||
# conflict with systemctl, so do NOT uncomment this
|
||||
# daemonize = /var/log/taos/taosanode/taosanode.log
|
||||
|
||||
# log directory
|
||||
logto = /var/log/taos/taosanode/taosanode.log
|
||||
|
||||
# wWSGI monitor port
|
||||
stats = 127.0.0.1:8387
|
||||
|
||||
# python virtual environment directory, used by Anode
|
||||
virtualenv = /usr/local/taos/taosanode/venv/
|
||||
|
||||
[taosanode]
|
||||
# default app log file
|
||||
app-log = /var/log/taos/taosanode/taosanode.app.log
|
||||
|
||||
# model storage directory
|
||||
model-dir = /usr/local/taos/taosanode/model/
|
||||
|
||||
# default log level
|
||||
log-level = DEBUG
|
||||
|
||||
```
|
||||
|
||||
**提示**
|
||||
请勿设置 `daemonize` 参数,该参数会导致 uWSGI 与 systemctl 冲突,从而无法正常启动。
|
||||
上面的示例配置文件 `taosanode.ini` 只包含了使用 Anode 提供服务的基础配置参数,对于 uWSGI 的其他配置参数设置及其含义和说明请参考 [uWSGIS官方文档](https://uwsgi-docs-zh.readthedocs.io/zh-cn/latest/Options.html)。
|
||||
|
||||
Anode 运行配置主要是以下:
|
||||
- app-log: Anode 服务运行产生的日志,用户可以调整其到需要的位置
|
||||
- model-dir: 采用算法针对已经存在的数据集的运行完成生成的模型存储位置
|
||||
- log-level: app-log文件的日志级别
|
||||
|
||||
|
||||
### ANode 基本操作
|
||||
#### 创建 ANode
|
||||
```sql
|
||||
CREATE ANODE {node_url}
|
||||
```
|
||||
node_url 是提供服务的 ANode 的 IP 和 PORT, 例如:`create anode 'http://localhost:6050'`。启动 ANode 以后如果不注册到 TDengine 集群中,则无法提供正常的服务。不建议 ANode 注册到两个或多个集群中。
|
||||
|
||||
#### 查看 ANode
|
||||
列出集群中所有的数据分析节点,包括其 `FQDN`, `PORT`, `STATUS`。
|
||||
```sql
|
||||
SHOW ANODES;
|
||||
```
|
||||
|
||||
#### 查看提供的时序数据分析服务
|
||||
|
||||
```SQL
|
||||
SHOW ANODES FULL;
|
||||
```
|
||||
|
||||
#### 刷新集群中的分析算法缓存
|
||||
```SQL
|
||||
UPDATE ANODE {node_id}
|
||||
UPDATE ALL ANODES
|
||||
```
|
||||
|
||||
#### 删除 ANode
|
||||
```sql
|
||||
DROP ANODE {anode_id}
|
||||
```
|
||||
删除 ANode 只是将 ANode 从 TDengine 集群中删除,管理 ANode 的启停仍然需要使用`systemctl`命令。
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
title: "数据分析预处理"
|
||||
sidebar_label: "数据分析预处理"
|
||||
---
|
||||
|
||||
import activity from './pic/activity.png';
|
||||
import wndata from './pic/white-noise-data.png'
|
||||
|
||||
### 分析流程
|
||||
在针对时序数据进行高级分析之前,首先进行数据的白噪声检查(White Noise Data check, WND)。整体的流程如下图所示。
|
||||
|
||||
<img src={activity} width="560" alt="预处理流程" />
|
||||
|
||||
- 对于时间序列数据预测分析,首先进行白噪声检查,不是白噪声数据,进行数据重采样和时间戳对齐的预处理,预处理完成后进行数据预测分析。
|
||||
- 对于时间序列异常检测,首先进行白噪声检查,检查通过以后无后续的处理流程,直接进行异常检测分析。
|
||||
|
||||
### 白噪声检查
|
||||
|
||||
<img src={wndata} width="430" alt="white-noise-data"/>
|
||||
|
||||
白噪声时序数据可以简单地认为是随机数构成的时序数据序列(如上图所示),随机数的时间序列没有分析的价值,因此会直接返回空结果。白噪声检查采用 `Ljung-Box` 检验,`Ljung-Box` 统计量的计算过程需遍历整个输入序列。如果用户能够明确输入序列一定不是白噪声序列,那么可以通过增加参数 `wncheck=0` 要求分析平台忽略白噪声输入时间序列检查,从而节省计算资源。
|
||||
TDgpt 暂不提供独立的时间序列白噪声检测功能。
|
||||
|
||||
|
||||
### 数据重采样和时间戳对齐
|
||||
|
||||
对于输入的时间序列数据,在对齐进行预测分析之前需要进行必要的预处理流程。预处理解决以下两个方面的问题:
|
||||
|
||||
- 真实时间序列数据时间戳未对齐。由于数据生成的原因或者网关给时间序列数据赋值时间戳并不能保证按照严格的时间间隔赋值,此时 分析平台会自动将输入数据按照用户指定的采样频率对时间戳进行对齐处理。例如输入时间序列 [11, 22, 29, 41],用户指定时间间隔为 10,该时间序列的时间戳将被自动重整为以下时间戳序列 [10, 20, 30, 40]。
|
||||
- 数据时间重采样。用户输入时间序列的采样频率超过了输出结果的频率,例如输入时间序列的采样频率是 5,输出结果的频率是 10,输入时间序列 [0, 5, 10, 15, 20, 25, 30] 将被重采用为间隔 为 10 的序列 [0, 10, 20,30],[5, 15, 25] 处的数据将被丢弃。
|
||||
|
||||
需要注意的是,数据输入平台不支持缺失数据补齐后进行的预测分析,如果输入时间序列数据 [11, 22, 29, 49],并且用户要求的时间间隔为 10,重整对齐后的序列是 [10, 20, 30, 50] 那么该序列进行预测分析将返回错误。
|
||||
|
|
@ -51,4 +51,4 @@ FORECAST(i32, "algo=arima,alpha=95,period=10,start_p=1,max_p=5,start_q=1,max_q=5
|
|||
|
||||
### 参考文献
|
||||
- https://en.wikipedia.org/wiki/Autoregressive_moving-average_model
|
||||
- https://baike.baidu.com/item/%E8%87%AA%E5%9B%9E%E5%BD%92%E6%BB%91%E5%8A%A8%E5%B9%B3%E5%9D%87%E6%A8%A1%E5%9E%8B/5023931?fromtitle=ARMA%E6%A8%A1%E5%9E%8B&fromid=8048415
|
||||
- [https://baike.baidu.com/item/自回归滑动平均模型/5023931](https://baike.baidu.com/item/%E8%87%AA%E5%9B%9E%E5%BD%92%E6%BB%91%E5%8A%A8%E5%B9%B3%E5%9D%87%E6%A8%A1%E5%9E%8B/5023931)
|
|
@ -0,0 +1,72 @@
|
|||
---
|
||||
title: 预测算法
|
||||
description: 预测算法
|
||||
---
|
||||
|
||||
时序数据预测处理以持续一个时间段的时序数据作为输入,预测接下来一个连续时间区间内时间序列数据分布及运行的趋势。用户可以指定输出的(预测)时间序列数据点的数量,因此其输出的结果行数不确定。为此,我们引入了 `FORECAST` 函数提供预测服务。基础数据(用于预测的历史时间序列数据)是该函数的输入,预测结果是该函数的输出。用户可以通过 `FORECAST` 函数调用 Anode 提供的预测算法提供的服务。
|
||||
|
||||
##### 语法
|
||||
```SQL
|
||||
FORECAST(column_expr, option_expr)
|
||||
|
||||
option_expr: {"
|
||||
algo=expr1
|
||||
[,wncheck=1|0]
|
||||
[,conf=conf_val]
|
||||
[,every=every_val]
|
||||
[,rows=rows_val]
|
||||
[,start=start_ts_val]
|
||||
[,expr2]
|
||||
"}
|
||||
|
||||
```
|
||||
1. `column_expr`:预测的时序数据列。与异常检测相同,只支持数值类型列输入。
|
||||
2. `options`:异常检测函数的参数,使用规则与 anomaly_window 相同。预测支持 `conf`, `every`, `rows`, `start`, `rows` 几个控制参数,其含义如下:
|
||||
|
||||
**参数说明**
|
||||
|
||||
|参数|含义|默认值|
|
||||
|---|---|---|
|
||||
|algo|预测分析使用的算法|holtwinters|
|
||||
|wncheck|白噪声(white noise data)检查|默认值为 1,0 表示不进行检查|
|
||||
|conf|预测数据的置信区间范围 ,取值范围 [0, 100]|95|
|
||||
|every|预测数据的采样间隔|输入数据的采样间隔|
|
||||
|start|预测结果的开始时间戳|输入数据最后一个时间戳加上一个采样时间段|
|
||||
|rows|预测结果的记录数|10|
|
||||
|
||||
1. 预测查询结果新增三个伪列,具体如下:`_FROWTS`:预测结果的时间戳、`_FLOW`:置信区间下界、`_FHIGH`:置信区间上界, 对于没有置信区间的预测算法,其置信区间同预测结果
|
||||
2. 更改参数 `START`:返回预测结果的起始时间,改变起始时间不会影响返回的预测数值,只影响起始时间。
|
||||
3. `EVERY`:可以与输入数据的采样频率不同。采样频率只能低于或等于输入数据采样频率,不能**高于**输入数据的采样频率。
|
||||
4. 对于某些不需要计算置信区间的算法,即使指定了置信区间,返回的结果中其上下界退化成为一个点。
|
||||
|
||||
**示例**
|
||||
|
||||
```SQL
|
||||
--- 使用 arima 算法进行预测,预测结果是 10 条记录(默认值),数据进行白噪声检查,默认置信区间 95%.
|
||||
SELECT _flow, _fhigh, _frowts, FORECAST(i32, "algo=arima")
|
||||
FROM foo;
|
||||
|
||||
--- 使用 arima 算法进行预测,输入数据的是周期数据,每 10 个采样点是一个周期。返回置信区间是 95%.
|
||||
SELECT _flow, _fhigh, _frowts, FORECAST(i32, "algo=arima,alpha=95,period=10")
|
||||
FROM foo;
|
||||
```
|
||||
```
|
||||
taos> select _flow, _fhigh, _frowts, forecast(i32) from foo;
|
||||
_flow | _fhigh | _frowts | forecast(i32) |
|
||||
========================================================================================
|
||||
10.5286684 | 41.8038254 | 2020-01-01 00:01:35.001 | 26 |
|
||||
-21.9861946 | 83.3938904 | 2020-01-01 00:01:36.001 | 30 |
|
||||
-78.5686035 | 144.6729126 | 2020-01-01 00:01:37.001 | 33 |
|
||||
-154.9797363 | 230.3057709 | 2020-01-01 00:01:38.001 | 37 |
|
||||
-253.9852905 | 337.6083984 | 2020-01-01 00:01:39.001 | 41 |
|
||||
-375.7857971 | 466.4594727 | 2020-01-01 00:01:40.001 | 45 |
|
||||
-514.8043823 | 622.4426270 | 2020-01-01 00:01:41.001 | 53 |
|
||||
-680.6343994 | 796.2861328 | 2020-01-01 00:01:42.001 | 57 |
|
||||
-868.4956665 | 992.8603516 | 2020-01-01 00:01:43.001 | 62 |
|
||||
-1076.1566162 | 1214.4498291 | 2020-01-01 00:01:44.001 | 69 |
|
||||
```
|
||||
|
||||
|
||||
**可用预测算法**
|
||||
- arima
|
||||
- holtwinters
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Anomaly-detection"
|
||||
sidebar_label: "Anomaly-detection"
|
||||
title: "异常检测算法"
|
||||
sidebar_label: "异常检测算法"
|
||||
---
|
||||
|
||||
本节讲述异常检测算法模型的使用方法。
|
||||
|
@ -18,7 +18,7 @@ sidebar_label: "Anomaly-detection"
|
|||
|k|标准差倍数|选填|3|
|
||||
|
||||
|
||||
- IQR<sup>[2]</sup>:四分位距 (Interquartile range, IQR) 是一种衡量变异性的方法. 四分位数将一个按等级排序的数据集划分为四个相等的部分。即 Q1(第 1 个四分位数)、Q2(第 2 个四分位数)和 Q3(第 3 个四分位数)。IQR 定义为 Q3–Q1,位于 Q3+1.5。无输入参数。
|
||||
- IQR<sup>[2]</sup>:四分位距 (Interquartile range, IQR) 是一种衡量变异性的方法. 四分位数将一个按等级排序的数据集划分为四个相等的部分。即 Q1(第 1 个四分位数)、Q2(第 2 个四分位数)和 Q3(第 3 个四分位数)。IQR 定义为 $Q3–Q1$,位于 $Q3+1.5$。无输入参数。
|
||||
|
||||
- Grubbs<sup>[3]</sup>: 又称为 Grubbs' test,即最大标准残差测试。Grubbs 通常用作检验最大值、最小值偏离均值的程度是否为异常,该单变量数据集遵循近似标准正态分布。非正态分布数据集不能使用该方法。无输入参数。
|
||||
|
||||
|
@ -26,19 +26,19 @@ sidebar_label: "Anomaly-detection"
|
|||
|
||||
|参数|说明|是否必选|默认值|
|
||||
|---|---|---|---|
|
||||
|k|异常点在输入数据集中占比,范围是$`1\le K \le 49.9`$ |选填|5|
|
||||
|k|异常点在输入数据集中占比,范围是 $1\le K \le 49.9$ |选填|5|
|
||||
|
||||
|
||||
### 基于数据密度的检测方法
|
||||
LOF<sup>[5]</sup>: 局部离群因子(LOF,又叫局部异常因子)算法是 Breunig 于 2000 年提出的一种基于密度的局部离群点检测算法,该方法适用于不同类簇密度分散情况迥异的数据。根据数据点周围的数据密集情况,首先计算每个数据点的一个局部可达密度,然后通过局部可达密度进一步计算得到每个数据点的一个离群因子,该离群因子即标识了一个数据点的离群程度,因子值越大,表示离群程度越高,因子值越小,表示离群程度越低。最后,输出离群程度最大的 top(n) 个点。
|
||||
LOF<sup>[5]</sup>: 局部离群因子(LOF,又叫局部异常因子)算法是 Breunig 于 2000 年提出的一种基于密度的局部离群点检测算法,该方法适用于不同类簇密度分散情况迥异的数据。根据数据点周围的数据密集情况,首先计算每个数据点的一个局部可达密度,然后通过局部可达密度进一步计算得到每个数据点的一个离群因子,该离群因子即标识了一个数据点的离群程度,因子值越大,表示离群程度越高,因子值越小,表示离群程度越低。最后,输出离群程度最大的 $top(n)$ 个点。
|
||||
|
||||
|
||||
### 基于深度学习的检测方法
|
||||
### 基于自编码器的检测方法
|
||||
使用自动编码器的异常检测模型。可以对具有周期性的数据具有较好的检测结果。但是使用该模型需要针对输入的时序数据进行训练,同时将训练完成的模型部署到服务目录中,才能够运行与使用。
|
||||
|
||||
|
||||
### 参考文献
|
||||
1. https://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule
|
||||
1. [https://en.wikipedia.org/wiki/68–95–99.7 rule](https://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule)
|
||||
2. https://en.wikipedia.org/wiki/Interquartile_range
|
||||
3. Adikaram, K. K. L. B.; Hussein, M. A.; Effenberger, M.; Becker, T. (2015-01-14). "Data Transformation Technique to Improve the Outlier Detection Power of Grubbs's Test for Data Expected to Follow Linear Relation". Journal of Applied Mathematics. 2015: 1–9. doi:10.1155/2015/708948.
|
||||
4. Hochenbaum, O. S. Vallis, and A. Kejariwal. 2017. Automatic Anomaly Detection in the Cloud Via Statistical Learning. arXiv preprint arXiv:1704.07706 (2017).
|
|
@ -0,0 +1,78 @@
|
|||
---
|
||||
title: 异常检测算法
|
||||
description: 异常检测算法
|
||||
---
|
||||
|
||||
import ad from '../pic/anomaly-detection.png';
|
||||
|
||||
时序数据异常检测,在TDengine 查询处理中以异常窗口的形式服务。因此,可以将异常检测获得的窗口视为一种特殊的**事件窗口**,区别在于异常窗口的触发条件和结束条件不是用户指定,而是检测算法自动识别。因此,可以应用在事件窗口上的函数均可应用在异常窗口中。由于异常检测结果是一个时间窗口,因此调用异常检测的方式也与使用事件窗口的方式相同,在 `WHERE` 子句中使用 `ANOMALY_WINDOW` 关键词即可调用时序数据异常检测服务,同时窗口伪列(`_WSTART`, `_WEND`, `_WDURATION`)也能够像其他窗口函数一样使用。例如:
|
||||
|
||||
```SQL
|
||||
SELECT _wstart, _wend, SUM(i32)
|
||||
FROM foo
|
||||
ANOMALY_WINDOW(i32, "algo=iqr");
|
||||
```
|
||||
|
||||
如下图所示,Anode 将返回时序数据异常窗口 $[10:51:30, 10:53:40]$
|
||||
|
||||
<img src={ad} width="760" alt="异常检测" />
|
||||
|
||||
在此基础上,用户可以针对异常窗口内的时序数据进行查询聚合、变换处理等操作。
|
||||
|
||||
### 语法
|
||||
|
||||
```SQL
|
||||
ANOMALY_WINDOW(column_name, option_expr)
|
||||
|
||||
option_expr: {"
|
||||
algo=expr1
|
||||
[,wncheck=1|0]
|
||||
[,expr2]
|
||||
"}
|
||||
```
|
||||
|
||||
1. `column_name`:进行时序数据异常检测的输入数据列,当前只支持单列,且只能是数值类型,不能是字符类型(例如:`NCHAR` `VARCHAR` `VARBINARY`等类型),**不支持函数表达式**。
|
||||
2. `options`:字符串。其中使用 K=V 调用异常检测算法及与算法相关的参数。采用逗号分隔的 K=V 字符串表示,其中的字符串不需要使用单引号、双引号、或转义号等符号,不能使用中文及其他宽字符。例如:`algo=ksigma,k=2` 表示进行异常检测的算法是 ksigma,该算法接受的输入参数是 2。
|
||||
3. 异常检测的结果可以作为外层查询的子查询输入,在 `SELECT` 子句中使用的聚合函数或标量函数与其他类型的窗口查询相同。
|
||||
4. 输入数据默认进行白噪声检查,如果输入数据是白噪声,将不会有任何(异常)窗口信息返回。
|
||||
|
||||
**参数说明**
|
||||
|参数|含义|默认值|
|
||||
|---|---|---|
|
||||
|algo|异常检测调用的算法|iqr|
|
||||
|wncheck|对输入数据列是否进行白噪声检查|取值为 0 或者 1,默认值为 1,表示进行白噪声检查|
|
||||
|
||||
异常检测的返回结果以窗口形式呈现,因此窗口查询相关的伪列在这种场景下仍然可用。可用的伪列如下:
|
||||
1. `_WSTART`: 异常窗口开始时间戳
|
||||
2. `_WEND`:异常窗口结束时间戳
|
||||
3. `_WDURATION`:异常窗口持续时间
|
||||
|
||||
**示例**
|
||||
```SQL
|
||||
--- 使用 iqr 算法进行异常检测,检测列 i32 列。
|
||||
SELECT _wstart, _wend, SUM(i32)
|
||||
FROM ai.atb
|
||||
ANOMALY_WINDOW(i32, "algo=iqr");
|
||||
|
||||
--- 使用 ksigma 算法进行异常检测,输入参数 k 值为 2,检测列 i32 列
|
||||
SELECT _wstart, _wend, SUM(i32)
|
||||
FROM ai.atb
|
||||
ANOMALY_WINDOW(i32, "algo=ksigma,k=2");
|
||||
```
|
||||
|
||||
```
|
||||
taos> SELECT _wstart, _wend, count(*) FROM ai.atb ANOMAYL_WINDOW(i32);
|
||||
_wstart | _wend | count(*) |
|
||||
====================================================================
|
||||
2020-01-01 00:00:16.000 | 2020-01-01 00:00:17.000 | 2 |
|
||||
Query OK, 1 row(s) in set (0.028946s)
|
||||
```
|
||||
|
||||
|
||||
**可用异常检测算法**
|
||||
- iqr
|
||||
- ksigma
|
||||
- grubbs
|
||||
- lof
|
||||
- shesd
|
||||
- tac
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
title: "预测算法"
|
||||
sidebar_label: "预测算法"
|
||||
---
|
||||
|
||||
### 输入约定
|
||||
`execute` 是预测算法处理的核心方法。框架调用该方法之前,在对象属性参数 `self.list` 中已经设置完毕用于预测的历史时间序列数据。
|
||||
|
||||
### 输出约定及父类属性说明
|
||||
`execute` 方法执行完成后的返回一个如下字典对象, 预测返回结果如下:
|
||||
```python
|
||||
return {
|
||||
"mse": mse, # 预测算法的拟合数据最小均方误差(minimum squared error)
|
||||
"res": res # 结果数组 [时间戳数组, 预测结果数组, 预测结果执行区间下界数组,预测结果执行区间上界数组]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
预测算法的父类 `AbstractForecastService` 包含的对象属性如下:
|
||||
|
||||
|属性名称|说明|默认值|
|
||||
|---|---|---|
|
||||
|period|输入时间序列的周期性,多少个数据点表示一个完整的周期。如果没有周期性,设置为 0 即可| 0|
|
||||
|start_ts|预测结果的开始时间| 0|
|
||||
|time_step|预测结果的两个数据点之间时间间隔|0 |
|
||||
|fc_rows|预测结果的数量| 0 |
|
||||
|return_conf|预测结果中是否包含置信区间范围,如果不包含置信区间,那么上界和下界与自身相同| 1|
|
||||
|conf|置信区间分位数|95|
|
||||
|
||||
|
||||
|
||||
### 示例代码
|
||||
下面我们开发一个示例预测算法,对于任何输入的时间序列数据,固定返回值 1 作为预测结果。
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
from service import AbstractForecastService
|
||||
|
||||
# 算法实现类名称 需要以下划线 "_" 开始,并以 Service 结束
|
||||
class _MyForecastService(AbstractForecastService):
|
||||
""" 定义类,从 AbstractForecastService 继承并实现其定义的抽象方法 execute """
|
||||
|
||||
# 定义算法调用关键词,全小写ASCII码
|
||||
name = 'myfc'
|
||||
|
||||
# 该算法的描述信息(建议添加)
|
||||
desc = """return the forecast time series data"""
|
||||
|
||||
def __init__(self):
|
||||
"""类初始化方法"""
|
||||
super().__init__()
|
||||
|
||||
def execute(self):
|
||||
""" 算法逻辑的核心实现"""
|
||||
res = []
|
||||
|
||||
"""这个预测算法固定返回 1 作为预测值,预测值的数量是用户通过 self.fc_rows 指定"""
|
||||
ts_list = [self.start_ts + i * self.time_step for i in range(self.fc_rows)]
|
||||
res.app(ts_list) # 设置预测结果时间戳列
|
||||
|
||||
"""生成全部为 1 的预测结果 """
|
||||
res_list = [1] * self.fc_rows
|
||||
res.append(res_list)
|
||||
|
||||
"""检查用户输入,是否要求返回预测置信区间上下界"""
|
||||
if self.return_conf:
|
||||
"""对于没有计算预测置信区间上下界的算法,直接返回预测值作为上下界即可"""
|
||||
bound_list = [1] * self.fc_rows
|
||||
res.append(bound_list) # 预测结果置信区间下界
|
||||
res.append(bound_list) # 预测结果执行区间上界
|
||||
|
||||
"""返回结果"""
|
||||
return { "res": res, "mse": 0}
|
||||
|
||||
|
||||
def set_params(self, params):
|
||||
"""该算法无需任何输入参数,直接重载父类该函数,不处理算法参数设置逻辑"""
|
||||
pass
|
||||
```
|
||||
将该文件保存在 `./taosanalytics/algo/ad/` 目录下,然后重启 taosanode 服务。然后就可以通过 SQL 语句调用该检测算法。
|
||||
|
||||
```SQL
|
||||
--- 对 col 列进行异常检测,通过指定 algo 参数为 myad 来调用新添加的异常检测类
|
||||
SELECT COUNT(*) FROM foo ANOMALY_DETECTION(col, 'algo=myad')
|
||||
```
|
||||
|
||||
将该文件保存在 `./taosanalytics/algo/fc/` 目录下,然后重启 taosanode 服务。通过执行 `SHOW ANODES FULL` 能够看到新加入的算法,然后就可以通过 SQL 语句调用该预测算法。
|
||||
|
||||
```SQL
|
||||
--- 对 col 列进行异常检测,通过指定 algo 参数为 myfc 来调用新添加的预测类
|
||||
SELECT _flow, _fhigh, _frowts, FORECAST(col_name, "algo=myfc")
|
||||
FROM foo;
|
||||
```
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
title: "异常检测"
|
||||
sidebar_label: "异常检测"
|
||||
---
|
||||
|
||||
### 输入约定
|
||||
`execute` 是算法处理的核心方法。框架调用该方法之前,在对象属性参数 `self.list` 中已经设置完毕用于异常检测的时间序列数据。
|
||||
|
||||
### 输出约定
|
||||
`execute` 方法执行完成后的返回值是长度与 `self.list` 相同的数组,数组位置 -1 的标识异常值点。
|
||||
> 例如:对于输入测量值序列 $[2, 2, 2, 2, 100]$, 假设 100 是异常点,那么方法返回的结果数组则为 $[1, 1, 1, 1, -1]$。
|
||||
|
||||
|
||||
### 示例代码
|
||||
下面我们开发一个示例异常检测算法,在异常检测中,将输入时间序列值的最后一个值设置为异常值,并返回结果。
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
from service import AbstractAnomalyDetectionService
|
||||
|
||||
# 算法实现类名称 需要以下划线 "_" 开始,并以 Service 结束
|
||||
class _MyAnomalyDetectionService(AbstractAnomalyDetectionService):
|
||||
""" 定义类,从 AbstractAnomalyDetectionService 继承,并实现 AbstractAnomalyDetectionService 类的抽象方法 """
|
||||
|
||||
# 定义算法调用关键词,全小写ASCII码
|
||||
name = 'myad'
|
||||
|
||||
# 该算法的描述信息(建议添加)
|
||||
desc = """return the last value as the anomaly data"""
|
||||
|
||||
def __init__(self):
|
||||
"""类初始化方法"""
|
||||
super().__init__()
|
||||
|
||||
def execute(self):
|
||||
""" 算法逻辑的核心实现"""
|
||||
|
||||
"""创建一个长度为 len(self.list),全部值为 1 的结果数组,然后将最后一个值设置为 -1,表示最后一个值是异常值"""
|
||||
res = [1] * len(self.list)
|
||||
res[-1] = -1
|
||||
|
||||
"""返回结果数组"""
|
||||
return res
|
||||
|
||||
|
||||
def set_params(self, params):
|
||||
"""该算法无需任何输入参数,直接重载父类该函数,不处理算法参数设置逻辑"""
|
||||
pass
|
||||
```
|
||||
|
||||
将该文件保存在 `./taosanalytics/algo/ad/` 目录下,然后重启 taosanode 服务。然后就可以通过 SQL 语句调用该检测算法。
|
||||
|
||||
```SQL
|
||||
--- 对 col 列进行异常检测,通过指定 algo 参数为 myad 来调用新添加的异常检测类
|
||||
SELECT COUNT(*) FROM foo ANOMALY_DETECTION(col, 'algo=myad')
|
||||
```
|
||||
|
||||
|
||||
### 单元测试
|
||||
|
||||
在测试目录`taosanalytics/test`中的 anomaly_test.py 中增加单元测试用例或添加新的测试文件。框架中使用了 Python Unit test 包。
|
||||
|
||||
```python
|
||||
def test_myad(self):
|
||||
""" 测试 _IqrService 类 """
|
||||
s = loader.get_service("myad")
|
||||
|
||||
# 设置需要进行检测的输入数据
|
||||
s.set_input_list(AnomalyDetectionTest.input_list)
|
||||
|
||||
r = s.execute()
|
||||
|
||||
# 最后一个点是异常点
|
||||
self.assertEqual(r[-1], -1)
|
||||
self.assertEqual(len(r), len(AnomalyDetectionTest.input_list))
|
||||
```
|
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
title: "算法开发者指南"
|
||||
sidebar_label: "算法开发者指南"
|
||||
---
|
||||
TDgpt 是一个可扩展的时序数据高级分析平台,用户仅按照简易的步骤就能将新分析算法添加到分析平台中。将开发完成的算法代码文件放入对应的目录文件夹,然后重启 Anode 即可完成扩展升级。Anode 启动后会自动加载特定目录的分析算法。用户可以直接使用 SQL 语句调用添加到 TDgpt 系统中的分析算法。得益于 TDgpt 与 taosd 的松散耦合关系,分析平台升级对 taosd 完全没有影响。应用系统也不需要做任何更改就能够完成分析功能和分析算法的升级。
|
||||
|
||||
这种方式能够按需扩展新分析算法,极大地拓展了 TDgpt 适应的范围,用户可以将契合业务场景开发的(预测、异常检测)分析算法嵌入到 TDgpt,并通过 SQL 语句进行调用。在不更改或更改非常少的应用系统代码的前提下,就能够快速完成分析功能的平滑升级。
|
||||
|
||||
本节说明如何将预测算法和异常检测算法添加到 TDengine 分析平台。
|
||||
|
||||
## 目录结构
|
||||
首先需要了解TDgpt的目录结构。其主体目录结构如下图:
|
||||
|
||||
```bash
|
||||
.
|
||||
├── cfg
|
||||
├── model
|
||||
│ └── ac_detection
|
||||
├── release
|
||||
├── script
|
||||
└── taosanalytics
|
||||
├── algo
|
||||
│ ├── ad
|
||||
│ └── fc
|
||||
├── misc
|
||||
└── test
|
||||
|
||||
```
|
||||
|
||||
|目录|说明|
|
||||
|---|---|
|
||||
|taosanalytics| 源代码目录,其下包含了算法具体保存目录 algo,放置杂项目录 misc,单元测试和集成测试目录 test。 algo 目录下 ad 保存异常检测算法代码,fc 目录保存预测算法代码|
|
||||
|script|是安装脚本和发布脚本放置目录|
|
||||
|model|放置针对数据集完成的训练模型|
|
||||
|cfg|配置文件目录|
|
||||
|
||||
## 约定与限制
|
||||
|
||||
- 异常检测算法的 Python 代码文件需放在 `./taos/algo/ad` 目录中
|
||||
- 预测算法 Python 代码文件需要放在 `./taos/algo/fc` 目录中
|
||||
|
||||
|
||||
### 类命名规范
|
||||
|
||||
由于算法采用自动加载,因此其只识别按照特定命名方式的类。算法类的名称需要以下划线开始,以 Service 结尾。例如:`_KsigmaService` 是 KSigma 异常检测算法类。
|
||||
|
||||
### 类继承约定
|
||||
|
||||
- 异常检测算法需要从 `AbstractAnomalyDetectionService` 继承,并实现其核心抽象方法 `execute`
|
||||
- 预测算法需要从 `AbstractForecastService` 继承,同样需要实现其核心抽象方法 `execute`
|
||||
|
||||
### 类属性初始化
|
||||
每个算法实现的类需要静态初始化两个类属性,分别是:
|
||||
|
||||
- `name`:触发调用的关键词,全小写英文字母。该名称也是通过 `SHOW` 命令查看可用分析算法是显示的名称。
|
||||
- `desc`:算法的描述信息
|
||||
|
||||
```SQL
|
||||
--- algo 后面的参数 algo_name 即为类名称 `name`
|
||||
SELECT COUNT(*) FROM foo ANOMALY_DETECTION(col_name, 'algo=algo_name')
|
||||
```
|
||||
|
||||
## 添加具有训练模型的分析算法
|
||||
|
||||
某些深度学习的分析算法需要使用输入时间序列数据进行训练,然后生成针对训练数据集的分析模型。这种情况下,同一个分析算法对应不同的输入数据集有不同的分析模型。
|
||||
这种类型的分析算法要添加到 TDgpt 中,首先需要在 `model` 目录中建立目录,将采用该算法针对不同的输入时间序列数据生成的训练模型均保存在该目录下。如下图所示,针对不同的数据集,采用自编码器训练的数据异常检测算法生成的模型均保存在该目录下。为了确保模型能够正常读取加载,要求存储的模型使用`joblib`库进行序列化保存。
|
||||
采用训练-保存模型的方式可以一次训练,多次调用的优势。避免动态训练调用所带来的反复训练开销。
|
||||
|
||||
调用已经保存的模型,需要首先调用`set_params`方法,并在参数中指定调用模型的名称 `{"model": "ad_encoder_keras"}` 即可调用该模型进行计算。调用方式如下:
|
||||
|
||||
```python
|
||||
def test_autoencoder_ad(self):
|
||||
# 获取特定的算法对象
|
||||
# ...
|
||||
|
||||
# 指定调用的模型,该模型是之前针对该数据集进行训练获得
|
||||
s.set_params({"model": "ad_encoder_keras"})
|
||||
|
||||
# 执行检查动作,并返回结果
|
||||
r = s.execute()
|
||||
```
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
sidebar_label: TDgpt
|
||||
title: TDgpt
|
||||
---
|
||||
|
||||
import TDgpt from './pic/data-analysis.png';
|
||||
|
||||
## 概述
|
||||
|
||||
TDgpt 是 TDengine Enterprise 中针对时序数据提供高级分析功能的企业级组件,能够独立于 TDengine 主进程部署和运行,不消耗和占用 TDengine 主进程的资源,通过内置接口向 TDengine 提供运行时动态扩展的高级时序数据分析功能。TDgpt 具有服务无状态、功能易扩展、快速弹性部署、应用轻量化、高安全性等特点。
|
||||
TDgpt 运行在部署于 TDengine 集群中的 AI Node (ANode)中。每个 TDengine 集群中可以部署一个或若干个 ANode 节点,不同的 ANode 节点之间不相关,无同步或协同的要求。ANode 注册到 TDengine 集群以后,就可以通过内部接口提供服务。TDgpt 提供的高级时序数据分析服务可分为时序数据异常检测和时序数据预测分析两个类别。
|
||||
|
||||
下图是部署了 TDgpt 的 TDengine 集群示意图。
|
||||
<img src={TDgpt} width="560" alt="TDgpt架构图" />
|
||||
|
||||
通过注册指令将 ANode 注册到 MNode 中以后,就加入到 TDengine 集群,并可被查询引擎动态调用执行。在查询处理过程中,查询引擎根据生成的物理执行计划,**按需**向 ANode 请求高级时序数据分析服务。用户可通过SQL语句与 ANode 节点交互,并使用其提供的全部分析服务。需要注意的是 ANode 不直接接受用户的数据分析请求。同时 ANode 提供高效的动态注册机制,其注册和卸载过程完全不影响 TDengine 集群的服务,只影响提供对应的查询服务能力。
|
||||
|
||||
TDgpt 提供的高级数据分析功能分为时序数据异常检测和时序数据预测。
|
||||
- 时序数据异常检测的结果采用异常窗口的形式提供,即分析系统自动将算法检测到的连续异常数据以时间窗口的形式返回,其使用方式与 TDengine 中其他类型的时间窗口(例如状态窗口、事件窗口)类似。特别地,可以将异常数据窗口视作为一种特殊的**事件窗口(Event Window)**,因此状态窗口可使用的所有查询操作均可应用在异常窗口上。
|
||||
- 时序数据预测是基于输入的时间序列数据,使用指定(或默认)预测算法给出输入时序数据后续时间序列的**预测**观测值数据。因此,不同于异常检测是以窗口的形式存在,时序数据预测在 TDengine 中是一个(不确定输出)函数。
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 324 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 87 KiB |
|
@ -1,170 +0,0 @@
|
|||
---
|
||||
title: "addins"
|
||||
sidebar_label: "addins"
|
||||
---
|
||||
|
||||
本节说明如何将自己开发的预测算法和异常检测算法整合到 TDengine 分析平台,并能够通过 SQL 语句进行调用。
|
||||
|
||||
## 目录结构
|
||||
|
||||

|
||||
|
||||
|目录|说明|
|
||||
|---|---|
|
||||
|taos|Python 源代码目录,其下包含了算法具体保存目录 algo,放置杂项目录 misc,单元测试和集成测试目录 test。 algo 目录下 ad 放置异常检测算法代码,fc 放置预测算法代码|
|
||||
|script|是安装脚本和发布脚本放置目录|
|
||||
|model|放置针对数据集完成的训练模型|
|
||||
|cfg|配置文件目录|
|
||||
|
||||
## 约定与限制
|
||||
|
||||
定义异常检测算法的 Python 代码文件需放在 /taos/algo/ad 目录中,预测算法 Python 代码文件需要放在 /taos/algo/fc 目录中,以确保系统启动的时候能够正常加载对应目录下的 Python 文件。
|
||||
|
||||
|
||||
### 类命名规范
|
||||
|
||||
算法类的名称需要以下划线开始,以 Service 结尾。例如:_KsigmaService 是 KSigma 异常检测算法的实现类。
|
||||
|
||||
### 类继承约定
|
||||
|
||||
- 异常检测算法需要从 `AbstractAnomalyDetectionService` 继承,并实现其核心抽象方法 `execute`
|
||||
- 预测算法需要从 `AbstractForecastService` 继承,同样需要实现其核心抽象方法 `execute`
|
||||
|
||||
### 类属性初始化
|
||||
每个算法实现的类需要静态初始化两个类属性,分别是:
|
||||
|
||||
- `name`:触发调用的关键词,全小写英文字母
|
||||
- `desc`:算法的描述信息
|
||||
|
||||
### 核心方法输入与输出约定
|
||||
|
||||
`execute` 是算法处理的核心方法。调用该方法的时候,`self.list` 已经设置好输入数组。
|
||||
|
||||
异常检测输出结果
|
||||
|
||||
`execute` 的返回值是长度与 `self.list` 相同的数组,数组位置为 -1 的即为异常值点。例如:输入数组是 [2, 2, 2, 2, 100], 如果 100 是异常点,那么返回值是 [1, 1, 1, 1, -1]。
|
||||
|
||||
预测输出结果
|
||||
|
||||
对于预测算法,`AbstractForecastService` 的对象属性说明如下:
|
||||
|
||||
|属性名称|说明|默认值|
|
||||
|---|---|---|
|
||||
|period|输入时间序列的周期性,多少个数据点表示一个完整的周期。如果没有周期性,那么设置为 0 即可| 0|
|
||||
|start_ts|预测结果的开始时间| 0|
|
||||
|time_step|预测结果的两个数据点之间时间间隔|0 |
|
||||
|fc_rows|预测结果的数量| 0 |
|
||||
|return_conf|预测结果中是否包含置信区间范围,如果不包含置信区间,那么上界和下界与自身相同| 1|
|
||||
|conf|置信区间分位数 0.05|
|
||||
|
||||
|
||||
预测返回结果如下:
|
||||
```python
|
||||
return {
|
||||
"rows": self.fc_rows, # 预测数据行数
|
||||
"period": self.period, # 数据周期性,同输入
|
||||
"algo": "holtwinters", # 预测使用的算法
|
||||
"mse": mse, # 预测算法的 mse
|
||||
"res": res # 结果数组 [时间戳数组, 预测结果数组, 预测结果执行区间下界数组,预测结果执行区间上界数组]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 示例代码
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
from service import AbstractAnomalyDetectionService
|
||||
|
||||
# 算法实现类名称 需要以下划线 "_" 开始,并以 Service 结束,如下 _IqrService 是 IQR 异常检测算法的实现类。
|
||||
class _IqrService(AbstractAnomalyDetectionService):
|
||||
""" IQR algorithm 定义类,从 AbstractAnomalyDetectionService 继承,并实现 AbstractAnomalyDetectionService 类的抽象函数 """
|
||||
|
||||
# 定义算法调用关键词,全小写ASCII码(必须添加)
|
||||
name = 'iqr'
|
||||
|
||||
# 该算法的描述信息(建议添加)
|
||||
desc = """found the anomaly data according to the inter-quartile range"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def execute(self):
|
||||
""" execute 是算法实现逻辑的核心实现,直接修改该实现即可 """
|
||||
|
||||
# self.list 是输入数值列,list 类型,例如:[1,2,3,4,5]。设置 self.list 的方法在父类中已经进行了定义。实现自己的算法,修改该文件即可,以下代码使用自己的实现替换即可。
|
||||
#lower = np.quantile(self.list, 0.25)
|
||||
#upper = np.quantile(self.list, 0.75)
|
||||
|
||||
#min_val = lower - 1.5 * (upper - lower)
|
||||
#max_val = upper + 1.5 * (upper - lower)
|
||||
#threshold = [min_val, max_val]
|
||||
|
||||
# 返回值是与输入数值列长度相同的数据列,异常值对应位置是 -1。例如上述输入数据列,返回数值列是 [1, 1, 1, 1, -1],表示 [5] 是异常值。
|
||||
return [-1 if k < threshold[0] or k > threshold[1] else 1 for k in self.list]
|
||||
|
||||
|
||||
def set_params(self, params):
|
||||
"""该算法无需任何输入参数,直接重载父类该函数,不处理算法参数设置逻辑"""
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
## 单元测试
|
||||
|
||||
在测试文件目录中的 anomaly_test.py 中增加单元测试用例。
|
||||
|
||||
```python
|
||||
def test_iqr(self):
|
||||
""" 测试 _IqrService 类 """
|
||||
s = loader.get_service("iqr")
|
||||
|
||||
# 设置需要进行检测的输入数据
|
||||
s.set_input_list(AnomalyDetectionTest.input_list)
|
||||
|
||||
# 测试 set_params 的处理逻辑
|
||||
try:
|
||||
s.set_params({"k": 2})
|
||||
except ValueError as e:
|
||||
self.assertEqual(1, 0)
|
||||
|
||||
r = s.execute()
|
||||
|
||||
# 绘制异常检测结果
|
||||
draw_ad_results(AnomalyDetectionTest.input_list, r, "iqr")
|
||||
|
||||
# 检查结果
|
||||
self.assertEqual(r[-1], -1)
|
||||
self.assertEqual(len(r), len(AnomalyDetectionTest.input_list))
|
||||
```
|
||||
|
||||
## 需要模型的算法
|
||||
|
||||
针对特定数据集,进行模型训练的算法,在训练完成后。需要将训练得到的模型保存在 model 目录中。需要注意的是,针对每个算法,需要建立独立的文件夹。例如 auto_encoder 的训练算法在 model 目录下建立 autoencoder 的目录,使用该算法针对不同数据集训练得到的模型,均需要放置在该目录下。
|
||||
|
||||
训练完成后的模型,使用 joblib 进行保存。
|
||||
|
||||
并在 model 目录下建立对应的文件夹存放该模型。
|
||||
|
||||
保存模型的调用,可参考 encoder.py 的方式,用户通过调用 set_params 方法,并指定参数 `{"model": "ad_encoder_keras"}` 的方式,可以调用该模型进行计算。
|
||||
|
||||
具体的调用方式如下:
|
||||
|
||||
```python
|
||||
def test_autoencoder_ad(self):
|
||||
# 获取特定的算法服务
|
||||
s = loader.get_service("ac")
|
||||
data = self.__load_remote_data_for_ad()
|
||||
|
||||
# 设置异常检查的输入数据
|
||||
s.set_input_list(data)
|
||||
|
||||
# 指定调用的模型,该模型是之前针对该数据集进行训练获得
|
||||
s.set_params({"model": "ad_encoder_keras"})
|
||||
# 执行检查动作,并返回结果
|
||||
r = s.execute()
|
||||
|
||||
num_of_error = -(sum(filter(lambda x: x == -1, r)))
|
||||
self.assertEqual(num_of_error, 109)
|
||||
```
|
||||
|
|
@ -1,322 +0,0 @@
|
|||
---
|
||||
sidebar_label: 数据分析
|
||||
title: 数据分析功能
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
ANode(Analysis Node)是 TDengine 提供数据分析功能的扩展组件,通过 Restful 接口提供分析服务,拓展 TDengine 的功能,支持时间序列高级分析。
|
||||
ANode 是无状态的数据分析节点,集群中可以存在多个 ANode 节点,相互之间没有关联。将 ANode 注册到 TDengine 集群以后,通过 SQL 语句即可调用并完成时序分析任务。
|
||||
下图是数据分析的技术架构示意图。
|
||||
|
||||

|
||||
|
||||
## 安装部署
|
||||
### 环境准备
|
||||
ANode 要求节点上准备有 Python 3.10 及以上版本,以及相应的 Python 包自动安装组件 Pip,同时请确保能够正常连接互联网。
|
||||
|
||||
### 安装及卸载
|
||||
使用专门的 ANode 安装包 TDengine-enterprise-anode-1.x.x.tar.gz 进行 ANode 的安装部署工作,安装过程与 TDengine 的安装流程一致。
|
||||
|
||||
```bash
|
||||
tar -xzvf TDengine-enterprise-anode-1.0.0.tar.gz
|
||||
cd TDengine-enterprise-anode-1.0.0
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
卸载 ANode,执行命令 `rmtaosanode` 即可。
|
||||
|
||||
### 其他
|
||||
为了避免 ANode 安装后影响目标节点现有的 Python 库。 ANode 使用 Python 虚拟环境运行,安装后的默认 Python 目录处于 `/var/lib/taos/taosanode/venv/`。为了避免反复安装虚拟环境带来的开销,卸载 ANode 并不会自动删除该虚拟环境,如果您确认不需要 Python 的虚拟环境,可以手动删除。
|
||||
|
||||
## 启动及停止服务
|
||||
安装 ANode 以后,可以使用 `systemctl` 来管理 ANode 的服务。使用如下命令可以启动/停止/检查状态。
|
||||
|
||||
```bash
|
||||
systemctl start taosanoded
|
||||
systemctl stop taosanoded
|
||||
systemctl status taosanoded
|
||||
```
|
||||
|
||||
## 目录及配置说明
|
||||
|目录/文件|说明|
|
||||
|---------------|------|
|
||||
|/usr/local/taos/taosanode/bin|可执行文件目录|
|
||||
|/usr/local/taos/taosanode/resource|资源文件目录,链接到文件夹 /var/lib/taos/taosanode/resource/|
|
||||
|/usr/local/taos/taosanode/lib|库文件目录|
|
||||
|/var/lib/taos/taosanode/model/|模型文件目录,链接到文件夹 /var/lib/taos/taosanode/model|
|
||||
|/var/log/taos/taosanode/|日志文件目录|
|
||||
|/etc/taos/taosanode.ini|配置文件|
|
||||
|
||||
### 配置说明
|
||||
|
||||
Anode 提供的 RestFul 服务使用 uWSGI 驱动,因此 ANode 和 uWSGI 的配置信息存放在同一个配置文件中,具体如下:
|
||||
|
||||
```ini
|
||||
[uwsgi]
|
||||
# charset
|
||||
env = LC_ALL = en_US.UTF-8
|
||||
|
||||
# ip:port
|
||||
http = 127.0.0.1:6050
|
||||
|
||||
# the local unix socket file than communicate to Nginx
|
||||
#socket = 127.0.0.1:8001
|
||||
#socket-timeout = 10
|
||||
|
||||
# base directory
|
||||
chdir = /usr/local/taos/taosanode/lib
|
||||
|
||||
# initialize python file
|
||||
wsgi-file = /usr/local/taos/taosanode/lib/taos/app.py
|
||||
|
||||
# call module of uWSGI
|
||||
callable = app
|
||||
|
||||
# auto remove unix Socket and pid file when stopping
|
||||
vacuum = true
|
||||
|
||||
# socket exec model
|
||||
#chmod-socket = 664
|
||||
|
||||
# uWSGI pid
|
||||
uid = root
|
||||
|
||||
# uWSGI gid
|
||||
gid = root
|
||||
|
||||
# main process
|
||||
master = true
|
||||
|
||||
# the number of worker processes
|
||||
processes = 2
|
||||
|
||||
# pid file
|
||||
pidfile = /usr/local/taos/taosanode/taosanode.pid
|
||||
|
||||
# enable threads
|
||||
enable-threads = true
|
||||
|
||||
# the number of threads for each process
|
||||
threads = 4
|
||||
|
||||
# memory useage report
|
||||
memory-report = true
|
||||
|
||||
# smooth restart
|
||||
reload-mercy = 10
|
||||
|
||||
# conflict with systemctl, so do NOT uncomment this
|
||||
# daemonize = /var/log/taos/taosanode/taosanode.log
|
||||
|
||||
# log directory
|
||||
logto = /var/log/taos/taosanode/taosanode.log
|
||||
|
||||
# wWSGI monitor port
|
||||
stats = 127.0.0.1:8387
|
||||
|
||||
# python virtual environment directory
|
||||
virtualenv = /usr/local/taos/taosanode/venv/
|
||||
|
||||
[taosanode]
|
||||
# default app log file
|
||||
app-log = /var/log/taos/taosanode/taosanode.app.log
|
||||
|
||||
# model storage directory
|
||||
model-dir = /usr/local/taos/taosanode/model/
|
||||
|
||||
# default log level
|
||||
log-level = DEBUG
|
||||
|
||||
# draw the query results
|
||||
draw-result = 0
|
||||
```
|
||||
|
||||
**提示**
|
||||
请勿设置 `daemonize` 参数,该参数会导致 uWSGI 与 systemctl 冲突,从而无法正常启动。
|
||||
|
||||
|
||||
## ANode 基本操作
|
||||
### 管理 ANode
|
||||
#### 创建 ANode
|
||||
```sql
|
||||
CREATE ANODE {node_url}
|
||||
```
|
||||
node_url 是提供服务的 ANode 的 IP 和 PORT, 例如:`create anode 'http://localhost:6050'`。启动 ANode 以后如果不注册到 TDengine 集群中,则无法提供正常的服务。不建议 ANode 注册到两个或多个集群中。
|
||||
|
||||
#### 查看 ANode
|
||||
列出集群中所有的数据分析节点,包括其 `FQDN`, `PORT`, `STATUS`。
|
||||
```sql
|
||||
SHOW ANODES;
|
||||
```
|
||||
|
||||
#### 查看提供的时序数据分析服务
|
||||
|
||||
```SQL
|
||||
SHOW ANODES FULL;
|
||||
```
|
||||
|
||||
#### 强制刷新集群中的分析算法缓存
|
||||
```SQL
|
||||
UPDATE ANODE {node_id}
|
||||
UPDATE ALL ANODES
|
||||
```
|
||||
|
||||
#### 删除 ANode
|
||||
```sql
|
||||
DROP ANODE {anode_id}
|
||||
```
|
||||
删除 ANode 只是将 ANode 从 TDengine 集群中删除,管理 ANode 的启停仍然需要使用`systemctl`命令。
|
||||
|
||||
### 时序数据分析功能
|
||||
|
||||
#### 白噪声检查
|
||||
|
||||
分析平台提供的 Restful 服务要求输入的时间序列不能是白噪声时间序列(White Noise Data, WND)和随机数序列 , 因此针对所有数据均默认进行白噪声检查。当前白噪声检查采用通行的 `Ljung-Box` 检验,`Ljung-Box` 统计量检查过程需要遍历整个输入序列并进行计算。
|
||||
如果用户能够明确输入序列一定不是白噪声序列,那么可以通过输入参数,指定预测之前忽略该检查,从而节省分析过程的 CPU 计算资源。
|
||||
同时支持独立地针对输入序列进行白噪声检测(该检测功能暂不独立对外开放)。
|
||||
|
||||
|
||||
#### 数据重采样和时间戳对齐
|
||||
|
||||
分析平台支持将输入数据进行重采样预处理,从而确保输出结果按照用户指定的等间隔进行处理。处理过程分为两种类别:
|
||||
|
||||
- 数据时间戳对齐。由于真实数据可能并非严格按照查询指定的时间戳输入。此时分析平台会自动将数据的时间间隔按照指定的时间间隔进行对齐。例如输入时间序列 [11, 22, 29, 41],用户指定时间间隔为 10,该序列将被对齐重整为以下序列 [10, 20, 30, 40]。
|
||||
- 数据时间重采样。用户输入时间序列的采样频率超过了输出结果的频率,例如输入时间序列的采样频率是 5,输出结果的频率是 10,输入时间序列 [0, 5, 10, 15, 20, 25, 30] 将被重采用为间隔 为 10 的序列 [0, 10, 20,30],[5, 15, 25] 处的数据将被丢弃。
|
||||
|
||||
需要注意的是,数据输入平台不支持缺失数据补齐后进行的预测分析,如果输入时间序列数据 [11, 22, 29, 49],并且用户要求的时间间隔为 10,重整对齐后的序列是 [10, 20, 30, 50] 那么该序列进行预测分析将返回错误。
|
||||
|
||||
|
||||
#### 时序数据异常检测
|
||||
异常检测是针对输入的时序数据,使用预设或用户指定的算法确定时间序列中**可能**出现异常的时间序列点,对于时间序列中若干个连续的异常点,将自动合并成为一个连续的(闭区间)异常窗口。对于只有单个点的场景,异常窗口窗口退化成为一个起始时间和结束时间相同的点。
|
||||
异常检测生成的异常窗口受检测算法和算法参数的共同影响,对于异常窗口范围内的数据,可以应用 TDengine 提供的聚合和标量函数进行查询或变换处理。
|
||||
对于输入时间序列 (1, 20), (2, 22), (3, 91), (4, 120), (5, 18), (6, 19)。系统检测到 (3, 91), (4, 120) 为异常点,那么返回的异常窗口是闭区间 [3, 4]。
|
||||
|
||||
|
||||
##### 语法
|
||||
|
||||
```SQL
|
||||
ANOMALY_WINDOW(column_name, option_expr)
|
||||
|
||||
option_expr: {"
|
||||
algo=expr1
|
||||
[,wncheck=1|0]
|
||||
[,expr2]
|
||||
"}
|
||||
```
|
||||
|
||||
1. `column`:进行时序数据异常检测的输入数据列,当前只支持单列,且只能是数值类型,不能是字符类型(例如:`NCHAR` `VARCHAR` `VARBINARY`等类型),**不支持函数表达式**。
|
||||
2. `options`:字符串。其中使用 K=V 调用异常检测算法及与算法相关的参数。采用逗号分隔的 K=V 字符串表示,其中的字符串不需要使用单引号、双引号、或转义号等符号,不能使用中文及其他宽字符。例如:`algo=ksigma,k=2` 表示进行异常检测的算法是 ksigma,该算法接受的输入参数是 2。
|
||||
3. 异常检测的结果可以作为外层查询的子查询输入,在 `SELECT` 子句中使用的聚合函数或标量函数与其他类型的窗口查询相同。
|
||||
4. 输入数据默认进行白噪声检查,如果输入数据是白噪声,将不会有任何(异常)窗口信息返回。
|
||||
|
||||
**参数说明**
|
||||
|参数|含义|默认值|
|
||||
|---|---|---|
|
||||
|algo|异常检测调用的算法|iqr|
|
||||
|wncheck|对输入数据列是否进行白噪声检查|取值为 0 或者 1,默认值为 1,表示进行白噪声检查|
|
||||
|
||||
异常检测的返回结果以窗口形式呈现,因此窗口查询相关的伪列在这种场景下仍然可用。可以使用的伪列如下:
|
||||
1. `_WSTART`: 异常窗口开始时间戳
|
||||
2. `_WEND`:异常窗口结束时间戳
|
||||
3. `_WDURATION`:异常窗口持续时间
|
||||
|
||||
**示例**
|
||||
```SQL
|
||||
--- 使用 iqr 算法进行异常检测,检测列 i32 列。
|
||||
SELECT _wstart, _wend, SUM(i32)
|
||||
FROM ai.atb
|
||||
ANOMALY_WINDOW(i32, "algo=iqr");
|
||||
|
||||
--- 使用 ksigma 算法进行异常检测,输入参数 k 值为 2,检测列 i32 列
|
||||
SELECT _wstart, _wend, SUM(i32)
|
||||
FROM ai.atb
|
||||
ANOMALY_WINDOW(i32, "algo=ksigma,k=2");
|
||||
```
|
||||
|
||||
```
|
||||
taos> SELECT _wstart, _wend, count(*) FROM ai.atb ANOMAYL_WINDOW(i32);
|
||||
_wstart | _wend | count(*) |
|
||||
====================================================================
|
||||
2020-01-01 00:00:16.000 | 2020-01-01 00:00:16.001 | 1 |
|
||||
Query OK, 1 row(s) in set (0.028946s)
|
||||
```
|
||||
|
||||
|
||||
**可用异常检测算法**
|
||||
- iqr
|
||||
- ksigma
|
||||
- grubbs
|
||||
- lof
|
||||
- shesd
|
||||
- tac
|
||||
|
||||
|
||||
#### 时序数据预测
|
||||
数据预测以一段训练数据作为输入,预测接下来一个连续时间区间内,时序数据的趋势。
|
||||
|
||||
##### 语法
|
||||
```SQL
|
||||
FORECAST(column_expr, option_expr)
|
||||
|
||||
option_expr: {"
|
||||
algo=expr1
|
||||
[,wncheck=1|0]
|
||||
[,conf=conf_val]
|
||||
[,every=every_val]
|
||||
[,rows=rows_val]
|
||||
[,start=start_ts_val]
|
||||
[,expr2]
|
||||
"}
|
||||
|
||||
```
|
||||
1. `column_expr`:预测的时序数据列。与异常检测相同,只支持数值类型输入。
|
||||
2. `options`:异常检测函数的参数,使用规则与 anomaly_window 相同。预测还支持 `conf`, `every`, `rows`, `start`, `rows` 几个参数,其含义如下:
|
||||
|
||||
**参数说明**
|
||||
|
||||
|参数|含义|默认值|
|
||||
|---|---|---|
|
||||
|algo|预测分析使用的算法|holtwinters|
|
||||
|wncheck|白噪声(white noise data)检查|默认值为 1,0 表示不进行检查|
|
||||
|conf|预测数据的置信区间范围 ,取值范围 [0, 100]|95|
|
||||
|every|预测数据的采样间隔|输入数据的采样间隔|
|
||||
|start|预测结果的开始时间戳|输入数据最后一个时间戳加上一个采样时间段|
|
||||
|rows|预测结果的记录数|10|
|
||||
|
||||
1. 预测查询结果新增了三个伪列,具体如下:`_FROWTS`:预测结果的时间戳、`_FLOW`:置信区间下界、`_FHIGH`:置信区间上界, 对于没有置信区间的预测算法,其置信区间同预测结果
|
||||
2. 更改参数 `START`:返回预测结果的起始时间,改变起始时间不会影响返回的预测数值,只影响起始时间。
|
||||
3. `EVERY`:可以与输入数据的采样频率不同。采样频率只能低于或等于输入数据采样频率,不能**高于**输入数据的采样频率。
|
||||
4. 对于某些不需要计算置信区间的算法,即使指定了置信区间,返回的结果中其上下界退化成为一个点。
|
||||
|
||||
**示例**
|
||||
|
||||
```SQL
|
||||
--- 使用 arima 算法进行预测,预测结果是 10 条记录(默认值),数据进行白噪声检查,默认置信区间 95%.
|
||||
SELECT _flow, _fhigh, _frowts, FORECAST(i32, "algo=arima")
|
||||
FROM ai.ftb;
|
||||
|
||||
--- 使用 arima 算法进行预测,输入数据的是周期数据,每 10 个采样点是一个周期。返回置信区间是 95%.
|
||||
SELECT _flow, _fhigh, _frowts, FORECAST(i32, "algo=arima,alpha=95,period=10")
|
||||
FROM ai.ftb;
|
||||
```
|
||||
```
|
||||
taos> select _flow, _fhigh, _frowts, forecast(i32) from ai.ftb;
|
||||
_flow | _fhigh | _frowts | forecast(i32) |
|
||||
========================================================================================
|
||||
10.5286684 | 41.8038254 | 2020-01-01 00:01:35.001 | 26 |
|
||||
-21.9861946 | 83.3938904 | 2020-01-01 00:01:36.001 | 30 |
|
||||
-78.5686035 | 144.6729126 | 2020-01-01 00:01:37.001 | 33 |
|
||||
-154.9797363 | 230.3057709 | 2020-01-01 00:01:38.001 | 37 |
|
||||
-253.9852905 | 337.6083984 | 2020-01-01 00:01:39.001 | 41 |
|
||||
-375.7857971 | 466.4594727 | 2020-01-01 00:01:40.001 | 45 |
|
||||
-514.8043823 | 622.4426270 | 2020-01-01 00:01:41.001 | 53 |
|
||||
-680.6343994 | 796.2861328 | 2020-01-01 00:01:42.001 | 57 |
|
||||
-868.4956665 | 992.8603516 | 2020-01-01 00:01:43.001 | 62 |
|
||||
-1076.1566162 | 1214.4498291 | 2020-01-01 00:01:44.001 | 69 |
|
||||
```
|
||||
|
||||
|
||||
**可用预测算法**
|
||||
- arima
|
||||
- holtwinters
|
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 7.1 KiB |
|
@ -26,6 +26,22 @@ SHOW USERS;
|
|||
```sql
|
||||
ALTER USER TEST DROP HOST HOST_NAME1
|
||||
```
|
||||
说明
|
||||
- 开源版和企业版本都能添加成功,且可以查询到,但是开源版本不会对 IP 做任何限制。
|
||||
- create user u_write pass 'taosdata1' host 'iprange1','iprange2', 可以一次添加多个 iprange, 服务端会做去重,去重的逻辑是需要 iprange 完全一样
|
||||
- 默认会把 127.0.0.1 添加到白名单列表,且在白名单列表可以查询
|
||||
- 集群的节点 IP 集合会自动添加到白名单列表,但是查询不到。
|
||||
- taosadaper 和 taosd 不在一个机器的时候,需要把 taosadaper IP 手动添加到 taosd 白名单列表中
|
||||
- 集群情况下,各个节点 enableWhiteList 成一样,或者全为 false,或者全为 true, 要不然集群无法启动
|
||||
- 白名单变更生效时间 1s,不超过 2s, 每次变更对收发性能有些微影响(多一次判断,可以忽略),变更完之后、影响忽略不计, 变更过程中对集群没有影响,对正在访问客户端也没有影响(假设这些客户端的 IP 包含在 white list 内)
|
||||
- 如果添加两个 ip range, 192.168.1.1/16(假设为 A), 192.168.1.1/24(假设为 B), 严格来说,A 包含了 B,但是考虑情况太复杂,并不会对 A 和 B 做合并
|
||||
- 要删除的时候,必须严格匹配。 也就是如果添加的是 192.168.1.1/24, 要删除也是 192.168.1.1/24
|
||||
- 只有 root 才有权限对其他用户增删 ip white list
|
||||
- 兼容之前的版本,但是不支持从当前版本回退到之前版本
|
||||
- x.x.x.x/32 和 x.x.x.x 属于同一个 iprange, 显示为 x.x.x.x
|
||||
- 如果客户端拿到的 0.0.0.0/0, 说明没有开启白名单
|
||||
- 如果白名单发生了改变, 客户端会在 heartbeat 里检测到。
|
||||
- 针对一个 user, 添加的 IP 个数上限是 2048
|
||||
|
||||
## 审计日志
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ taosKeeper 是 TDengine 3.0 版本监控指标的导出工具,通过简单的
|
|||
|
||||
taosKeeper 有两种安装方式:
|
||||
|
||||
- 安装 TDengine 官方安装包的同时会自动安装 taosKeeper, 详情请参考[ TDengine 安装](../../../get-started/)。
|
||||
- 安装 TDengine 官方安装包的同时会自动安装 taosKeeper, 详情请参考[TDengine 安装](../../../get-started/)。
|
||||
|
||||
- 单独编译 taosKeeper 并安装,详情请参考 [taosKeeper](https://github.com/taosdata/taoskeeper) 仓库。
|
||||
|
||||
|
@ -22,55 +22,64 @@ taosKeeper 有两种安装方式:
|
|||
taosKeeper 需要在操作系统终端执行,该工具支持三种配置方式:命令行参数、环境变量 和 配置文件。优先级为:命令行参数、环境变量、配置文件参数。 一般我们推荐使用配置文件。
|
||||
|
||||
### 命令行参数和环境变量
|
||||
|
||||
命令行参数 和 环境变量说明可以参考命令 `taoskeeper --help` 的输出。下面是一个例子:
|
||||
|
||||
```shell
|
||||
Usage of taosKeeper v3.3.2.0:
|
||||
--debug enable debug mode. Env "TAOS_KEEPER_DEBUG"
|
||||
-P, --port int http port. Env "TAOS_KEEPER_PORT" (default 6043)
|
||||
--logLevel string log level (panic fatal error warn warning info debug trace). Env "TAOS_KEEPER_LOG_LEVEL" (default "info")
|
||||
--gopoolsize int coroutine size. Env "TAOS_KEEPER_POOL_SIZE" (default 50000)
|
||||
-R, --RotationInterval string interval for refresh metrics, such as "300ms", Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Env "TAOS_KEEPER_ROTATION_INTERVAL" (default "15s")
|
||||
--tdengine.host string TDengine server's ip. Env "TAOS_KEEPER_TDENGINE_HOST" (default "127.0.0.1")
|
||||
--tdengine.port int TDengine REST server(taosAdapter)'s port. Env "TAOS_KEEPER_TDENGINE_PORT" (default 6041)
|
||||
--tdengine.username string TDengine server's username. Env "TAOS_KEEPER_TDENGINE_USERNAME" (default "root")
|
||||
--tdengine.password string TDengine server's password. Env "TAOS_KEEPER_TDENGINE_PASSWORD" (default "taosdata")
|
||||
--tdengine.usessl TDengine server use ssl or not. Env "TAOS_KEEPER_TDENGINE_USESSL"
|
||||
--metrics.prefix string prefix in metrics names. Env "TAOS_KEEPER_METRICS_PREFIX"
|
||||
--metrics.database.name string database for storing metrics data. Env "TAOS_KEEPER_METRICS_DATABASE" (default "log")
|
||||
--metrics.tables stringArray export some tables that are not super table, multiple values split with white space. Env "TAOS_KEEPER_METRICS_TABLES"
|
||||
--environment.incgroup whether running in cgroup. Env "TAOS_KEEPER_ENVIRONMENT_INCGROUP"
|
||||
--log.path string log path. Env "TAOS_KEEPER_LOG_PATH" (default "/var/log/taos")
|
||||
--log.rotationCount uint log rotation count. Env "TAOS_KEEPER_LOG_ROTATION_COUNT" (default 5)
|
||||
--log.rotationTime duration log rotation time. Env "TAOS_KEEPER_LOG_ROTATION_TIME" (default 24h0m0s)
|
||||
--log.rotationSize string log rotation size(KB MB GB), must be a positive integer. Env "TAOS_KEEPER_LOG_ROTATION_SIZE" (default "100000000")
|
||||
-c, --config string config path default /etc/taos/taoskeeper.toml
|
||||
-V, --version Print the version and exit
|
||||
-h, --help Print this help message and exit
|
||||
Usage of taoskeeper v3.3.3.0:
|
||||
-R, --RotationInterval string interval for refresh metrics, such as "300ms", Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Env "TAOS_KEEPER_ROTATION_INTERVAL" (default "15s")
|
||||
-c, --config string config path default /etc/taos/taoskeeper.toml
|
||||
--drop string run taoskeeper in command mode, only support old_taosd_metric_stables.
|
||||
--environment.incgroup whether running in cgroup. Env "TAOS_KEEPER_ENVIRONMENT_INCGROUP"
|
||||
--fromTime string parameter of transfer, example: 2020-01-01T00:00:00+08:00 (default "2020-01-01T00:00:00+08:00")
|
||||
--gopoolsize int coroutine size. Env "TAOS_KEEPER_POOL_SIZE" (default 50000)
|
||||
-h, --help Print this help message and exit
|
||||
--instanceId int instance ID. Env "TAOS_KEEPER_INSTANCE_ID" (default 64)
|
||||
--log.compress whether to compress old log. Env "TAOS_KEEPER_LOG_COMPRESS"
|
||||
--log.keepDays uint log retention days, must be a positive integer. Env "TAOS_KEEPER_LOG_KEEP_DAYS" (default 30)
|
||||
--log.level string log level (trace debug info warning error). Env "TAOS_KEEPER_LOG_LEVEL" (default "info")
|
||||
--log.path string log path. Env "TAOS_KEEPER_LOG_PATH" (default "/var/log/taos")
|
||||
--log.reservedDiskSize string reserved disk size for log dir (KB MB GB), must be a positive integer. Env "TAOS_KEEPER_LOG_RESERVED_DISK_SIZE" (default "1GB")
|
||||
--log.rotationCount uint log rotation count. Env "TAOS_KEEPER_LOG_ROTATION_COUNT" (default 5)
|
||||
--log.rotationSize string log rotation size(KB MB GB), must be a positive integer. Env "TAOS_KEEPER_LOG_ROTATION_SIZE" (default "1GB")
|
||||
--log.rotationTime duration deprecated: log rotation time always 24 hours. Env "TAOS_KEEPER_LOG_ROTATION_TIME" (default 24h0m0s)
|
||||
--logLevel string log level (trace debug info warning error). Env "TAOS_KEEPER_LOG_LEVEL" (default "info")
|
||||
--metrics.database.name string database for storing metrics data. Env "TAOS_KEEPER_METRICS_DATABASE" (default "log")
|
||||
--metrics.database.options.buffer int database option buffer for audit database. Env "TAOS_KEEPER_METRICS_BUFFER" (default 64)
|
||||
--metrics.database.options.cachemodel string database option cachemodel for audit database. Env "TAOS_KEEPER_METRICS_CACHEMODEL" (default "both")
|
||||
--metrics.database.options.keep int database option buffer for audit database. Env "TAOS_KEEPER_METRICS_KEEP" (default 90)
|
||||
--metrics.database.options.vgroups int database option vgroups for audit database. Env "TAOS_KEEPER_METRICS_VGROUPS" (default 1)
|
||||
--metrics.prefix string prefix in metrics names. Env "TAOS_KEEPER_METRICS_PREFIX"
|
||||
--metrics.tables stringArray export some tables that are not super table, multiple values split with white space. Env "TAOS_KEEPER_METRICS_TABLES"
|
||||
-P, --port int http port. Env "TAOS_KEEPER_PORT" (default 6043)
|
||||
--tdengine.host string TDengine server's ip. Env "TAOS_KEEPER_TDENGINE_HOST" (default "127.0.0.1")
|
||||
--tdengine.password string TDengine server's password. Env "TAOS_KEEPER_TDENGINE_PASSWORD" (default "taosdata")
|
||||
--tdengine.port int TDengine REST server(taosAdapter)'s port. Env "TAOS_KEEPER_TDENGINE_PORT" (default 6041)
|
||||
--tdengine.username string TDengine server's username. Env "TAOS_KEEPER_TDENGINE_USERNAME" (default "root")
|
||||
--tdengine.usessl TDengine server use ssl or not. Env "TAOS_KEEPER_TDENGINE_USESSL"
|
||||
--transfer string run taoskeeper in command mode, only support old_taosd_metric. transfer old metrics data to new tables and exit
|
||||
-V, --version Print the version and exit
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 配置文件
|
||||
|
||||
taosKeeper 支持用 `taoskeeper -c <keeper config file>` 命令来指定配置文件。
|
||||
若不指定配置文件,taosKeeper 会使用默认配置文件,其路径为: `/etc/taos/taoskeeper.toml` 。
|
||||
taosKeeper 支持用 `taoskeeper -c <keeper config file>` 命令来指定配置文件。
|
||||
若不指定配置文件,taosKeeper 会使用默认配置文件,其路径为: `/etc/taos/taoskeeper.toml` 。
|
||||
若既不指定 taosKeeper 配置文件,且 `/etc/taos/taoskeeper.toml` 也不存在,将使用默认配置。
|
||||
|
||||
**下面是配置文件的示例:**
|
||||
```toml
|
||||
# Start with debug middleware for gin
|
||||
debug = false
|
||||
|
||||
# Listen port, default is 6043
|
||||
```toml
|
||||
# The ID of the currently running taoskeeper instance, default is 64.
|
||||
instanceId = 64
|
||||
|
||||
# Listening port, default is 6043.
|
||||
port = 6043
|
||||
|
||||
# log level
|
||||
loglevel = "info"
|
||||
|
||||
# go pool size
|
||||
# Go pool size
|
||||
gopoolsize = 50000
|
||||
|
||||
# interval for metrics
|
||||
# Interval for metrics
|
||||
RotationInterval = "15s"
|
||||
|
||||
[tdengine]
|
||||
|
@ -81,20 +90,21 @@ password = "taosdata"
|
|||
usessl = false
|
||||
|
||||
[metrics]
|
||||
# metrics prefix in metrics names.
|
||||
# Metrics prefix in metrics names.
|
||||
prefix = "taos"
|
||||
|
||||
# export some tables that are not super table
|
||||
# Export some tables that are not super table.
|
||||
tables = []
|
||||
|
||||
# database for storing metrics data
|
||||
# Database for storing metrics data.
|
||||
[metrics.database]
|
||||
name = "log"
|
||||
# database options for db storing metrics data
|
||||
|
||||
# Database options for db storing metrics data.
|
||||
[metrics.database.options]
|
||||
vgroups = 1
|
||||
buffer = 64
|
||||
KEEP = 90
|
||||
keep = 90
|
||||
cachemodel = "both"
|
||||
|
||||
[environment]
|
||||
|
@ -102,9 +112,19 @@ cachemodel = "both"
|
|||
incgroup = false
|
||||
|
||||
[log]
|
||||
rotationCount = 5
|
||||
rotationTime = "24h"
|
||||
rotationSize = 100000000
|
||||
# The directory where log files are stored.
|
||||
# path = "/var/log/taos"
|
||||
level = "info"
|
||||
# Number of log file rotations before deletion.
|
||||
rotationCount = 30
|
||||
# The number of days to retain log files.
|
||||
keepDays = 30
|
||||
# The maximum size of a log file before rotation.
|
||||
rotationSize = "1GB"
|
||||
# If set to true, log files will be compressed.
|
||||
compress = false
|
||||
# Minimum disk space to reserve. Log files will not be written if disk space falls below this limit.
|
||||
reservedDiskSize = "1GB"
|
||||
```
|
||||
|
||||
## 启动
|
||||
|
@ -118,7 +138,6 @@ monitorFqdn localhost # taoskeeper 服务的 FQDN
|
|||
|
||||
TDengine 监控配置相关,具体请参考:[TDengine 监控配置](../../../operation/monitor)。
|
||||
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Linux" value="linux">
|
||||
|
||||
|
@ -188,8 +207,7 @@ Active: inactive (dead)
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## 健康检查
|
||||
## 健康检查
|
||||
|
||||
可以访问 taosKeeper 的 `check_health` 接口来判断服务是否存活,如果服务正常则会返回 HTTP 200 状态码:
|
||||
|
||||
|
@ -208,7 +226,6 @@ Content-Length: 21
|
|||
{"version":"3.3.2.3"}
|
||||
```
|
||||
|
||||
|
||||
## 数据收集与监控
|
||||
|
||||
taosKeeper 作为 TDengine 监控指标的导出工具,可以将 TDengine 产生的监控数据记录在指定数据库中(默认的监控数据是 `log`),这些监控数据可以用来配置 TDengine 监控。
|
||||
|
@ -216,6 +233,7 @@ taosKeeper 作为 TDengine 监控指标的导出工具,可以将 TDengine 产
|
|||
### 查看监控数据
|
||||
|
||||
可以查看 `log` 库下的超级表,每个超级表都对应一组监控指标,具体指标不再赘述。
|
||||
|
||||
```shell
|
||||
taos> use log;
|
||||
Database changed.
|
||||
|
@ -251,17 +269,14 @@ taos> select last_row(*) from taosd_dnodes_info;
|
|||
Query OK, 1 row(s) in set (0.003168s)
|
||||
```
|
||||
|
||||
|
||||
### 使用 TDInsight 配置监控
|
||||
|
||||
收集到监控数据以后,就可以使用 TDInsight 来配置 TDengine 的监控,具体请参考 [TDinsight 参考手册](../tdinsight/)
|
||||
|
||||
收集到监控数据以后,就可以使用 TDInsight 来配置 TDengine 的监控,具体请参考 [TDinsight 参考手册](../tdinsight/)。
|
||||
|
||||
## 集成 Prometheus
|
||||
|
||||
taoskeeper 提供了 `/metrics` 接口,返回了 Prometheus 格式的监控数据,Prometheus 可以从 taoskeeper 抽取监控数据,实现通过 Prometheus 监控 TDengine 的目的。
|
||||
|
||||
|
||||
### 导出监控指标
|
||||
|
||||
下面通过 `curl` 命令展示 `/metrics` 接口返回的数据格式:
|
||||
|
@ -298,9 +313,11 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
#### taosd 集群
|
||||
|
||||
##### 监控信息支持的标签
|
||||
|
||||
- `cluster_id`: 集群 id
|
||||
|
||||
##### 相关指标及其含义
|
||||
|
||||
| 指标名称 | 类型 | 含义 |
|
||||
| ----------------------------------- | ------- | ------------------------------------- |
|
||||
| taos_cluster_info_connections_total | counter | 总连接数 |
|
||||
|
@ -328,11 +345,13 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
#### dnode
|
||||
|
||||
##### 监控信息支持的标签
|
||||
|
||||
- `cluster_id`: 集群 id
|
||||
- `dnode_ep`: dnode 端点
|
||||
- `dnode_id`:dnode id
|
||||
|
||||
##### 相关指标及其含义
|
||||
|
||||
| 指标名称 | 类型 | 含义 |
|
||||
| ------------------------------ | ------- | ---------------------------------------------------------------------------------------- |
|
||||
| taos_d_info_status | gauge | dnode 状态,标签 value 表示状态, ready 表示正常, offline 表示下线, unknown 表示未知。 |
|
||||
|
@ -361,13 +380,15 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
#### 数据目录
|
||||
|
||||
##### 监控信息支持的标签
|
||||
|
||||
- `cluster_id`: 集群 id
|
||||
- `dnode_ep`: dnode 端点
|
||||
- `dnode_id`:dnode id
|
||||
- `data_dir_name`:数据目录名
|
||||
- `data_dir_level`:数据目录级别
|
||||
- `data_dir_level`:数据目录级别
|
||||
|
||||
##### 相关指标及其含义
|
||||
|
||||
| 指标名称 | 类型 | 含义 |
|
||||
| --------------------------------- | ----- | -------------------- |
|
||||
| taos_taosd_dnodes_data_dirs_avail | gauge | 可用空间(单位 Byte) |
|
||||
|
@ -377,12 +398,14 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
#### 日志目录
|
||||
|
||||
##### 监控信息支持的标签
|
||||
|
||||
- `cluster_id`: 集群 id
|
||||
- `dnode_ep`: dnode 端点
|
||||
- `dnode_id`:dnode id
|
||||
- `log_dir_name`:日志目录名
|
||||
|
||||
##### 相关指标及其含义
|
||||
|
||||
| 指标名称 | 类型 | 含义 |
|
||||
| -------------------------------- | ----- | -------------------- |
|
||||
| taos_taosd_dnodes_log_dirs_avail | gauge | 可用空间(单位 Byte) |
|
||||
|
@ -392,11 +415,13 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
#### 日志数量
|
||||
|
||||
##### 监控信息支持的标签
|
||||
|
||||
- `cluster_id`: 集群 id
|
||||
- `dnode_ep`: dnode 端点
|
||||
- `dnode_id`:dnode id
|
||||
|
||||
##### 相关指标及其含义
|
||||
|
||||
| 指标名称 | 类型 | 含义 |
|
||||
| ---------------------- | ------- | ------------ |
|
||||
| taos_log_summary_debug | counter | 调试日志数量 |
|
||||
|
@ -404,14 +429,15 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
| taos_log_summary_info | counter | 信息日志数量 |
|
||||
| taos_log_summary_trace | counter | 跟踪日志数量 |
|
||||
|
||||
|
||||
#### taosadapter
|
||||
|
||||
##### 监控信息支持的标签
|
||||
|
||||
- `endpoint`:端点
|
||||
- `req_type`:请求类型,0 表示 rest,1 表示 websocket
|
||||
|
||||
##### 相关指标及其含义
|
||||
|
||||
| 指标名称 | 类型 | 含义 |
|
||||
| -------------------------------------- | ------- | -------------------- |
|
||||
| taos_adapter_requests_fail | counter | 失败的请求数 |
|
||||
|
@ -433,9 +459,11 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
#### taoskeeper
|
||||
|
||||
##### 监控信息支持的标签
|
||||
|
||||
- `identify`: 节点 endpoint
|
||||
|
||||
##### 相关指标及其含义
|
||||
|
||||
| 指标名称 | 类型 | 含义 |
|
||||
| ----------------------- | ----- | ------------------------------------- |
|
||||
| taos_keeper_monitor_cpu | gauge | taoskeeper CPU 使用率(取值范围 0~1) |
|
||||
|
@ -444,6 +472,7 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
#### 其他 taosd 集群监控项
|
||||
|
||||
##### taos_m_info_role
|
||||
|
||||
- **标签**:
|
||||
- `cluster_id`: 集群 id
|
||||
- `mnode_ep`: mnode 端点
|
||||
|
@ -453,6 +482,7 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
- **含义**: mnode 角色
|
||||
|
||||
##### taos_taos_sql_req_count
|
||||
|
||||
- **标签**:
|
||||
- `cluster_id`: 集群 id
|
||||
- `result`: 请求结果(取值范围: Success, Failed)
|
||||
|
@ -462,6 +492,7 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
- **含义**: SQL 请求数量
|
||||
|
||||
##### taos_taosd_sql_req_count
|
||||
|
||||
- **标签**:
|
||||
- `cluster_id`: 集群 id
|
||||
- `dnode_ep`: dnode 端点
|
||||
|
@ -474,6 +505,7 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
- **含义**: SQL 请求数量
|
||||
|
||||
##### taos_taosd_vgroups_info_status
|
||||
|
||||
- **标签**:
|
||||
- `cluster_id`: 集群 id
|
||||
- `database_name`: 数据库名称
|
||||
|
@ -482,6 +514,7 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
- **含义**: 虚拟组状态。 0 为 unsynced,表示没有leader选出;1 为 ready。
|
||||
|
||||
##### taos_taosd_vgroups_info_tables_num
|
||||
|
||||
- **标签**:
|
||||
- `cluster_id`: 集群 id
|
||||
- `database_name`: 数据库名称
|
||||
|
@ -490,6 +523,7 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
- **含义**: 虚拟组表数量
|
||||
|
||||
##### taos_taosd_vnodes_info_role
|
||||
|
||||
- **标签**:
|
||||
- `cluster_id`: 集群 id
|
||||
- `database_name`: 数据库名称
|
||||
|
@ -499,7 +533,6 @@ taos_cluster_info_first_ep_dnode_id{cluster_id="554014120921134497"} 1
|
|||
- **类型**: gauge
|
||||
- **含义**: 虚拟节点角色
|
||||
|
||||
|
||||
### 抽取配置
|
||||
|
||||
Prometheus 提供了 `scrape_configs` 配置如何从 endpoint 抽取监控数据,通常只需要修改 `static_configs` 中的 targets 配置为 taoskeeper 的 endpoint 地址,更多配置信息请参考 [Prometheus 配置文档](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config)。
|
||||
|
@ -521,8 +554,6 @@ scrape_configs:
|
|||
|
||||
在 Grafana Dashboard 菜单点击 `import`,dashboard ID 填写 `18587`,点击 `Load` 按钮即可导入 `TaosKeeper Prometheus Dashboard for 3.x` dashboard。
|
||||
|
||||
|
||||
|
||||
## taosKeeper 监控指标
|
||||
|
||||
taosKeeper 也会将自己采集的监控数据写入监控数据库,默认是 `log` 库,可以在 taoskeeper 配置文件中修改。
|
||||
|
|
|
@ -122,6 +122,7 @@ alter_database_option: {
|
|||
| KEEP value
|
||||
| WAL_RETENTION_PERIOD value
|
||||
| WAL_RETENTION_SIZE value
|
||||
| MINROWS value
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -188,23 +189,23 @@ TRIM DATABASE db_name;
|
|||
FLUSH DATABASE db_name;
|
||||
```
|
||||
|
||||
落盘内存中的数据。在关闭节点之前,执行这条命令可以避免重启后的数据回放,加速启动过程。
|
||||
落盘内存中的数据。在关闭节点之前,执行这条命令可以避免重启后的预写数据日志回放,加速启动过程。
|
||||
|
||||
## 调整VGROUP中VNODE的分布
|
||||
## 调整 VGROUP 中 VNODE 的分布
|
||||
|
||||
```sql
|
||||
REDISTRIBUTE VGROUP vgroup_no DNODE dnode_id1 [DNODE dnode_id2] [DNODE dnode_id3]
|
||||
```
|
||||
|
||||
按照给定的dnode列表,调整vgroup中的vnode分布。因为副本数目最大为3,所以最多输入3个dnode。
|
||||
按照给定的 dnode 列表,调整 vgroup 中的 vnode 分布。因为副本数目最大为 3,所以最多输入 3 个 dnode。
|
||||
|
||||
## 自动调整VGROUP中VNODE的分布
|
||||
## 自动调整 VGROUP 中 LEADER 的分布
|
||||
|
||||
```sql
|
||||
BALANCE VGROUP
|
||||
BALANCE VGROUP LEADER
|
||||
```
|
||||
|
||||
自动调整集群所有vgroup中的vnode分布,相当于在vnode级别对集群进行数据的负载均衡操作。
|
||||
触发集群所有 vgroup 中的 leader 重新选主,对集群各节点进行负载再均衡操作。
|
||||
|
||||
## 查看数据库工作状态
|
||||
|
||||
|
|
|
@ -1569,7 +1569,7 @@ COUNT({* | expr})
|
|||
ELAPSED(ts_primary_key [, time_unit])
|
||||
```
|
||||
|
||||
**功能说明**:elapsed函数表达了统计周期内连续的时间长度,和twa函数配合使用可以计算统计曲线下的面积。在通过INTERVAL子句指定窗口的情况下,统计在给定时间范围内的每个窗口内有数据覆盖的时间范围;如果没有INTERVAL子句,则返回整个给定时间范围内的有数据覆盖的时间范围。注意,ELAPSED返回的并不是时间范围的绝对值,而是绝对值除以time_unit所得到的单位个数。
|
||||
**功能说明**:elapsed 函数表达了统计周期内连续的时间长度,和 twa 函数配合使用可以计算统计曲线下的面积。在通过 INTERVAL 子句指定窗口的情况下,统计在给定时间范围内的每个窗口内有数据覆盖的时间范围;如果没有 INTERVAL 子句,则返回整个给定时间范围内的有数据覆盖的时间范围。注意,ELAPSED 返回的并不是时间范围的绝对值,而是绝对值除以 time_unit 所得到的单位个数。流计算仅在 FORCE_WINDOW_CLOSE 模式下支持该函数。
|
||||
|
||||
**返回结果类型**:DOUBLE。
|
||||
|
||||
|
@ -1578,15 +1578,15 @@ ELAPSED(ts_primary_key [, time_unit])
|
|||
**适用于**: 表,超级表,嵌套查询的外层查询
|
||||
|
||||
**说明**:
|
||||
- ts_primary_key参数只能是表的第一列,即 TIMESTAMP 类型的主键列。
|
||||
- 按time_unit参数指定的时间单位返回,最小是数据库的时间分辨率。time_unit 参数未指定时,以数据库的时间分辨率为时间单位。支持的时间单位 time_unit 如下:
|
||||
- ts_primary_key 参数只能是表的第一列,即 TIMESTAMP 类型的主键列。
|
||||
- 按 time_unit 参数指定的时间单位返回,最小是数据库的时间分辨率。time_unit 参数未指定时,以数据库的时间分辨率为时间单位。支持的时间单位 time_unit 如下:
|
||||
1b(纳秒), 1u(微秒),1a(毫秒),1s(秒),1m(分),1h(小时),1d(天), 1w(周)。
|
||||
- 可以和interval组合使用,返回每个时间窗口的时间戳差值。需要特别注意的是,除第一个时间窗口和最后一个时间窗口外,中间窗口的时间戳差值均为窗口长度。
|
||||
- order by asc/desc不影响差值的计算结果。
|
||||
- 对于超级表,需要和group by tbname子句组合使用,不可以直接使用。
|
||||
- 对于普通表,不支持和group by子句组合使用。
|
||||
- 对于嵌套查询,仅当内层查询会输出隐式时间戳列时有效。例如select elapsed(ts) from (select diff(value) from sub1)语句,diff函数会让内层查询输出隐式时间戳列,此为主键列,可以用于elapsed函数的第一个参数。相反,例如select elapsed(ts) from (select * from sub1) 语句,ts列输出到外层时已经没有了主键列的含义,无法使用elapsed函数。此外,elapsed函数作为一个与时间线强依赖的函数,形如select elapsed(ts) from (select diff(value) from st group by tbname)尽管会返回一条计算结果,但并无实际意义,这种用法后续也将被限制。
|
||||
- 不支持与leastsquares、diff、derivative、top、bottom、last_row、interp等函数混合使用。
|
||||
- 可以和 interval 组合使用,返回每个时间窗口的时间戳差值。需要特别注意的是,除第一个时间窗口和最后一个时间窗口外,中间窗口的时间戳差值均为窗口长度。
|
||||
- order by asc/desc 不影响差值的计算结果。
|
||||
- 对于超级表,需要和 group by tbname 子句组合使用,不可以直接使用。
|
||||
- 对于普通表,不支持和 group by 子句组合使用。
|
||||
- 对于嵌套查询,仅当内层查询会输出隐式时间戳列时有效。例如 select elapsed(ts) from (select diff(value) from sub1) 语句,diff 函数会让内层查询输出隐式时间戳列,此为主键列,可以用于 elapsed 函数的第一个参数。相反,例如 select elapsed(ts) from (select * from sub1) 语句,ts 列输出到外层时已经没有了主键列的含义,无法使用 elapsed 函数。此外,elapsed 函数作为一个与时间线强依赖的函数,形如 select elapsed(ts) from (select diff(value) from st group by tbname)尽 管会返回一条计算结果,但并无实际意义,这种用法后续也将被限制。
|
||||
- 不支持与 leastsquares、diff、derivative、top、bottom、last_row、interp 等函数混合使用。
|
||||
|
||||
|
||||
### LEASTSQUARES
|
||||
|
@ -1829,14 +1829,14 @@ ignore_null_values: {
|
|||
|
||||
- INTERP 用于在指定时间断面获取指定列的记录值,如果该时间断面不存在符合条件的行数据,那么会根据 FILL 参数的设定进行插值。
|
||||
- INTERP 的输入数据为指定列的数据,可以通过条件语句(where 子句)来对原始列数据进行过滤,如果没有指定过滤条件则输入为全部数据。
|
||||
- INTERP 需要同时与 RANGE,EVERY 和 FILL 关键字一起使用。
|
||||
- INTERP 的输出时间范围根据 RANGE(timestamp1, timestamp2)字段来指定,需满足 timestamp1 \<= timestamp2。其中 timestamp1 为输出时间范围的起始值,即如果 timestamp1 时刻符合插值条件则 timestamp1 为输出的第一条记录,timestamp2 为输出时间范围的结束值,即输出的最后一条记录的 timestamp 不能大于 timestamp2。
|
||||
- INTERP SQL 查询需要同时与 RANGE,EVERY 和 FILL 关键字一起使用;流计算不能使用 RANGE,需要 EVERY 和 FILL 关键字一起使用。
|
||||
- INTERP 的输出时间范围根据 RANGE(timestamp1, timestamp2) 字段来指定,需满足 timestamp1 \<= timestamp2。其中 timestamp1 为输出时间范围的起始值,即如果 timestamp1 时刻符合插值条件则 timestamp1 为输出的第一条记录,timestamp2 为输出时间范围的结束值,即输出的最后一条记录的 timestamp 不能大于 timestamp2。
|
||||
- INTERP 根据 EVERY(time_unit) 字段来确定输出时间范围内的结果条数,即从 timestamp1 开始每隔固定长度的时间(time_unit 值)进行插值,time_unit 可取值时间单位:1a(毫秒),1s(秒),1m(分),1h(小时),1d(天),1w(周)。例如 EVERY(500a) 将对于指定数据每500毫秒间隔进行一次插值.
|
||||
- INTERP 根据 FILL 字段来决定在每个符合输出条件的时刻如何进行插值。关于 FILL 子句如何使用请参考 [FILL 子句](../distinguished/#fill-子句)
|
||||
- INTERP 可以在 RANGE 字段中只指定唯一的时间戳对单个时间点进行插值,在这种情况下,EVERY 字段可以省略。例如:SELECT INTERP(col) FROM tb RANGE('2023-01-01 00:00:00') FILL(linear).
|
||||
- INTERP 作用于超级表时, 会将该超级表下的所有子表数据按照主键列排序后进行插值计算,也可以搭配 PARTITION BY tbname 使用,将结果强制规约到单个时间线。
|
||||
- INTERP 可以与伪列 _irowts 一起使用,返回插值点所对应的时间戳(3.0.2.0版本以后支持)。
|
||||
- INTERP 可以与伪列 _isfilled 一起使用,显示返回结果是否为原始记录或插值算法产生的数据(3.0.3.0版本以后支持)。
|
||||
- INTERP 可以与伪列 _irowts 一起使用,返回插值点所对应的时间戳(3.0.2.0 版本以后支持)。
|
||||
- INTERP 可以与伪列 _isfilled 一起使用,显示返回结果是否为原始记录或插值算法产生的数据(3.0.3.0 版本以后支持)。
|
||||
- INTERP 对于带复合主键的表的查询,若存在相同时间戳的数据,则只有对应的复合主键最小的数据参与运算。
|
||||
|
||||
### LAST
|
||||
|
@ -2180,7 +2180,7 @@ STATEDURATION(expr, oper, val, unit)
|
|||
TWA(expr)
|
||||
```
|
||||
|
||||
**功能说明**:时间加权平均函数。统计表中某列在一段时间内的时间加权平均。对于存在复合主键的表的查询,若时间戳相同的数据存在多条,则只有对应的复合主键最小的数据参与运算。
|
||||
**功能说明**:时间加权平均函数。统计表中某列在一段时间内的时间加权平均。对于存在复合主键的表的查询,若时间戳相同的数据存在多条,则只有对应的复合主键最小的数据参与运算。流计算仅在 FORCE_WINDOW_CLOSE 模式下支持该函数。
|
||||
|
||||
**返回数据类型**:DOUBLE。
|
||||
|
||||
|
|
|
@ -143,13 +143,14 @@ SELECT * from information_schema.`ins_streams`;
|
|||
|
||||
在创建流时,可以通过 TRIGGER 指令指定流式计算的触发模式。
|
||||
|
||||
对于非窗口计算,流式计算的触发是实时的;对于窗口计算,目前提供 3 种触发模式,默认为 WINDOW_CLOSE:
|
||||
对于非窗口计算,流式计算的触发是实时的;对于窗口计算,目前提供 4 种触发模式,默认为 WINDOW_CLOSE:
|
||||
|
||||
1. AT_ONCE:写入立即触发
|
||||
|
||||
2. WINDOW_CLOSE:窗口关闭时触发(窗口关闭由事件时间决定,可配合 watermark 使用)
|
||||
|
||||
3. MAX_DELAY time:若窗口关闭,则触发计算。若窗口未关闭,且未关闭时长超过 max delay 指定的时间,则触发计算。
|
||||
4. FORCE_WINDOW_CLOSE:以操作系统当前时间为准,只计算当前关闭窗口的结果,并推送出去。窗口只会在被关闭的时刻计算一次,后续不会再重复计算。该模式当前只支持 INTERVAL 窗口(不支持滑动);FILL_HISTORY 必须为 0,IGNORE EXPIRED 必须为 1,IGNORE UPDATE 必须为 1;FILL 只支持 PREV 、NULL、NONE、VALUE。
|
||||
|
||||
由于窗口关闭是由事件时间决定的,如事件流中断、或持续延迟,则事件时间无法更新,可能导致无法得到最新的计算结果。
|
||||
|
||||
|
|
|
@ -154,6 +154,8 @@ typedef enum EStreamType {
|
|||
STREAM_TRANS_STATE,
|
||||
STREAM_MID_RETRIEVE,
|
||||
STREAM_PARTITION_DELETE_DATA,
|
||||
STREAM_GET_RESULT,
|
||||
STREAM_DROP_CHILD_TABLE,
|
||||
} EStreamType;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
@ -383,6 +385,10 @@ typedef struct STUidTagInfo {
|
|||
#define TABLE_NAME_COLUMN_INDEX 6
|
||||
#define PRIMARY_KEY_COLUMN_INDEX 7
|
||||
|
||||
//steam get result block column
|
||||
#define DATA_TS_COLUMN_INDEX 0
|
||||
#define DATA_VERSION_COLUMN_INDEX 1
|
||||
|
||||
// stream create table block column
|
||||
#define UD_TABLE_NAME_COLUMN_INDEX 0
|
||||
#define UD_GROUPID_COLUMN_INDEX 1
|
||||
|
@ -396,6 +402,8 @@ int32_t dumpConfToDataBlock(SSDataBlock* pBlock, int32_t startCol);
|
|||
#define TSMA_RES_STB_EXTRA_COLUMN_NUM 4 // 3 columns: _wstart, _wend, _wduration, 1 tag: tbname
|
||||
|
||||
static inline bool isTsmaResSTb(const char* stbName) {
|
||||
static bool showTsmaTables = false;
|
||||
if (showTsmaTables) return false;
|
||||
const char* pos = strstr(stbName, TSMA_RES_STB_POSTFIX);
|
||||
if (pos && strlen(stbName) == (pos - stbName) + strlen(TSMA_RES_STB_POSTFIX)) {
|
||||
return true;
|
||||
|
|
|
@ -188,7 +188,6 @@ extern int32_t tsMaxRetryWaitTime;
|
|||
extern bool tsUseAdapter;
|
||||
extern int32_t tsMetaCacheMaxSize;
|
||||
extern int32_t tsSlowLogThreshold;
|
||||
extern int32_t tsSlowLogThresholdTest;
|
||||
extern char tsSlowLogExceptDb[];
|
||||
extern int32_t tsSlowLogScope;
|
||||
extern int32_t tsSlowLogMaxLen;
|
||||
|
|
|
@ -467,9 +467,11 @@ typedef enum ENodeType {
|
|||
QUERY_NODE_PHYSICAL_PLAN_MERGE_COUNT,
|
||||
QUERY_NODE_PHYSICAL_PLAN_STREAM_COUNT,
|
||||
QUERY_NODE_PHYSICAL_PLAN_STREAM_MID_INTERVAL,
|
||||
QUERY_NODE_PHYSICAL_PLAN_STREAM_CONTINUE_INTERVAL,
|
||||
QUERY_NODE_PHYSICAL_PLAN_MERGE_ANOMALY,
|
||||
QUERY_NODE_PHYSICAL_PLAN_STREAM_ANOMALY,
|
||||
QUERY_NODE_PHYSICAL_PLAN_FORECAST_FUNC,
|
||||
QUERY_NODE_PHYSICAL_PLAN_STREAM_INTERP_FUNC,
|
||||
} ENodeType;
|
||||
|
||||
typedef struct {
|
||||
|
@ -674,7 +676,7 @@ typedef struct {
|
|||
int32_t tsSlowLogThreshold;
|
||||
int32_t tsSlowLogMaxLen;
|
||||
int32_t tsSlowLogScope;
|
||||
int32_t tsSlowLogThresholdTest;
|
||||
int32_t tsSlowLogThresholdTest; //Obsolete
|
||||
char tsSlowLogExceptDb[TSDB_DB_NAME_LEN];
|
||||
} SMonitorParas;
|
||||
|
||||
|
@ -2305,6 +2307,7 @@ typedef struct {
|
|||
typedef struct {
|
||||
SExplainRsp rsp;
|
||||
uint64_t qId;
|
||||
uint64_t cId;
|
||||
uint64_t tId;
|
||||
int64_t rId;
|
||||
int32_t eId;
|
||||
|
@ -2658,6 +2661,7 @@ typedef struct SSubQueryMsg {
|
|||
SMsgHead header;
|
||||
uint64_t sId;
|
||||
uint64_t queryId;
|
||||
uint64_t clientId;
|
||||
uint64_t taskId;
|
||||
int64_t refId;
|
||||
int32_t execId;
|
||||
|
@ -2687,6 +2691,7 @@ typedef struct {
|
|||
SMsgHead header;
|
||||
uint64_t sId;
|
||||
uint64_t queryId;
|
||||
uint64_t clientId;
|
||||
uint64_t taskId;
|
||||
int32_t execId;
|
||||
} SQueryContinueReq;
|
||||
|
@ -2721,6 +2726,7 @@ typedef struct {
|
|||
SMsgHead header;
|
||||
uint64_t sId;
|
||||
uint64_t queryId;
|
||||
uint64_t clientId;
|
||||
uint64_t taskId;
|
||||
int32_t execId;
|
||||
SOperatorParam* pOpParam;
|
||||
|
@ -2736,6 +2742,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
uint64_t queryId;
|
||||
uint64_t clientId;
|
||||
uint64_t taskId;
|
||||
int64_t refId;
|
||||
int32_t execId;
|
||||
|
@ -2782,6 +2789,7 @@ typedef struct {
|
|||
SMsgHead header;
|
||||
uint64_t sId;
|
||||
uint64_t queryId;
|
||||
uint64_t clientId;
|
||||
uint64_t taskId;
|
||||
int64_t refId;
|
||||
int32_t execId;
|
||||
|
@ -2795,6 +2803,7 @@ typedef struct {
|
|||
SMsgHead header;
|
||||
uint64_t sId;
|
||||
uint64_t queryId;
|
||||
uint64_t clientId;
|
||||
uint64_t taskId;
|
||||
int64_t refId;
|
||||
int32_t execId;
|
||||
|
@ -2811,6 +2820,7 @@ typedef struct {
|
|||
SMsgHead header;
|
||||
uint64_t sId;
|
||||
uint64_t queryId;
|
||||
uint64_t clientId;
|
||||
uint64_t taskId;
|
||||
int64_t refId;
|
||||
int32_t execId;
|
||||
|
@ -2827,9 +2837,11 @@ typedef struct {
|
|||
int32_t code;
|
||||
} STaskDropRsp;
|
||||
|
||||
#define STREAM_TRIGGER_AT_ONCE 1
|
||||
#define STREAM_TRIGGER_WINDOW_CLOSE 2
|
||||
#define STREAM_TRIGGER_MAX_DELAY 3
|
||||
#define STREAM_TRIGGER_AT_ONCE 1
|
||||
#define STREAM_TRIGGER_WINDOW_CLOSE 2
|
||||
#define STREAM_TRIGGER_MAX_DELAY 3
|
||||
#define STREAM_TRIGGER_FORCE_WINDOW_CLOSE 4
|
||||
|
||||
#define STREAM_DEFAULT_IGNORE_EXPIRED 1
|
||||
#define STREAM_FILL_HISTORY_ON 1
|
||||
#define STREAM_FILL_HISTORY_OFF 0
|
||||
|
@ -3216,6 +3228,7 @@ int tDecodeSVCreateTbBatchRsp(SDecoder* pCoder, SVCreateTbBatchRsp* pRsp);
|
|||
typedef struct {
|
||||
char* name;
|
||||
uint64_t suid; // for tmq in wal format
|
||||
int64_t uid;
|
||||
int8_t igNotExists;
|
||||
} SVDropTbReq;
|
||||
|
||||
|
@ -4257,6 +4270,7 @@ typedef struct {
|
|||
SMsgHead header;
|
||||
uint64_t sId;
|
||||
uint64_t queryId;
|
||||
uint64_t clientId;
|
||||
uint64_t taskId;
|
||||
uint32_t sqlLen;
|
||||
uint32_t phyLen;
|
||||
|
|
|
@ -62,7 +62,8 @@ static FORCE_INLINE int64_t taosGetTimestampToday(int32_t precision) {
|
|||
int64_t factor = (precision == TSDB_TIME_PRECISION_MILLI) ? 1000
|
||||
: (precision == TSDB_TIME_PRECISION_MICRO) ? 1000000
|
||||
: 1000000000;
|
||||
time_t t = taosTime(NULL);
|
||||
time_t t;
|
||||
(void) taosTime(&t);
|
||||
struct tm tm;
|
||||
(void) taosLocalTime(&t, &tm, NULL, 0);
|
||||
tm.tm_hour = 0;
|
||||
|
|
|
@ -16,394 +16,7 @@
|
|||
#ifndef _TD_COMMON_TOKEN_H_
|
||||
#define _TD_COMMON_TOKEN_H_
|
||||
|
||||
#define TK_OR 1
|
||||
#define TK_AND 2
|
||||
#define TK_UNION 3
|
||||
#define TK_ALL 4
|
||||
#define TK_MINUS 5
|
||||
#define TK_EXCEPT 6
|
||||
#define TK_INTERSECT 7
|
||||
#define TK_NK_BITAND 8
|
||||
#define TK_NK_BITOR 9
|
||||
#define TK_NK_LSHIFT 10
|
||||
#define TK_NK_RSHIFT 11
|
||||
#define TK_NK_PLUS 12
|
||||
#define TK_NK_MINUS 13
|
||||
#define TK_NK_STAR 14
|
||||
#define TK_NK_SLASH 15
|
||||
#define TK_NK_REM 16
|
||||
#define TK_NK_CONCAT 17
|
||||
#define TK_CREATE 18
|
||||
#define TK_ACCOUNT 19
|
||||
#define TK_NK_ID 20
|
||||
#define TK_PASS 21
|
||||
#define TK_NK_STRING 22
|
||||
#define TK_ALTER 23
|
||||
#define TK_PPS 24
|
||||
#define TK_TSERIES 25
|
||||
#define TK_STORAGE 26
|
||||
#define TK_STREAMS 27
|
||||
#define TK_QTIME 28
|
||||
#define TK_DBS 29
|
||||
#define TK_USERS 30
|
||||
#define TK_CONNS 31
|
||||
#define TK_STATE 32
|
||||
#define TK_NK_COMMA 33
|
||||
#define TK_HOST 34
|
||||
#define TK_IS_IMPORT 35
|
||||
#define TK_NK_INTEGER 36
|
||||
#define TK_CREATEDB 37
|
||||
#define TK_USER 38
|
||||
#define TK_ENABLE 39
|
||||
#define TK_SYSINFO 40
|
||||
#define TK_ADD 41
|
||||
#define TK_DROP 42
|
||||
#define TK_GRANT 43
|
||||
#define TK_ON 44
|
||||
#define TK_TO 45
|
||||
#define TK_REVOKE 46
|
||||
#define TK_FROM 47
|
||||
#define TK_SUBSCRIBE 48
|
||||
#define TK_READ 49
|
||||
#define TK_WRITE 50
|
||||
#define TK_NK_DOT 51
|
||||
#define TK_WITH 52
|
||||
#define TK_ENCRYPT_KEY 53
|
||||
#define TK_ANODE 54
|
||||
#define TK_UPDATE 55
|
||||
#define TK_ANODES 56
|
||||
#define TK_DNODE 57
|
||||
#define TK_PORT 58
|
||||
#define TK_DNODES 59
|
||||
#define TK_RESTORE 60
|
||||
#define TK_NK_IPTOKEN 61
|
||||
#define TK_FORCE 62
|
||||
#define TK_UNSAFE 63
|
||||
#define TK_CLUSTER 64
|
||||
#define TK_LOCAL 65
|
||||
#define TK_QNODE 66
|
||||
#define TK_BNODE 67
|
||||
#define TK_SNODE 68
|
||||
#define TK_MNODE 69
|
||||
#define TK_VNODE 70
|
||||
#define TK_DATABASE 71
|
||||
#define TK_USE 72
|
||||
#define TK_FLUSH 73
|
||||
#define TK_TRIM 74
|
||||
#define TK_S3MIGRATE 75
|
||||
#define TK_COMPACT 76
|
||||
#define TK_IF 77
|
||||
#define TK_NOT 78
|
||||
#define TK_EXISTS 79
|
||||
#define TK_BUFFER 80
|
||||
#define TK_CACHEMODEL 81
|
||||
#define TK_CACHESIZE 82
|
||||
#define TK_COMP 83
|
||||
#define TK_DURATION 84
|
||||
#define TK_NK_VARIABLE 85
|
||||
#define TK_MAXROWS 86
|
||||
#define TK_MINROWS 87
|
||||
#define TK_KEEP 88
|
||||
#define TK_PAGES 89
|
||||
#define TK_PAGESIZE 90
|
||||
#define TK_TSDB_PAGESIZE 91
|
||||
#define TK_PRECISION 92
|
||||
#define TK_REPLICA 93
|
||||
#define TK_VGROUPS 94
|
||||
#define TK_SINGLE_STABLE 95
|
||||
#define TK_RETENTIONS 96
|
||||
#define TK_SCHEMALESS 97
|
||||
#define TK_WAL_LEVEL 98
|
||||
#define TK_WAL_FSYNC_PERIOD 99
|
||||
#define TK_WAL_RETENTION_PERIOD 100
|
||||
#define TK_WAL_RETENTION_SIZE 101
|
||||
#define TK_WAL_ROLL_PERIOD 102
|
||||
#define TK_WAL_SEGMENT_SIZE 103
|
||||
#define TK_STT_TRIGGER 104
|
||||
#define TK_TABLE_PREFIX 105
|
||||
#define TK_TABLE_SUFFIX 106
|
||||
#define TK_S3_CHUNKPAGES 107
|
||||
#define TK_S3_KEEPLOCAL 108
|
||||
#define TK_S3_COMPACT 109
|
||||
#define TK_KEEP_TIME_OFFSET 110
|
||||
#define TK_ENCRYPT_ALGORITHM 111
|
||||
#define TK_NK_COLON 112
|
||||
#define TK_BWLIMIT 113
|
||||
#define TK_START 114
|
||||
#define TK_TIMESTAMP 115
|
||||
#define TK_END 116
|
||||
#define TK_TABLE 117
|
||||
#define TK_NK_LP 118
|
||||
#define TK_NK_RP 119
|
||||
#define TK_USING 120
|
||||
#define TK_FILE 121
|
||||
#define TK_STABLE 122
|
||||
#define TK_COLUMN 123
|
||||
#define TK_MODIFY 124
|
||||
#define TK_RENAME 125
|
||||
#define TK_TAG 126
|
||||
#define TK_SET 127
|
||||
#define TK_NK_EQ 128
|
||||
#define TK_TAGS 129
|
||||
#define TK_BOOL 130
|
||||
#define TK_TINYINT 131
|
||||
#define TK_SMALLINT 132
|
||||
#define TK_INT 133
|
||||
#define TK_INTEGER 134
|
||||
#define TK_BIGINT 135
|
||||
#define TK_FLOAT 136
|
||||
#define TK_DOUBLE 137
|
||||
#define TK_BINARY 138
|
||||
#define TK_NCHAR 139
|
||||
#define TK_UNSIGNED 140
|
||||
#define TK_JSON 141
|
||||
#define TK_VARCHAR 142
|
||||
#define TK_MEDIUMBLOB 143
|
||||
#define TK_BLOB 144
|
||||
#define TK_VARBINARY 145
|
||||
#define TK_GEOMETRY 146
|
||||
#define TK_DECIMAL 147
|
||||
#define TK_COMMENT 148
|
||||
#define TK_MAX_DELAY 149
|
||||
#define TK_WATERMARK 150
|
||||
#define TK_ROLLUP 151
|
||||
#define TK_TTL 152
|
||||
#define TK_SMA 153
|
||||
#define TK_DELETE_MARK 154
|
||||
#define TK_FIRST 155
|
||||
#define TK_LAST 156
|
||||
#define TK_SHOW 157
|
||||
#define TK_FULL 158
|
||||
#define TK_PRIVILEGES 159
|
||||
#define TK_DATABASES 160
|
||||
#define TK_TABLES 161
|
||||
#define TK_STABLES 162
|
||||
#define TK_MNODES 163
|
||||
#define TK_QNODES 164
|
||||
#define TK_ARBGROUPS 165
|
||||
#define TK_FUNCTIONS 166
|
||||
#define TK_INDEXES 167
|
||||
#define TK_ACCOUNTS 168
|
||||
#define TK_APPS 169
|
||||
#define TK_CONNECTIONS 170
|
||||
#define TK_LICENCES 171
|
||||
#define TK_GRANTS 172
|
||||
#define TK_LOGS 173
|
||||
#define TK_MACHINES 174
|
||||
#define TK_ENCRYPTIONS 175
|
||||
#define TK_QUERIES 176
|
||||
#define TK_SCORES 177
|
||||
#define TK_TOPICS 178
|
||||
#define TK_VARIABLES 179
|
||||
#define TK_BNODES 180
|
||||
#define TK_SNODES 181
|
||||
#define TK_TRANSACTIONS 182
|
||||
#define TK_DISTRIBUTED 183
|
||||
#define TK_CONSUMERS 184
|
||||
#define TK_SUBSCRIPTIONS 185
|
||||
#define TK_VNODES 186
|
||||
#define TK_ALIVE 187
|
||||
#define TK_VIEWS 188
|
||||
#define TK_VIEW 189
|
||||
#define TK_COMPACTS 190
|
||||
#define TK_NORMAL 191
|
||||
#define TK_CHILD 192
|
||||
#define TK_LIKE 193
|
||||
#define TK_TBNAME 194
|
||||
#define TK_QTAGS 195
|
||||
#define TK_AS 196
|
||||
#define TK_SYSTEM 197
|
||||
#define TK_TSMA 198
|
||||
#define TK_INTERVAL 199
|
||||
#define TK_RECURSIVE 200
|
||||
#define TK_TSMAS 201
|
||||
#define TK_FUNCTION 202
|
||||
#define TK_INDEX 203
|
||||
#define TK_COUNT 204
|
||||
#define TK_LAST_ROW 205
|
||||
#define TK_META 206
|
||||
#define TK_ONLY 207
|
||||
#define TK_TOPIC 208
|
||||
#define TK_CONSUMER 209
|
||||
#define TK_GROUP 210
|
||||
#define TK_DESC 211
|
||||
#define TK_DESCRIBE 212
|
||||
#define TK_RESET 213
|
||||
#define TK_QUERY 214
|
||||
#define TK_CACHE 215
|
||||
#define TK_EXPLAIN 216
|
||||
#define TK_ANALYZE 217
|
||||
#define TK_VERBOSE 218
|
||||
#define TK_NK_BOOL 219
|
||||
#define TK_RATIO 220
|
||||
#define TK_NK_FLOAT 221
|
||||
#define TK_OUTPUTTYPE 222
|
||||
#define TK_AGGREGATE 223
|
||||
#define TK_BUFSIZE 224
|
||||
#define TK_LANGUAGE 225
|
||||
#define TK_REPLACE 226
|
||||
#define TK_STREAM 227
|
||||
#define TK_INTO 228
|
||||
#define TK_PAUSE 229
|
||||
#define TK_RESUME 230
|
||||
#define TK_PRIMARY 231
|
||||
#define TK_KEY 232
|
||||
#define TK_TRIGGER 233
|
||||
#define TK_AT_ONCE 234
|
||||
#define TK_WINDOW_CLOSE 235
|
||||
#define TK_IGNORE 236
|
||||
#define TK_EXPIRED 237
|
||||
#define TK_FILL_HISTORY 238
|
||||
#define TK_SUBTABLE 239
|
||||
#define TK_UNTREATED 240
|
||||
#define TK_KILL 241
|
||||
#define TK_CONNECTION 242
|
||||
#define TK_TRANSACTION 243
|
||||
#define TK_BALANCE 244
|
||||
#define TK_VGROUP 245
|
||||
#define TK_LEADER 246
|
||||
#define TK_MERGE 247
|
||||
#define TK_REDISTRIBUTE 248
|
||||
#define TK_SPLIT 249
|
||||
#define TK_DELETE 250
|
||||
#define TK_INSERT 251
|
||||
#define TK_NK_BIN 252
|
||||
#define TK_NK_HEX 253
|
||||
#define TK_NULL 254
|
||||
#define TK_NK_QUESTION 255
|
||||
#define TK_NK_ALIAS 256
|
||||
#define TK_NK_ARROW 257
|
||||
#define TK_ROWTS 258
|
||||
#define TK_QSTART 259
|
||||
#define TK_QEND 260
|
||||
#define TK_QDURATION 261
|
||||
#define TK_WSTART 262
|
||||
#define TK_WEND 263
|
||||
#define TK_WDURATION 264
|
||||
#define TK_IROWTS 265
|
||||
#define TK_ISFILLED 266
|
||||
#define TK_FLOW 267
|
||||
#define TK_FHIGH 268
|
||||
#define TK_FROWTS 269
|
||||
#define TK_CAST 270
|
||||
#define TK_POSITION 271
|
||||
#define TK_IN 272
|
||||
#define TK_FOR 273
|
||||
#define TK_NOW 274
|
||||
#define TK_TODAY 275
|
||||
#define TK_RAND 276
|
||||
#define TK_SUBSTR 277
|
||||
#define TK_SUBSTRING 278
|
||||
#define TK_BOTH 279
|
||||
#define TK_TRAILING 280
|
||||
#define TK_LEADING 281
|
||||
#define TK_TIMEZONE 282
|
||||
#define TK_CLIENT_VERSION 283
|
||||
#define TK_SERVER_VERSION 284
|
||||
#define TK_SERVER_STATUS 285
|
||||
#define TK_CURRENT_USER 286
|
||||
#define TK_PI 287
|
||||
#define TK_CASE 288
|
||||
#define TK_WHEN 289
|
||||
#define TK_THEN 290
|
||||
#define TK_ELSE 291
|
||||
#define TK_BETWEEN 292
|
||||
#define TK_IS 293
|
||||
#define TK_NK_LT 294
|
||||
#define TK_NK_GT 295
|
||||
#define TK_NK_LE 296
|
||||
#define TK_NK_GE 297
|
||||
#define TK_NK_NE 298
|
||||
#define TK_MATCH 299
|
||||
#define TK_NMATCH 300
|
||||
#define TK_CONTAINS 301
|
||||
#define TK_JOIN 302
|
||||
#define TK_INNER 303
|
||||
#define TK_LEFT 304
|
||||
#define TK_RIGHT 305
|
||||
#define TK_OUTER 306
|
||||
#define TK_SEMI 307
|
||||
#define TK_ANTI 308
|
||||
#define TK_ASOF 309
|
||||
#define TK_WINDOW 310
|
||||
#define TK_WINDOW_OFFSET 311
|
||||
#define TK_JLIMIT 312
|
||||
#define TK_SELECT 313
|
||||
#define TK_NK_HINT 314
|
||||
#define TK_DISTINCT 315
|
||||
#define TK_WHERE 316
|
||||
#define TK_PARTITION 317
|
||||
#define TK_BY 318
|
||||
#define TK_SESSION 319
|
||||
#define TK_STATE_WINDOW 320
|
||||
#define TK_EVENT_WINDOW 321
|
||||
#define TK_COUNT_WINDOW 322
|
||||
#define TK_ANOMALY_WINDOW 323
|
||||
#define TK_SLIDING 324
|
||||
#define TK_FILL 325
|
||||
#define TK_VALUE 326
|
||||
#define TK_VALUE_F 327
|
||||
#define TK_NONE 328
|
||||
#define TK_PREV 329
|
||||
#define TK_NULL_F 330
|
||||
#define TK_LINEAR 331
|
||||
#define TK_NEXT 332
|
||||
#define TK_HAVING 333
|
||||
#define TK_RANGE 334
|
||||
#define TK_EVERY 335
|
||||
#define TK_ORDER 336
|
||||
#define TK_SLIMIT 337
|
||||
#define TK_SOFFSET 338
|
||||
#define TK_LIMIT 339
|
||||
#define TK_OFFSET 340
|
||||
#define TK_ASC 341
|
||||
#define TK_NULLS 342
|
||||
#define TK_ABORT 343
|
||||
#define TK_AFTER 344
|
||||
#define TK_ATTACH 345
|
||||
#define TK_BEFORE 346
|
||||
#define TK_BEGIN 347
|
||||
#define TK_BITAND 348
|
||||
#define TK_BITNOT 349
|
||||
#define TK_BITOR 350
|
||||
#define TK_BLOCKS 351
|
||||
#define TK_CHANGE 352
|
||||
#define TK_COMMA 353
|
||||
#define TK_CONCAT 354
|
||||
#define TK_CONFLICT 355
|
||||
#define TK_COPY 356
|
||||
#define TK_DEFERRED 357
|
||||
#define TK_DELIMITERS 358
|
||||
#define TK_DETACH 359
|
||||
#define TK_DIVIDE 360
|
||||
#define TK_DOT 361
|
||||
#define TK_EACH 362
|
||||
#define TK_FAIL 363
|
||||
#define TK_GLOB 364
|
||||
#define TK_ID 365
|
||||
#define TK_IMMEDIATE 366
|
||||
#define TK_IMPORT 367
|
||||
#define TK_INITIALLY 368
|
||||
#define TK_INSTEAD 369
|
||||
#define TK_ISNULL 370
|
||||
#define TK_MODULES 371
|
||||
#define TK_NK_BITNOT 372
|
||||
#define TK_NK_SEMI 373
|
||||
#define TK_NOTNULL 374
|
||||
#define TK_OF 375
|
||||
#define TK_PLUS 376
|
||||
#define TK_PRIVILEGE 377
|
||||
#define TK_RAISE 378
|
||||
#define TK_RESTRICT 379
|
||||
#define TK_ROW 380
|
||||
#define TK_STAR 381
|
||||
#define TK_STATEMENT 382
|
||||
#define TK_STRICT 383
|
||||
#define TK_STRING 384
|
||||
#define TK_TIMES 385
|
||||
#define TK_VALUES 386
|
||||
#define TK_VARIABLE 387
|
||||
#define TK_WAL 388
|
||||
#include "ttokenauto.h"
|
||||
|
||||
#define TK_NK_SPACE 600
|
||||
#define TK_NK_COMMENT 601
|
||||
|
|
|
@ -31,7 +31,7 @@ typedef void* DataSinkHandle;
|
|||
struct SRpcMsg;
|
||||
struct SSubplan;
|
||||
|
||||
typedef int32_t (*localFetchFp)(void*, uint64_t, uint64_t, uint64_t, int64_t, int32_t, void**, SArray*);
|
||||
typedef int32_t (*localFetchFp)(void*, uint64_t, uint64_t, uint64_t, uint64_t, int64_t, int32_t, void**, SArray*);
|
||||
|
||||
typedef struct {
|
||||
void* handle;
|
||||
|
@ -222,8 +222,8 @@ int32_t qStreamSourceScanParamForHistoryScanStep2(qTaskInfo_t tinfo, SVersionRan
|
|||
int32_t qStreamRecoverFinish(qTaskInfo_t tinfo);
|
||||
bool qStreamScanhistoryFinished(qTaskInfo_t tinfo);
|
||||
int32_t qStreamInfoResetTimewindowFilter(qTaskInfo_t tinfo);
|
||||
void resetTaskInfo(qTaskInfo_t tinfo);
|
||||
|
||||
void qResetTaskInfoCode(qTaskInfo_t tinfo);
|
||||
int32_t qGetStreamIntervalExecInfo(qTaskInfo_t tinfo, int64_t* pWaterMark, SInterval* pInterval, STimeWindow* pLastWindow);
|
||||
int32_t qStreamOperatorReleaseState(qTaskInfo_t tInfo);
|
||||
int32_t qStreamOperatorReloadState(qTaskInfo_t tInfo);
|
||||
|
||||
|
|
|
@ -39,8 +39,10 @@ extern "C" {
|
|||
#define META_READER_LOCK 0x0
|
||||
#define META_READER_NOLOCK 0x1
|
||||
|
||||
#define STREAM_STATE_BUFF_HASH 1
|
||||
#define STREAM_STATE_BUFF_SORT 2
|
||||
#define STREAM_STATE_BUFF_HASH 1
|
||||
#define STREAM_STATE_BUFF_SORT 2
|
||||
#define STREAM_STATE_BUFF_HASH_SORT 3
|
||||
#define STREAM_STATE_BUFF_HASH_SEARCH 4
|
||||
|
||||
typedef struct SMeta SMeta;
|
||||
typedef TSKEY (*GetTsFun)(void*);
|
||||
|
@ -325,18 +327,24 @@ typedef struct {
|
|||
int64_t number;
|
||||
void* pStreamFileState;
|
||||
int32_t buffIndex;
|
||||
int32_t hashIter;
|
||||
void* pHashData;
|
||||
int64_t minGpId;
|
||||
} SStreamStateCur;
|
||||
|
||||
typedef struct SStateStore {
|
||||
int32_t (*streamStatePutParName)(SStreamState* pState, int64_t groupId, const char* tbname);
|
||||
int32_t (*streamStateGetParName)(SStreamState* pState, int64_t groupId, void** pVal, bool onlyCache,
|
||||
int32_t* pWinCode);
|
||||
int32_t (*streamStateDeleteParName)(SStreamState* pState, int64_t groupId);
|
||||
|
||||
int32_t (*streamStateAddIfNotExist)(SStreamState* pState, const SWinKey* key, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
void (*streamStateReleaseBuf)(SStreamState* pState, void* pVal, bool used);
|
||||
void (*streamStateClearBuff)(SStreamState* pState, void* pVal);
|
||||
void (*streamStateFreeVal)(void* val);
|
||||
int32_t (*streamStateGetPrev)(SStreamState* pState, const SWinKey* pKey, SWinKey* pResKey, void** pVal,
|
||||
int32_t* pVLen, int32_t* pWinCode);
|
||||
|
||||
int32_t (*streamStatePut)(SStreamState* pState, const SWinKey* key, const void* value, int32_t vLen);
|
||||
int32_t (*streamStateGet)(SStreamState* pState, const SWinKey* key, void** pVal, int32_t* pVLen, int32_t* pWinCode);
|
||||
|
@ -349,8 +357,15 @@ typedef struct SStateStore {
|
|||
int32_t (*streamStateGetInfo)(SStreamState* pState, void* pKey, int32_t keyLen, void** pVal, int32_t* pLen);
|
||||
|
||||
int32_t (*streamStateFillPut)(SStreamState* pState, const SWinKey* key, const void* value, int32_t vLen);
|
||||
int32_t (*streamStateFillGet)(SStreamState* pState, const SWinKey* key, void** pVal, int32_t* pVLen);
|
||||
int32_t (*streamStateFillGet)(SStreamState* pState, const SWinKey* key, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
int32_t (*streamStateFillAddIfNotExist)(SStreamState* pState, const SWinKey* key, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
void (*streamStateFillDel)(SStreamState* pState, const SWinKey* key);
|
||||
int32_t (*streamStateFillGetNext)(SStreamState* pState, const SWinKey* pKey, SWinKey* pResKey, void** pVal,
|
||||
int32_t* pVLen, int32_t* pWinCode);
|
||||
int32_t (*streamStateFillGetPrev)(SStreamState* pState, const SWinKey* pKey, SWinKey* pResKey, void** pVal,
|
||||
int32_t* pVLen, int32_t* pWinCode);
|
||||
|
||||
void (*streamStateCurNext)(SStreamState* pState, SStreamStateCur* pCur);
|
||||
void (*streamStateCurPrev)(SStreamState* pState, SStreamStateCur* pCur);
|
||||
|
@ -361,9 +376,12 @@ typedef struct SStateStore {
|
|||
SStreamStateCur* (*streamStateFillSeekKeyPrev)(SStreamState* pState, const SWinKey* key);
|
||||
void (*streamStateFreeCur)(SStreamStateCur* pCur);
|
||||
|
||||
int32_t (*streamStateGetGroupKVByCur)(SStreamStateCur* pCur, SWinKey* pKey, const void** pVal, int32_t* pVLen);
|
||||
int32_t (*streamStateFillGetGroupKVByCur)(SStreamStateCur* pCur, SWinKey* pKey, const void** pVal, int32_t* pVLen);
|
||||
int32_t (*streamStateGetKVByCur)(SStreamStateCur* pCur, SWinKey* pKey, const void** pVal, int32_t* pVLen);
|
||||
|
||||
void (*streamStateSetFillInfo)(SStreamState* pState);
|
||||
void (*streamStateClearExpiredState)(SStreamState* pState);
|
||||
|
||||
int32_t (*streamStateSessionAddIfNotExist)(SStreamState* pState, SSessionKey* key, TSKEY gap, void** pVal,
|
||||
int32_t* pVLen, int32_t* pWinCode);
|
||||
int32_t (*streamStateSessionPut)(SStreamState* pState, const SSessionKey* key, void* value, int32_t vLen);
|
||||
|
@ -400,8 +418,8 @@ typedef struct SStateStore {
|
|||
SUpdateInfo** ppInfo);
|
||||
void (*updateInfoAddCloseWindowSBF)(SUpdateInfo* pInfo);
|
||||
void (*updateInfoDestoryColseWinSBF)(SUpdateInfo* pInfo);
|
||||
int32_t (*updateInfoSerialize)(void* buf, int32_t bufLen, const SUpdateInfo* pInfo, int32_t* pLen);
|
||||
int32_t (*updateInfoDeserialize)(void* buf, int32_t bufLen, SUpdateInfo* pInfo);
|
||||
int32_t (*updateInfoSerialize)(SEncoder* pEncoder, const SUpdateInfo* pInfo);
|
||||
int32_t (*updateInfoDeserialize)(SDecoder* pDeCoder, SUpdateInfo* pInfo);
|
||||
|
||||
SStreamStateCur* (*streamStateSessionSeekKeyNext)(SStreamState* pState, const SSessionKey* key);
|
||||
SStreamStateCur* (*streamStateCountSeekKeyPrev)(SStreamState* pState, const SSessionKey* pKey, COUNT_TYPE count);
|
||||
|
@ -411,6 +429,11 @@ typedef struct SStateStore {
|
|||
int32_t (*streamFileStateInit)(int64_t memSize, uint32_t keySize, uint32_t rowSize, uint32_t selectRowSize,
|
||||
GetTsFun fp, void* pFile, TSKEY delMark, const char* id, int64_t ckId, int8_t type,
|
||||
struct SStreamFileState** ppFileState);
|
||||
|
||||
int32_t (*streamStateGroupPut)(SStreamState* pState, int64_t groupId, void* value, int32_t vLen);
|
||||
SStreamStateCur* (*streamStateGroupGetCur)(SStreamState* pState);
|
||||
void (*streamStateGroupCurNext)(SStreamStateCur* pCur);
|
||||
int32_t (*streamStateGroupGetKVByCur)(SStreamStateCur* pCur, int64_t* pKey, void** pVal, int32_t* pVLen);
|
||||
|
||||
void (*streamFileStateDestroy)(struct SStreamFileState* pFileState);
|
||||
void (*streamFileStateClear)(struct SStreamFileState* pFileState);
|
||||
|
|
|
@ -194,14 +194,26 @@ typedef struct SIndefRowsFuncLogicNode {
|
|||
bool isTimeLineFunc;
|
||||
} SIndefRowsFuncLogicNode;
|
||||
|
||||
typedef struct SStreamNodeOption {
|
||||
int8_t triggerType;
|
||||
int64_t watermark;
|
||||
int64_t deleteMark;
|
||||
int8_t igExpired;
|
||||
int8_t igCheckUpdate;
|
||||
int8_t destHasPrimaryKey;
|
||||
} SStreamNodeOption;
|
||||
|
||||
typedef struct SInterpFuncLogicNode {
|
||||
SLogicNode node;
|
||||
SNodeList* pFuncs;
|
||||
STimeWindow timeRange;
|
||||
int64_t interval;
|
||||
EFillMode fillMode;
|
||||
SNode* pFillValues; // SNodeListNode
|
||||
SNode* pTimeSeries; // SColumnNode
|
||||
SLogicNode node;
|
||||
SNodeList* pFuncs;
|
||||
STimeWindow timeRange;
|
||||
int64_t interval;
|
||||
int8_t intervalUnit;
|
||||
int8_t precision;
|
||||
EFillMode fillMode;
|
||||
SNode* pFillValues; // SNodeListNode
|
||||
SNode* pTimeSeries; // SColumnNode
|
||||
SStreamNodeOption streamNodeOption;
|
||||
} SInterpFuncLogicNode;
|
||||
|
||||
typedef struct SForecastFuncLogicNode {
|
||||
|
@ -505,17 +517,21 @@ typedef struct SIndefRowsFuncPhysiNode {
|
|||
} SIndefRowsFuncPhysiNode;
|
||||
|
||||
typedef struct SInterpFuncPhysiNode {
|
||||
SPhysiNode node;
|
||||
SNodeList* pExprs;
|
||||
SNodeList* pFuncs;
|
||||
STimeWindow timeRange;
|
||||
int64_t interval;
|
||||
int8_t intervalUnit;
|
||||
EFillMode fillMode;
|
||||
SNode* pFillValues; // SNodeListNode
|
||||
SNode* pTimeSeries; // SColumnNode
|
||||
SPhysiNode node;
|
||||
SNodeList* pExprs;
|
||||
SNodeList* pFuncs;
|
||||
STimeWindow timeRange;
|
||||
int64_t interval;
|
||||
int8_t intervalUnit;
|
||||
int8_t precision;
|
||||
EFillMode fillMode;
|
||||
SNode* pFillValues; // SNodeListNode
|
||||
SNode* pTimeSeries; // SColumnNode
|
||||
SStreamNodeOption streamNodeOption;
|
||||
} SInterpFuncPhysiNode;
|
||||
|
||||
typedef SInterpFuncPhysiNode SStreamInterpFuncPhysiNode;
|
||||
|
||||
typedef struct SForecastFuncPhysiNode {
|
||||
SPhysiNode node;
|
||||
SNodeList* pExprs;
|
||||
|
@ -608,6 +624,7 @@ typedef struct SAggPhysiNode {
|
|||
typedef struct SDownstreamSourceNode {
|
||||
ENodeType type;
|
||||
SQueryNodeAddr addr;
|
||||
uint64_t clientId;
|
||||
uint64_t taskId;
|
||||
uint64_t schedId;
|
||||
int32_t execId;
|
||||
|
@ -650,7 +667,7 @@ typedef struct SWindowPhysiNode {
|
|||
int64_t watermark;
|
||||
int64_t deleteMark;
|
||||
int8_t igExpired;
|
||||
int8_t destHasPrimayKey;
|
||||
int8_t destHasPrimaryKey;
|
||||
bool mergeDataBlock;
|
||||
} SWindowPhysiNode;
|
||||
|
||||
|
|
|
@ -457,6 +457,7 @@ typedef struct SSelectStmt {
|
|||
bool hasCountFunc;
|
||||
bool hasUdaf;
|
||||
bool hasStateKey;
|
||||
bool hasTwaOrElapsedFunc;
|
||||
bool onlyHasKeepOrderFunc;
|
||||
bool groupSort;
|
||||
bool tagScan;
|
||||
|
|
|
@ -105,11 +105,11 @@ void qWorkerDestroy(void **qWorkerMgmt);
|
|||
|
||||
int32_t qWorkerGetStat(SReadHandle *handle, void *qWorkerMgmt, SQWorkerStat *pStat);
|
||||
|
||||
int32_t qWorkerProcessLocalQuery(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t tId, int64_t rId, int32_t eId,
|
||||
SQWMsg *qwMsg, SArray *explainRes);
|
||||
int32_t qWorkerProcessLocalQuery(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t cId, uint64_t tId, int64_t rId,
|
||||
int32_t eId, SQWMsg *qwMsg, SArray *explainRes);
|
||||
|
||||
int32_t qWorkerProcessLocalFetch(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t tId, int64_t rId, int32_t eId,
|
||||
void **pRsp, SArray *explainRes);
|
||||
int32_t qWorkerProcessLocalFetch(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t cId, uint64_t tId, int64_t rId,
|
||||
int32_t eId, void **pRsp, SArray *explainRes);
|
||||
|
||||
int32_t qWorkerDbgEnableDebug(char *option);
|
||||
|
||||
|
|
|
@ -83,6 +83,9 @@ void schedulerStopQueryHb(void* pTrans);
|
|||
int32_t schedulerUpdatePolicy(int32_t policy);
|
||||
int32_t schedulerEnableReSchedule(bool enableResche);
|
||||
|
||||
int32_t initClientId(void);
|
||||
uint64_t getClientId(void);
|
||||
|
||||
/**
|
||||
* Cancel query job
|
||||
* @param pJob
|
||||
|
|
|
@ -49,6 +49,8 @@ void streamStateClear(SStreamState* pState);
|
|||
void streamStateSetNumber(SStreamState* pState, int32_t number, int32_t tsIdex);
|
||||
void streamStateSaveInfo(SStreamState* pState, void* pKey, int32_t keyLen, void* pVal, int32_t vLen);
|
||||
int32_t streamStateGetInfo(SStreamState* pState, void* pKey, int32_t keyLen, void** pVal, int32_t* pLen);
|
||||
int32_t streamStateGetPrev(SStreamState* pState, const SWinKey* pKey, SWinKey* pResKey, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
|
||||
// session window
|
||||
int32_t streamStateSessionAddIfNotExist(SStreamState* pState, SSessionKey* key, TSKEY gap, void** pVal, int32_t* pVLen,
|
||||
|
@ -75,8 +77,14 @@ int32_t streamStateStateAddIfNotExist(SStreamState* pState, SSessionKey* key, ch
|
|||
|
||||
// fill
|
||||
int32_t streamStateFillPut(SStreamState* pState, const SWinKey* key, const void* value, int32_t vLen);
|
||||
int32_t streamStateFillGet(SStreamState* pState, const SWinKey* key, void** pVal, int32_t* pVLen);
|
||||
int32_t streamStateFillGet(SStreamState* pState, const SWinKey* key, void** pVal, int32_t* pVLen, int32_t* pWinCode);
|
||||
int32_t streamStateFillAddIfNotExist(SStreamState* pState, const SWinKey* key, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
void streamStateFillDel(SStreamState* pState, const SWinKey* key);
|
||||
int32_t streamStateFillGetNext(SStreamState* pState, const SWinKey* pKey, SWinKey* pResKey, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
int32_t streamStateFillGetPrev(SStreamState* pState, const SWinKey* pKey, SWinKey* pResKey, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
|
||||
int32_t streamStateAddIfNotExist(SStreamState* pState, const SWinKey* key, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
|
@ -96,14 +104,25 @@ SStreamStateCur* streamStateFillSeekKeyPrev(SStreamState* pState, const SWinKey*
|
|||
void streamStateFreeCur(SStreamStateCur* pCur);
|
||||
void streamStateResetCur(SStreamStateCur* pCur);
|
||||
|
||||
int32_t streamStateGetGroupKVByCur(SStreamStateCur* pCur, SWinKey* pKey, const void** pVal, int32_t* pVLen);
|
||||
int32_t streamStateFillGetGroupKVByCur(SStreamStateCur* pCur, SWinKey* pKey, const void** pVal, int32_t* pVLen);
|
||||
int32_t streamStateGetKVByCur(SStreamStateCur* pCur, SWinKey* pKey, const void** pVal, int32_t* pVLen);
|
||||
|
||||
// twa
|
||||
void streamStateSetFillInfo(SStreamState* pState);
|
||||
void streamStateClearExpiredState(SStreamState* pState);
|
||||
|
||||
void streamStateCurNext(SStreamState* pState, SStreamStateCur* pCur);
|
||||
void streamStateCurPrev(SStreamState* pState, SStreamStateCur* pCur);
|
||||
|
||||
int32_t streamStatePutParName(SStreamState* pState, int64_t groupId, const char* tbname);
|
||||
int32_t streamStateGetParName(SStreamState* pState, int64_t groupId, void** pVal, bool onlyCache, int32_t* pWinCode);
|
||||
int32_t streamStateDeleteParName(SStreamState* pState, int64_t groupId);
|
||||
|
||||
// group id
|
||||
int32_t streamStateGroupPut(SStreamState* pState, int64_t groupId, void* value, int32_t vLen);
|
||||
SStreamStateCur* streamStateGroupGetCur(SStreamState* pState);
|
||||
void streamStateGroupCurNext(SStreamStateCur* pCur);
|
||||
int32_t streamStateGroupGetKVByCur(SStreamStateCur* pCur, int64_t* pKey, void** pVal, int32_t* pVLen);
|
||||
|
||||
void streamStateReloadInfo(SStreamState* pState, TSKEY ts);
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ enum {
|
|||
|
||||
enum {
|
||||
TASK_TRIGGER_STATUS__INACTIVE = 1,
|
||||
TASK_TRIGGER_STATUS__ACTIVE,
|
||||
TASK_TRIGGER_STATUS__MAY_ACTIVE,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
@ -295,9 +295,10 @@ typedef struct SStreamStatus {
|
|||
int32_t schedIdleTime; // idle time before invoke again
|
||||
int64_t lastExecTs; // last exec time stamp
|
||||
int32_t inScanHistorySentinel;
|
||||
bool appendTranstateBlock; // has append the transfer state data block already
|
||||
bool appendTranstateBlock; // has appended the transfer state data block already
|
||||
bool removeBackendFiles; // remove backend files on disk when free stream tasks
|
||||
SConsenChkptInfo consenChkptInfo;
|
||||
STimeWindow latestForceWindow; // latest generated time window, only valid in
|
||||
} SStreamStatus;
|
||||
|
||||
typedef struct SDataRange {
|
||||
|
@ -306,14 +307,16 @@ typedef struct SDataRange {
|
|||
} SDataRange;
|
||||
|
||||
typedef struct SSTaskBasicInfo {
|
||||
int32_t nodeId; // vgroup id or snode id
|
||||
SEpSet epSet;
|
||||
SEpSet mnodeEpset; // mnode epset for send heartbeat
|
||||
int32_t selfChildId;
|
||||
int32_t totalLevel;
|
||||
int8_t taskLevel;
|
||||
int8_t fillHistory; // is fill history task or not
|
||||
int64_t delaySchedParam; // in msec
|
||||
int32_t nodeId; // vgroup id or snode id
|
||||
SEpSet epSet;
|
||||
SEpSet mnodeEpset; // mnode epset for send heartbeat
|
||||
int32_t selfChildId;
|
||||
int32_t trigger;
|
||||
int8_t taskLevel;
|
||||
int8_t fillHistory; // is fill history task or not
|
||||
int64_t delaySchedParam; // in msec
|
||||
int64_t watermark; // extracted from operators
|
||||
SInterval interval;
|
||||
} SSTaskBasicInfo;
|
||||
|
||||
typedef struct SStreamRetrieveReq SStreamRetrieveReq;
|
||||
|
@ -459,7 +462,7 @@ struct SStreamTask {
|
|||
struct SStreamMeta* pMeta;
|
||||
SSHashObj* pNameMap;
|
||||
void* pBackend;
|
||||
int8_t subtableWithoutMd5;
|
||||
int8_t subtableWithoutMd5; // only for tsma stream tasks
|
||||
char reserve[256];
|
||||
char* backendPath;
|
||||
};
|
||||
|
@ -544,8 +547,9 @@ typedef struct STaskUpdateEntry {
|
|||
|
||||
typedef int32_t (*__state_trans_user_fn)(SStreamTask*, void* param);
|
||||
|
||||
int32_t tNewStreamTask(int64_t streamId, int8_t taskLevel, SEpSet* pEpset, bool fillHistory, int64_t triggerParam,
|
||||
SArray* pTaskList, bool hasFillhistory, int8_t subtableWithoutMd5, SStreamTask** pTask);
|
||||
int32_t tNewStreamTask(int64_t streamId, int8_t taskLevel, SEpSet* pEpset, bool fillHistory, int32_t trigger,
|
||||
int64_t triggerParam, SArray* pTaskList, bool hasFillhistory, int8_t subtableWithoutMd5,
|
||||
SStreamTask** pTask);
|
||||
void tFreeStreamTask(void* pTask);
|
||||
int32_t tEncodeStreamTask(SEncoder* pEncoder, const SStreamTask* pTask);
|
||||
int32_t tDecodeStreamTask(SDecoder* pDecoder, SStreamTask* pTask);
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
#ifndef _STREAM_FILE_STATE_H_
|
||||
#define _STREAM_FILE_STATE_H_
|
||||
|
||||
#include "os.h"
|
||||
|
||||
#include "storageapi.h"
|
||||
#include "tarray.h"
|
||||
#include "tdef.h"
|
||||
|
@ -37,7 +35,7 @@ typedef void (*_state_buff_cleanup_fn)(void* pRowBuff);
|
|||
typedef void* (*_state_buff_create_statekey_fn)(SRowBuffPos* pPos, int64_t num);
|
||||
|
||||
typedef int32_t (*_state_file_remove_fn)(SStreamFileState* pFileState, const void* pKey);
|
||||
typedef int32_t (*_state_file_get_fn)(SStreamFileState* pFileState, void* pKey, void* data, int32_t* pDataLen);
|
||||
typedef int32_t (*_state_file_get_fn)(SStreamFileState* pFileState, void* pKey, void** data, int32_t* pDataLen);
|
||||
typedef int32_t (*_state_file_clear_fn)(SStreamState* pState);
|
||||
|
||||
typedef int32_t (*_state_fun_get_fn)(SStreamFileState* pFileState, void* pKey, int32_t keyLen, void** pVal,
|
||||
|
@ -45,6 +43,8 @@ typedef int32_t (*_state_fun_get_fn)(SStreamFileState* pFileState, void* pKey, i
|
|||
|
||||
typedef int32_t (*range_cmpr_fn)(const SSessionKey* pWin1, const SSessionKey* pWin2);
|
||||
|
||||
typedef int (*__session_compare_fn_t)(const void* pWin, const void* pDatas, int pos);
|
||||
|
||||
int32_t streamFileStateInit(int64_t memSize, uint32_t keySize, uint32_t rowSize, uint32_t selectRowSize, GetTsFun fp,
|
||||
void* pFile, TSKEY delMark, const char* taskId, int64_t checkpointId, int8_t type,
|
||||
struct SStreamFileState** ppFileState);
|
||||
|
@ -54,6 +54,8 @@ bool needClearDiskBuff(SStreamFileState* pFileState);
|
|||
void streamFileStateReleaseBuff(SStreamFileState* pFileState, SRowBuffPos* pPos, bool used);
|
||||
void streamFileStateClearBuff(SStreamFileState* pFileState, SRowBuffPos* pPos);
|
||||
|
||||
int32_t addRowBuffIfNotExist(SStreamFileState* pFileState, void* pKey, int32_t keyLen, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
int32_t getRowBuff(SStreamFileState* pFileState, void* pKey, int32_t keyLen, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
void deleteRowBuff(SStreamFileState* pFileState, const void* pKey, int32_t keyLen);
|
||||
|
@ -71,9 +73,11 @@ int32_t streamFileStateGetSelectRowSize(SStreamFileState* pFileState);
|
|||
void streamFileStateReloadInfo(SStreamFileState* pFileState, TSKEY ts);
|
||||
|
||||
void* getRowStateBuff(SStreamFileState* pFileState);
|
||||
void* getSearchBuff(SStreamFileState* pFileState);
|
||||
void* getStateFileStore(SStreamFileState* pFileState);
|
||||
bool isDeteled(SStreamFileState* pFileState, TSKEY ts);
|
||||
bool isFlushedState(SStreamFileState* pFileState, TSKEY ts, TSKEY gap);
|
||||
TSKEY getFlushMark(SStreamFileState* pFileState);
|
||||
SRowBuffPos* getNewRowPosForWrite(SStreamFileState* pFileState);
|
||||
int32_t getRowStateRowSize(SStreamFileState* pFileState);
|
||||
|
||||
|
@ -94,6 +98,7 @@ int32_t recoverSesssion(SStreamFileState* pFileState, int64_t ckId);
|
|||
void sessionWinStateClear(SStreamFileState* pFileState);
|
||||
void sessionWinStateCleanup(void* pBuff);
|
||||
|
||||
SStreamStateCur* createStateCursor(SStreamFileState* pFileState);
|
||||
SStreamStateCur* sessionWinStateSeekKeyCurrentPrev(SStreamFileState* pFileState, const SSessionKey* pWinKey);
|
||||
SStreamStateCur* sessionWinStateSeekKeyCurrentNext(SStreamFileState* pFileState, const SSessionKey* pWinKey);
|
||||
SStreamStateCur* sessionWinStateSeekKeyNext(SStreamFileState* pFileState, const SSessionKey* pWinKey);
|
||||
|
@ -103,6 +108,8 @@ void sessionWinStateMoveToNext(SStreamStateCur* pCur);
|
|||
int32_t sessionWinStateGetKeyByRange(SStreamFileState* pFileState, const SSessionKey* key, SSessionKey* curKey,
|
||||
range_cmpr_fn cmpFn);
|
||||
|
||||
int32_t binarySearch(void* keyList, int num, const void* key, __session_compare_fn_t cmpFn);
|
||||
|
||||
// state window
|
||||
int32_t getStateWinResultBuff(SStreamFileState* pFileState, SSessionKey* key, char* pKeyData, int32_t keyDataLen,
|
||||
state_key_cmpr_fn fn, void** pVal, int32_t* pVLen, int32_t* pWinCode);
|
||||
|
@ -117,6 +124,34 @@ int32_t getSessionRowBuff(SStreamFileState* pFileState, void* pKey, int32_t keyL
|
|||
int32_t* pWinCode);
|
||||
int32_t getFunctionRowBuff(SStreamFileState* pFileState, void* pKey, int32_t keyLen, void** pVal, int32_t* pVLen);
|
||||
|
||||
// time slice
|
||||
int32_t getHashSortRowBuff(SStreamFileState* pFileState, const SWinKey* pKey, void** pVal, int32_t* pVLen,
|
||||
int32_t* pWinCode);
|
||||
int32_t hashSortFileGetFn(SStreamFileState* pFileState, void* pKey, void** data, int32_t* pDataLen);
|
||||
int32_t hashSortFileRemoveFn(SStreamFileState* pFileState, const void* pKey);
|
||||
void clearSearchBuff(SStreamFileState* pFileState);
|
||||
int32_t getHashSortNextRow(SStreamFileState* pFileState, const SWinKey* pKey, SWinKey* pResKey, void** pVal,
|
||||
int32_t* pVLen, int32_t* pWinCode);
|
||||
int32_t getHashSortPrevRow(SStreamFileState* pFileState, const SWinKey* pKey, SWinKey* pResKey, void** ppVal,
|
||||
int32_t* pVLen, int32_t* pWinCode);
|
||||
int32_t recoverFillSnapshot(SStreamFileState* pFileState, int64_t ckId);
|
||||
void deleteHashSortRowBuff(SStreamFileState* pFileState, const SWinKey* pKey);
|
||||
|
||||
//group
|
||||
int32_t streamFileStateGroupPut(SStreamFileState* pFileState, int64_t groupId, void* value, int32_t vLen);
|
||||
void streamFileStateGroupCurNext(SStreamStateCur* pCur);
|
||||
int32_t streamFileStateGroupGetKVByCur(SStreamStateCur* pCur, int64_t* pKey, void** pVal, int32_t* pVLen);
|
||||
SSHashObj* getGroupIdCache(SStreamFileState* pFileState);
|
||||
int fillStateKeyCompare(const void* pWin1, const void* pDatas, int pos);
|
||||
int32_t getRowStatePrevRow(SStreamFileState* pFileState, const SWinKey* pKey, SWinKey* pResKey, void** ppVal,
|
||||
int32_t* pVLen, int32_t* pWinCode);
|
||||
int32_t addSearchItem(SStreamFileState* pFileState, SArray* pWinStates, const SWinKey* pKey);
|
||||
|
||||
//twa
|
||||
void setFillInfo(SStreamFileState* pFileState);
|
||||
void clearExpiredState(SStreamFileState* pFileState);
|
||||
int32_t addArrayBuffIfNotExist(SSHashObj* pSearchBuff, uint64_t groupId, SArray** ppResStates);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -36,8 +36,8 @@ bool updateInfoIsTableInserted(SUpdateInfo* pInfo, int64_t tbUid);
|
|||
void updateInfoDestroy(SUpdateInfo* pInfo);
|
||||
void updateInfoAddCloseWindowSBF(SUpdateInfo* pInfo);
|
||||
void updateInfoDestoryColseWinSBF(SUpdateInfo* pInfo);
|
||||
int32_t updateInfoSerialize(void* buf, int32_t bufLen, const SUpdateInfo* pInfo, int32_t* pLen);
|
||||
int32_t updateInfoDeserialize(void* buf, int32_t bufLen, SUpdateInfo* pInfo);
|
||||
int32_t updateInfoSerialize(SEncoder* pEncoder, const SUpdateInfo* pInfo);
|
||||
int32_t updateInfoDeserialize(SDecoder* pDeCoder, SUpdateInfo* pInfo);
|
||||
void windowSBfDelete(SUpdateInfo* pInfo, uint64_t count);
|
||||
int32_t windowSBfAdd(SUpdateInfo* pInfo, uint64_t count);
|
||||
bool isIncrementalTimeStamp(SUpdateInfo* pInfo, uint64_t tableId, TSKEY ts, void* pPkVal, int32_t len);
|
||||
|
|
|
@ -138,6 +138,7 @@ typedef struct {
|
|||
int8_t scanMeta;
|
||||
int8_t deleteMsg;
|
||||
int8_t enableRef;
|
||||
int8_t scanDropCtb;
|
||||
} SWalFilterCond;
|
||||
|
||||
// todo hide this struct
|
||||
|
|
|
@ -93,7 +93,7 @@ static FORCE_INLINE int64_t taosGetMonoTimestampMs() {
|
|||
char *taosStrpTime(const char *buf, const char *fmt, struct tm *tm);
|
||||
struct tm *taosLocalTime(const time_t *timep, struct tm *result, char *buf, int32_t bufSize);
|
||||
struct tm *taosLocalTimeNolock(struct tm *result, const time_t *timep, int dst);
|
||||
time_t taosTime(time_t *t);
|
||||
int32_t taosTime(time_t *t);
|
||||
time_t taosMktime(struct tm *timep);
|
||||
int64_t user_mktime64(const uint32_t year, const uint32_t mon, const uint32_t day, const uint32_t hour,
|
||||
const uint32_t min, const uint32_t sec, int64_t time_zone);
|
||||
|
|
|
@ -208,6 +208,7 @@ int32_t taosGetErrSize();
|
|||
#define TSDB_CODE_TSC_COMPRESS_PARAM_ERROR TAOS_DEF_ERROR_CODE(0, 0X0233)
|
||||
#define TSDB_CODE_TSC_COMPRESS_LEVEL_ERROR TAOS_DEF_ERROR_CODE(0, 0X0234)
|
||||
#define TSDB_CODE_TSC_FAIL_GENERATE_JSON TAOS_DEF_ERROR_CODE(0, 0X0235)
|
||||
#define TSDB_CODE_TSC_STMT_BIND_NUMBER_ERROR TAOS_DEF_ERROR_CODE(0, 0X0236)
|
||||
#define TSDB_CODE_TSC_INTERNAL_ERROR TAOS_DEF_ERROR_CODE(0, 0X02FF)
|
||||
|
||||
// mnode-common
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import subprocess
|
||||
import re
|
||||
|
||||
# 执行 git fetch 命令并捕获输出
|
||||
def git_fetch():
|
||||
result = subprocess.run(['git', 'fetch'], capture_output=True, text=True)
|
||||
return result
|
||||
|
||||
# 解析分支名称
|
||||
def parse_branch_name_type1(error_output):
|
||||
# 使用正则表达式匹配 'is at' 前的分支名称
|
||||
match = re.search(r"error: cannot lock ref '(refs/remotes/origin/[^']+)': is at", error_output)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
# 解析第二种错误中的分支名称
|
||||
def parse_branch_name_type2(error_output):
|
||||
# 使用正则表达式匹配 'exists' 前的第一个引号内的分支名称
|
||||
match = re.search(r"'(refs/remotes/origin/[^']+)' exists;", error_output)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
# 执行 git update-ref -d 命令
|
||||
def git_update_ref(branch_name):
|
||||
if branch_name:
|
||||
subprocess.run(['git', 'update-ref', '-d', f'{branch_name}'], check=True)
|
||||
|
||||
# 解析错误类型并执行相应的修复操作
|
||||
def handle_error(error_output):
|
||||
# 错误类型1:本地引用的提交ID与远程不一致
|
||||
if "is at" in error_output and "but expected" in error_output:
|
||||
branch_name = parse_branch_name_type1(error_output)
|
||||
if branch_name:
|
||||
print(f"Detected error type 1, attempting to delete ref for branch: {branch_name}")
|
||||
git_update_ref(branch_name)
|
||||
else:
|
||||
print("Error parsing branch name for type 1.")
|
||||
# 错误类型2:尝试创建新的远程引用时,本地已经存在同名的引用
|
||||
elif "exists; cannot create" in error_output:
|
||||
branch_name = parse_branch_name_type2(error_output)
|
||||
if branch_name:
|
||||
print(f"Detected error type 2, attempting to delete ref for branch: {branch_name}")
|
||||
git_update_ref(branch_name)
|
||||
else:
|
||||
print("Error parsing branch name for type 2.")
|
||||
|
||||
# 主函数
|
||||
def main():
|
||||
fetch_result = git_fetch()
|
||||
if fetch_result.returncode != 0: # 如果 git fetch 命令失败
|
||||
error_output = fetch_result.stderr
|
||||
handle_error(error_output)
|
||||
else:
|
||||
print("Git fetch successful.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,319 @@
|
|||
body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
/* do not increase min-width as some may use split screens */
|
||||
min-width: 800px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
p {
|
||||
color: black;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* SUMMARY INFORMATION
|
||||
******************************/
|
||||
#environment td {
|
||||
padding: 5px;
|
||||
border: 1px solid #e6e6e6;
|
||||
vertical-align: top;
|
||||
}
|
||||
#environment tr:nth-child(odd) {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
#environment ul {
|
||||
margin: 0;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* TEST RESULT COLORS
|
||||
******************************/
|
||||
span.passed,
|
||||
.passed .col-result {
|
||||
color: green;
|
||||
}
|
||||
|
||||
span.skipped,
|
||||
span.xfailed,
|
||||
span.rerun,
|
||||
.skipped .col-result,
|
||||
.xfailed .col-result,
|
||||
.rerun .col-result {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
span.error,
|
||||
span.failed,
|
||||
span.xpassed,
|
||||
.error .col-result,
|
||||
.failed .col-result,
|
||||
.xpassed .col-result {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.col-links__extra {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* RESULTS TABLE
|
||||
*
|
||||
* 1. Table Layout
|
||||
* 2. Extra
|
||||
* 3. Sorting items
|
||||
*
|
||||
******************************/
|
||||
/*------------------
|
||||
* 1. Table Layout
|
||||
*------------------*/
|
||||
#results-table {
|
||||
border: 1px solid #e6e6e6;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
#results-table th,
|
||||
#results-table td {
|
||||
padding: 5px;
|
||||
border: 1px solid #e6e6e6;
|
||||
text-align: left;
|
||||
}
|
||||
#results-table th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*------------------
|
||||
* 2. Extra
|
||||
*------------------*/
|
||||
.logwrapper {
|
||||
max-height: 230px;
|
||||
overflow-y: scroll;
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.logwrapper.expanded {
|
||||
max-height: none;
|
||||
}
|
||||
.logwrapper.expanded .logexpander:after {
|
||||
content: "collapse [-]";
|
||||
}
|
||||
.logwrapper .logexpander {
|
||||
z-index: 1;
|
||||
position: sticky;
|
||||
top: 10px;
|
||||
width: max-content;
|
||||
border: 1px solid;
|
||||
border-radius: 3px;
|
||||
padding: 5px 7px;
|
||||
margin: 10px 0 10px calc(100% - 80px);
|
||||
cursor: pointer;
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.logwrapper .logexpander:after {
|
||||
content: "expand [+]";
|
||||
}
|
||||
.logwrapper .logexpander:hover {
|
||||
color: #000;
|
||||
border-color: #000;
|
||||
}
|
||||
.logwrapper .log {
|
||||
min-height: 40px;
|
||||
position: relative;
|
||||
top: -50px;
|
||||
height: calc(100% + 50px);
|
||||
border: 1px solid #e6e6e6;
|
||||
color: black;
|
||||
display: block;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
padding: 5px;
|
||||
padding-right: 80px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
div.media {
|
||||
border: 1px solid #e6e6e6;
|
||||
float: right;
|
||||
height: 240px;
|
||||
margin: 0 5px;
|
||||
overflow: hidden;
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.media-container {
|
||||
display: grid;
|
||||
grid-template-columns: 25px auto 25px;
|
||||
align-items: center;
|
||||
flex: 1 1;
|
||||
overflow: hidden;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.media-container--fullscreen {
|
||||
grid-template-columns: 0px auto 0px;
|
||||
}
|
||||
|
||||
.media-container__nav--right,
|
||||
.media-container__nav--left {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.media-container__viewport {
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
height: inherit;
|
||||
}
|
||||
.media-container__viewport img,
|
||||
.media-container__viewport video {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.media__name,
|
||||
.media__counter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
flex: 0 0 25px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.collapsible td:not(.col-links) {
|
||||
cursor: pointer;
|
||||
}
|
||||
.collapsible td:not(.col-links):hover::after {
|
||||
color: #bbb;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.col-result {
|
||||
width: 130px;
|
||||
}
|
||||
.col-result:hover::after {
|
||||
content: " (hide details)";
|
||||
}
|
||||
|
||||
.col-result.collapsed:hover::after {
|
||||
content: " (show details)";
|
||||
}
|
||||
|
||||
#environment-header h2:hover::after {
|
||||
content: " (hide details)";
|
||||
color: #bbb;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#environment-header.collapsed h2:hover::after {
|
||||
content: " (show details)";
|
||||
color: #bbb;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/*------------------
|
||||
* 3. Sorting items
|
||||
*------------------*/
|
||||
.sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sortable.desc:after {
|
||||
content: " ";
|
||||
position: relative;
|
||||
left: 5px;
|
||||
bottom: -12.5px;
|
||||
border: 10px solid #4caf50;
|
||||
border-bottom: 0;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
}
|
||||
.sortable.asc:after {
|
||||
content: " ";
|
||||
position: relative;
|
||||
left: 5px;
|
||||
bottom: 12.5px;
|
||||
border: 10px solid #4caf50;
|
||||
border-top: 0;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
.hidden, .summary__reload__button.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.summary__data {
|
||||
flex: 0 0 550px;
|
||||
}
|
||||
.summary__reload {
|
||||
flex: 1 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.summary__reload__button {
|
||||
flex: 0 0 300px;
|
||||
display: flex;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
background-color: #4caf50;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.summary__reload__button:hover {
|
||||
background-color: #46a049;
|
||||
}
|
||||
.summary__spacer {
|
||||
flex: 0 0 550px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.filters,
|
||||
.collapse {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.filters button,
|
||||
.collapse button {
|
||||
color: #999;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.filters button:hover,
|
||||
.collapse button:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.filter__label {
|
||||
margin-right: 10px;
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
# conftest.py
|
||||
import pytest
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption(
|
||||
"--verMode", default="enterprise", help="community or enterprise"
|
||||
)
|
||||
parser.addoption(
|
||||
"--tVersion", default="3.3.2.6", help="the version of taos"
|
||||
)
|
||||
parser.addoption(
|
||||
"--baseVersion", default="smoking", help="the path of nas"
|
||||
)
|
||||
parser.addoption(
|
||||
"--sourcePath", default="nas", help="only support nas currently"
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
# Collect the setup and teardown of each test case and their std information
|
||||
setup_stdout_info = {}
|
||||
teardown_stdout_info = {}
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_runtest_makereport(item, call):
|
||||
outcome = yield
|
||||
rep = outcome.get_result()
|
||||
|
||||
# Record the std of setup and teardown
|
||||
if call.when == 'setup':
|
||||
for i in rep.sections:
|
||||
if i[0] == "Captured stdout setup":
|
||||
if not setup_stdout_info:
|
||||
setup_stdout_info[item.nodeid] = i[1]
|
||||
elif call.when == 'teardown':
|
||||
for i in rep.sections:
|
||||
if i[0] == "Captured stdout teardown":
|
||||
teardown_stdout_info[item.nodeid] = i[1]
|
||||
|
||||
|
||||
# Insert setup and teardown's std in the summary section
|
||||
def pytest_html_results_summary(prefix, summary, postfix):
|
||||
if setup_stdout_info or teardown_stdout_info:
|
||||
rows = []
|
||||
|
||||
# Insert setup stdout
|
||||
if setup_stdout_info:
|
||||
for nodeid, stdout in setup_stdout_info.items():
|
||||
html_content = '''
|
||||
<tr>
|
||||
<td><b><span style="font-size: larger; color: black;">Setup:</span></b></td>
|
||||
<td colspan="4">
|
||||
<a href="#" id="toggleSetup">Show Setup</a>
|
||||
<div id="setupContent" class="collapsible-content" style="display: none; white-space: pre-wrap; margin-top: 5px;">
|
||||
<pre>{}</pre>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
'''.format(stdout.strip())
|
||||
|
||||
# 如果需要在 Python 脚本中生成 HTML,并使用 JavaScript 控制折叠内容的显示,可以这样做:
|
||||
|
||||
html_script = '''
|
||||
<script>
|
||||
document.getElementById('toggleSetup').addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
var setupContentDiv = document.getElementById('setupContent');
|
||||
setupContentDiv.style.display = setupContentDiv.style.display === 'none' ? 'block' : 'none';
|
||||
var buttonText = setupContentDiv.style.display === 'none' ? 'Show Setup' : 'Hide Setup';
|
||||
this.textContent = buttonText;
|
||||
});
|
||||
</script>
|
||||
'''
|
||||
|
||||
# 输出完整的 HTML 代码
|
||||
final_html = html_content + html_script
|
||||
rows.append(final_html)
|
||||
rows.append("<br>")
|
||||
# Insert teardown stdout
|
||||
if teardown_stdout_info:
|
||||
for nodeid, stdout in teardown_stdout_info.items():
|
||||
html_content = '''
|
||||
<tr>
|
||||
<td><b><span style="font-size: larger; color: black;">Teardown:</span></b></td>
|
||||
<td colspan="4">
|
||||
<a href="#" id="toggleTeardown">Show Teardown</a>
|
||||
<div id="teardownContent" class="collapsible-content" style="display: none; white-space: pre-wrap; margin-top: 5px;">
|
||||
<pre>{}</pre>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
'''.format(stdout.strip())
|
||||
|
||||
# 如果需要在 Python 脚本中生成 HTML,并使用 JavaScript 控制折叠内容的显示,可以这样做:
|
||||
|
||||
html_script = '''
|
||||
<script>
|
||||
document.getElementById('toggleTeardown').addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
var teardownContentDiv = document.getElementById('teardownContent');
|
||||
teardownContentDiv.style.display = teardownContentDiv.style.display === 'none' ? 'block' : 'none';
|
||||
var buttonText = teardownContentDiv.style.display === 'none' ? 'Show Teardown' : 'Hide Teardown';
|
||||
this.textContent = buttonText;
|
||||
});
|
||||
</script>
|
||||
'''
|
||||
|
||||
# 输出完整的 HTML 代码
|
||||
final_html = html_content + html_script
|
||||
rows.append(final_html)
|
||||
|
||||
prefix.extend(rows)
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/expect
|
||||
set packageName [lindex $argv 0]
|
||||
set packageSuffix [lindex $argv 1]
|
||||
set timeout 30
|
||||
if { ${packageSuffix} == "deb" } {
|
||||
spawn dpkg -i ${packageName}
|
||||
} elseif { ${packageSuffix} == "rpm"} {
|
||||
spawn rpm -ivh ${packageName}
|
||||
}
|
||||
expect "*one:"
|
||||
send "\r"
|
||||
expect "*skip:"
|
||||
send "\r"
|
||||
|
||||
expect eof
|
|
@ -0,0 +1,57 @@
|
|||
set baseVersion=%1%
|
||||
set version=%2%
|
||||
set verMode=%3%
|
||||
set sType=%4%
|
||||
echo %fileType%
|
||||
rem stop services
|
||||
if EXIST C:\TDengine (
|
||||
if EXIST C:\TDengine\stop-all.bat (
|
||||
call C:\TDengine\stop-all.bat /silent
|
||||
echo "***************Stop taos services***************"
|
||||
)
|
||||
if exist C:\TDengine\unins000.exe (
|
||||
call C:\TDengine\unins000.exe /silent
|
||||
echo "***************uninstall TDengine***************"
|
||||
)
|
||||
rd /S /q C:\TDengine
|
||||
)
|
||||
if EXIST C:\ProDB (
|
||||
if EXIST C:\ProDB\stop-all.bat (
|
||||
call C:\ProDB\stop-all.bat /silent
|
||||
echo "***************Stop taos services***************"
|
||||
)
|
||||
if exist C:\ProDB\unins000.exe (
|
||||
call C:\ProDB\unins000.exe /silent
|
||||
echo "***************uninstall TDengine***************"
|
||||
)
|
||||
rd /S /q C:\ProDB
|
||||
)
|
||||
if "%verMode%"=="enterprise" (
|
||||
if "%sType%"=="client" (
|
||||
set fileType=enterprise-client
|
||||
) else (
|
||||
set fileType=enterprise
|
||||
)
|
||||
) else (
|
||||
set fileType=%sType%
|
||||
)
|
||||
|
||||
if "%baseVersion%"=="ProDB" (
|
||||
echo %fileType%
|
||||
set installer=ProDB-%fileType%-%version%-Windows-x64.exe
|
||||
) else (
|
||||
echo %fileType%
|
||||
set installer=TDengine-%fileType%-%version%-Windows-x64.exe
|
||||
)
|
||||
|
||||
if "%baseVersion%"=="ProDB" (
|
||||
echo %installer%
|
||||
scp root@192.168.1.213:/nas/OEM/ProDB/v%version%/%installer% C:\workspace
|
||||
) else (
|
||||
echo %installer%
|
||||
scp root@192.168.1.213:/nas/TDengine/%baseVersion%/v%version%/%verMode%/%installer% C:\workspace
|
||||
)
|
||||
|
||||
echo "***************Finish installer transfer!***************"
|
||||
C:\workspace\%installer% /silent
|
||||
echo "***************Finish install!***************"
|
|
@ -0,0 +1,325 @@
|
|||
#!/bin/sh
|
||||
|
||||
|
||||
function usage() {
|
||||
echo "$0"
|
||||
echo -e "\t -f test file type,server/client/tools/"
|
||||
echo -e "\t -m pacakage version Type,community/enterprise"
|
||||
echo -e "\t -l package type,lite or not"
|
||||
echo -e "\t -c operation type,x64/arm64"
|
||||
echo -e "\t -v pacakage version,3.0.1.7"
|
||||
echo -e "\t -o pacakage version,3.0.1.7"
|
||||
echo -e "\t -s source Path,web/nas"
|
||||
echo -e "\t -t package Type,tar/rpm/deb"
|
||||
echo -e "\t -h help"
|
||||
}
|
||||
|
||||
|
||||
#parameter
|
||||
scriptDir=$(dirname $(readlink -f $0))
|
||||
version="3.0.1.7"
|
||||
originversion="smoking"
|
||||
testFile="server"
|
||||
verMode="communtity"
|
||||
sourcePath="nas"
|
||||
cpuType="x64"
|
||||
lite="true"
|
||||
packageType="tar"
|
||||
subFile="package.tar.gz"
|
||||
while getopts "m:c:f:l:s:o:t:v:h" opt; do
|
||||
case $opt in
|
||||
m)
|
||||
verMode=$OPTARG
|
||||
;;
|
||||
v)
|
||||
version=$OPTARG
|
||||
;;
|
||||
f)
|
||||
testFile=$OPTARG
|
||||
;;
|
||||
l)
|
||||
lite=$OPTARG
|
||||
;;
|
||||
s)
|
||||
sourcePath=$OPTARG
|
||||
;;
|
||||
o)
|
||||
originversion=$OPTARG
|
||||
;;
|
||||
c)
|
||||
cpuType=$OPTARG
|
||||
;;
|
||||
t)
|
||||
packageType=$OPTARG
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
?)
|
||||
echo "Invalid option: -$OPTARG"
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
systemType=`uname`
|
||||
if [ ${systemType} == "Darwin" ]; then
|
||||
platform="macOS"
|
||||
else
|
||||
platform="Linux"
|
||||
fi
|
||||
|
||||
echo "testFile:${testFile},verMode:${verMode},lite:${lite},cpuType:${cpuType},packageType:${packageType},version-${version},originversion:${originversion},sourcePath:${sourcePath}"
|
||||
# Color setting
|
||||
RED='\033[41;30m'
|
||||
GREEN='\033[1;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[1;34m'
|
||||
GREEN_DARK='\033[0;32m'
|
||||
YELLOW_DARK='\033[0;33m'
|
||||
BLUE_DARK='\033[0;34m'
|
||||
GREEN_UNDERLINE='\033[4;32m'
|
||||
NC='\033[0m'
|
||||
if [ "${originversion}" = "ProDB" ]; then
|
||||
TDengine="ProDB"
|
||||
else
|
||||
TDengine="TDengine"
|
||||
fi
|
||||
if [[ ${verMode} = "enterprise" ]];then
|
||||
prePackage="${TDengine}-enterprise"
|
||||
if [[ ${testFile} = "client" ]];then
|
||||
prePackage="${TDengine}-enterprise-${testFile}"
|
||||
fi
|
||||
elif [ ${verMode} = "community" ];then
|
||||
prePackage="${TDengine}-${testFile}"
|
||||
fi
|
||||
if [ ${lite} = "true" ];then
|
||||
packageLite="-Lite"
|
||||
elif [ ${lite} = "false" ];then
|
||||
packageLite=""
|
||||
fi
|
||||
if [[ "$packageType" = "tar" ]] ;then
|
||||
packageType="tar.gz"
|
||||
fi
|
||||
|
||||
tdPath="${prePackage}-${version}"
|
||||
|
||||
packageName="${tdPath}-${platform}-${cpuType}${packageLite}.${packageType}"
|
||||
|
||||
if [ "$testFile" == "server" ] ;then
|
||||
installCmd="install.sh"
|
||||
elif [ ${testFile} = "client" ];then
|
||||
installCmd="install_client.sh"
|
||||
fi
|
||||
|
||||
echo "tdPath:${tdPath},packageName:${packageName}}"
|
||||
cmdInstall() {
|
||||
command=$1
|
||||
if command -v ${command} ;then
|
||||
echoColor YD "${command} is already installed"
|
||||
else
|
||||
if command -v apt ;then
|
||||
apt-get install ${command} -y
|
||||
elif command -v yum ;then
|
||||
yum -y install ${command}
|
||||
echoColor YD "you should install ${command} manually"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
echoColor() {
|
||||
color=$1
|
||||
command=$2
|
||||
if [ ${color} = 'Y' ];then
|
||||
echo -e "${YELLOW}${command}${NC}"
|
||||
elif [ ${color} = 'YD' ];then
|
||||
echo -e "${YELLOW_DARK}${command}${NC}"
|
||||
elif [ ${color} = 'R' ];then
|
||||
echo -e "${RED}${command}${NC}"
|
||||
elif [ ${color} = 'G' ];then
|
||||
echo -e "${GREEN}${command}${NC}\r\n"
|
||||
elif [ ${color} = 'B' ];then
|
||||
echo -e "${BLUE}${command}${NC}"
|
||||
elif [ ${color} = 'BD' ];then
|
||||
echo -e "${BLUE_DARK}${command}${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
wgetFile() {
|
||||
|
||||
file=$1
|
||||
versionPath=$2
|
||||
sourceP=$3
|
||||
nasServerIP="192.168.1.213"
|
||||
if [ "${originversion}" = "ProDB" ]; then
|
||||
packagePath="/nas/OEM/ProDB/v${versionPath}"
|
||||
else
|
||||
packagePath="/nas/TDengine/${originversion}/v${versionPath}/${verMode}"
|
||||
fi
|
||||
if [ -f ${file} ];then
|
||||
echoColor YD "${file} already exists ,it will delete it and download it again "
|
||||
# rm -rf ${file}
|
||||
fi
|
||||
|
||||
if [[ ${sourceP} = 'web' ]];then
|
||||
echoColor BD "====download====:wget https://www.taosdata.com/assets-download/3.0/${file}"
|
||||
wget https://www.taosdata.com/assets-download/3.0/${file}
|
||||
elif [[ ${sourceP} = 'nas' ]];then
|
||||
echoColor BD "====download====:scp root@${nasServerIP}:${packagePath}/${file} ."
|
||||
scp root@${nasServerIP}:${packagePath}/${file} .
|
||||
fi
|
||||
}
|
||||
|
||||
function newPath {
|
||||
|
||||
buildPath=$1
|
||||
|
||||
if [ ! -d ${buildPath} ] ;then
|
||||
echoColor BD "mkdir -p ${buildPath}"
|
||||
mkdir -p ${buildPath}
|
||||
else
|
||||
echoColor YD "${buildPath} already exists"
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
echoColor G "===== install basesoft ====="
|
||||
cmdInstall tree
|
||||
cmdInstall wget
|
||||
cmdInstall expect
|
||||
|
||||
echoColor G "===== Uninstall all components of TDeingne ====="
|
||||
|
||||
if command -v rmtaos ;then
|
||||
echoColor YD "uninstall all components of TDeingne:rmtaos"
|
||||
rmtaos
|
||||
else
|
||||
echoColor YD "os doesn't include TDengine"
|
||||
fi
|
||||
|
||||
if [[ ${packageName} =~ "server" ]] ;then
|
||||
echoColor BD " pkill -9 taosd "
|
||||
pkill -9 taosd
|
||||
fi
|
||||
|
||||
if command -v rmprodb ;then
|
||||
echoColor YD "uninstall all components of TDeingne:rmprodb"
|
||||
rmprodb
|
||||
else
|
||||
echoColor YD "os doesn't include TDengine"
|
||||
fi
|
||||
|
||||
if [[ ${packageName} =~ "server" ]] ;then
|
||||
echoColor BD " pkill -9 prodbd "
|
||||
pkill -9 prodbd
|
||||
fi
|
||||
|
||||
echoColor G "===== new workroom path ====="
|
||||
installPath="/usr/local/src/packageTest"
|
||||
|
||||
if [ ${systemType} == "Darwin" ]; then
|
||||
installPath="${WORK_DIR}/packageTest"
|
||||
fi
|
||||
|
||||
newPath ${installPath}
|
||||
|
||||
#if [ -d ${installPath}/${tdPath} ] ;then
|
||||
# echoColor BD "rm -rf ${installPath}/${tdPath}/*"
|
||||
# rm -rf ${installPath}/${tdPath}/*
|
||||
#fi
|
||||
|
||||
echoColor G "===== download installPackage ====="
|
||||
cd ${installPath} && wgetFile ${packageName} ${version} ${sourcePath}
|
||||
#cd ${oriInstallPath} && wgetFile ${originPackageName} ${originversion} ${sourcePath}
|
||||
|
||||
|
||||
cd ${installPath}
|
||||
cp -r ${scriptDir}/debRpmAutoInstall.sh .
|
||||
|
||||
packageSuffix=$(echo ${packageName} | awk -F '.' '{print $NF}')
|
||||
|
||||
|
||||
if [ ! -f debRpmAutoInstall.sh ];then
|
||||
echo '#!/usr/bin/expect ' > debRpmAutoInstall.sh
|
||||
echo 'set packageName [lindex $argv 0]' >> debRpmAutoInstall.sh
|
||||
echo 'set packageSuffix [lindex $argv 1]' >> debRpmAutoInstall.sh
|
||||
echo 'set timeout 30 ' >> debRpmAutoInstall.sh
|
||||
echo 'if { ${packageSuffix} == "deb" } {' >> debRpmAutoInstall.sh
|
||||
echo ' spawn dpkg -i ${packageName} ' >> debRpmAutoInstall.sh
|
||||
echo '} elseif { ${packageSuffix} == "rpm"} {' >> debRpmAutoInstall.sh
|
||||
echo ' spawn rpm -ivh ${packageName}' >> debRpmAutoInstall.sh
|
||||
echo '}' >> debRpmAutoInstall.sh
|
||||
echo 'expect "*one:"' >> debRpmAutoInstall.sh
|
||||
echo 'send "\r"' >> debRpmAutoInstall.sh
|
||||
echo 'expect "*skip:"' >> debRpmAutoInstall.sh
|
||||
echo 'send "\r" ' >> debRpmAutoInstall.sh
|
||||
fi
|
||||
|
||||
|
||||
echoColor G "===== install Package ====="
|
||||
|
||||
if [[ ${packageName} =~ "deb" ]];then
|
||||
cd ${installPath}
|
||||
dpkg -r taostools
|
||||
dpkg -r tdengine
|
||||
if [[ ${packageName} =~ "TDengine" ]];then
|
||||
echoColor BD "./debRpmAutoInstall.sh ${packageName} ${packageSuffix}" && chmod 755 debRpmAutoInstall.sh && ./debRpmAutoInstall.sh ${packageName} ${packageSuffix}
|
||||
else
|
||||
echoColor BD "dpkg -i ${packageName}" && dpkg -i ${packageName}
|
||||
fi
|
||||
elif [[ ${packageName} =~ "rpm" ]];then
|
||||
cd ${installPath}
|
||||
sudo rpm -e tdengine
|
||||
sudo rpm -e taostools
|
||||
if [[ ${packageName} =~ "TDengine" ]];then
|
||||
echoColor BD "./debRpmAutoInstall.sh ${packageName} ${packageSuffix}" && chmod 755 debRpmAutoInstall.sh && ./debRpmAutoInstall.sh ${packageName} ${packageSuffix}
|
||||
else
|
||||
echoColor BD "rpm -ivh ${packageName}" && rpm -ivh ${packageName}
|
||||
fi
|
||||
elif [[ ${packageName} =~ "tar" ]];then
|
||||
echoColor G "===== check installPackage File of tar ====="
|
||||
|
||||
cd ${installPath}
|
||||
echoColor YD "unzip the new installation package"
|
||||
echoColor BD "tar -xf ${packageName}" && tar -xf ${packageName}
|
||||
|
||||
cd ${installPath}/${tdPath} && tree -I "driver" > ${installPath}/now_${version}_checkfile
|
||||
|
||||
cd ${installPath}
|
||||
diff ${installPath}/base_${originversion}_checkfile ${installPath}/now_${version}_checkfile > ${installPath}/diffFile.log
|
||||
diffNumbers=`cat ${installPath}/diffFile.log |wc -l `
|
||||
|
||||
if [ ${diffNumbers} != 0 ];then
|
||||
echoColor R "The number and names of files is different from the previous installation package"
|
||||
diffLog=`cat ${installPath}/diffFile.log`
|
||||
echoColor Y "${diffLog}"
|
||||
exit -1
|
||||
else
|
||||
echoColor G "The number and names of files are the same as previous installation packages"
|
||||
rm -rf ${installPath}/diffFile.log
|
||||
fi
|
||||
echoColor YD "===== install Package of tar ====="
|
||||
cd ${installPath}/${tdPath}
|
||||
if [ ${testFile} = "server" ];then
|
||||
echoColor BD "bash ${installCmd} -e no "
|
||||
bash ${installCmd} -e no
|
||||
else
|
||||
echoColor BD "bash ${installCmd} "
|
||||
bash ${installCmd}
|
||||
fi
|
||||
elif [[ ${packageName} =~ "pkg" ]];then
|
||||
cd ${installPath}
|
||||
sudo installer -pkg ${packageName} -target /
|
||||
echoColor YD "===== install Package successfully! ====="
|
||||
fi
|
||||
|
||||
#cd ${installPath}
|
||||
#
|
||||
#rm -rf ${installPath}/${packageName}
|
||||
#if [ ${platform} == "Linux" ]; then
|
||||
# rm -rf ${installPath}/${tdPath}/
|
||||
#fi
|
||||
echoColor YD "===== end of shell file ====="
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
import subprocess
|
||||
|
||||
|
||||
def run_cmd(command):
|
||||
print("CMD:", command)
|
||||
result = subprocess.run(command, capture_output=True, text=True, shell=True)
|
||||
print("STDOUT:", result.stdout)
|
||||
print("STDERR:", result.stderr)
|
||||
print("Return Code:", result.returncode)
|
||||
#assert result.returncode == 0
|
||||
return result
|
|
@ -0,0 +1,21 @@
|
|||
import pytest
|
||||
|
||||
# python3 -m pytest test_server.py -v --html=/var/www/html/report.html --json-report --json-report-file="/var/www/html/report.json" --timeout=60
|
||||
|
||||
# pytest.main(["-s", "-v"])
|
||||
import pytest
|
||||
|
||||
import subprocess
|
||||
|
||||
|
||||
# define cmd function
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
pytest.main(['--html=report.html'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,17 @@
|
|||
pytest-html
|
||||
pytest-json-report
|
||||
pytest-timeout
|
||||
taospy
|
||||
numpy
|
||||
fabric2
|
||||
psutil
|
||||
pandas
|
||||
toml
|
||||
distro
|
||||
requests
|
||||
pexpect
|
||||
faker
|
||||
pyopenssl
|
||||
taos-ws-py
|
||||
taospy
|
||||
tzlocal
|
|
@ -0,0 +1,11 @@
|
|||
rm -rf %WIN_TDENGINE_ROOT_DIR%\debug
|
||||
mkdir %WIN_TDENGINE_ROOT_DIR%\debug
|
||||
mkdir %WIN_TDENGINE_ROOT_DIR%\debug\build
|
||||
mkdir %WIN_TDENGINE_ROOT_DIR%\debug\build\bin
|
||||
xcopy C:\TDengine\taos*.exe %WIN_TDENGINE_ROOT_DIR%\debug\build\bin
|
||||
|
||||
set case_out_file=%cd%\case.out
|
||||
|
||||
cd %WIN_TDENGINE_ROOT_DIR%\tests\system-test
|
||||
python3 .\test.py -f 0-others\taosShell.py
|
||||
python3 .\test.py -f 6-cluster\5dnode3mnodeSep1VnodeStopDnodeModifyMeta.py -N 6 -M 3
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/bash
|
||||
ulimit -c unlimited
|
||||
|
||||
rm -rf ${TDENGINE_ROOT_DIR}/debug
|
||||
mkdir ${TDENGINE_ROOT_DIR}/debug
|
||||
mkdir ${TDENGINE_ROOT_DIR}/debug/build
|
||||
mkdir ${TDENGINE_ROOT_DIR}/debug/build/bin
|
||||
|
||||
systemType=`uname`
|
||||
if [ ${systemType} == "Darwin" ]; then
|
||||
cp /usr/local/bin/taos* ${TDENGINE_ROOT_DIR}/debug/build/bin/
|
||||
else
|
||||
cp /usr/bin/taos* ${TDENGINE_ROOT_DIR}/debug/build/bin/
|
||||
fi
|
||||
|
||||
case_out_file=`pwd`/case.out
|
||||
python3 -m pip install -r ${TDENGINE_ROOT_DIR}/tests/requirements.txt >> $case_out_file
|
||||
python3 -m pip install taos-ws-py taospy >> $case_out_file
|
||||
|
||||
cd ${TDENGINE_ROOT_DIR}/tests/army
|
||||
python3 ./test.py -f query/query_basic.py -N 3 >> $case_out_file
|
||||
|
||||
cd ${TDENGINE_ROOT_DIR}/tests/system-test
|
||||
python3 ./test.py -f 1-insert/insert_column_value.py >> $case_out_file
|
||||
python3 ./test.py -f 2-query/primary_ts_base_5.py >> $case_out_file
|
||||
python3 ./test.py -f 2-query/case_when.py >> $case_out_file
|
||||
python3 ./test.py -f 2-query/partition_limit_interval.py >> $case_out_file
|
||||
python3 ./test.py -f 2-query/join.py >> $case_out_file
|
||||
python3 ./test.py -f 2-query/fill.py >> $case_out_file
|
|
@ -0,0 +1,251 @@
|
|||
#!/usr/bin/python
|
||||
###################################################################
|
||||
# Copyright (c) 2016 by TAOS Technologies, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is proprietary and confidential to TAOS Technologies.
|
||||
# No part of this file may be reproduced, stored, transmitted,
|
||||
# disclosed or used in any form or by any means other than as
|
||||
# expressly provided by the written permission from Jianhui Tao
|
||||
#
|
||||
###################################################################
|
||||
# install pip
|
||||
# pip install src/connector/python/
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys, os
|
||||
import re
|
||||
import platform
|
||||
import getopt
|
||||
import subprocess
|
||||
# from this import d
|
||||
import time
|
||||
|
||||
# input for server
|
||||
|
||||
opts, args = getopt.gnu_getopt(sys.argv[1:], 'h:P:v:u', [
|
||||
'host=', 'Port=', 'version='])
|
||||
serverHost = ""
|
||||
serverPort = 0
|
||||
version = ""
|
||||
uninstall = False
|
||||
for key, value in opts:
|
||||
if key in ['--help']:
|
||||
print('A collection of test cases written using Python')
|
||||
print('-h serverHost')
|
||||
print('-P serverPort')
|
||||
print('-v test client version')
|
||||
print('-u test uninstall process, will uninstall TDengine')
|
||||
sys.exit(0)
|
||||
|
||||
if key in ['-h']:
|
||||
serverHost = value
|
||||
if key in ['-P']:
|
||||
serverPort = int(value)
|
||||
if key in ['-v']:
|
||||
version = value
|
||||
if key in ['-u']:
|
||||
uninstall = True
|
||||
if not serverHost:
|
||||
print("Please input use -h to specify your server host.")
|
||||
sys.exit(0)
|
||||
if not version:
|
||||
print("No version specified, will not run version check.")
|
||||
if serverPort == 0:
|
||||
serverPort = 6030
|
||||
print("No server port specified, use default 6030.")
|
||||
|
||||
|
||||
system = platform.system()
|
||||
|
||||
arch = platform.machine()
|
||||
|
||||
databaseName = re.sub(r'[^a-zA-Z0-9]', '', subprocess.getoutput("hostname")).lower()
|
||||
# install taospy
|
||||
taospy_version = ""
|
||||
if system == 'Windows':
|
||||
taospy_version = subprocess.getoutput("pip3 show taospy|findstr Version")
|
||||
else:
|
||||
taospy_version = subprocess.getoutput("pip3 show taospy|grep Version| awk -F ':' '{print $2}' ")
|
||||
|
||||
print("taospy version %s " % taospy_version)
|
||||
if taospy_version == "":
|
||||
subprocess.getoutput("pip3 install git+https://github.com/taosdata/taos-connector-python.git")
|
||||
print("install taos python connector")
|
||||
else:
|
||||
subprocess.getoutput("pip3 install taospy")
|
||||
|
||||
# prepare data by taosBenchmark
|
||||
cmd = "taosBenchmark -y -a 3 -n 100 -t 100 -d %s -h %s -P %d &" % (databaseName, serverHost, serverPort)
|
||||
process_out = subprocess.getoutput(cmd)
|
||||
print(cmd)
|
||||
#os.system("taosBenchmark -y -a 3 -n 100 -t 100 -d %s -h %s -P %d" % (databaseName, serverHost, serverPort))
|
||||
taosBenchmark_test_result = True
|
||||
time.sleep(10)
|
||||
import taos
|
||||
|
||||
conn = taos.connect(host=serverHost,
|
||||
user="root",
|
||||
password="taosdata",
|
||||
database=databaseName,
|
||||
port=serverPort,
|
||||
timezone="Asia/Shanghai") # default your host's timezone
|
||||
|
||||
server_version = conn.server_info
|
||||
print("server_version", server_version)
|
||||
client_version = conn.client_info
|
||||
print("client_version", client_version) # 3.0.0.0
|
||||
|
||||
# Execute a sql and get its result set. It's useful for SELECT statement
|
||||
result: taos.TaosResult = conn.query("SELECT count(*) from meters")
|
||||
|
||||
data = result.fetch_all()
|
||||
print(data)
|
||||
if data[0][0] !=10000:
|
||||
print(" taosBenchmark work not as expected ")
|
||||
print("!!!!!!!!!!!Test Result: taosBenchmark test failed! !!!!!!!!!!")
|
||||
sys.exit(1)
|
||||
#else:
|
||||
# print("**********Test Result: taosBenchmark test passed **********")
|
||||
|
||||
|
||||
# drop database of test
|
||||
taos_test_result = False
|
||||
print("drop database test")
|
||||
print("run taos -s 'drop database %s;' -h %s -P %d" % (databaseName, serverHost, serverPort))
|
||||
taos_cmd_outpur = subprocess.getoutput('taos -s "drop database %s;" -h %s -P %d' % (databaseName, serverHost, serverPort))
|
||||
print(taos_cmd_outpur)
|
||||
if ("Drop OK" in taos_cmd_outpur):
|
||||
taos_test_result = True
|
||||
#print("*******Test Result: taos test passed ************")
|
||||
|
||||
version_test_result = False
|
||||
if version:
|
||||
print("Client info is: %s"%conn.client_info)
|
||||
taos_V_output = ""
|
||||
if system == "Windows":
|
||||
taos_V_output = subprocess.getoutput("taos -V | findstr version")
|
||||
else:
|
||||
taos_V_output = subprocess.getoutput("taos -V | grep version")
|
||||
|
||||
print("taos -V output is: %s" % taos_V_output)
|
||||
if version in taos_V_output and version in conn.client_info:
|
||||
version_test_result = True
|
||||
#print("*******Test Result: Version check passed ************")
|
||||
|
||||
conn.close()
|
||||
if uninstall:
|
||||
print("Start to run rmtaos")
|
||||
leftFile = False
|
||||
print("Platform: ", system)
|
||||
|
||||
if system == "Linux":
|
||||
# 创建一个subprocess.Popen对象,并使用stdin和stdout进行交互
|
||||
process = subprocess.Popen(['rmtaos'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||
# 向子进程发送输入
|
||||
process.stdin.write("y\n")
|
||||
process.stdin.flush() # 确保输入被发送到子进程
|
||||
process.stdin.write("I confirm that I would like to delete all data, log and configuration files\n")
|
||||
process.stdin.flush() # 确保输入被发送到子进程
|
||||
# 关闭子进程的stdin,防止它无限期等待更多输入
|
||||
process.stdin.close()
|
||||
# 等待子进程结束
|
||||
process.wait()
|
||||
# 检查目录清除情况
|
||||
out = subprocess.getoutput("ls /etc/systemd/system/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/bin/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/bin/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/lib/libtaos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/lib64/libtaos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/include/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/taos")
|
||||
#print(out)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files in /usr/local/taos:%s" % out)
|
||||
leftFile = True
|
||||
if not leftFile:
|
||||
print("*******Test Result: uninstall test passed ************")
|
||||
|
||||
elif system == "Darwin":
|
||||
# 创建一个subprocess.Popen对象,并使用stdin和stdout进行交互
|
||||
process = subprocess.Popen(['sudo', 'rmtaos'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||
# 向子进程发送输入
|
||||
process.stdin.write("y\n")
|
||||
process.stdin.flush() # 确保输入被发送到子进程
|
||||
process.stdin.write("I confirm that I would like to delete all data, log and configuration files\n")
|
||||
process.stdin.flush() # 确保输入被发送到子进程
|
||||
# 关闭子进程的stdin,防止它无限期等待更多输入
|
||||
process.stdin.close()
|
||||
# 等待子进程结束
|
||||
process.wait()
|
||||
# 检查目录清除情况
|
||||
out = subprocess.getoutput("ls /usr/local/bin/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/lib/libtaos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/include/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
#out = subprocess.getoutput("ls /usr/local/Cellar/tdengine/")
|
||||
#print(out)
|
||||
#if out:
|
||||
# print("Uninstall left some files: /usr/local/Cellar/tdengine/%s" % out)
|
||||
# leftFile = True
|
||||
#if not leftFile:
|
||||
# print("*******Test Result: uninstall test passed ************")
|
||||
|
||||
elif system == "Windows":
|
||||
process = subprocess.Popen(['unins000','/silent'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||
process.wait()
|
||||
time.sleep(10)
|
||||
out = subprocess.getoutput("ls C:\TDengine")
|
||||
print(out)
|
||||
if len(out.split("\n")) > 3:
|
||||
leftFile = True
|
||||
print("Uninstall left some files: %s" % out)
|
||||
|
||||
if taosBenchmark_test_result:
|
||||
print("**********Test Result: taosBenchmark test passed! **********")
|
||||
if taos_test_result:
|
||||
print("**********Test Result: taos test passed! **********")
|
||||
else:
|
||||
print("!!!!!!!!!!!Test Result: taos test failed! !!!!!!!!!!")
|
||||
if version_test_result:
|
||||
print("**********Test Result: version test passed! **********")
|
||||
else:
|
||||
print("!!!!!!!!!!!Test Result: version test failed! !!!!!!!!!!")
|
||||
if not leftFile:
|
||||
print("**********Test Result: uninstall test passed! **********")
|
||||
else:
|
||||
print("!!!!!!!!!!!Test Result: uninstall test failed! !!!!!!!!!!")
|
||||
if taosBenchmark_test_result and taos_test_result and version_test_result and not leftFile:
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
|
@ -0,0 +1,380 @@
|
|||
def sync_source(branch_name) {
|
||||
sh '''
|
||||
hostname
|
||||
ip addr|grep 192|awk '{print $2}'|sed "s/\\/.*//"
|
||||
echo ''' + branch_name + '''
|
||||
'''
|
||||
sh '''
|
||||
cd ${TDENGINE_ROOT_DIR}
|
||||
set +e
|
||||
git reset --hard
|
||||
git fetch || git fetch
|
||||
git checkout -f '''+branch_name+'''
|
||||
git reset --hard origin/'''+branch_name+'''
|
||||
git log | head -n 20
|
||||
git clean -fxd
|
||||
set -e
|
||||
'''
|
||||
return 1
|
||||
}
|
||||
def sync_source_win() {
|
||||
bat '''
|
||||
hostname
|
||||
taskkill /f /t /im taosd.exe
|
||||
ipconfig
|
||||
set
|
||||
date /t
|
||||
time /t
|
||||
'''
|
||||
bat '''
|
||||
echo %branch_name%
|
||||
cd %WIN_TDENGINE_ROOT_DIR%
|
||||
git reset --hard
|
||||
git fetch || git fetch
|
||||
git checkout -f ''' + env.BRANCH_NAME + '''
|
||||
git reset --hard origin/''' + env.BRANCH_NAME + '''
|
||||
git branch
|
||||
git restore .
|
||||
git remote prune origin
|
||||
git pull || git pull
|
||||
git log | head -n 20
|
||||
git clean -fxd
|
||||
'''
|
||||
return 1
|
||||
}
|
||||
pipeline {
|
||||
agent none
|
||||
parameters {
|
||||
choice(
|
||||
name: 'sourcePath',
|
||||
choices: ['nas','web'],
|
||||
description: 'Choice which way to download the installation pacakge;web is Office Web and nas means taos nas server '
|
||||
)
|
||||
choice(
|
||||
name: 'verMode',
|
||||
choices: ['enterprise','community'],
|
||||
description: 'Choice which types of package you want do check '
|
||||
)
|
||||
string (
|
||||
name:'version',
|
||||
defaultValue:'3.3.2.0',
|
||||
description: 'Release version number,eg: 3.0.0.1'
|
||||
)
|
||||
string (
|
||||
name:'baseVersion',
|
||||
defaultValue:'smoking',
|
||||
description: 'Tnas root path. eg:smoking, 3.3'
|
||||
)
|
||||
choice (
|
||||
name:'mode',
|
||||
choices: ['server','client'],
|
||||
description: 'Choose which mode of package you want do run '
|
||||
)
|
||||
choice (
|
||||
name:'smoke_branch',
|
||||
choices: ['test/3.0/smokeTest','test/main/smokeTest','test/3.1/smokeTest'],
|
||||
description: 'Choose which mode of package you want do run '
|
||||
)
|
||||
string (
|
||||
name:'runPlatforms',
|
||||
defaultValue:'server_Linux_x64, server_Linux_arm64, server_Windows_x64, server_Mac_x64',
|
||||
description: 'run package list hotfix usually run: server: server_Linux_x64, server_Linux_arm64 client: client_Linux_x64, client_Linux_arm64 release usually run: enterprise server: server_Linux_x64, server_Linux_arm64, server_Windows_x64 enterprise client: client_Linux_x64, client_Linux_arm64, client_Windows_x64 community server: server_Linux_x64, server_Linux_arm64, server_Mac_x64, server_Mac_arm64(not supported), server_Linux_x64_lite(not supported) community client: client_Linux_x64, client_Linux_arm64, client_Windows_x64, client_Mac_x64, client_Mac_arm64(not supported), client_Linux_x64_lite(not supported)'
|
||||
)
|
||||
}
|
||||
environment{
|
||||
WORK_DIR = "/var/lib/jenkins/workspace"
|
||||
TDINTERNAL_ROOT_DIR = '/var/lib/jenkins/workspace/TDinternal'
|
||||
TDENGINE_ROOT_DIR = '/var/lib/jenkins/workspace/TDinternal/community'
|
||||
BRANCH_NAME = "${smoke_branch}"
|
||||
}
|
||||
stages {
|
||||
stage ('Start Server for Client Test') {
|
||||
when {
|
||||
beforeAgent true
|
||||
expression { mode == 'client' }
|
||||
}
|
||||
agent{label " ubuntu18 "}
|
||||
steps {
|
||||
timeout(time: 30, unit: 'MINUTES'){
|
||||
sync_source("${BRANCH_NAME}")
|
||||
withEnv(['JENKINS_NODE_COOKIE=dontkillme']) {
|
||||
sh '''
|
||||
cd ${TDENGINE_ROOT_DIR}/packaging/smokeTest
|
||||
bash getAndRunInstaller.sh -m ${verMode} -f server -l false -c x64 -v ${version} -o ${baseVersion} -s ${sourcePath} -t tar
|
||||
bash start3NodesServer.sh
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('Run SmokeTest') {
|
||||
parallel {
|
||||
stage('server_Linux_x64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
expression { mode == 'server' }
|
||||
expression { runPlatforms.contains('server_Linux_x64') }
|
||||
}
|
||||
}
|
||||
agent{label " ubuntu16 "}
|
||||
steps {
|
||||
timeout(time: 30, unit: 'MINUTES'){
|
||||
sync_source("${BRANCH_NAME}")
|
||||
sh '''
|
||||
mkdir -p /var/www/html/${baseVersion}/${version}/${verMode}/json
|
||||
cd ${TDENGINE_ROOT_DIR}/packaging/smokeTest
|
||||
bash getAndRunInstaller.sh -m ${verMode} -f server -l false -c x64 -v ${version} -o ${baseVersion} -s ${sourcePath} -t tar
|
||||
python3 -m pytest test_server.py -v --html=/var/www/html/${baseVersion}/${version}/${verMode}/${mode}_linux_x64_report.html --json-report --json-report-file=report.json --timeout=300 --verMode=${verMode} --tVersion=${version} --baseVersion=${baseVersion} --sourcePath=${sourcePath} || true
|
||||
cp report.json /var/www/html/${baseVersion}/${version}/${verMode}/json/${mode}_linux_x64_report.json
|
||||
curl "http://192.168.0.176/api/addSmoke?version=${version}&tag=${baseVersion}&type=${verMode}&role=server&build=linux_x64"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('server_Linux_arm64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
expression { mode == 'server' }
|
||||
expression { runPlatforms.contains('server_Linux_arm64') }
|
||||
}
|
||||
}
|
||||
agent{label "worker06_arm64"}
|
||||
steps {
|
||||
timeout(time: 60, unit: 'MINUTES'){
|
||||
sync_source("${BRANCH_NAME}")
|
||||
sh '''
|
||||
cd ${TDENGINE_ROOT_DIR}/packaging/smokeTest
|
||||
bash getAndRunInstaller.sh -m ${verMode} -f server -l false -c arm64 -v ${version} -o ${baseVersion} -s ${sourcePath} -t tar
|
||||
python3 -m pytest test_server.py -v --html=${mode}_linux_arm64_report.html --json-report --json-report-file=report.json --timeout=600 --verMode=${verMode} --tVersion=${version} --baseVersion=${baseVersion} --sourcePath=${sourcePath} || true
|
||||
scp ${mode}_linux_arm64_report.html root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/
|
||||
scp report.json root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/json/${mode}_linux_arm64_report.json
|
||||
curl "http://192.168.0.176/api/addSmoke?version=${version}&tag=${baseVersion}&type=${verMode}&role=server&build=linux_arm64"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('server_Mac_x64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
expression { mode == 'server' }
|
||||
expression { runPlatforms.contains('server_Mac_x64') }
|
||||
}
|
||||
}
|
||||
agent{label " release_Darwin_x64 "}
|
||||
environment{
|
||||
WORK_DIR = "/Users/zwen/jenkins/workspace"
|
||||
TDINTERNAL_ROOT_DIR = '/Users/zwen/jenkins/workspace/TDinternal'
|
||||
TDENGINE_ROOT_DIR = '/Users/zwen/jenkins/workspace/TDinternal/community'
|
||||
}
|
||||
steps {
|
||||
timeout(time: 30, unit: 'MINUTES'){
|
||||
sync_source("${BRANCH_NAME}")
|
||||
sh '''
|
||||
cd ${TDENGINE_ROOT_DIR}/packaging/smokeTest
|
||||
bash getAndRunInstaller.sh -m ${verMode} -f server -l false -c x64 -v ${version} -o ${baseVersion} -s ${sourcePath} -t pkg
|
||||
python3 -m pytest -v -k linux --html=${mode}_Mac_x64_report.html --json-report --json-report-file=report.json --timeout=300 --verMode=${verMode} --tVersion=${version} --baseVersion=${baseVersion} --sourcePath=${sourcePath} || true
|
||||
scp ${mode}_Mac_x64_report.html root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/
|
||||
scp report.json root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/json/${mode}_Mac_x64_report.json
|
||||
curl "http://192.168.0.176/api/addSmoke?version=${version}&tag=${baseVersion}&type=${verMode}&role=server&build=Mac_x64"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('server_Mac_arm64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
expression { mode == 'server' }
|
||||
expression { runPlatforms.contains('server_Mac_arm64') }
|
||||
}
|
||||
}
|
||||
agent{label " release_Darwin_arm64 "}
|
||||
environment{
|
||||
WORK_DIR = "/Users/zwen/jenkins/workspace"
|
||||
TDINTERNAL_ROOT_DIR = '/Users/zwen/jenkins/workspace/TDinternal'
|
||||
TDENGINE_ROOT_DIR = '/Users/zwen/jenkins/workspace/TDinternal/community'
|
||||
}
|
||||
steps {
|
||||
timeout(time: 30, unit: 'MINUTES'){
|
||||
sync_source("${BRANCH_NAME}")
|
||||
sh '''
|
||||
cd ${TDENGINE_ROOT_DIR}/packaging/smokeTest
|
||||
bash getAndRunInstaller.sh -m ${verMode} -f server -l false -c arm64 -v ${version} -o ${baseVersion} -s ${sourcePath} -t pkg
|
||||
python3 -m pytest -v -k linux --html=${mode}_Mac_arm64_report.html --json-report --json-report-file=report.json --timeout=300 --verMode=${verMode} --tVersion=${version} --baseVersion=${baseVersion} --sourcePath=${sourcePath} || true
|
||||
scp ${mode}_Mac_arm64_report.html root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/
|
||||
scp report.json root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/json/${mode}_Mac_arm64_report.json
|
||||
curl "http://192.168.0.176/api/addSmoke?version=${version}&tag=${baseVersion}&type=${verMode}&role=server&build=Mac_arm64"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('server_Windows_x64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
expression { mode == 'server' }
|
||||
expression { runPlatforms.contains('server_Windows_x64') }
|
||||
}
|
||||
}
|
||||
agent{label " windows11 "}
|
||||
environment{
|
||||
WIN_WORK_DIR="C:\\workspace"
|
||||
WIN_TDINTERNAL_ROOT_DIR="C:\\workspace\\TDinternal"
|
||||
WIN_TDENGINE_ROOT_DIR="C:\\workspace\\TDinternal\\community"
|
||||
}
|
||||
steps {
|
||||
timeout(time: 30, unit: 'MINUTES'){
|
||||
sync_source_win()
|
||||
bat '''
|
||||
cd %WIN_TDENGINE_ROOT_DIR%\\packaging\\smokeTest
|
||||
call getAndRunInstaller.bat %baseVersion% %version% %verMode% server
|
||||
cd %WIN_TDENGINE_ROOT_DIR%\\packaging\\smokeTest
|
||||
pip3 install -r pytest_require.txt
|
||||
python3 -m pytest test_server.py -v --html=%mode%_Windows_x64_report.html --json-report --json-report-file=report.json --timeout=300 --verMode=%verMode% --tVersion=%version% --baseVersion=%baseVersion% --sourcePath=%sourcePath%
|
||||
scp %mode%_Windows_x64_report.html root@192.168.0.21:/var/www/html/%baseVersion%/%version%/%verMode%/
|
||||
scp report.json root@192.168.0.21:/var/www/html/%baseVersion%/%version%/%verMode%/json/%mode%_Windows_x64_report.json
|
||||
curl "http://192.168.0.176/api/addSmoke?version=%version%&tag=%baseVersion%&type=%verMode%&role=server&build=Windows_x64"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('client_Linux_x64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
expression { mode == 'client' }
|
||||
expression { runPlatforms.contains('client_Linux_x64') }
|
||||
}
|
||||
}
|
||||
agent{label " ubuntu16 "}
|
||||
steps {
|
||||
timeout(time: 30, unit: 'MINUTES'){
|
||||
sync_source("${BRANCH_NAME}")
|
||||
sh '''
|
||||
mkdir -p /var/www/html/${baseVersion}/${version}/${verMode}/json
|
||||
cd ${TDENGINE_ROOT_DIR}/packaging/smokeTest
|
||||
bash getAndRunInstaller.sh -m ${verMode} -f client -l false -c x64 -v ${version} -o ${baseVersion} -s ${sourcePath} -t tar
|
||||
python3 -m pytest test_client.py -v --html=/var/www/html/${baseVersion}/${version}/${verMode}/${mode}_linux_x64_report.html --json-report --json-report-file=report.json --timeout=300 --verMode=${verMode} --tVersion=${version} --baseVersion=${baseVersion} --sourcePath=${sourcePath} || true
|
||||
cp report.json /var/www/html/${baseVersion}/${version}/${verMode}/json/${mode}_linux_x64_report.json
|
||||
curl "http://192.168.0.176/api/addSmoke?version=${version}&tag=${baseVersion}&type=${verMode}&role=client&build=linux_x64"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('client_Linux_arm64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
expression { mode == 'client' }
|
||||
expression { runPlatforms.contains('client_Linux_arm64') }
|
||||
}
|
||||
}
|
||||
agent{label " worker06_arm64 "}
|
||||
steps {
|
||||
timeout(time: 30, unit: 'MINUTES'){
|
||||
sync_source("${BRANCH_NAME}")
|
||||
sh '''
|
||||
cd ${TDENGINE_ROOT_DIR}/packaging/smokeTest
|
||||
bash getAndRunInstaller.sh -m ${verMode} -f client -l false -c arm64 -v ${version} -o ${baseVersion} -s ${sourcePath} -t tar
|
||||
python3 -m pytest test_client.py -v --html=${mode}_linux_arm64_report.html --json-report --json-report-file=report.json --timeout=300 --verMode=${verMode} --tVersion=${version} --baseVersion=${baseVersion} --sourcePath=${sourcePath} || true
|
||||
scp ${mode}_linux_arm64_report.html root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/
|
||||
scp report.json root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/json/${mode}_linux_arm64_report.json
|
||||
curl "http://192.168.0.176/api/addSmoke?version=${version}&tag=${baseVersion}&type=${verMode}&role=client&build=linux_arm64"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('client_Mac_x64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
expression { mode == 'client' }
|
||||
expression { runPlatforms.contains('client_Mac_x64') }
|
||||
}
|
||||
}
|
||||
agent{label " release_Darwin_x64 "}
|
||||
environment{
|
||||
WORK_DIR = "/Users/zwen/jenkins/workspace"
|
||||
TDINTERNAL_ROOT_DIR = '/Users/zwen/jenkins/workspace/TDinternal'
|
||||
TDENGINE_ROOT_DIR = '/Users/zwen/jenkins/workspace/TDinternal/community'
|
||||
}
|
||||
steps {
|
||||
timeout(time: 30, unit: 'MINUTES'){
|
||||
sync_source("${BRANCH_NAME}")
|
||||
sh '''
|
||||
cd ${TDENGINE_ROOT_DIR}/packaging/smokeTest
|
||||
bash getAndRunInstaller.sh -m ${verMode} -f client -l false -c x64 -v ${version} -o ${baseVersion} -s ${sourcePath} -t pkg
|
||||
rm -rf /opt/taos/main/TDinternal/debug/* || true
|
||||
python3 -m pytest test_client.py -v --html=${mode}_Mac_x64_report.html --json-report --json-report-file=report.json --timeout=300 --verMode=${verMode} --tVersion=${version} --baseVersion=${baseVersion} --sourcePath=${sourcePath} || true
|
||||
scp ${mode}_Mac_x64_report.html root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/
|
||||
scp report.json root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/json/${mode}_Mac_x64_report.json
|
||||
curl "http://192.168.0.176/api/addSmoke?version=${version}&tag=${baseVersion}&type=${verMode}&role=client&build=Mac_x64"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ('client_Mac_arm64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
expression { mode == 'client' }
|
||||
expression { runPlatforms.contains('client_Mac_arm64') }
|
||||
}
|
||||
}
|
||||
agent{label " release_Darwin_arm64 "}
|
||||
environment{
|
||||
WORK_DIR = "/Users/zwen/jenkins/workspace"
|
||||
TDINTERNAL_ROOT_DIR = '/Users/zwen/jenkins/workspace/TDinternal'
|
||||
TDENGINE_ROOT_DIR = '/Users/zwen/jenkins/workspace/TDinternal/community'
|
||||
}
|
||||
steps {
|
||||
timeout(time: 30, unit: 'MINUTES'){
|
||||
sync_source("${BRANCH_NAME}")
|
||||
sh '''
|
||||
cd ${TDENGINE_ROOT_DIR}/packaging/smokeTest
|
||||
bash getAndRunInstaller.sh -m ${verMode} -f client -l false -c arm64 -v ${version} -o ${baseVersion} -s ${sourcePath} -t pkg
|
||||
rm -rf /opt/taos/main/TDinternal/debug/* || true
|
||||
python3 -m pytest test_client.py -v --html=${mode}_Mac_arm64_report.html --json-report --json-report-file=report.json --timeout=300 --verMode=${verMode} --tVersion=${version} --baseVersion=${baseVersion} --sourcePath=${sourcePath} || true
|
||||
scp ${mode}_Mac_arm64_report.html root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/
|
||||
scp report.json root@192.168.0.21:/var/www/html/${baseVersion}/${version}/${verMode}/json/${mode}_Mac_arm64_report.json
|
||||
curl "http://192.168.0.176/api/addSmoke?version=${version}&tag=${baseVersion}&type=${verMode}&role=client&build=Mac_arm64"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('client_Windows_x64') {
|
||||
when {
|
||||
beforeAgent true
|
||||
allOf {
|
||||
expression { mode == 'client' }
|
||||
expression { runPlatforms.contains('client_Windows_x64') }
|
||||
}
|
||||
}
|
||||
agent{label " windows71 "}
|
||||
environment{
|
||||
WIN_WORK_DIR="C:\\workspace"
|
||||
WIN_TDINTERNAL_ROOT_DIR="C:\\workspace\\TDinternal"
|
||||
WIN_TDENGINE_ROOT_DIR="C:\\workspace\\TDinternal\\community"
|
||||
}
|
||||
steps {
|
||||
timeout(time: 30, unit: 'MINUTES'){
|
||||
sync_source_win()
|
||||
bat '''
|
||||
cd %WIN_TDENGINE_ROOT_DIR%\\packaging\\smokeTest
|
||||
call getAndRunInstaller.bat %baseVersion% %version% %verMode% client
|
||||
pip3 install -r pytest_require.txt
|
||||
python3 -m pytest test_client.py -v --html=%mode%_Windows_x64_report.html --json-report --json-report-file=report.json --timeout=300 --verMode=%verMode% --tVersion=%version% --baseVersion=%baseVersion% --sourcePath=%sourcePath%
|
||||
scp %mode%_Windows_x64_report.html root@192.168.0.21:/var/www/html/%baseVersion%/%version%/%verMode%/
|
||||
scp report.json root@192.168.0.21:/var/www/html/%baseVersion%/%version%/%verMode%/json/%mode%_Windows_x64_report.json
|
||||
curl "http://192.168.0.176/api/addSmoke?version=%version%&tag=%baseVersion%&type=%verMode%&role=client&build=Windows_x64"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#!/bin/bash
|
||||
BUILD_ID=dontKillMe
|
||||
|
||||
#******This script setup 3 nodes env for remote client installer test. Only for Linux *********
|
||||
|
||||
pwd=`pwd`
|
||||
hostname=`hostname`
|
||||
if [ -z $JENKINS_HOME ]; then
|
||||
workdir="${pwd}/cluster"
|
||||
echo $workdir
|
||||
else
|
||||
workdir="${JENKINS_HOME}/workspace/cluster"
|
||||
echo $workdir
|
||||
fi
|
||||
|
||||
name="taos"
|
||||
if command -v prodb ;then
|
||||
name="prodb"
|
||||
fi
|
||||
|
||||
# Stop all taosd processes
|
||||
for(( i=0; i<3; i++))
|
||||
do
|
||||
pid=$(ps -ef | grep ${name}d | grep -v grep | awk '{print $2}')
|
||||
if [ -n "$pid" ]; then
|
||||
${csudo}kill -9 $pid || :
|
||||
fi
|
||||
done
|
||||
|
||||
# Init 3 dnodes workdir and config file
|
||||
rm -rf ${workdir}
|
||||
mkdir ${workdir}
|
||||
mkdir ${workdir}/output
|
||||
mkdir ${workdir}/dnode1
|
||||
mkdir ${workdir}/dnode1/data
|
||||
mkdir ${workdir}/dnode1/log
|
||||
mkdir ${workdir}/dnode1/cfg
|
||||
touch ${workdir}/dnode1/cfg/${name}.cfg
|
||||
echo -e "firstEp ${hostname}:6031\nsecondEp ${hostname}:6032\nfqdn ${hostname}\nserverPort 6031\nlogDir ${workdir}/dnode1/log\ndataDir ${workdir}/dnode1/data\n" >> ${workdir}/dnode1/cfg/${name}.cfg
|
||||
|
||||
# Start first node
|
||||
nohup ${name}d -c ${workdir}/dnode1/cfg/${name}.cfg & > /dev/null
|
||||
sleep 5
|
||||
|
||||
${name} -P 6031 -s "CREATE DNODE \`${hostname}:6032\`;CREATE DNODE \`${hostname}:6033\`"
|
||||
|
||||
mkdir ${workdir}/dnode2
|
||||
mkdir ${workdir}/dnode2/data
|
||||
mkdir ${workdir}/dnode2/log
|
||||
mkdir ${workdir}/dnode2/cfg
|
||||
touch ${workdir}/dnode2/cfg/${name}.cfg
|
||||
echo -e "firstEp ${hostname}:6031\nsecondEp ${hostname}:6032\nfqdn ${hostname}\nserverPort 6032\nlogDir ${workdir}/dnode2/log\ndataDir ${workdir}/dnode2/data\n" >> ${workdir}/dnode2/cfg/${name}.cfg
|
||||
|
||||
nohup ${name}d -c ${workdir}/dnode2/cfg/${name}.cfg & > /dev/null
|
||||
sleep 5
|
||||
|
||||
mkdir ${workdir}/dnode3
|
||||
mkdir ${workdir}/dnode3/data
|
||||
mkdir ${workdir}/dnode3/log
|
||||
mkdir ${workdir}/dnode3/cfg
|
||||
touch ${workdir}/dnode3/cfg/${name}.cfg
|
||||
echo -e "firstEp ${hostname}:6031\nsecondEp ${hostname}:6032\nfqdn ${hostname}\nserverPort 6033\nlogDir ${workdir}/dnode3/log\ndataDir ${workdir}/dnode3/data\n" >> ${workdir}/dnode3/cfg/${name}.cfg
|
||||
|
||||
nohup ${name}d -c ${workdir}/dnode3/cfg/${name}.cfg & > /dev/null
|
||||
sleep 5
|
||||
|
||||
${name} -P 6031 -s "CREATE MNODE ON DNODE 2;CREATE MNODE ON DNODE 3;"
|
|
@ -0,0 +1,137 @@
|
|||
import pytest
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import getopt
|
||||
import re
|
||||
import time
|
||||
import taos
|
||||
from versionCheckAndUninstallforPytest import UninstallTaos
|
||||
|
||||
# python3 smokeTestClient.py -h 192.168.0.22 -P 6031 -v ${version} -u
|
||||
|
||||
OEM = ["ProDB"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def get_config(request):
|
||||
verMode = request.config.getoption("--verMode")
|
||||
taosVersion = request.config.getoption("--tVersion")
|
||||
baseVersion = request.config.getoption("--baseVersion")
|
||||
sourcePath = request.config.getoption("--sourcePath")
|
||||
config = {
|
||||
"verMode": verMode,
|
||||
"taosVersion": taosVersion,
|
||||
"baseVersion": baseVersion,
|
||||
"sourcePath": sourcePath,
|
||||
"system": platform.system(),
|
||||
"arch": platform.machine(),
|
||||
"serverHost": "192.168.0.22",
|
||||
"serverPort": 6031,
|
||||
"databaseName": re.sub(r'[^a-zA-Z0-9]', '', subprocess.getoutput("hostname")).lower()
|
||||
}
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def setup_module(get_config):
|
||||
config = get_config
|
||||
# install taospy
|
||||
if config["system"] == 'Windows':
|
||||
taospy_version = subprocess.getoutput("pip3 show taospy|findstr Version")
|
||||
else:
|
||||
taospy_version = subprocess.getoutput("pip3 show taospy|grep Version| awk -F ':' '{print $2}' ")
|
||||
|
||||
print("taospy version %s " % taospy_version)
|
||||
if taospy_version == "":
|
||||
subprocess.getoutput("pip3 install git+https://github.com/taosdata/taos-connector-python.git")
|
||||
print("install taos python connector")
|
||||
else:
|
||||
subprocess.getoutput("pip3 install taospy")
|
||||
|
||||
|
||||
def get_connect(host, port, database=None):
|
||||
conn = taos.connect(host=host,
|
||||
user="root",
|
||||
password="taosdata",
|
||||
database=database,
|
||||
port=port,
|
||||
timezone="Asia/Shanghai") # default your host's timezone
|
||||
return conn
|
||||
|
||||
|
||||
def run_cmd(command):
|
||||
print("CMD: %s" % command)
|
||||
result = subprocess.run(command, capture_output=True, text=True, shell=True)
|
||||
print("STDOUT:", result.stdout)
|
||||
print("STDERR:", result.stderr)
|
||||
print("Return Code:", result.returncode)
|
||||
assert result.returncode == 0
|
||||
return result
|
||||
|
||||
|
||||
class TestClient:
|
||||
@pytest.mark.all
|
||||
def test_basic(self, get_config, setup_module):
|
||||
config = get_config
|
||||
name = "taos"
|
||||
|
||||
if config["baseVersion"] in OEM:
|
||||
name = config["baseVersion"].lower()
|
||||
if config["baseVersion"] in OEM and config["system"] == 'Windows':
|
||||
cmd = f'{name} -s "create database {config["databaseName"]};" -h {config["serverHost"]} -P {config["serverPort"]}'
|
||||
run_cmd(cmd)
|
||||
cmd = f'{name} -s "CREATE STABLE {config["databaseName"]}.meters (`ts` TIMESTAMP,`current` FLOAT, `phase` FLOAT) TAGS (`groupid` INT, `location` VARCHAR(24));" -h {config["serverHost"]} -P {config["serverPort"]}'
|
||||
run_cmd(cmd)
|
||||
else:
|
||||
cmd = f'{name}Benchmark -y -a 3 -n 100 -t 100 -d {config["databaseName"]} -h {config["serverHost"]} -P {config["serverPort"]} &'
|
||||
run_cmd(cmd)
|
||||
# os.system("taosBenchmark -y -a 3 -n 100 -t 100 -d %s -h %s -P %d" % (databaseName, serverHost, serverPort))
|
||||
time.sleep(5)
|
||||
conn = get_connect(config["serverHost"], config["serverPort"], config["databaseName"])
|
||||
sql = "SELECT count(*) from meters"
|
||||
result: taos.TaosResult = conn.query(sql)
|
||||
data = result.fetch_all()
|
||||
print("SQL: %s" % sql)
|
||||
print("Result: %s" % data)
|
||||
if config["system"] == 'Windows' and config["baseVersion"] in OEM:
|
||||
pass
|
||||
elif data[0][0] != 10000:
|
||||
raise f"{name}Benchmark work not as expected "
|
||||
# drop database of test
|
||||
cmd = f'{name} -s "drop database {config["databaseName"]};" -h {config["serverHost"]} -P {config["serverPort"]}'
|
||||
result = run_cmd(cmd)
|
||||
assert "Drop OK" in result.stdout
|
||||
conn.close()
|
||||
|
||||
@pytest.mark.all
|
||||
def test_version(self, get_config, setup_module):
|
||||
config = get_config
|
||||
conn = get_connect(config["serverHost"], config["serverPort"])
|
||||
server_version = conn.server_info
|
||||
print("server_version: ", server_version)
|
||||
client_version = conn.client_info
|
||||
print("client_version: ", client_version)
|
||||
name = "taos"
|
||||
if config["baseVersion"] in OEM:
|
||||
name = config["baseVersion"].lower()
|
||||
if config["system"] == "Windows":
|
||||
taos_V_output = subprocess.getoutput(f"{name} -V | findstr version")
|
||||
else:
|
||||
taos_V_output = subprocess.getoutput(f"{name} -V | grep version")
|
||||
assert config["taosVersion"] in taos_V_output
|
||||
assert config["taosVersion"] in client_version
|
||||
if config["taosVersion"] not in server_version:
|
||||
print("warning: client version is not same as server version")
|
||||
conn.close()
|
||||
|
||||
@pytest.mark.all
|
||||
def test_uninstall(self, get_config, setup_module):
|
||||
config = get_config
|
||||
name = "taos"
|
||||
if config["baseVersion"] in OEM:
|
||||
name = config["baseVersion"].lower()
|
||||
subprocess.getoutput("rm /usr/local/bin/taos")
|
||||
subprocess.getoutput("pkill taosd")
|
||||
UninstallTaos(config["taosVersion"], config["verMode"], True, name)
|
|
@ -0,0 +1,240 @@
|
|||
import pytest
|
||||
import subprocess
|
||||
import os
|
||||
from versionCheckAndUninstallforPytest import UninstallTaos
|
||||
import platform
|
||||
import re
|
||||
import time
|
||||
import signal
|
||||
|
||||
system = platform.system()
|
||||
current_path = os.path.abspath(os.path.dirname(__file__))
|
||||
if system == 'Windows':
|
||||
with open(r"%s\test_server_windows_case" % current_path) as f:
|
||||
cases = f.read().splitlines()
|
||||
else:
|
||||
with open("%s/test_server_unix_case" % current_path) as f:
|
||||
cases = f.read().splitlines()
|
||||
|
||||
OEM = ["ProDB"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def get_config(request):
|
||||
verMode = request.config.getoption("--verMode")
|
||||
taosVersion = request.config.getoption("--tVersion")
|
||||
baseVersion = request.config.getoption("--baseVersion")
|
||||
sourcePath = request.config.getoption("--sourcePath")
|
||||
config = {
|
||||
"verMode": verMode,
|
||||
"taosVersion": taosVersion,
|
||||
"baseVersion": baseVersion,
|
||||
"sourcePath": sourcePath,
|
||||
"system": platform.system(),
|
||||
"arch": platform.machine()
|
||||
}
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def setup_module(get_config):
|
||||
def run_cmd(command):
|
||||
print("CMD:", command)
|
||||
result = subprocess.run(command, capture_output=True, text=True, shell=True)
|
||||
print("STDOUT:", result.stdout)
|
||||
print("STDERR:", result.stderr)
|
||||
print("Return Code:", result.returncode)
|
||||
assert result.returncode == 0
|
||||
return result
|
||||
|
||||
# setup before module tests
|
||||
config = get_config
|
||||
# bash getAndRunInstaller.sh -m ${verMode} -f server -l false -c x64 -v ${version} -o ${baseVersion} -s ${sourcePath} -t tar
|
||||
# t = "tar"
|
||||
# if config["system"] == "Darwin":
|
||||
# t = "pkg"
|
||||
# cmd = "bash getAndRunInstaller.sh -m %s -f server -l false -c x64 -v %s -o %s -s %s -t %s" % (
|
||||
# config["verMode"], config["taosVersion"], config["baseVersion"], config["sourcePath"], t)
|
||||
# run_cmd(cmd)
|
||||
if config["system"] == "Windows":
|
||||
cmd = r"mkdir ..\..\debug\build\bin"
|
||||
else:
|
||||
cmd = "mkdir -p ../../debug/build/bin/"
|
||||
subprocess.getoutput(cmd)
|
||||
if config["system"] == "Linux" or config["system"] == "Darwin" : # add tmq_sim
|
||||
cmd = "cp -rf ../../../debug/build/bin/tmq_sim ../../debug/build/bin/."
|
||||
subprocess.getoutput(cmd)
|
||||
if config["system"] == "Darwin":
|
||||
cmd = "sudo cp -rf /usr/local/bin/taos* ../../debug/build/bin/"
|
||||
elif config["system"] == "Windows":
|
||||
cmd = r"xcopy C:\TDengine\taos*.exe ..\..\debug\build\bin /Y"
|
||||
else:
|
||||
if config["baseVersion"] in OEM:
|
||||
cmd = '''sudo find /usr/bin -name 'prodb*' -exec sh -c 'for file; do cp "$file" "../../debug/build/bin/taos${file##/usr/bin/%s}"; done' sh {} +''' % (
|
||||
config["baseVersion"].lower())
|
||||
else:
|
||||
cmd = "sudo cp /usr/bin/taos* ../../debug/build/bin/"
|
||||
run_cmd(cmd)
|
||||
if config["baseVersion"] in OEM: # mock OEM
|
||||
cmd = "sed -i 's/taos.cfg/%s.cfg/g' ../../tests/pytest/util/dnodes.py" % config["baseVersion"].lower()
|
||||
run_cmd(cmd)
|
||||
cmd = "sed -i 's/taosdlog.0/%sdlog.0/g' ../../tests/pytest/util/dnodes.py" % config["baseVersion"].lower()
|
||||
run_cmd(cmd)
|
||||
cmd = "sed -i 's/taos.cfg/%s.cfg/g' ../../tests/army/frame/server/dnode.py" % config["baseVersion"].lower()
|
||||
run_cmd(cmd)
|
||||
cmd = "sed -i 's/taosdlog.0/%sdlog.0/g' ../../tests/army/frame/server/dnode.py" % config["baseVersion"].lower()
|
||||
run_cmd(cmd)
|
||||
cmd = "ln -s /usr/bin/prodb /usr/local/bin/taos"
|
||||
subprocess.getoutput(cmd)
|
||||
|
||||
# yield
|
||||
#
|
||||
# name = "taos"
|
||||
# if config["baseVersion"] in OEM:
|
||||
# name = config["baseVersion"].lower()
|
||||
# subprocess.getoutput("rm /usr/local/bin/taos")
|
||||
# subprocess.getoutput("pkill taosd")
|
||||
# UninstallTaos(config["taosVersion"], config["verMode"], True, name)
|
||||
|
||||
|
||||
# use pytest fixture to exec case
|
||||
@pytest.fixture(params=cases)
|
||||
def run_command(request):
|
||||
commands = request.param
|
||||
if commands.strip().startswith("#"):
|
||||
pytest.skip("This case has been marked as skipped")
|
||||
d, command = commands.strip().split(",")
|
||||
if system == "Windows":
|
||||
cmd = r"cd %s\..\..\tests\%s && %s" % (current_path, d, command)
|
||||
else:
|
||||
cmd = "cd %s/../../tests/%s&&sudo %s" % (current_path, d, command)
|
||||
print(cmd)
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, shell=True)
|
||||
return {
|
||||
"command": command,
|
||||
"stdout": result.stdout,
|
||||
"stderr": result.stderr,
|
||||
"returncode": result.returncode
|
||||
}
|
||||
|
||||
|
||||
class TestServer:
|
||||
@pytest.mark.all
|
||||
def test_taosd_up(self, setup_module):
|
||||
# start process
|
||||
if system == 'Windows':
|
||||
subprocess.getoutput("taskkill /IM taosd.exe /F")
|
||||
cmd = "..\\..\\debug\\build\\bin\\taosd.exe"
|
||||
else:
|
||||
subprocess.getoutput("pkill taosd")
|
||||
cmd = "../../debug/build/bin/taosd"
|
||||
process = subprocess.Popen(
|
||||
[cmd],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
# monitor output
|
||||
while True:
|
||||
line = process.stdout.readline()
|
||||
if line:
|
||||
print(line.strip())
|
||||
if "succeed to write dnode" in line:
|
||||
time.sleep(5)
|
||||
# 发送终止信号
|
||||
os.kill(process.pid, signal.SIGKILL)
|
||||
# Waiting for the process to be completely killed
|
||||
time.sleep(5)
|
||||
break
|
||||
|
||||
@pytest.mark.all
|
||||
def test_execute_cases(self, setup_module, run_command):
|
||||
# assert the result
|
||||
if run_command['returncode'] != 0:
|
||||
print(f"Running command: {run_command['command']}")
|
||||
print("STDOUT:", run_command['stdout'])
|
||||
print("STDERR:", run_command['stderr'])
|
||||
print("Return Code:", run_command['returncode'])
|
||||
else:
|
||||
print(f"Running command: {run_command['command']}")
|
||||
if len(run_command['stdout']) > 1000:
|
||||
print("STDOUT:", run_command['stdout'][:1000] + "...")
|
||||
else:
|
||||
print("STDOUT:", run_command['stdout'])
|
||||
print("STDERR:", run_command['stderr'])
|
||||
print("Return Code:", run_command['returncode'])
|
||||
|
||||
assert run_command[
|
||||
'returncode'] == 0, f"Command '{run_command['command']}' failed with return code {run_command['returncode']}"
|
||||
|
||||
@pytest.mark.all
|
||||
@pytest.mark.check_version
|
||||
def test_check_version(self, get_config, setup_module):
|
||||
config = get_config
|
||||
databaseName = re.sub(r'[^a-zA-Z0-9]', '', subprocess.getoutput("hostname")).lower()
|
||||
# install taospy
|
||||
taospy_version = ""
|
||||
system = config["system"]
|
||||
version = config["taosVersion"]
|
||||
verMode = config["verMode"]
|
||||
if system == 'Windows':
|
||||
taospy_version = subprocess.getoutput("pip3 show taospy|findstr Version")
|
||||
else:
|
||||
taospy_version = subprocess.getoutput("pip3 show taospy|grep Version| awk -F ':' '{print $2}' ")
|
||||
|
||||
print("taospy version %s " % taospy_version)
|
||||
if taospy_version == "":
|
||||
subprocess.getoutput("pip3 install git+https://github.com/taosdata/taos-connector-python.git")
|
||||
print("install taos python connector")
|
||||
else:
|
||||
subprocess.getoutput("pip3 install taospy")
|
||||
|
||||
# start taosd server
|
||||
if system == 'Windows':
|
||||
cmd = ["C:\\TDengine\\start-all.bat"]
|
||||
# elif system == 'Linux':
|
||||
# cmd = "systemctl start taosd".split(' ')
|
||||
else:
|
||||
# cmd = "sudo launchctl start com.tdengine.taosd".split(' ')
|
||||
cmd = "start-all.sh"
|
||||
process_out = subprocess.Popen(cmd,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||
print(cmd)
|
||||
time.sleep(5)
|
||||
|
||||
import taos
|
||||
conn = taos.connect()
|
||||
check_list = {}
|
||||
check_list["server_version"] = conn.server_info
|
||||
check_list["client_version"] = conn.client_info
|
||||
# Execute sql get version info
|
||||
result: taos.TaosResult = conn.query("SELECT server_version()")
|
||||
check_list["select_server"] = result.fetch_all()[0][0]
|
||||
result: taos.TaosResult = conn.query("SELECT client_version()")
|
||||
check_list["select_client"] = result.fetch_all()[0][0]
|
||||
conn.close()
|
||||
|
||||
binary_files = ["taos", "taosd", "taosadapter", "taoskeeper", "taosBenchmark"]
|
||||
if verMode.lower() == "enterprise":
|
||||
binary_files.append("taosx")
|
||||
if config["baseVersion"] in OEM:
|
||||
binary_files = [i.replace("taos", config["baseVersion"].lower()) for i in binary_files]
|
||||
if system == "Windows":
|
||||
for i in binary_files:
|
||||
check_list[i] = subprocess.getoutput("%s -V | findstr version" % i)
|
||||
else:
|
||||
for i in binary_files:
|
||||
check_list[i] = subprocess.getoutput("%s -V | grep version | awk -F ' ' '{print $3}'" % i)
|
||||
for i in check_list:
|
||||
print("%s version is: %s" % (i, check_list[i]))
|
||||
assert version in check_list[i]
|
||||
|
||||
@pytest.mark.all
|
||||
def test_uninstall(self, get_config, setup_module):
|
||||
config = get_config
|
||||
name = "taos"
|
||||
if config["baseVersion"] in OEM:
|
||||
name = config["baseVersion"].lower()
|
||||
subprocess.getoutput("rm /usr/local/bin/taos")
|
||||
subprocess.getoutput("pkill taosd")
|
||||
UninstallTaos(config["taosVersion"], config["verMode"], True, name)
|
|
@ -0,0 +1,10 @@
|
|||
system-test,python3 ./test.py -f 2-query/join.py
|
||||
system-test,python3 ./test.py -f 1-insert/insert_column_value.py
|
||||
system-test,python3 ./test.py -f 2-query/primary_ts_base_5.py
|
||||
system-test,python3 ./test.py -f 2-query/case_when.py
|
||||
system-test,python3 ./test.py -f 2-query/partition_limit_interval.py
|
||||
system-test,python3 ./test.py -f 2-query/fill.py
|
||||
army,python3 ./test.py -f query/query_basic.py -N 3
|
||||
system-test,python3 ./test.py -f 7-tmq/basic5.py
|
||||
system-test,python3 ./test.py -f 8-stream/stream_basic.py
|
||||
system-test,python3 ./test.py -f 6-cluster/5dnode3mnodeStop.py -N 5 -M 3
|
|
@ -0,0 +1,2 @@
|
|||
system-test,python3 .\test.py -f 0-others\taosShell.py
|
||||
system-test,python3 .\test.py -f 6-cluster\5dnode3mnodeSep1VnodeStopDnodeModifyMeta.py -N 6 -M 3
|
|
@ -0,0 +1,260 @@
|
|||
#!/usr/bin/python
|
||||
###################################################################
|
||||
# Copyright (c) 2016 by TAOS Technologies, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is proprietary and confidential to TAOS Technologies.
|
||||
# No part of this file may be reproduced, stored, transmitted,
|
||||
# disclosed or used in any form or by any means other than as
|
||||
# expressly provided by the written permission from Jianhui Tao
|
||||
#
|
||||
###################################################################
|
||||
# install pip
|
||||
# pip install src/connector/python/
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys, os
|
||||
import re
|
||||
import platform
|
||||
import getopt
|
||||
import subprocess
|
||||
# from this import d
|
||||
import time
|
||||
|
||||
# input for server
|
||||
|
||||
opts, args = getopt.gnu_getopt(sys.argv[1:], 'v:m:u', ['version=', 'verMode='])
|
||||
serverHost = ""
|
||||
serverPort = 0
|
||||
version = ""
|
||||
uninstall = False
|
||||
verMode = ""
|
||||
for key, value in opts:
|
||||
if key in ['--help']:
|
||||
print('A collection of test cases written using Python')
|
||||
print('-v test client version')
|
||||
print('-u test uninstall process, will uninstall TDengine')
|
||||
sys.exit(0)
|
||||
|
||||
if key in ['-v']:
|
||||
version = value
|
||||
if key in ['-u']:
|
||||
uninstall = True
|
||||
if key in ['-m']:
|
||||
verMode = value
|
||||
if not version:
|
||||
print("No version specified, will not run version check.")
|
||||
|
||||
|
||||
system = platform.system()
|
||||
arch = platform.machine()
|
||||
|
||||
databaseName = re.sub(r'[^a-zA-Z0-9]', '', subprocess.getoutput("hostname")).lower()
|
||||
# install taospy
|
||||
taospy_version = ""
|
||||
if system == 'Windows':
|
||||
taospy_version = subprocess.getoutput("pip3 show taospy|findstr Version")
|
||||
else:
|
||||
taospy_version = subprocess.getoutput("pip3 show taospy|grep Version| awk -F ':' '{print $2}' ")
|
||||
|
||||
print("taospy version %s " % taospy_version)
|
||||
if taospy_version == "":
|
||||
subprocess.getoutput("pip3 install git+https://github.com/taosdata/taos-connector-python.git")
|
||||
print("install taos python connector")
|
||||
else:
|
||||
subprocess.getoutput("pip3 install taospy")
|
||||
|
||||
# start taosd server
|
||||
if system == 'Windows':
|
||||
cmd = ["C:\\TDengine\\start-all.bat"]
|
||||
elif system == 'Linux':
|
||||
cmd = "systemctl start taosd".split(' ')
|
||||
else:
|
||||
cmd = "sudo launchctl start com.tdengine.taosd".split(' ')
|
||||
process_out = subprocess.Popen(cmd,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||
print(cmd)
|
||||
time.sleep(5)
|
||||
|
||||
#get taosc version info
|
||||
version_test_result = False
|
||||
if version:
|
||||
import taos
|
||||
conn = taos.connect()
|
||||
server_version = conn.server_info
|
||||
print("server_version", server_version)
|
||||
client_version = conn.client_info
|
||||
print("client_version", client_version)
|
||||
# Execute sql get version info
|
||||
result: taos.TaosResult = conn.query("SELECT server_version()")
|
||||
select_server = result.fetch_all()[0][0]
|
||||
print("SELECT server_version():" + select_server)
|
||||
result: taos.TaosResult = conn.query("SELECT client_version()")
|
||||
select_client = result.fetch_all()[0][0]
|
||||
print("SELECT client_version():" + select_client)
|
||||
conn.close()
|
||||
|
||||
taos_V_output = ""
|
||||
taosd_V_output = ""
|
||||
taosadapter_V_output = ""
|
||||
taoskeeper_V_output = ""
|
||||
taosx_V_output = ""
|
||||
taosB_V_output = ""
|
||||
taosxVersion = False
|
||||
if system == "Windows":
|
||||
taos_V_output = subprocess.getoutput("taos -V | findstr version")
|
||||
taosd_V_output = subprocess.getoutput("taosd -V | findstr version")
|
||||
taosadapter_V_output = subprocess.getoutput("taosadapter -V | findstr version")
|
||||
taoskeeper_V_output = subprocess.getoutput("taoskeeper -V | findstr version")
|
||||
taosB_V_output = subprocess.getoutput("taosBenchmark -V | findstr version")
|
||||
if verMode == "Enterprise":
|
||||
taosx_V_output = subprocess.getoutput("taosx -V | findstr version")
|
||||
else:
|
||||
taos_V_output = subprocess.getoutput("taos -V | grep version | awk -F ' ' '{print $3}'")
|
||||
taosd_V_output = subprocess.getoutput("taosd -V | grep version | awk -F ' ' '{print $3}'")
|
||||
taosadapter_V_output = subprocess.getoutput("taosadapter -V | grep version | awk -F ' ' '{print $3}'")
|
||||
taoskeeper_V_output = subprocess.getoutput("taoskeeper -V | grep version | awk -F ' ' '{print $3}'")
|
||||
taosB_V_output = subprocess.getoutput("taosBenchmark -V | grep version | awk -F ' ' '{print $3}'")
|
||||
if verMode == "Enterprise":
|
||||
taosx_V_output = subprocess.getoutput("taosx -V | grep version | awk -F ' ' '{print $3}'")
|
||||
|
||||
print("taos -V output is: %s" % taos_V_output)
|
||||
print("taosd -V output is: %s" % taosd_V_output)
|
||||
print("taosadapter -V output is: %s" % taosadapter_V_output)
|
||||
print("taoskeeper -V output is: %s" % taoskeeper_V_output)
|
||||
print("taosBenchmark -V output is: %s" % taosB_V_output)
|
||||
if verMode == "Enterprise":
|
||||
print("taosx -V output is: %s" % taosx_V_output)
|
||||
taosxVersion = version in taosx_V_output
|
||||
else:
|
||||
taosxVersion = True
|
||||
if (version in client_version
|
||||
and version in server_version
|
||||
and version in select_server
|
||||
and version in select_client
|
||||
and version in taos_V_output
|
||||
and version in taosd_V_output
|
||||
and version in taosadapter_V_output
|
||||
and version in taoskeeper_V_output
|
||||
and version in taosB_V_output
|
||||
and taosxVersion
|
||||
):
|
||||
version_test_result = True
|
||||
leftFile = False
|
||||
if uninstall:
|
||||
print("Start to run rmtaos")
|
||||
print("Platform: ", system)
|
||||
# stop taosd server
|
||||
if system == 'Windows':
|
||||
cmd = "C:\\TDengine\\stop_all.bat"
|
||||
elif system == 'Linux':
|
||||
cmd = "systemctl stop taosd"
|
||||
else:
|
||||
cmd = "sudo launchctl stop com.tdengine.taosd"
|
||||
process_out = subprocess.getoutput(cmd)
|
||||
print(cmd)
|
||||
time.sleep(10)
|
||||
if system == "Linux":
|
||||
# 创建一个subprocess.Popen对象,并使用stdin和stdout进行交互
|
||||
process = subprocess.Popen(['rmtaos'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||
# 向子进程发送输入
|
||||
process.stdin.write("y\n")
|
||||
process.stdin.flush() # 确保输入被发送到子进程
|
||||
process.stdin.write("I confirm that I would like to delete all data, log and configuration files\n")
|
||||
process.stdin.flush() # 确保输入被发送到子进程
|
||||
# 关闭子进程的stdin,防止它无限期等待更多输入
|
||||
process.stdin.close()
|
||||
# 等待子进程结束
|
||||
process.wait()
|
||||
# 检查目录清除情况
|
||||
out = subprocess.getoutput("ls /etc/systemd/system/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/bin/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/bin/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/lib/libtaos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/lib64/libtaos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/include/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/taos")
|
||||
#print(out)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files in /usr/local/taos:%s" % out)
|
||||
leftFile = True
|
||||
if not leftFile:
|
||||
print("*******Test Result: uninstall test passed ************")
|
||||
|
||||
elif system == "Darwin":
|
||||
# 创建一个subprocess.Popen对象,并使用stdin和stdout进行交互
|
||||
process = subprocess.Popen(['sudo', 'rmtaos'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||
# 向子进程发送输入
|
||||
process.stdin.write("y\n")
|
||||
process.stdin.flush() # 确保输入被发送到子进程
|
||||
process.stdin.write("I confirm that I would like to delete all data, log and configuration files\n")
|
||||
process.stdin.flush() # 确保输入被发送到子进程
|
||||
# 关闭子进程的stdin,防止它无限期等待更多输入
|
||||
process.stdin.close()
|
||||
# 等待子进程结束
|
||||
process.wait()
|
||||
# 检查目录清除情况
|
||||
out = subprocess.getoutput("ls /usr/local/bin/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/lib/libtaos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/include/taos*")
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
#out = subprocess.getoutput("ls /usr/local/Cellar/tdengine/")
|
||||
#print(out)
|
||||
#if out:
|
||||
# print("Uninstall left some files: /usr/local/Cellar/tdengine/%s" % out)
|
||||
# leftFile = True
|
||||
#if not leftFile:
|
||||
# print("*******Test Result: uninstall test passed ************")
|
||||
|
||||
elif system == "Windows":
|
||||
process = subprocess.Popen(['unins000','/silent'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||
process.wait()
|
||||
time.sleep(10)
|
||||
out = subprocess.getoutput("ls C:\TDengine")
|
||||
print(out)
|
||||
if len(out.split("\n")) > 3:
|
||||
leftFile = True
|
||||
print("Uninstall left some files: %s" % out)
|
||||
|
||||
if version_test_result:
|
||||
print("**********Test Result: version test passed! **********")
|
||||
else:
|
||||
print("!!!!!!!!!!!Test Result: version test failed! !!!!!!!!!!")
|
||||
if not leftFile:
|
||||
print("**********Test Result: uninstall test passed! **********")
|
||||
else:
|
||||
print("!!!!!!!!!!!Test Result: uninstall test failed! !!!!!!!!!!")
|
||||
if version_test_result and not leftFile:
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
#!/usr/bin/python
|
||||
###################################################################
|
||||
# Copyright (c) 2016 by TAOS Technologies, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This file is proprietary and confidential to TAOS Technologies.
|
||||
# No part of this file may be reproduced, stored, transmitted,
|
||||
# disclosed or used in any form or by any means other than as
|
||||
# expressly provided by the written permission from Jianhui Tao
|
||||
#
|
||||
###################################################################
|
||||
# install pip
|
||||
# pip install src/connector/python/
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys, os
|
||||
import re
|
||||
import platform
|
||||
import getopt
|
||||
import subprocess
|
||||
# from this import d
|
||||
import time
|
||||
from lib import run_cmd
|
||||
|
||||
|
||||
# input for server
|
||||
def UninstallTaos(version, verMode, uninstall, name):
|
||||
if not version:
|
||||
raise "No version specified, will not run version check."
|
||||
|
||||
system = platform.system()
|
||||
arch = platform.machine()
|
||||
leftFile = False
|
||||
if uninstall:
|
||||
print("Start to run rm%s" % name)
|
||||
print("Platform: ", system)
|
||||
# stop taosd server
|
||||
if system == 'Windows':
|
||||
cmd = "C:\\TDengine\\stop_all.bat"
|
||||
else:
|
||||
cmd = "stop_all.sh"
|
||||
process_out = subprocess.getoutput(cmd)
|
||||
print(cmd)
|
||||
time.sleep(5)
|
||||
print("start to rm%s" % name)
|
||||
if system == "Linux":
|
||||
# 启动命令
|
||||
process = subprocess.Popen(['rm%s' % name], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, text=True)
|
||||
|
||||
# 发送交互输入
|
||||
stdout, stderr = process.communicate(
|
||||
input="y\nI confirm that I would like to delete all data, log and configuration files\n")
|
||||
|
||||
# 打印输出(可选)
|
||||
print(stdout)
|
||||
print(stderr)
|
||||
# 检查目录清除情况
|
||||
out = subprocess.getoutput("ls /etc/systemd/system/%s*" % name)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/bin/%s*" % name)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/bin/%s*" % name)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/lib/lib%s*" % name)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/lib64/lib%s*" % name)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/include/%s*" % name)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/%s" % name)
|
||||
# print(out)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files in /usr/local/%s:%s" % (name, out))
|
||||
leftFile = True
|
||||
if not leftFile:
|
||||
print("*******Test Result: uninstall test passed ************")
|
||||
|
||||
elif system == "Darwin":
|
||||
# 创建一个subprocess.Popen对象,并使用stdin和stdout进行交互
|
||||
process = subprocess.Popen(['sudo', 'rm%s' % name],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||
# 向子进程发送输入
|
||||
process.stdin.write("y\n")
|
||||
process.stdin.flush() # 确保输入被发送到子进程
|
||||
process.stdin.write("I confirm that I would like to delete all data, log and configuration files\n")
|
||||
process.stdin.flush() # 确保输入被发送到子进程
|
||||
# 关闭子进程的stdin,防止它无限期等待更多输入
|
||||
process.stdin.close()
|
||||
# 等待子进程结束
|
||||
process.wait()
|
||||
# 检查目录清除情况
|
||||
out = subprocess.getoutput("ls /usr/local/bin/%s*" % name)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/lib/lib%s*" % name)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
out = subprocess.getoutput("ls /usr/local/include/%s*" % name)
|
||||
if "No such file or directory" not in out:
|
||||
print("Uninstall left some files: %s" % out)
|
||||
leftFile = True
|
||||
# out = subprocess.getoutput("ls /usr/local/Cellar/tdengine/")
|
||||
# print(out)
|
||||
# if out:
|
||||
# print("Uninstall left some files: /usr/local/Cellar/tdengine/%s" % out)
|
||||
# leftFile = True
|
||||
# if not leftFile:
|
||||
# print("*******Test Result: uninstall test passed ************")
|
||||
|
||||
elif system == "Windows":
|
||||
process = subprocess.Popen(['unins000', '/silent'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||
process.wait()
|
||||
time.sleep(10)
|
||||
for file in ["C:\TDengine\\taos.exe", "C:\TDengine\\unins000.exe", "C:\ProDB\prodb.exe",
|
||||
"C:\ProDB\\unins000.exe"]:
|
||||
if os.path.exists(file):
|
||||
leftFile = True
|
||||
if leftFile:
|
||||
raise "uninstall %s fail, please check" % name
|
||||
else:
|
||||
print("**********Test Result: uninstall test passed! **********")
|
|
@ -145,7 +145,14 @@ function kill_taosd() {
|
|||
|
||||
function install_main_path() {
|
||||
#create install main dir and all sub dir
|
||||
${csudo}rm -rf ${install_main_dir} || :
|
||||
${csudo}rm -rf ${install_main_dir}/cfg || :
|
||||
${csudo}rm -rf ${install_main_dir}/bin || :
|
||||
${csudo}rm -rf ${install_main_dir}/driver || :
|
||||
${csudo}rm -rf ${install_main_dir}/examples || :
|
||||
${csudo}rm -rf ${install_main_dir}/include || :
|
||||
${csudo}rm -rf ${install_main_dir}/share || :
|
||||
${csudo}rm -rf ${install_main_dir}/log || :
|
||||
|
||||
${csudo}mkdir -p ${install_main_dir}
|
||||
${csudo}mkdir -p ${install_main_dir}/cfg
|
||||
${csudo}mkdir -p ${install_main_dir}/bin
|
||||
|
|
|
@ -294,8 +294,7 @@ static void deregisterRequest(SRequestObj *pRequest) {
|
|||
}
|
||||
}
|
||||
|
||||
if ((duration >= pTscObj->pAppInfo->serverCfg.monitorParas.tsSlowLogThreshold * 1000000UL ||
|
||||
duration >= pTscObj->pAppInfo->serverCfg.monitorParas.tsSlowLogThresholdTest * 1000000UL) &&
|
||||
if ((duration >= pTscObj->pAppInfo->serverCfg.monitorParas.tsSlowLogThreshold * 1000000UL) &&
|
||||
checkSlowLogExceptDb(pRequest, pTscObj->pAppInfo->serverCfg.monitorParas.tsSlowLogExceptDb)) {
|
||||
(void)atomic_add_fetch_64((int64_t *)&pActivity->numOfSlowQueries, 1);
|
||||
if (pTscObj->pAppInfo->serverCfg.monitorParas.tsSlowLogScope & reqType) {
|
||||
|
@ -983,6 +982,7 @@ void taos_init_imp(void) {
|
|||
SCatalogCfg cfg = {.maxDBCacheNum = 100, .maxTblCacheNum = 100};
|
||||
ENV_ERR_RET(catalogInit(&cfg), "failed to init catalog");
|
||||
ENV_ERR_RET(schedulerInit(), "failed to init scheduler");
|
||||
ENV_ERR_RET(initClientId(), "failed to init clientId");
|
||||
|
||||
tscDebug("starting to initialize TAOS driver");
|
||||
|
||||
|
|
|
@ -1803,7 +1803,7 @@ int taos_stmt_bind_param(TAOS_STMT *stmt, TAOS_MULTI_BIND *bind) {
|
|||
|
||||
if (bind->num > 1) {
|
||||
tscError("invalid bind number %d for %s", bind->num, __FUNCTION__);
|
||||
terrno = TSDB_CODE_INVALID_PARA;
|
||||
terrno = TSDB_CODE_TSC_STMT_BIND_NUMBER_ERROR;
|
||||
return terrno;
|
||||
}
|
||||
|
||||
|
@ -1819,7 +1819,7 @@ int taos_stmt_bind_param_batch(TAOS_STMT *stmt, TAOS_MULTI_BIND *bind) {
|
|||
|
||||
if (bind->num <= 0 || bind->num > INT16_MAX) {
|
||||
tscError("invalid bind num %d", bind->num);
|
||||
terrno = TSDB_CODE_INVALID_PARA;
|
||||
terrno = TSDB_CODE_TSC_STMT_BIND_NUMBER_ERROR;
|
||||
return terrno;
|
||||
}
|
||||
|
||||
|
@ -1831,7 +1831,7 @@ int taos_stmt_bind_param_batch(TAOS_STMT *stmt, TAOS_MULTI_BIND *bind) {
|
|||
}
|
||||
if (0 == insert && bind->num > 1) {
|
||||
tscError("only one row data allowed for query");
|
||||
terrno = TSDB_CODE_INVALID_PARA;
|
||||
terrno = TSDB_CODE_TSC_STMT_BIND_NUMBER_ERROR;
|
||||
return terrno;
|
||||
}
|
||||
|
||||
|
@ -1859,7 +1859,7 @@ int taos_stmt_bind_single_param_batch(TAOS_STMT *stmt, TAOS_MULTI_BIND *bind, in
|
|||
}
|
||||
if (0 == insert && bind->num > 1) {
|
||||
tscError("only one row data allowed for query");
|
||||
terrno = TSDB_CODE_INVALID_PARA;
|
||||
terrno = TSDB_CODE_TSC_STMT_BIND_NUMBER_ERROR;
|
||||
return terrno;
|
||||
}
|
||||
|
||||
|
@ -2019,7 +2019,7 @@ int taos_stmt2_bind_param(TAOS_STMT2 *stmt, TAOS_STMT2_BINDV *bindv, int32_t col
|
|||
|
||||
if (bind->num <= 0 || bind->num > INT16_MAX) {
|
||||
tscError("invalid bind num %d", bind->num);
|
||||
terrno = TSDB_CODE_INVALID_PARA;
|
||||
terrno = TSDB_CODE_TSC_STMT_BIND_NUMBER_ERROR;
|
||||
return terrno;
|
||||
}
|
||||
|
||||
|
@ -2027,7 +2027,7 @@ int taos_stmt2_bind_param(TAOS_STMT2 *stmt, TAOS_STMT2_BINDV *bindv, int32_t col
|
|||
(void)stmtIsInsert2(stmt, &insert);
|
||||
if (0 == insert && bind->num > 1) {
|
||||
tscError("only one row data allowed for query");
|
||||
terrno = TSDB_CODE_INVALID_PARA;
|
||||
terrno = TSDB_CODE_TSC_STMT_BIND_NUMBER_ERROR;
|
||||
return terrno;
|
||||
}
|
||||
|
||||
|
|