From b7fa353ead19ed7706c2fe3b7024d5ed603defac Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Tue, 18 Mar 2025 09:51:20 +0800 Subject: [PATCH 1/7] ci: fix concurrency issue for manual mode --- .github/workflows/tdengine-test.yml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tdengine-test.yml b/.github/workflows/tdengine-test.yml index 97f2638e28..3ab6d3c1d1 100644 --- a/.github/workflows/tdengine-test.yml +++ b/.github/workflows/tdengine-test.yml @@ -33,7 +33,9 @@ on: type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number }}-${{ github.event.inputs.specified_target_branch }}-${{ github.event.inputs.specified_pr_number }}-TDengine + group: ${{ github.workflow }}-${{ github.event_name }}- + ${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || inputs.specified_target_branch }}- + ${{ github.event_name == 'pull_request' && github.event.pull_request.number || inputs.specified_pr_number }}-TDengine cancel-in-progress: true env: @@ -45,24 +47,24 @@ jobs: if: ${{ github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'}} with: tdinternal: false - specified_source_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || github.event.inputs.specified_source_branch }} - specified_target_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || github.event.inputs.specified_target_branch }} - specified_pr_number: ${{ github.event_name == 'pull_request' && 'unavailable' || github.event.inputs.specified_pr_number }} + specified_source_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_source_branch }} + specified_target_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_target_branch }} + specified_pr_number: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_pr_number }} run-tests-on-mac: uses: taosdata/.github/.github/workflows/run-tests-on-macos.yml@main if: ${{ github.event_name == 'pull_request' }} with: tdinternal: false - specified_source_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || github.event.inputs.specified_source_branch }} - specified_target_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || github.event.inputs.specified_target_branch }} - specified_pr_number: ${{ github.event_name == 'pull_request' && 'unavailable' || github.event.inputs.specified_pr_number }} + specified_source_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_source_branch }} + specified_target_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_target_branch }} + specified_pr_number: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_pr_number }} run-tests-on-windows: uses: taosdata/.github/.github/workflows/run-tests-on-windows.yml@main if: ${{ github.event_name == 'pull_request' }} with: tdinternal: false - specified_source_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || github.event.inputs.specified_source_branch }} - specified_target_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || github.event.inputs.specified_target_branch }} - specified_pr_number: ${{ github.event_name == 'pull_request' && 'unavailable' || github.event.inputs.specified_pr_number }} + specified_source_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_source_branch }} + specified_target_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_target_branch }} + specified_pr_number: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_pr_number }} From 8f89aec7cb302bf557e2c0bf5c44769ec7550641 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Tue, 18 Mar 2025 13:34:17 +0800 Subject: [PATCH 2/7] feat(gpt): add sample autoencoder anomaly detection model, and some internal refactor. (#30227) * fix(stream): support packaging enterprise edition. * feat(gpt): support lstm and do some internal refactor, add sample autoencoder model. * feat(gpt): support lstm and do some internal refactor, add sample autoencoder model. * test(gpt): disable model case. * test(gpt): disable model case. * doc: fix title error in doc. --- .../06-TDgpt/04-forecast/04-lstm.md | 31 +++++++ .../04-machine-learning.md | 4 +- .../sample-ad-autoencoder.info | Bin 0 -> 2080 bytes .../sample-ad-autoencoder.keras | Bin 0 -> 154798 bytes .../taosanalytics/algo/ad/autoencoder.py | 16 ++-- tools/tdgpt/taosanalytics/algo/fc/arima.py | 18 ---- tools/tdgpt/taosanalytics/algo/fc/lstm.py | 81 ++++++++++++++++++ .../tdgpt/taosanalytics/test/anomaly_test.py | 11 ++- tools/tdgpt/taosanalytics/test/unit_test.py | 2 +- 9 files changed, 127 insertions(+), 36 deletions(-) create mode 100644 docs/zh/06-advanced/06-TDgpt/04-forecast/04-lstm.md create mode 100644 tools/tdgpt/model/sample-ad-autoencoder/sample-ad-autoencoder.info create mode 100644 tools/tdgpt/model/sample-ad-autoencoder/sample-ad-autoencoder.keras create mode 100644 tools/tdgpt/taosanalytics/algo/fc/lstm.py diff --git a/docs/zh/06-advanced/06-TDgpt/04-forecast/04-lstm.md b/docs/zh/06-advanced/06-TDgpt/04-forecast/04-lstm.md new file mode 100644 index 0000000000..dd00a18885 --- /dev/null +++ b/docs/zh/06-advanced/06-TDgpt/04-forecast/04-lstm.md @@ -0,0 +1,31 @@ +--- +title: "LSTM" +sidebar_label: "LSTM" +--- + +本节说明 LSTM 模型的使用方法。 + +## 功能概述 + +LSTM模型即长短期记忆网络(Long Short Term Memory),是一种特殊的循环神经网络,适用于处理时间序列数据、自然语言处理等任务,通过其独特的门控机制,能够有效捕捉长期依赖关系, +解决传统RNN的梯度消失问题,从而对序列数据进行准确预测,不过它不直接提供计算的置信区间范围结果。 + + +完整的调用SQL语句如下: +```SQL +SELECT _frowts, FORECAST(i32, "algo=lstm,alpha=95,period=10,start_p=1,max_p=5,start_q=1,max_q=5") from foo +``` + +```json5 +{ +"rows": fc_rows, // 返回结果的行数 +"period": period, // 返回结果的周期性,同输入 +"alpha": alpha, // 返回结果的置信区间,同输入 +"algo": "lstm", // 返回结果使用的算法 +"mse": mse, // 拟合输入时间序列时候生成模型的最小均方误差(MSE) +"res": res // 列模式的结果 +} +``` + +### 参考文献 +- [1] Hochreiter S. Long Short-term Memory[J]. Neural Computation MIT-Press, 1997. \ No newline at end of file diff --git a/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/04-machine-learning.md b/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/04-machine-learning.md index b752d446eb..80a5cbe972 100644 --- a/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/04-machine-learning.md +++ b/docs/zh/06-advanced/06-TDgpt/05-anomaly-detection/04-machine-learning.md @@ -3,7 +3,9 @@ title: "机器学习算法" sidebar_label: "机器学习算法" --- -Autoencoder[1]: TDgpt 内置使用自编码器(Autoencoder)的异常检测算法,对周期性的时间序列数据具有较好的检测结果。使用该模型需要针对输入时序数据进行预训练,同时将训练完成的模型保存在到服务目录 `ad_autoencoder` 中,然后在 SQL 语句中指定调用该算法模型即可使用。 +Autoencoder[1]: TDgpt 内置使用自编码器(Autoencoder)的异常检测算法, +对周期性的时间序列数据具有较好的检测结果。使用该模型需要针对输入时序数据进行预训练, +同时将训练完成的模型保存在到服务目录 `ad_autoencoder` 中,然后在 SQL 语句中指定调用该算法模型即可使用。 ```SQL --- 在 options 中增加 model 的名称,ad_autoencoder_foo, 针对 foo 数据集(表)训练的采用自编码器的异常检测模型进行异常检测 diff --git a/tools/tdgpt/model/sample-ad-autoencoder/sample-ad-autoencoder.info b/tools/tdgpt/model/sample-ad-autoencoder/sample-ad-autoencoder.info new file mode 100644 index 0000000000000000000000000000000000000000..0703c99255711fea915231b648487d8d5bfc75b2 GIT binary patch literal 2080 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 TY>vG5u$Ok3D?hffyqlt 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%0Dje-jhZwq;oSK#U^9OY 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 literal 0 HcmV?d00001 diff --git a/tools/tdgpt/taosanalytics/algo/ad/autoencoder.py b/tools/tdgpt/taosanalytics/algo/ad/autoencoder.py index e58db3f54b..89813656fc 100644 --- a/tools/tdgpt/taosanalytics/algo/ad/autoencoder.py +++ b/tools/tdgpt/taosanalytics/algo/ad/autoencoder.py @@ -4,6 +4,7 @@ import os.path import joblib +import keras import numpy as np import pandas as pd @@ -13,8 +14,8 @@ from taosanalytics.util import create_sequences class _AutoEncoderDetectionService(AbstractAnomalyDetectionService): - name = 'ad_encoder' - desc = "anomaly detection based on auto encoder" + name = 'sample_ad_model' + desc = "sample anomaly detection model based on auto encoder" def __init__(self): super().__init__() @@ -25,7 +26,7 @@ class _AutoEncoderDetectionService(AbstractAnomalyDetectionService): self.threshold = None self.time_interval = None self.model = None - self.dir = 'ad_autoencoder' + self.dir = 'sample-ad-autoencoder' self.root_path = conf.get_model_directory() @@ -61,11 +62,6 @@ class _AutoEncoderDetectionService(AbstractAnomalyDetectionService): # Detect all the samples which are anomalies. anomalies = mae > self.threshold - # syslogger.log_inst( - # "Number of anomaly samples: %f, Indices of anomaly samples:{}". - # format(np.sum(anomalies), np.where(anomalies)) - # ) - # data i is an anomaly if samples [(i - timesteps + 1) to (i)] are anomalies ad_indices = [] for data_idx in range(self.time_interval - 1, @@ -82,13 +78,13 @@ class _AutoEncoderDetectionService(AbstractAnomalyDetectionService): name = params['model'] - module_file_path = f'{self.root_path}/{name}.dat' + module_file_path = f'{self.root_path}/{name}.keras' module_info_path = f'{self.root_path}/{name}.info' app_logger.log_inst.info("try to load module:%s", module_file_path) if os.path.exists(module_file_path): - self.model = joblib.load(module_file_path) + self.model = keras.models.load_model(module_file_path) else: app_logger.log_inst.error("failed to load autoencoder model file: %s", module_file_path) raise FileNotFoundError(f"{module_file_path} not found") diff --git a/tools/tdgpt/taosanalytics/algo/fc/arima.py b/tools/tdgpt/taosanalytics/algo/fc/arima.py index 9e087a5e9e..fa587c3604 100644 --- a/tools/tdgpt/taosanalytics/algo/fc/arima.py +++ b/tools/tdgpt/taosanalytics/algo/fc/arima.py @@ -68,24 +68,6 @@ class _ArimaService(AbstractForecastService): fc = model.predict(n_periods=fc_rows, return_conf_int=self.return_conf, alpha=self.conf) - # plt.plot(source_data, label='training') - # plt.plot(xrange, actual_data, label='actual') - - # fc_list = fc.tolist() - # fc_without_diff = restore_from_diff(self.list, fc_list, 2) - # print(fc_without_diff) - - # plt.plot(xrange, fc_without_diff, label='fc') - - # residuals = pd.DataFrame(model.arima_res_.resid) - # wn = is_white_noise(residuals) - # print("residual is white noise:", wn) - - # fig, ax = plt.subplots(1, 2) - # residuals.plot(title="Residuals", ax=ax[0]) - # residuals.plot(kind='kde', title='Density', ax=ax[1]) - # plt.show() - res1 = [fc[0].tolist(), fc[1][:, 0].tolist(), fc[1][:, 1].tolist()] if self.return_conf else [fc.tolist()] diff --git a/tools/tdgpt/taosanalytics/algo/fc/lstm.py b/tools/tdgpt/taosanalytics/algo/fc/lstm.py new file mode 100644 index 0000000000..5edae7fc9f --- /dev/null +++ b/tools/tdgpt/taosanalytics/algo/fc/lstm.py @@ -0,0 +1,81 @@ +# encoding:utf-8 +# pylint: disable=c0103 +""" auto encoder algorithms to detect anomaly for time series data""" +import os.path + +import keras + +from taosanalytics.algo.forecast import insert_ts_list +from taosanalytics.conf import app_logger, conf +from taosanalytics.service import AbstractForecastService + + +class _LSTMService(AbstractForecastService): + name = 'sample_forecast_model' + desc = "sample forecast model based on LSTM" + + def __init__(self): + super().__init__() + + self.table_name = None + self.mean = None + self.std = None + self.threshold = None + self.time_interval = None + self.model = None + self.dir = 'sample-fc-lstm' + + self.root_path = conf.get_model_directory() + + self.root_path = self.root_path + f'/{self.dir}/' + + if not os.path.exists(self.root_path): + app_logger.log_inst.error( + "%s ad algorithm failed to locate default module directory:" + "%s, not active", self.__class__.__name__, self.root_path) + else: + app_logger.log_inst.info("%s ad algorithm root path is: %s", self.__class__.__name__, + self.root_path) + + def execute(self): + if self.input_is_empty(): + return [] + + if self.model is None: + raise FileNotFoundError("not load autoencoder model yet, or load model failed") + + res = self.model.predict(self.list) + + insert_ts_list(res, self.start_ts, self.time_step, self.fc_rows) + + if self.return_conf: + res1 = [res.tolist(), res.tolist(), res.tolist()], None + else: + res1 = [res.tolist()], None + + # add the conf range if required + return { + "mse": None, + "res": res1 + } + + def set_params(self, params): + + if "model" not in params: + raise ValueError("model needs to be specified") + + name = params['model'] + + module_file_path = f'{self.root_path}/{name}.keras' + # module_info_path = f'{self.root_path}/{name}.info' + + app_logger.log_inst.info("try to load module:%s", module_file_path) + + if os.path.exists(module_file_path): + self.model = keras.models.load_model(module_file_path) + else: + app_logger.log_inst.error("failed to load LSTM model file: %s", module_file_path) + raise FileNotFoundError(f"{module_file_path} not found") + + def get_params(self): + return {"dir": self.dir + '/*'} diff --git a/tools/tdgpt/taosanalytics/test/anomaly_test.py b/tools/tdgpt/taosanalytics/test/anomaly_test.py index f44a7f0d52..bc173cd25b 100644 --- a/tools/tdgpt/taosanalytics/test/anomaly_test.py +++ b/tools/tdgpt/taosanalytics/test/anomaly_test.py @@ -141,15 +141,14 @@ class AnomalyDetectionTest(unittest.TestCase): def test_autoencoder_ad(self): """for local test only, disabled it in github action""" - pass - + pass # data = self.__load_remote_data_for_ad() # - # s = loader.get_service("ad_encoder") + # s = loader.get_service("sample_ad_model") # s.set_input_list(data) # # try: - # s.set_params({"model": "ad_encoder_"}) + # s.set_params({"model": "sample-ad-autoencoder"}) # except ValueError as e: # app_logger.log_inst.error(f"failed to set the param for auto_encoder algorithm, reason:{e}") # return @@ -157,9 +156,9 @@ class AnomalyDetectionTest(unittest.TestCase): # r = s.execute() # # num_of_error = -(sum(filter(lambda x: x == -1, r))) - # self.assertEqual(num_of_error, 109) - # # draw_ad_results(data, r, "autoencoder") + # + # self.assertEqual(num_of_error, 109) def test_get_all_services(self): """Test get all services""" diff --git a/tools/tdgpt/taosanalytics/test/unit_test.py b/tools/tdgpt/taosanalytics/test/unit_test.py index f6ecdf0d5b..aef689a8b6 100644 --- a/tools/tdgpt/taosanalytics/test/unit_test.py +++ b/tools/tdgpt/taosanalytics/test/unit_test.py @@ -99,7 +99,7 @@ class ServiceTest(unittest.TestCase): if item["type"] == "anomaly-detection": self.assertEqual(len(item["algo"]), 6) else: - self.assertEqual(len(item["algo"]), 2) + self.assertEqual(len(item["algo"]), 3) if __name__ == '__main__': From a70e840df6e5c7af6a940ff85edb6f18dce0cf93 Mon Sep 17 00:00:00 2001 From: Simon Guan Date: Tue, 18 Mar 2025 13:49:23 +0800 Subject: [PATCH 3/7] docs: update keywords (#30234) * docs: update keywords * docs: minor changes * docs: minor changes * docs: fminro changes --- docs/en/14-reference/03-taos-sql/20-keywords.md | 1 + docs/zh/06-advanced/03-stream.md | 2 +- docs/zh/06-advanced/06-TDgpt/04-forecast/04-lstm.md | 8 ++++---- docs/zh/14-reference/03-taos-sql/14-stream.md | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/en/14-reference/03-taos-sql/20-keywords.md b/docs/en/14-reference/03-taos-sql/20-keywords.md index 98fec29e9b..d1bd59298f 100644 --- a/docs/en/14-reference/03-taos-sql/20-keywords.md +++ b/docs/en/14-reference/03-taos-sql/20-keywords.md @@ -35,6 +35,7 @@ The list of keywords is as follows: | AS | | | ASC | | | ASOF | | +| ASYNC | 3.3.6.0+ | | AT_ONCE | | | ATTACH | | | AUTO | 3.3.5.0+ | diff --git a/docs/zh/06-advanced/03-stream.md b/docs/zh/06-advanced/03-stream.md index ac922353d7..4bd5d517fd 100644 --- a/docs/zh/06-advanced/03-stream.md +++ b/docs/zh/06-advanced/03-stream.md @@ -102,7 +102,7 @@ PARTITION 子句中,为 tbname 定义了一个别名 tname, 在 PARTITION 通过启用 fill_history 选项,创建的流计算任务将具备处理创建前、创建过程中以及创建后写入的数据的能力。这意味着,无论数据是在流创建之前还是之后写入的,都将纳入流计算的范围,从而确保数据的完整性和一致性。这一设置为用户提供了更大的灵活性,使其能够根据实际需求灵活处理历史数据和新数据。 注意: -- 开启 fill_history 时,创建流需要找到历史数据的分界点,如果历史数据很多,可能会导致创建流任务耗时较长,此时可以通过 fill_history 1 async(3.3.6.0版本开始支持) 语法将创建流的任务放在后台处理,创建流的语句可立即返回,不阻塞后面的操作。async 只对 fill_history 1 起效,fill_history 0 时建流很快,不需要异步处理。 +- 开启 fill_history 时,创建流需要找到历史数据的分界点,如果历史数据很多,可能会导致创建流任务耗时较长,此时可以通过 fill_history 1 async(v3.3.6.0 开始支持) 语法将创建流的任务放在后台处理,创建流的语句可立即返回,不阻塞后面的操作。async 只对 fill_history 1 起效,fill_history 0 时建流很快,不需要异步处理。 - 通过 show streams 可查看后台建流的进度(ready 状态表示成功,init 状态表示正在建流,failed 状态表示建流失败,失败时 message 列可以查看原因。对于建流失败的情况可以删除流重新建立)。 diff --git a/docs/zh/06-advanced/06-TDgpt/04-forecast/04-lstm.md b/docs/zh/06-advanced/06-TDgpt/04-forecast/04-lstm.md index dd00a18885..28ae919de0 100644 --- a/docs/zh/06-advanced/06-TDgpt/04-forecast/04-lstm.md +++ b/docs/zh/06-advanced/06-TDgpt/04-forecast/04-lstm.md @@ -7,11 +7,11 @@ sidebar_label: "LSTM" ## 功能概述 -LSTM模型即长短期记忆网络(Long Short Term Memory),是一种特殊的循环神经网络,适用于处理时间序列数据、自然语言处理等任务,通过其独特的门控机制,能够有效捕捉长期依赖关系, -解决传统RNN的梯度消失问题,从而对序列数据进行准确预测,不过它不直接提供计算的置信区间范围结果。 +LSTM 模型即长短期记忆网络(Long Short Term Memory),是一种特殊的循环神经网络,适用于处理时间序列数据、自然语言处理等任务,通过其独特的门控机制,能够有效捕捉长期依赖关系, +解决传统 RNN 的梯度消失问题,从而对序列数据进行准确预测,不过它不直接提供计算的置信区间范围结果。 -完整的调用SQL语句如下: +完整的调用 SQL 语句如下: ```SQL SELECT _frowts, FORECAST(i32, "algo=lstm,alpha=95,period=10,start_p=1,max_p=5,start_q=1,max_q=5") from foo ``` @@ -21,7 +21,7 @@ SELECT _frowts, FORECAST(i32, "algo=lstm,alpha=95,period=10,start_p=1,max_p=5,st "rows": fc_rows, // 返回结果的行数 "period": period, // 返回结果的周期性,同输入 "alpha": alpha, // 返回结果的置信区间,同输入 -"algo": "lstm", // 返回结果使用的算法 +"algo": "lstm", // 返回结果使用的算法 "mse": mse, // 拟合输入时间序列时候生成模型的最小均方误差(MSE) "res": res // 列模式的结果 } diff --git a/docs/zh/14-reference/03-taos-sql/14-stream.md b/docs/zh/14-reference/03-taos-sql/14-stream.md index 41013bfd65..1dff6ad091 100644 --- a/docs/zh/14-reference/03-taos-sql/14-stream.md +++ b/docs/zh/14-reference/03-taos-sql/14-stream.md @@ -128,7 +128,7 @@ create stream if not exists s1 fill_history 1 into st1 as select count(*) from 如果该流任务已经彻底过期,并且您不再想让它检测或处理数据,您可以手动删除它,被计算出的数据仍会被保留。 注意: -- 开启 fill_history 时,创建流需要找到历史数据的分界点,如果历史数据很多,可能会导致创建流任务耗时较长,此时可以通过 fill_history 1 async(3.3.6.0版本开始支持) 语法将创建流的任务放在后台处理,创建流的语句可立即返回,不阻塞后面的操作。async 只对 fill_history 1 起效,fill_history 0 时建流很快,不需要异步处理。 +- 开启 fill_history 时,创建流需要找到历史数据的分界点,如果历史数据很多,可能会导致创建流任务耗时较长,此时可以通过 fill_history 1 async(v3.3.6.0 开始支持) 语法将创建流的任务放在后台处理,创建流的语句可立即返回,不阻塞后面的操作。async 只对 fill_history 1 起效,fill_history 0 时建流很快,不需要异步处理。 - 通过 show streams 可查看后台建流的进度(ready 状态表示成功,init 状态表示正在建流,failed 状态表示建流失败,失败时 message 列可以查看原因。对于建流失败的情况可以删除流重新建立)。 From 2523e60d3b2388e0520088dd3474e9de14a97bd4 Mon Sep 17 00:00:00 2001 From: Zhixiao Bao <62235797+xiao-77@users.noreply.github.com> Date: Tue, 18 Mar 2025 14:01:06 +0800 Subject: [PATCH 4/7] fix: remove meaningless error logs when fetching child table metadata. (#30201) * Enh: remove useless error logs. * Fix: solving performance issues. * fix: format code --------- Co-authored-by: Simon Guan --- source/libs/catalog/inc/catalogInt.h | 2 +- source/libs/catalog/src/ctgAsync.c | 4 ++-- source/libs/catalog/src/ctgCache.c | 7 ++++++- source/libs/qcom/src/querymsg.c | 2 ++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/source/libs/catalog/inc/catalogInt.h b/source/libs/catalog/inc/catalogInt.h index e7cf49f22b..09021e4d27 100644 --- a/source/libs/catalog/inc/catalogInt.h +++ b/source/libs/catalog/inc/catalogInt.h @@ -1030,7 +1030,7 @@ int32_t ctgdShowStatInfo(void); int32_t ctgRemoveTbMetaFromCache(SCatalog* pCtg, SName* pTableName, bool syncReq); int32_t ctgGetTbMetaFromCache(SCatalog* pCtg, SCtgTbMetaCtx* ctx, STableMeta** pTableMeta); int32_t ctgGetTbMetasFromCache(SCatalog* pCtg, SRequestConnInfo* pConn, SCtgTbMetasCtx* ctx, int32_t dbIdx, - int32_t* fetchIdx, int32_t baseResIdx, SArray* pList); + int32_t* fetchIdx, int32_t baseResIdx, SArray* pList, bool autoCreate); int32_t ctgGetTbNamesFromCache(SCatalog* pCtg, SRequestConnInfo* pConn, SCtgTbNamesCtx* ctx, int32_t dbIdx, int32_t* fetchIdx, int32_t baseResIdx, SArray* pList); int32_t ctgCloneDbCfgInfo(void* pSrc, SDbCfgInfo** ppDst); diff --git a/source/libs/catalog/src/ctgAsync.c b/source/libs/catalog/src/ctgAsync.c index 19aef559a1..c43c414d1c 100644 --- a/source/libs/catalog/src/ctgAsync.c +++ b/source/libs/catalog/src/ctgAsync.c @@ -3263,7 +3263,7 @@ int32_t ctgLaunchGetTbMetasTask(SCtgTask* pTask) { autoCreate = pReq->autoCreate; ctgDebug("start to check tb metas in db:%s, tbNum:%d", pReq->dbFName, (int32_t)taosArrayGetSize(pReq->pTables)); - CTG_ERR_RET(ctgGetTbMetasFromCache(pCtg, pConn, pCtx, i, &fetchIdx, baseResIdx, pReq->pTables)); + CTG_ERR_RET(ctgGetTbMetasFromCache(pCtg, pConn, pCtx, i, &fetchIdx, baseResIdx, pReq->pTables, autoCreate)); baseResIdx += taosArrayGetSize(pReq->pTables); } @@ -3301,7 +3301,7 @@ int32_t ctgLaunchGetTbMetasTask(SCtgTask* pTask) { } SCtgTaskReq tReq; - tReq.autoCreateCtb = (autoCreate && i == pCtx->fetchNum - 1) ? 1 : 0; + tReq.autoCreateCtb = (pFetch->flag & CTG_FLAG_NOT_STB) ? 1 : 0; tReq.pTask = pTask; tReq.msgIdx = pFetch->fetchIdx; CTG_ERR_RET(ctgAsyncRefreshTbMeta(&tReq, pFetch->flag, pName, &pFetch->vgId)); diff --git a/source/libs/catalog/src/ctgCache.c b/source/libs/catalog/src/ctgCache.c index 5348633579..d583aea43d 100644 --- a/source/libs/catalog/src/ctgCache.c +++ b/source/libs/catalog/src/ctgCache.c @@ -3483,7 +3483,7 @@ int32_t ctgGetTbMetaBFromCache(SCatalog* pCtg, SRequestConnInfo *pConn, SCtgTbMe #endif int32_t ctgGetTbMetasFromCache(SCatalog *pCtg, SRequestConnInfo *pConn, SCtgTbMetasCtx *ctx, int32_t dbIdx, - int32_t *fetchIdx, int32_t baseResIdx, SArray *pList) { + int32_t *fetchIdx, int32_t baseResIdx, SArray *pList, bool autoCreate) { int32_t tbNum = taosArrayGetSize(pList); char dbFName[TSDB_DB_FNAME_LEN] = {0}; int32_t flag = CTG_FLAG_UNKNOWN_STB; @@ -3527,6 +3527,11 @@ int32_t ctgGetTbMetasFromCache(SCatalog *pCtg, SRequestConnInfo *pConn, SCtgTbMe CTG_ERR_JRET(TSDB_CODE_CTG_INVALID_INPUT); } + // for auto create, the second table is child table + if (autoCreate && (i == 1)) { + CTG_FLAG_SET_STB(flag, TSDB_CHILD_TABLE); + } + pCache = taosHashAcquire(dbCache->tbCache, pName->tname, strlen(pName->tname)); if (NULL == pCache) { ctgDebug("tb:%s, tb not in cache, db:%s", pName->tname, dbFName); diff --git a/source/libs/qcom/src/querymsg.c b/source/libs/qcom/src/querymsg.c index 08e530141e..73a87e0389 100644 --- a/source/libs/qcom/src/querymsg.c +++ b/source/libs/qcom/src/querymsg.c @@ -81,6 +81,8 @@ int32_t queryBuildTableMetaReqMsg(void *input, char **msg, int32_t msgSize, int3 STableInfoReq infoReq = {0}; infoReq.option = pInput->option; infoReq.header.vgId = pInput->vgId; + infoReq.autoCreateCtb = pInput->autoCreateCtb; + if (pInput->dbFName) { tstrncpy(infoReq.dbFName, pInput->dbFName, TSDB_DB_FNAME_LEN); } From 3bc6635de3b5a06d13e532d5127aea6ae5a9e862 Mon Sep 17 00:00:00 2001 From: WANG MINGMING Date: Tue, 18 Mar 2025 14:11:00 +0800 Subject: [PATCH 5/7] feat[TS-6115]: support deletion of topics with active consumers. (#30232) * feat[TS-6115]: drop topic & consumer group in force * feat[TS-6115]: drop topic & consumer group in force * feat[TS-6115]: drop topic & consumer group in force --- docs/en/06-advanced/01-subscription.md | 9 +- docs/en/14-reference/03-taos-sql/13-tmq.md | 8 +- docs/zh/06-advanced/01-subscription.md | 9 +- docs/zh/14-reference/03-taos-sql/13-tmq.md | 8 +- include/common/tmsg.h | 2 + include/libs/nodes/cmdnodes.h | 2 + source/common/src/msg/tmsg.c | 9 ++ source/dnode/mnode/impl/inc/mndSubscribe.h | 2 +- source/dnode/mnode/impl/src/mndSubscribe.c | 32 ++-- source/dnode/mnode/impl/src/mndTopic.c | 28 ++-- source/libs/parser/inc/parAst.h | 4 +- source/libs/parser/inc/sql.y | 4 +- source/libs/parser/src/parAstCreater.c | 6 +- source/libs/parser/src/parTranslater.c | 2 + tests/parallel_test/cases.task | 1 + tests/system-test/7-tmq/tmq_ts6115.py | 116 ++++++++++++++ utils/test/c/CMakeLists.txt | 9 ++ utils/test/c/tmq_ts6115.c | 170 +++++++++++++++++++++ 18 files changed, 380 insertions(+), 41 deletions(-) create mode 100644 tests/system-test/7-tmq/tmq_ts6115.py create mode 100644 utils/test/c/tmq_ts6115.c diff --git a/docs/en/06-advanced/01-subscription.md b/docs/en/06-advanced/01-subscription.md index 6092fddf8e..6fa04b9032 100644 --- a/docs/en/06-advanced/01-subscription.md +++ b/docs/en/06-advanced/01-subscription.md @@ -69,10 +69,10 @@ This statement creates a subscription that includes all table data in the databa ## Delete Topic -If you no longer need to subscribe to the data, you can delete the topic. Note that only topics that are not currently subscribed can be deleted. +If you no longer need to subscribe to the data, you can delete the topic. If the current topic is subscribed to by a consumer, it can be forcibly deleted using the FORCE syntax. After the forced deletion, the subscribed consumer will consume data with errors (FORCE syntax supported from version 3.3.6.0). ```sql -DROP TOPIC [IF EXISTS] topic_name; +DROP TOPIC [IF EXISTS] [FORCE] topic_name; ``` ## View Topics @@ -99,10 +99,10 @@ Displays information about all consumers in the current database, including the ### Delete Consumer Group -When creating a consumer, a consumer group is assigned to the consumer. Consumers cannot be explicitly deleted, but the consumer group can be deleted with the following statement when there are no consumers in the group: +When creating a consumer, a consumer group is assigned to the consumer. Consumers cannot be explicitly deleted, but the consumer group can be deleted. If there are consumers in the current consumer group who are consuming, the FORCE syntax can be used to force deletion. After forced deletion, subscribed consumers will consume data with errors (FORCE syntax supported from version 3.3.6.0). ```sql -DROP CONSUMER GROUP [IF EXISTS] cgroup_name ON topic_name; +DROP CONSUMER GROUP [IF EXISTS] [FORCE] cgroup_name ON topic_name; ``` ## Data Subscription @@ -137,6 +137,7 @@ If the following 3 data entries were written, then during replay, the first entr When using the data subscription's replay feature, note the following: +- Enable replay function by configuring the consumption parameter enable.replay to true - The replay function of data subscription only supports data playback for query subscriptions; supertable and database subscriptions do not support playback. - Replay does not support progress saving. - Because data playback itself requires processing time, there is a precision error of several tens of milliseconds in playback. diff --git a/docs/en/14-reference/03-taos-sql/13-tmq.md b/docs/en/14-reference/03-taos-sql/13-tmq.md index c358f824f7..bd5b4410d2 100644 --- a/docs/en/14-reference/03-taos-sql/13-tmq.md +++ b/docs/en/14-reference/03-taos-sql/13-tmq.md @@ -58,11 +58,11 @@ Note: Subscriptions to supertables and databases are advanced subscription modes ## Delete topic -If you no longer need to subscribe to data, you can delete the topic, but note: only TOPICS that are not currently being subscribed to can be deleted. +If you no longer need to subscribe to the data, you can delete the topic. If the current topic is subscribed to by a consumer, it can be forcibly deleted using the FORCE syntax. After the forced deletion, the subscribed consumer will consume data with errors (FORCE syntax supported from version 3.3.6.0) ```sql /* Delete topic */ -DROP TOPIC [IF EXISTS] topic_name; +DROP TOPIC [IF EXISTS] [FORCE] topic_name; ``` At this point, if there are consumers on this subscription topic, they will receive an error. @@ -81,8 +81,10 @@ Consumer groups can only be created through the TDengine client driver or APIs p ## Delete consumer group +When creating a consumer, a consumer group is assigned to the consumer. Consumers cannot be explicitly deleted, but the consumer group can be deleted. If there are consumers in the current consumer group who are consuming, the FORCE syntax can be used to force deletion. After forced deletion, subscribed consumers will consume data with errors (FORCE syntax supported from version 3.3.6.0). + ```sql -DROP CONSUMER GROUP [IF EXISTS] cgroup_name ON topic_name; +DROP CONSUMER GROUP [IF EXISTS] [FORCE] cgroup_name ON topic_name; ``` Deletes the consumer group `cgroup_name` on the topic `topic_name`. diff --git a/docs/zh/06-advanced/01-subscription.md b/docs/zh/06-advanced/01-subscription.md index 0760f44446..8bd44bf2a0 100644 --- a/docs/zh/06-advanced/01-subscription.md +++ b/docs/zh/06-advanced/01-subscription.md @@ -64,10 +64,10 @@ CREATE TOPIC [IF NOT EXISTS] topic_name [with meta] AS DATABASE db_name; ## 删除主题 -如果不再需要订阅数据,可以删除 topic,需要注意只有当前未在订阅中的 topic 才能被删除。 +如果不再需要订阅数据,可以删除 topic,如果当前 topic 被消费者订阅,通过 FORCE 语法可强制删除,强制删除后订阅的消费者会消费数据会出错(FORCE 语法3.3.6.0版本开始支持)。 ```sql -DROP TOPIC [IF EXISTS] topic_name; +DROP TOPIC [IF EXISTS] [FORCE] topic_name; ``` ## 查看主题 @@ -94,9 +94,9 @@ SHOW CONSUMERS; ### 删除消费组 -消费者创建的时候,会给消费者指定一个消费者组,消费者不能显式的删除,但是消费者组在组内没有消费者时可以通过下面语句删除: +消费者创建的时候,会给消费者指定一个消费者组,消费者不能显式的删除,但是可以删除消费者组。如果当前消费者组里有消费者在消费,通过 FORCE 语法可强制删除,强制删除后订阅的消费者会消费数据会出错(FORCE 语法3.3.6.0版本开始支持)。 ```sql -DROP CONSUMER GROUP [IF EXISTS] cgroup_name ON topic_name; +DROP CONSUMER GROUP [IF EXISTS] [FORCE] cgroup_name ON topic_name; ``` ## 数据订阅 @@ -129,6 +129,7 @@ TDengine 的数据订阅功能支持回放(replay)功能,允许用户按 ``` 使用数据订阅的回放功能时需要注意如下几项: +- 通过配置消费参数 enable.replay 为 true 开启回放功能。 - 数据订阅的回放功能仅查询订阅支持数据回放,超级表和库订阅不支持回放。 - 回放不支持进度保存。 - 因为数据回放本身需要处理时间,所以回放的精度存在几十毫秒的误差。 diff --git a/docs/zh/14-reference/03-taos-sql/13-tmq.md b/docs/zh/14-reference/03-taos-sql/13-tmq.md index 244caef7b0..e2407fa19a 100644 --- a/docs/zh/14-reference/03-taos-sql/13-tmq.md +++ b/docs/zh/14-reference/03-taos-sql/13-tmq.md @@ -59,11 +59,11 @@ CREATE TOPIC [IF NOT EXISTS] topic_name [with meta] AS DATABASE db_name; ## 删除 topic -如果不再需要订阅数据,可以删除 topic,需要注意:只有当前未在订阅中的 TOPIC 才能被删除。 +如果不再需要订阅数据,可以删除 topic,如果当前 topic 被消费者订阅,通过 FORCE 语法可强制删除,强制删除后订阅的消费者会消费数据会出错(FORCE 语法3.3.6.0版本开始支持)。 ```sql /* 删除 topic */ -DROP TOPIC [IF EXISTS] topic_name; +DROP TOPIC [IF EXISTS] [FORCE] topic_name; ``` 此时如果该订阅主题上存在 consumer,则此 consumer 会收到一个错误。 @@ -82,8 +82,10 @@ SHOW TOPICS; ## 删除消费组 +消费者创建的时候,会给消费者指定一个消费者组,消费者不能显式的删除,但是可以删除消费者组。如果当前消费者组里有消费者在消费,通过 FORCE 语法可强制删除,强制删除后订阅的消费者会消费数据会出错(FORCE 语法3.3.6.0版本开始支持)。 + ```sql -DROP CONSUMER GROUP [IF EXISTS] cgroup_name ON topic_name; +DROP CONSUMER GROUP [IF EXISTS] [FORCE] cgroup_name ON topic_name; ``` 删除主题 topic_name 上的消费组 cgroup_name。 diff --git a/include/common/tmsg.h b/include/common/tmsg.h index f59968140d..9fd0ad057c 100644 --- a/include/common/tmsg.h +++ b/include/common/tmsg.h @@ -3358,6 +3358,7 @@ typedef struct { int8_t igNotExists; int32_t sqlLen; char* sql; + int8_t force; } SMDropTopicReq; int32_t tSerializeSMDropTopicReq(void* buf, int32_t bufLen, SMDropTopicReq* pReq); @@ -3368,6 +3369,7 @@ typedef struct { char topic[TSDB_TOPIC_FNAME_LEN]; char cgroup[TSDB_CGROUP_LEN]; int8_t igNotExists; + int8_t force; } SMDropCgroupReq; int32_t tSerializeSMDropCgroupReq(void* buf, int32_t bufLen, SMDropCgroupReq* pReq); diff --git a/include/libs/nodes/cmdnodes.h b/include/libs/nodes/cmdnodes.h index 246b352d1a..7cb75e186e 100644 --- a/include/libs/nodes/cmdnodes.h +++ b/include/libs/nodes/cmdnodes.h @@ -553,6 +553,7 @@ typedef struct SDropTopicStmt { ENodeType type; char topicName[TSDB_TOPIC_NAME_LEN]; bool ignoreNotExists; + bool force; } SDropTopicStmt; typedef struct SDropCGroupStmt { @@ -560,6 +561,7 @@ typedef struct SDropCGroupStmt { char topicName[TSDB_TOPIC_NAME_LEN]; char cgroup[TSDB_CGROUP_LEN]; bool ignoreNotExists; + bool force; } SDropCGroupStmt; typedef struct SAlterClusterStmt { diff --git a/source/common/src/msg/tmsg.c b/source/common/src/msg/tmsg.c index bcb60adae0..f8a6da7378 100644 --- a/source/common/src/msg/tmsg.c +++ b/source/common/src/msg/tmsg.c @@ -6676,6 +6676,8 @@ int32_t tSerializeSMDropTopicReq(void *buf, int32_t bufLen, SMDropTopicReq *pReq TAOS_CHECK_EXIT(tEncodeCStr(&encoder, pReq->name)); TAOS_CHECK_EXIT(tEncodeI8(&encoder, pReq->igNotExists)); ENCODESQL(); + TAOS_CHECK_EXIT(tEncodeI8(&encoder, pReq->force)); + tEndEncode(&encoder); _exit: @@ -6699,6 +6701,9 @@ int32_t tDeserializeSMDropTopicReq(void *buf, int32_t bufLen, SMDropTopicReq *pR TAOS_CHECK_EXIT(tDecodeCStrTo(&decoder, pReq->name)); TAOS_CHECK_EXIT(tDecodeI8(&decoder, &pReq->igNotExists)); DECODESQL(); + if (!tDecodeIsEnd(&decoder)) { + TAOS_CHECK_EXIT(tDecodeI8(&decoder, &pReq->force)); + } tEndDecode(&decoder); _exit: @@ -6719,6 +6724,7 @@ int32_t tSerializeSMDropCgroupReq(void *buf, int32_t bufLen, SMDropCgroupReq *pR TAOS_CHECK_EXIT(tEncodeCStr(&encoder, pReq->topic)); TAOS_CHECK_EXIT(tEncodeCStr(&encoder, pReq->cgroup)); TAOS_CHECK_EXIT(tEncodeI8(&encoder, pReq->igNotExists)); + TAOS_CHECK_EXIT(tEncodeI8(&encoder, pReq->force)); tEndEncode(&encoder); _exit: @@ -6741,6 +6747,9 @@ int32_t tDeserializeSMDropCgroupReq(void *buf, int32_t bufLen, SMDropCgroupReq * TAOS_CHECK_EXIT(tDecodeCStrTo(&decoder, pReq->topic)); TAOS_CHECK_EXIT(tDecodeCStrTo(&decoder, pReq->cgroup)); TAOS_CHECK_EXIT(tDecodeI8(&decoder, &pReq->igNotExists)); + if (!tDecodeIsEnd(&decoder)) { + TAOS_CHECK_EXIT(tDecodeI8(&decoder, &pReq->force)); + } tEndDecode(&decoder); _exit: diff --git a/source/dnode/mnode/impl/inc/mndSubscribe.h b/source/dnode/mnode/impl/inc/mndSubscribe.h index 614c14c7d2..c24067e7ce 100644 --- a/source/dnode/mnode/impl/inc/mndSubscribe.h +++ b/source/dnode/mnode/impl/inc/mndSubscribe.h @@ -29,7 +29,7 @@ int32_t mndGetGroupNumByTopic(SMnode *pMnode, const char *topicName); int32_t mndAcquireSubscribeByKey(SMnode *pMnode, const char *key, SMqSubscribeObj** pSub); void mndReleaseSubscribe(SMnode *pMnode, SMqSubscribeObj *pSub); -int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topic); +int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topic, bool force); int32_t mndSetDropSubCommitLogs(SMnode *pMnode, STrans *pTrans, SMqSubscribeObj *pSub); bool mndRebTryStart(); diff --git a/source/dnode/mnode/impl/src/mndSubscribe.c b/source/dnode/mnode/impl/src/mndSubscribe.c index e5ab02996a..2e5b37f119 100644 --- a/source/dnode/mnode/impl/src/mndSubscribe.c +++ b/source/dnode/mnode/impl/src/mndSubscribe.c @@ -1112,12 +1112,13 @@ END: return code; } -static int32_t mndCheckConsumerByGroup(SMnode *pMnode, STrans *pTrans, char *cgroup, char *topic) { +static int32_t mndCheckConsumerByGroup(SMnode *pMnode, STrans *pTrans, char *cgroup, char *topic, bool deleteConsumer) { if (pMnode == NULL || pTrans == NULL || cgroup == NULL || topic == NULL) { return TSDB_CODE_INVALID_PARA; } void *pIter = NULL; SMqConsumerObj *pConsumer = NULL; + SMqConsumerObj *pConsumerNew = NULL; int code = 0; while (1) { pIter = sdbFetch(pMnode->pSdb, SDB_CONSUMER, pIter, (void **)&pConsumer); @@ -1130,18 +1131,27 @@ static int32_t mndCheckConsumerByGroup(SMnode *pMnode, STrans *pTrans, char *cgr continue; } - bool found = checkTopic(pConsumer->assignedTopics, topic); - if (found){ - mError("topic:%s, failed to drop since subscribed by consumer:0x%" PRIx64 ", in consumer group %s", - topic, pConsumer->consumerId, pConsumer->cgroup); - code = TSDB_CODE_MND_CGROUP_USED; - goto END; + if (deleteConsumer) { + MND_TMQ_RETURN_CHECK(tNewSMqConsumerObj(pConsumer->consumerId, pConsumer->cgroup, -1, NULL, NULL, &pConsumerNew)); + MND_TMQ_RETURN_CHECK(mndSetConsumerDropLogs(pTrans, pConsumerNew)); + tDeleteSMqConsumerObj(pConsumerNew); + pConsumerNew = NULL; + } else { + bool found = checkTopic(pConsumer->assignedTopics, topic); + if (found){ + mError("topic:%s, failed to drop since subscribed by consumer:0x%" PRIx64 ", in consumer group %s", + topic, pConsumer->consumerId, pConsumer->cgroup); + code = TSDB_CODE_MND_CGROUP_USED; + goto END; + } } + sdbRelease(pMnode->pSdb, pConsumer); } END: + tDeleteSMqConsumerObj(pConsumerNew); sdbRelease(pMnode->pSdb, pConsumer); sdbCancelFetch(pMnode->pSdb, pIter); return code; @@ -1173,7 +1183,7 @@ static int32_t mndProcessDropCgroupReq(SRpcMsg *pMsg) { } taosWLockLatch(&pSub->lock); - if (taosHashGetSize(pSub->consumerHash) != 0) { + if (!dropReq.force && taosHashGetSize(pSub->consumerHash) != 0) { code = TSDB_CODE_MND_CGROUP_USED; mError("cgroup:%s on topic:%s, failed to drop since %s", dropReq.cgroup, dropReq.topic, tstrerror(code)); goto END; @@ -1185,7 +1195,7 @@ static int32_t mndProcessDropCgroupReq(SRpcMsg *pMsg) { mndTransSetDbName(pTrans, pSub->dbName, NULL); MND_TMQ_RETURN_CHECK(mndTransCheckConflict(pMnode, pTrans)); MND_TMQ_RETURN_CHECK(sendDeleteSubToVnode(pMnode, pSub, pTrans)); - MND_TMQ_RETURN_CHECK(mndCheckConsumerByGroup(pMnode, pTrans, dropReq.cgroup, dropReq.topic)); + MND_TMQ_RETURN_CHECK(mndCheckConsumerByGroup(pMnode, pTrans, dropReq.cgroup, dropReq.topic, dropReq.force)); MND_TMQ_RETURN_CHECK(mndSetDropSubCommitLogs(pMnode, pTrans, pSub)); MND_TMQ_RETURN_CHECK(mndTransPrepare(pMnode, pTrans)); @@ -1409,7 +1419,7 @@ END: return code; } -int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topicName) { +int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topicName, bool force) { if (pMnode == NULL || pTrans == NULL || topicName == NULL) return TSDB_CODE_INVALID_PARA; SSdb *pSdb = pMnode->pSdb; int32_t code = 0; @@ -1428,7 +1438,7 @@ int32_t mndDropSubByTopic(SMnode *pMnode, STrans *pTrans, const char *topicName) } // iter all vnode to delete handle - if (taosHashGetSize(pSub->consumerHash) != 0) { + if (!force && taosHashGetSize(pSub->consumerHash) != 0) { code = TSDB_CODE_MND_IN_REBALANCE; goto END; } diff --git a/source/dnode/mnode/impl/src/mndTopic.c b/source/dnode/mnode/impl/src/mndTopic.c index 5c199eddbd..30c1f3070a 100644 --- a/source/dnode/mnode/impl/src/mndTopic.c +++ b/source/dnode/mnode/impl/src/mndTopic.c @@ -650,7 +650,7 @@ bool checkTopic(SArray *topics, char *topicName){ return false; } -static int32_t mndCheckConsumerByTopic(SMnode *pMnode, STrans *pTrans, char *topicName){ +static int32_t mndCheckConsumerByTopic(SMnode *pMnode, STrans *pTrans, char *topicName, bool deleteConsumer){ if (pMnode == NULL || pTrans == NULL || topicName == NULL) { return TSDB_CODE_INVALID_MSG; } @@ -658,24 +658,34 @@ static int32_t mndCheckConsumerByTopic(SMnode *pMnode, STrans *pTrans, char *top SSdb *pSdb = pMnode->pSdb; void *pIter = NULL; SMqConsumerObj *pConsumer = NULL; + SMqConsumerObj *pConsumerNew = NULL; + while (1) { pIter = sdbFetch(pSdb, SDB_CONSUMER, pIter, (void **)&pConsumer); if (pIter == NULL) { break; } - bool found = checkTopic(pConsumer->assignedTopics, topicName); - if (found){ - mError("topic:%s, failed to drop since subscribed by consumer:0x%" PRIx64 ", in consumer group %s", - topicName, pConsumer->consumerId, pConsumer->cgroup); - code = TSDB_CODE_MND_TOPIC_SUBSCRIBED; - goto END; + if (deleteConsumer) { + MND_TMQ_RETURN_CHECK(tNewSMqConsumerObj(pConsumer->consumerId, pConsumer->cgroup, -1, NULL, NULL, &pConsumerNew)); + MND_TMQ_RETURN_CHECK(mndSetConsumerDropLogs(pTrans, pConsumerNew)); + tDeleteSMqConsumerObj(pConsumerNew); + pConsumerNew = NULL; + } else { + bool found = checkTopic(pConsumer->assignedTopics, topicName); + if (found){ + mError("topic:%s, failed to drop since subscribed by consumer:0x%" PRIx64 ", in consumer group %s", + topicName, pConsumer->consumerId, pConsumer->cgroup); + code = TSDB_CODE_MND_TOPIC_SUBSCRIBED; + goto END; + } } sdbRelease(pSdb, pConsumer); } END: + tDeleteSMqConsumerObj(pConsumerNew); sdbRelease(pSdb, pConsumer); sdbCancelFetch(pSdb, pIter); return code; @@ -760,8 +770,8 @@ static int32_t mndProcessDropTopicReq(SRpcMsg *pReq) { MND_TMQ_RETURN_CHECK(mndCheckTopicPrivilege(pMnode, pReq->info.conn.user, MND_OPER_DROP_TOPIC, pTopic)); MND_TMQ_RETURN_CHECK(mndCheckDbPrivilegeByName(pMnode, pReq->info.conn.user, MND_OPER_READ_DB, pTopic->db)); - MND_TMQ_RETURN_CHECK(mndCheckConsumerByTopic(pMnode, pTrans, dropReq.name)); - MND_TMQ_RETURN_CHECK(mndDropSubByTopic(pMnode, pTrans, dropReq.name)); + MND_TMQ_RETURN_CHECK(mndCheckConsumerByTopic(pMnode, pTrans, dropReq.name, dropReq.force)); + MND_TMQ_RETURN_CHECK(mndDropSubByTopic(pMnode, pTrans, dropReq.name, dropReq.force)); if (pTopic->ntbUid != 0) { MND_TMQ_RETURN_CHECK(mndDropCheckInfoByTopic(pMnode, pTrans, pTopic)); diff --git a/source/libs/parser/inc/parAst.h b/source/libs/parser/inc/parAst.h index 2f5f344a95..8703b579eb 100644 --- a/source/libs/parser/inc/parAst.h +++ b/source/libs/parser/inc/parAst.h @@ -310,8 +310,8 @@ SNode* createCreateTopicStmtUseDb(SAstCreateContext* pCxt, bool ignoreExists, ST int8_t withMeta); SNode* createCreateTopicStmtUseTable(SAstCreateContext* pCxt, bool ignoreExists, SToken* pTopicName, SNode* pRealTable, int8_t withMeta, SNode* pWhere); -SNode* createDropTopicStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SToken* pTopicName); -SNode* createDropCGroupStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SToken* pCGroupId, SToken* pTopicName); +SNode* createDropTopicStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SToken* pTopicName, bool force); +SNode* createDropCGroupStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SToken* pCGroupId, SToken* pTopicName, bool force); SNode* createAlterClusterStmt(SAstCreateContext* pCxt, const SToken* pConfig, const SToken* pValue); SNode* createAlterLocalStmt(SAstCreateContext* pCxt, const SToken* pConfig, const SToken* pValue); SNode* createDefaultExplainOptions(SAstCreateContext* pCxt); diff --git a/source/libs/parser/inc/sql.y b/source/libs/parser/inc/sql.y index 77b2ae86bf..2307942287 100755 --- a/source/libs/parser/inc/sql.y +++ b/source/libs/parser/inc/sql.y @@ -778,8 +778,8 @@ cmd ::= CREATE TOPIC not_exists_opt(A) topic_name(B) with_meta(D) cmd ::= CREATE TOPIC not_exists_opt(A) topic_name(B) with_meta(E) STABLE full_table_name(C) where_clause_opt(D). { pCxt->pRootNode = createCreateTopicStmtUseTable(pCxt, A, &B, C, E, D); } -cmd ::= DROP TOPIC exists_opt(A) topic_name(B). { pCxt->pRootNode = createDropTopicStmt(pCxt, A, &B); } -cmd ::= DROP CONSUMER GROUP exists_opt(A) cgroup_name(B) ON topic_name(C). { pCxt->pRootNode = createDropCGroupStmt(pCxt, A, &B, &C); } +cmd ::= DROP TOPIC exists_opt(A) force_opt(C) topic_name(B). { pCxt->pRootNode = createDropTopicStmt(pCxt, A, &B, C); } +cmd ::= DROP CONSUMER GROUP exists_opt(A) force_opt(D) cgroup_name(B) ON topic_name(C). { pCxt->pRootNode = createDropCGroupStmt(pCxt, A, &B, &C, D); } /************************************************ desc/describe *******************************************************/ cmd ::= DESC full_table_name(A). { pCxt->pRootNode = createDescribeStmt(pCxt, A); } diff --git a/source/libs/parser/src/parAstCreater.c b/source/libs/parser/src/parAstCreater.c index 7c5a08161d..e44778fade 100644 --- a/source/libs/parser/src/parAstCreater.c +++ b/source/libs/parser/src/parAstCreater.c @@ -3814,7 +3814,7 @@ _err: return NULL; } -SNode* createDropTopicStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SToken* pTopicName) { +SNode* createDropTopicStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SToken* pTopicName, bool force) { CHECK_PARSER_STATUS(pCxt); CHECK_NAME(checkTopicName(pCxt, pTopicName)); SDropTopicStmt* pStmt = NULL; @@ -3822,12 +3822,13 @@ SNode* createDropTopicStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SToken CHECK_MAKE_NODE(pStmt); COPY_STRING_FORM_ID_TOKEN(pStmt->topicName, pTopicName); pStmt->ignoreNotExists = ignoreNotExists; + pStmt->force = force; return (SNode*)pStmt; _err: return NULL; } -SNode* createDropCGroupStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SToken* pCGroupId, SToken* pTopicName) { +SNode* createDropCGroupStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SToken* pCGroupId, SToken* pTopicName, bool force) { CHECK_PARSER_STATUS(pCxt); CHECK_NAME(checkTopicName(pCxt, pTopicName)); CHECK_NAME(checkCGroupName(pCxt, pCGroupId)); @@ -3835,6 +3836,7 @@ SNode* createDropCGroupStmt(SAstCreateContext* pCxt, bool ignoreNotExists, SToke pCxt->errCode = nodesMakeNode(QUERY_NODE_DROP_CGROUP_STMT, (SNode**)&pStmt); CHECK_MAKE_NODE(pStmt); pStmt->ignoreNotExists = ignoreNotExists; + pStmt->force = force; COPY_STRING_FORM_ID_TOKEN(pStmt->topicName, pTopicName); COPY_STRING_FORM_ID_TOKEN(pStmt->cgroup, pCGroupId); return (SNode*)pStmt; diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 61521c7ea1..d74834d298 100644 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -11773,6 +11773,7 @@ static int32_t translateDropTopic(STranslateContext* pCxt, SDropTopicStmt* pStmt snprintf(dropReq.name, sizeof(dropReq.name), "%d.%s", pCxt->pParseCxt->acctId, pStmt->topicName); dropReq.igNotExists = pStmt->ignoreNotExists; + dropReq.force = pStmt->force; int32_t code = buildCmdMsg(pCxt, TDMT_MND_TMQ_DROP_TOPIC, (FSerializeFunc)tSerializeSMDropTopicReq, &dropReq); tFreeSMDropTopicReq(&dropReq); @@ -11788,6 +11789,7 @@ static int32_t translateDropCGroup(STranslateContext* pCxt, SDropCGroupStmt* pSt if (TSDB_CODE_SUCCESS != code) return code; (void)tNameGetFullDbName(&name, dropReq.topic); dropReq.igNotExists = pStmt->ignoreNotExists; + dropReq.force = pStmt->force; tstrncpy(dropReq.cgroup, pStmt->cgroup, TSDB_CGROUP_LEN); return buildCmdMsg(pCxt, TDMT_MND_TMQ_DROP_CGROUP, (FSerializeFunc)tSerializeSMDropCgroupReq, &dropReq); diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index 3c030ffe1a..098075db39 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -506,6 +506,7 @@ ,,y,system-test,./pytest.sh python3 ./test.py -f 7-tmq/tmq_ts4563.py ,,y,system-test,./pytest.sh python3 ./test.py -f 7-tmq/tmq_td32526.py ,,y,system-test,./pytest.sh python3 ./test.py -f 7-tmq/tmq_td32471.py +,,y,system-test,./pytest.sh python3 ./test.py -f 7-tmq/tmq_ts6115.py ,,y,system-test,./pytest.sh python3 ./test.py -f 7-tmq/tmq_replay.py ,,y,system-test,./pytest.sh python3 ./test.py -f 7-tmq/tmqSeekAndCommit.py ,,n,system-test,python3 ./test.py -f 7-tmq/tmq_offset.py diff --git a/tests/system-test/7-tmq/tmq_ts6115.py b/tests/system-test/7-tmq/tmq_ts6115.py new file mode 100644 index 0000000000..ff38db25d2 --- /dev/null +++ b/tests/system-test/7-tmq/tmq_ts6115.py @@ -0,0 +1,116 @@ +import taos +import sys +import time +import socket +import os +import threading + +from util.log import * +from util.sql import * +from util.cases import * +from util.dnodes import * +from util.common import * + +sys.path.append("./7-tmq") + +insertJson = '''{ + "filetype": "insert", + "cfgdir": "/etc/taos", + "host": "localhost", + "port": 6030, + "user": "root", + "password": "taosdata", + "connection_pool_size": 10, + "thread_count": 10, + "create_table_thread_count": 10, + "result_file": "./insert-2-2-1.txt", + "confirm_parameter_prompt": "no", + "num_of_records_per_req": 3600, + "prepared_rand": 3600, + "chinese": "no", + "escape_character": "yes", + "continue_if_fail": "no", + "databases": [ + { + "dbinfo": { + "name": "ts6115", + "drop": "yes", + "vgroups": 10, + "precision": "ms", + "buffer": 512, + "cachemodel":"'both'", + "stt_trigger": 1 + }, + "super_tables": [ + { + "name": "stb", + "child_table_exists": "no", + "childtable_count": 10000, + "childtable_prefix": "d_", + "auto_create_table": "yes", + "batch_create_tbl_num": 10, + "data_source": "csv", + "insert_mode": "stmt", + "non_stop_mode": "no", + "line_protocol": "line", + "insert_rows": 1000, + "childtable_limit": 0, + "childtable_offset": 0, + "interlace_rows": 0, + "insert_interval": 0, + "partial_col_num": 0, + "timestamp_step": 1000, + "start_timestamp": "2024-11-01 00:00:00.000", + "sample_format": "csv", + "sample_file": "./td_double10000_juchi.csv", + "use_sample_ts": "no", + "tags_file": "", + "columns": [ + {"type": "DOUBLE", "name": "val"}, + { "type": "INT", "name": "quality"} + ], + "tags": [ + {"type": "INT", "name": "id", "max": 100, "min": 1} + ] + } + ] + } + ] +}''' + +class TDTestCase: + updatecfgDict = {'debugFlag': 135, 'asynclog': 0} + clientCfgDict = {'debugFlag': 135, 'asynclog': 0} + updatecfgDict["clientCfg"] = clientCfgDict + def init(self, conn, logSql, replicaVar=1): + self.replicaVar = int(replicaVar) + tdLog.debug(f"start to excute {__file__}") + tdSql.init(conn.cursor()) + #tdSql.init(conn.cursor(), logSql) # output sql.txt file + + def run(self): + + with open('ts-6115.json', 'w') as file: + file.write(insertJson) + + tdLog.info("start to insert data: taosBenchmark -f ts-6115.json") + if os.system("taosBenchmark -f ts-6115.json") != 0: + tdLog.exit("taosBenchmark -f ts-6115.json") + + tdLog.info("test ts-6115 ......") + + buildPath = tdCom.getBuildPath() + cmdStr = '%s/build/bin/tmq_ts6115'%(buildPath) + + tdLog.info(cmdStr) + if os.system(cmdStr) != 0: + tdLog.exit(cmdStr) + + return + + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) diff --git a/utils/test/c/CMakeLists.txt b/utils/test/c/CMakeLists.txt index f9e2938f56..e85fbf4d6d 100644 --- a/utils/test/c/CMakeLists.txt +++ b/utils/test/c/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(tmq_ts5466 tmq_ts5466.c) add_executable(tmq_td32526 tmq_td32526.c) add_executable(tmq_td32187 tmq_td32187.c) add_executable(tmq_ts5776 tmq_ts5776.c) +add_executable(tmq_ts6115 tmq_ts6115.c) add_executable(tmq_td32471 tmq_td32471.c) add_executable(tmq_td33798 tmq_td33798.c) add_executable(tmq_poll_test tmq_poll_test.c) @@ -96,6 +97,14 @@ target_link_libraries( PUBLIC os ) +target_link_libraries( + tmq_ts6115 + PUBLIC ${TAOS_LIB} + PUBLIC util + PUBLIC common + PUBLIC os +) + target_link_libraries( tmq_poll_test PUBLIC ${TAOS_LIB} diff --git a/utils/test/c/tmq_ts6115.c b/utils/test/c/tmq_ts6115.c new file mode 100644 index 0000000000..c5131d83fa --- /dev/null +++ b/utils/test/c/tmq_ts6115.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include "taos.h" +#include "types.h" + +const char* topic_name = "t1"; +bool pollStart = false; + +int32_t create_topic() { + printf("create topic\n"); + TAOS_RES* pRes; + TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); + ASSERT(pConn != NULL); + + pRes = taos_query(pConn, "drop topic if exists t1"); + ASSERT(taos_errno(pRes) == 0); + taos_free_result(pRes); + + pRes = taos_query(pConn, "create topic t1 as database ts6115"); + ASSERT(taos_errno(pRes) == 0); + taos_free_result(pRes); + + taos_close(pConn); + return 0; +} + +void* consumeThreadFunc(void* param) { + tmq_conf_t* conf = tmq_conf_new(); + tmq_conf_set(conf, "enable.auto.commit", "false"); + tmq_conf_set(conf, "auto.commit.interval.ms", "2000"); + tmq_conf_set(conf, "group.id", "group_1"); + tmq_conf_set(conf, "td.connect.user", "root"); + tmq_conf_set(conf, "td.connect.pass", "taosdata"); + tmq_conf_set(conf, "auto.offset.reset", "earliest"); + tmq_conf_set(conf, "msg.with.table.name", "false"); + + tmq_t* tmq = tmq_consumer_new(conf, NULL, 0); + tmq_conf_destroy(conf); + + // 创建订阅 topics 列表 + tmq_list_t* topicList = tmq_list_new(); + tmq_list_append(topicList, topic_name); + + // 启动订阅 + tmq_subscribe(tmq, topicList); + tmq_list_destroy(topicList); + + int32_t timeout = 200; + while (1) { + printf("start to poll\n"); + + TAOS_RES *pRes = tmq_consumer_poll(tmq, timeout); + pollStart = true; + if (pRes == NULL) { + printf("pRes is NULL, reason:%s\n", taos_errstr(NULL)); + break; + } + taos_free_result(pRes); + taosMsleep(200); + } + tmq_consumer_close(tmq); + return NULL; +} + +void* dropTopicThreadFunc(void* param) { + printf("drop topic\n"); + TAOS_RES* pRes; + TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); + ASSERT(pConn != NULL); + + while(!pollStart) { + taosSsleep(taosRand()%5); + } + pRes = taos_query(pConn, "drop topic force t1"); + ASSERT(taos_errno(pRes) == 0); + taos_free_result(pRes); + + pRes = taos_query(pConn, "show consumers"); + ASSERT(taos_errno(pRes) == 0); + ASSERT(taos_affected_rows(pRes) == 0); + taos_free_result(pRes); + + taos_close(pConn); + return NULL; +} + +void* dropCGroupThreadFunc(void* param) { + printf("drop topic\n"); + TAOS_RES* pRes; + TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); + ASSERT(pConn != NULL); + + while(!pollStart) { + taosSsleep(taosRand()%5); + } + + pRes = taos_query(pConn, "drop consumer group force group_1 on t1"); + ASSERT(taos_errno(pRes) == 0); + taos_free_result(pRes); + + pRes = taos_query(pConn, "show consumers"); + ASSERT(taos_errno(pRes) == 0); + ASSERT(taos_affected_rows(pRes) == 0); + taos_free_result(pRes); + + taos_close(pConn); + return NULL; +} + +int main(int argc, char* argv[]) { + printf("test start.........\n"); + + int32_t runTimes = 1; + TdThread thread1, thread2; + TdThreadAttr thattr; + taosThreadAttrInit(&thattr); + taosThreadAttrSetDetachState(&thattr, PTHREAD_CREATE_JOINABLE); + + for (int i = 0; i < runTimes; i++){ + printf("test drop topic times:%d\n", i); + + pollStart = false; + create_topic(); + taosThreadCreate(&(thread1), &thattr, dropTopicThreadFunc, NULL); + taosThreadCreate(&(thread2), &thattr, consumeThreadFunc, NULL); + + taosThreadJoin(thread1, NULL); + taosThreadClear(&thread1); + + taosThreadJoin(thread2, NULL); + taosThreadClear(&thread2); + } + + create_topic(); + + for (int i = 0; i < runTimes; i++){ + printf("test drop consumer group times:%d\n", i); + + pollStart = false; + taosThreadCreate(&(thread1), &thattr, dropCGroupThreadFunc, NULL); + taosThreadCreate(&(thread2), &thattr, consumeThreadFunc, NULL); + + taosThreadJoin(thread1, NULL); + taosThreadClear(&thread1); + + taosThreadJoin(thread2, NULL); + taosThreadClear(&thread2); + } + + printf("test end.........\n"); + return 0; +} From ac0872caf93e8aa7ed085d063819bc5b6df37503 Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Tue, 18 Mar 2025 14:47:22 +0800 Subject: [PATCH 6/7] ci: remove if condition for invoking test workflow --- .github/workflows/tdengine-test.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/tdengine-test.yml b/.github/workflows/tdengine-test.yml index 3ab6d3c1d1..436eedd0e2 100644 --- a/.github/workflows/tdengine-test.yml +++ b/.github/workflows/tdengine-test.yml @@ -44,7 +44,6 @@ env: jobs: run-tests-on-linux: uses: taosdata/.github/.github/workflows/run-tests-on-linux.yml@main - if: ${{ github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'}} with: tdinternal: false specified_source_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_source_branch }} @@ -53,7 +52,6 @@ jobs: run-tests-on-mac: uses: taosdata/.github/.github/workflows/run-tests-on-macos.yml@main - if: ${{ github.event_name == 'pull_request' }} with: tdinternal: false specified_source_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_source_branch }} @@ -62,7 +60,6 @@ jobs: run-tests-on-windows: uses: taosdata/.github/.github/workflows/run-tests-on-windows.yml@main - if: ${{ github.event_name == 'pull_request' }} with: tdinternal: false specified_source_branch: ${{ github.event_name == 'pull_request' && 'unavailable' || inputs.specified_source_branch }} From 0fb373ef034a8ec6c42e0eae8edcf847e61263cf Mon Sep 17 00:00:00 2001 From: xinsheng Ren <285808407@qq.com> Date: Tue, 18 Mar 2025 15:11:10 +0800 Subject: [PATCH 7/7] fix: scalarTest on mac (#30233) Co-authored-by: facetosea <25808407@qq.com> --- .../libs/scalar/test/scalar/scalarTests.cpp | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/source/libs/scalar/test/scalar/scalarTests.cpp b/source/libs/scalar/test/scalar/scalarTests.cpp index fec9d14ae0..5f6eea9cba 100644 --- a/source/libs/scalar/test/scalar/scalarTests.cpp +++ b/source/libs/scalar/test/scalar/scalarTests.cpp @@ -2014,7 +2014,7 @@ TEST(columnTest, smallint_value_add_int_column) { ASSERT_EQ(column->info.type, TSDB_DATA_TYPE_DOUBLE); ASSERT_EQ(column->info.bytes, tDataTypes[TSDB_DATA_TYPE_DOUBLE].bytes); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(column, i)), eRes[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(column, i)), eRes[i]); } taosArrayDestroyEx(blockList, scltFreeDataBlock); @@ -2058,7 +2058,7 @@ TEST(columnTest, bigint_column_multi_binary_column) { ASSERT_EQ(column->info.type, TSDB_DATA_TYPE_DOUBLE); ASSERT_EQ(column->info.bytes, tDataTypes[TSDB_DATA_TYPE_DOUBLE].bytes); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(column, i)), eRes[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(column, i)), eRes[i]); } taosArrayDestroyEx(blockList, scltFreeDataBlock); nodesDestroyNode(opNode); @@ -2838,7 +2838,7 @@ TEST(ScalarFunctionTest, absFunction_constant) { code = absFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), val_double); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), val_double); } scltDestroyDataBlock(pInput); scltDestroyDataBlock(pOutput); @@ -2852,7 +2852,7 @@ TEST(ScalarFunctionTest, absFunction_constant) { code = absFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), -val_double); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), -val_double); } scltDestroyDataBlock(pInput); scltDestroyDataBlock(pOutput); @@ -3051,7 +3051,7 @@ TEST(ScalarFunctionTest, absFunction_column) { code = absFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), val_double + i); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), val_double + i); PRINTF("double after ABS:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3072,7 +3072,7 @@ TEST(ScalarFunctionTest, absFunction_column) { code = absFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), -(val_double + i)); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), -(val_double + i)); PRINTF("double after ABS:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3100,7 +3100,7 @@ TEST(ScalarFunctionTest, sinFunction_constant) { code = sinFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("tiny_int after SIN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3118,7 +3118,7 @@ TEST(ScalarFunctionTest, sinFunction_constant) { code = sinFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("float after SIN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3150,7 +3150,7 @@ TEST(ScalarFunctionTest, sinFunction_column) { code = sinFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("tiny_int after SIN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3172,7 +3172,7 @@ TEST(ScalarFunctionTest, sinFunction_column) { code = sinFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("float after SIN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3200,7 +3200,7 @@ TEST(ScalarFunctionTest, cosFunction_constant) { code = cosFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("tiny_int after COS:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3218,7 +3218,7 @@ TEST(ScalarFunctionTest, cosFunction_constant) { code = cosFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("float after COS:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3250,7 +3250,7 @@ TEST(ScalarFunctionTest, cosFunction_column) { code = cosFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("tiny_int after COS:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3272,7 +3272,7 @@ TEST(ScalarFunctionTest, cosFunction_column) { code = cosFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("float after COS:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3300,7 +3300,7 @@ TEST(ScalarFunctionTest, tanFunction_constant) { code = tanFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("tiny_int after TAN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3318,7 +3318,7 @@ TEST(ScalarFunctionTest, tanFunction_constant) { code = tanFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("float after TAN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3400,7 +3400,7 @@ TEST(ScalarFunctionTest, asinFunction_constant) { code = asinFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("tiny_int after ASIN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3418,7 +3418,7 @@ TEST(ScalarFunctionTest, asinFunction_constant) { code = asinFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("float after ASIN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3450,7 +3450,7 @@ TEST(ScalarFunctionTest, asinFunction_column) { code = asinFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("tiny_int after ASIN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3471,7 +3471,7 @@ TEST(ScalarFunctionTest, asinFunction_column) { code = asinFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("float after ASIN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3517,7 +3517,7 @@ TEST(ScalarFunctionTest, acosFunction_constant) { code = acosFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("float after ACOS:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3548,7 +3548,7 @@ TEST(ScalarFunctionTest, acosFunction_column) { code = acosFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("tiny_int after ACOS:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3569,7 +3569,7 @@ TEST(ScalarFunctionTest, acosFunction_column) { code = acosFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("float after ACOS:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3597,7 +3597,7 @@ TEST(ScalarFunctionTest, atanFunction_constant) { code = atanFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("tiny_int after ATAN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3615,7 +3615,7 @@ TEST(ScalarFunctionTest, atanFunction_constant) { code = atanFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("float after ATAN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3646,7 +3646,7 @@ TEST(ScalarFunctionTest, atanFunction_column) { code = atanFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("tiny_int after ATAN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -3667,7 +3667,7 @@ TEST(ScalarFunctionTest, atanFunction_column) { code = atanFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("float after ATAN:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -3983,7 +3983,7 @@ TEST(ScalarFunctionTest, sqrtFunction_constant) { code = sqrtFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("tiny_int after SQRT:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -4001,7 +4001,7 @@ TEST(ScalarFunctionTest, sqrtFunction_constant) { code = sqrtFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("float after SQRT:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -4032,7 +4032,7 @@ TEST(ScalarFunctionTest, sqrtFunction_column) { code = sqrtFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("tiny_int after SQRT:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(pInput); @@ -4053,7 +4053,7 @@ TEST(ScalarFunctionTest, sqrtFunction_column) { code = sqrtFunction(pInput, 1, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("float after SQRT:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -4086,7 +4086,7 @@ TEST(ScalarFunctionTest, logFunction_constant) { code = logFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("tiny_int after LOG:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(input[0]); @@ -4108,7 +4108,7 @@ TEST(ScalarFunctionTest, logFunction_constant) { code = logFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("float after LOG:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(input[0]); @@ -4131,7 +4131,7 @@ TEST(ScalarFunctionTest, logFunction_constant) { code = logFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("tiny_int,float after LOG:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -4170,7 +4170,7 @@ TEST(ScalarFunctionTest, logFunction_column) { code = logFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("tiny_int after LOG:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(input[0]); @@ -4196,7 +4196,7 @@ TEST(ScalarFunctionTest, logFunction_column) { code = logFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("float after LOG:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(input[0]); @@ -4228,7 +4228,7 @@ TEST(ScalarFunctionTest, logFunction_column) { code = logFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("tiny_int,float after LOG:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -4263,7 +4263,7 @@ TEST(ScalarFunctionTest, powFunction_constant) { code = powFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("tiny_int after POW:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(input[0]); @@ -4285,7 +4285,7 @@ TEST(ScalarFunctionTest, powFunction_constant) { code = powFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("float after POW:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(input[0]); @@ -4308,7 +4308,7 @@ TEST(ScalarFunctionTest, powFunction_constant) { code = powFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result); PRINTF("tiny_int,float after POW:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -4347,7 +4347,7 @@ TEST(ScalarFunctionTest, powFunction_column) { code = powFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("tiny_int after POW:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } @@ -4374,7 +4374,7 @@ TEST(ScalarFunctionTest, powFunction_column) { code = powFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("float after POW:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); } scltDestroyDataBlock(input[0]); @@ -4406,7 +4406,7 @@ TEST(ScalarFunctionTest, powFunction_column) { code = powFunction(pInput, 2, pOutput); ASSERT_EQ(code, TSDB_CODE_SUCCESS); for (int32_t i = 0; i < rowNum; ++i) { - ASSERT_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); + ASSERT_DOUBLE_EQ(*((double *)colDataGetData(pOutput->columnData, i)), result[i]); PRINTF("tiny_int,float after POW:%f\n", *((double *)colDataGetData(pOutput->columnData, i))); }