• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #4988

16 Mar 2026 12:26PM UTC coverage: 75.821% (+1.9%) from 73.883%
#4988

push

travis-ci

web-flow
feat: support secure delete option. (#34591)

274 of 464 new or added lines in 29 files covered. (59.05%)

4404 existing lines in 23 files now uncovered.

337108 of 444611 relevant lines covered (75.82%)

146708292.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.45
/source/common/test/tmsgTest.cpp
1
#include <iostream>
2
#include <iomanip>
3
#include <fstream>
4
#include <sstream>
5
#include <string>
6
#include <vector>
7
#include <cstring>
8
#include <unistd.h>
9
#include <limits.h>
10
#include <sys/stat.h>
11
#include <arpa/inet.h>
12
#include <algorithm>
13
#include <unordered_map>
14
#include <gtest/gtest.h>
15

16
#include "tmsg.h"
17

18
#undef TD_MSG_NUMBER_
19
#undef TD_MSG_DICT_
20
#undef TD_MSG_INFO_
21
#define TD_MSG_TYPE_INFO_
22
#undef TD_MSG_RANGE_CODE_
23
#undef TD_MSG_SEG_CODE_
24
#include "tmsgdef.h"
25

26
#undef getline
27
#undef close
28

29
using namespace std;
30

31
enum class ParseStatus {
32
  Success,
33
  FileNotExist,
34
  FileNotOpen,
35
  ResponseWithoutRequest,
36
  RequestWithoutResponse
37
};
38

39
typedef struct {
40
  string name;
41
  string rspName;
42
  int32_t type;
43
  int32_t rspType;
44
} STestMsgTypeInfo;
45

46
string getExecutableDirectory() {
204✔
47
  char result[PATH_MAX];
48
  ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
204✔
49
  if (count != -1) {
204✔
50
    result[count] = '\0';
204✔
51
    string path(result);
204✔
52
    size_t pos = path.rfind('/');
204✔
53
    if (pos != string::npos) {
204✔
54
      path.erase(pos + 1);
204✔
55
    }
56
    return path;
408✔
57
  } else {
204✔
UNCOV
58
    throw std::runtime_error("Failed to get the executable's directory");
×
59
  }
60
}
61

62

63
// parses key-value pairs from strings
64
pair<string, int32_t> parseKeyValuePair(const string &line, char delim = '=') {
108,528✔
65
  size_t pos = line.find(delim);
108,528✔
66
  if (pos == string::npos)
108,528✔
UNCOV
67
    return make_pair("", 0);
×
68

69
  string key = line.substr(0, pos);
108,528✔
70

71
  // remove leading spaces
72
  size_t firstNotSpace = key.find_first_not_of(" ");
108,528✔
73
  if (firstNotSpace != string::npos) {
108,528✔
74
    key = key.substr(firstNotSpace);
108,528✔
75
  } else {
UNCOV
76
    key.clear();
×
77
  }
78

79
  // remove ending spaces
80
  size_t lastNotSpace = key.find_last_not_of(" ");
108,528✔
81
  if (lastNotSpace != string::npos) {
108,528✔
82
    key = key.substr(0, lastNotSpace + 1);
108,528✔
83
  }
84

85
  if (key.front() == '"' && key.back() == '"')
108,528✔
UNCOV
86
    key = key.substr(1, key.size() - 2);
×
87
  
88
  if (key.front() == '\'' && key.back() == '\'')
108,528✔
UNCOV
89
    key = key.substr(1, key.size() - 2);
×
90

91
  string valStr = line.substr(pos + 1);
108,528✔
92
  int32_t val = stoi(valStr);
108,528✔
93
  return make_pair(key, val);
108,528✔
94
}
108,528✔
95

96
// read the configuration file and parse it into the STestMsgTypeInfo array
97
ParseStatus readConfig(const string& filePath, vector<STestMsgTypeInfo>& msgTypes) {
204✔
98
  ifstream file(filePath);
204✔
99
  if (!file.is_open()) {
204✔
UNCOV
100
    if (file.fail() && errno == ENOENT) {
×
UNCOV
101
      cerr << "Error: The file does not exist, file: " << filePath << endl;
×
102
      return ParseStatus::FileNotExist;
×
103
    } else {
104
      cerr << "Error: Could not open the file, file: " << filePath << endl;
×
UNCOV
105
      return ParseStatus::FileNotOpen;
×
106
    }
107
  }
108

109
  auto endsWith = [](const string& str, const string& suffix) {
54,264✔
110
    if (str.length() < suffix.length()) {
54,264✔
UNCOV
111
      return false;
×
112
    }
113
    return equal(str.end() - suffix.length(), str.end(), suffix.begin());
54,264✔
114
  };
115

116

117
  bool evenLine = true;
204✔
118
  string line;
204✔
119
  string suffix("_RSP");
204✔
120
  pair<string, int32_t> reqKwInfo;
204✔
121
  while (std::getline(file, line)) {
108,732✔
122
    char delim = '#';
108,528✔
123
    if (line.find('=') != string::npos) {
108,528✔
124
      delim = '=';
108,528✔
UNCOV
125
    } else if (line.find(':') != string::npos) {
×
UNCOV
126
      delim = ':';
×
127
    } else if (line.find('{') != string::npos || line.find('}') != string::npos) {
×
128
      // TODO: parse json format
129
      continue; 
×
130
    } else {
131
      continue;
×
132
    }
133

134
    auto curKwInfo = parseKeyValuePair(line, delim);
108,528✔
135
    evenLine = ! evenLine;
108,528✔
136

137
    // check message type
138
    if (evenLine == false) {                                              // req msg
108,528✔
139
      reqKwInfo = curKwInfo;
54,264✔
140
    } else {                                                              // rsp msg
141
      if (reqKwInfo.first.empty()) {
54,264✔
UNCOV
142
        cerr << "Error: Found a response message without a matching request, rsp: " << curKwInfo.first << endl;
×
UNCOV
143
        return ParseStatus::ResponseWithoutRequest;
×
144
      } else if (!endsWith(curKwInfo.first, suffix)) {
54,264✔
145
        cerr << "Error: A request message was not followed by a matching response, req: " << reqKwInfo.first << endl;
×
UNCOV
146
        return ParseStatus::RequestWithoutResponse;
×
147
      } else {
148
        STestMsgTypeInfo msgInfo;
54,264✔
149
        msgInfo.name      = reqKwInfo.first;
54,264✔
150
        msgInfo.rspName   = curKwInfo.first;
54,264✔
151
        msgInfo.type      = reqKwInfo.second;
54,264✔
152
        msgInfo.rspType   = curKwInfo.second;
54,264✔
153
        msgTypes.push_back(msgInfo);
54,264✔
154

155
        // reset req info
156
        reqKwInfo    = make_pair("", -1); 
54,264✔
157
      }
54,264✔
158
    }
159
  }
108,528✔
160

161
  if (!reqKwInfo.first.empty()) {
204✔
UNCOV
162
    cerr << "Error: A request message was not followed by a matching response, req: " << reqKwInfo.first << endl;
×
UNCOV
163
    return ParseStatus::RequestWithoutResponse;
×
164
  }
165

166
  return ParseStatus::Success;
204✔
167
}
204✔
168

169
TEST(td_msg_test, msg_type_compatibility_test) {
816✔
170
  // cout << TMSG_INFO(TDMT_VND_DROP_TABLE) << endl;
171
  // cout << TMSG_INFO(TDMT_MND_DROP_SUPER_TABLE) << endl;
172
  // cout << TMSG_INFO(TDMT_MND_CREATE_SUPER_TABLE) << endl;
173

174
  // int32_t msgSize = sizeof(tMsgTypeInfo) / sizeof(SMsgTypeInfo);
175
  // for (int32_t i = 0; i < msgSize; ++i) {
176
  //   SMsgTypeInfo *pInfo = &tMsgTypeInfo[i];
177
  //   cout << i * 2 + 1 << " " << pInfo->name << " " << pInfo->type << endl;
178
  //   cout << i * 2 + 2 << " " << pInfo->rspName << " " << pInfo->rspType << endl;
179
  // }
180

181

182
  // current msgs: to map
183
  unordered_map<string, const SMsgTypeInfo*> map;
204✔
184
  for (const auto& info : tMsgTypeInfo) {
78,540✔
185
    map[info.name] = &info;
78,336✔
186
  }
187

188
  string configFileName = "msgTypeTable.ini";
204✔
189
  string execDir = getExecutableDirectory();
204✔
190
  string configFilePath(execDir + configFileName);
204✔
191

192
  vector<STestMsgTypeInfo> msgTypes;
204✔
193
  ParseStatus status = readConfig(configFilePath, msgTypes);
204✔
194

195
  switch (status) {
204✔
196
    case ParseStatus::Success:
204✔
197
      for (const auto& stdInfo : msgTypes) {
54,468✔
198
        auto it = map.find(stdInfo.name);
54,264✔
199
        if (it == map.end()) {
54,264✔
UNCOV
200
          FAIL() << "Error: Could not find msg: " << stdInfo.name << ".";
×
201
        } else {
202
          auto newInfo = it->second;
54,264✔
203

204
          ASSERT_STREQ(stdInfo.name.c_str(), newInfo->name);
54,264✔
205
          ASSERT_STREQ(stdInfo.rspName.c_str(), newInfo->rspName);
54,264✔
206
          ASSERT_EQ(stdInfo.type, newInfo->type) 
54,264✔
207
              << "Message type mismatch(" << stdInfo.name << "): expected " << stdInfo.type << ", got " << newInfo->type << ".";
54,264✔
208
          ASSERT_EQ(stdInfo.rspType, newInfo->rspType) 
54,264✔
209
              << "Message response type mismatch(" << stdInfo.rspName << "): expected " << stdInfo.rspType << ", got " << newInfo->rspType << ".";
54,264✔
210
        }
211
      }
212
      break;
204✔
UNCOV
213
    case ParseStatus::FileNotExist:
×
UNCOV
214
      FAIL() << "Error: The file does not exist, file: " << configFileName << ".";
×
215
      break;
216
    case ParseStatus::FileNotOpen:
×
UNCOV
217
      FAIL() << "Error: Could not open the file, file: " << configFileName << ".";
×
218
      break;
219
    case ParseStatus::ResponseWithoutRequest:
×
UNCOV
220
      FAIL() << "Error: Found a response message without a matching request.";
×
221
      break;
222
    case ParseStatus::RequestWithoutResponse:
×
UNCOV
223
      FAIL() << "Error: A request message was not followed by a matching response.";
×
224
      break;
225
    default:
×
UNCOV
226
      FAIL() << "Unknown Error.";
×
227
      break;
228
  }
229
}
204✔
230

UNCOV
231
size_t maxLengthOfMsgType() {
×
UNCOV
232
  size_t maxLen = 0;
×
233
  for (const auto& info : tMsgTypeInfo) {
×
234
    maxLen = std::max(maxLen, strlen(info.name));
×
235
    maxLen = std::max(maxLen, strlen(info.rspName));
×
236
  }
237
  return (maxLen / 4 + 1) * 4;
×
238
}
239

240

UNCOV
241
void generateConfigFile(const string& filePath) {
×
UNCOV
242
  size_t maxStringLength = maxLengthOfMsgType();
×
243
  std::ofstream file(filePath);
×
244
  if (!file.is_open()) {
×
245
    cerr << "Failed to open file for writing, at: " << filePath << "." << endl;
×
246
    return;
×
247
  }
248

UNCOV
249
  for (const auto& info : tMsgTypeInfo) {
×
UNCOV
250
      file << std::left << std::setw(maxStringLength) << info.name << "= " << info.type << endl;
×
251
      file << std::left << std::setw(maxStringLength) << info.rspName << "= " << info.rspType << endl;
×
252
  }
253

UNCOV
254
  if (file.fail()) {
×
UNCOV
255
    cerr << "An error occurred while writing to the file." << endl;
×
256
  } else {
257
    cout << "Data successfully written to file: " << filePath << endl;
×
258
  }
259

UNCOV
260
  file.close();
×
UNCOV
261
}
×
262

263
static int32_t serializeOldSVDeleteReq(void* buf, int32_t bufLen, SVDeleteReq* pReq) {
204✔
264
  const int32_t headLen = sizeof(SMsgHead);
204✔
265
  SEncoder      encoder = {0};
204✔
266
  tEncoderInit(&encoder, (uint8_t*)buf + headLen, bufLen - headLen);
204✔
267

268
  if (tStartEncode(&encoder) != 0) return -1;
204✔
269
  if (tEncodeU64(&encoder, pReq->sId) != 0) return -1;
408✔
270
  if (tEncodeU64(&encoder, pReq->queryId) != 0) return -1;
408✔
271
  if (tEncodeU64(&encoder, pReq->taskId) != 0) return -1;
408✔
272
  if (tEncodeU32(&encoder, pReq->sqlLen) != 0) return -1;
408✔
273
  if (tEncodeCStr(&encoder, pReq->sql) != 0) return -1;
408✔
274
  if (tEncodeBinary(&encoder, (const uint8_t*)pReq->msg, pReq->phyLen) != 0) return -1;
408✔
275
  if (tEncodeI8(&encoder, pReq->source) != 0) return -1;
408✔
276
  if (tEncodeU64(&encoder, pReq->clientId) != 0) return -1;
408✔
277
  tEndEncode(&encoder);
204✔
278

279
  int32_t  tlen = encoder.pos;
204✔
280
  SMsgHead* pHead = (SMsgHead*)buf;
204✔
281
  pHead->vgId = htonl(pReq->header.vgId);
204✔
282
  pHead->contLen = htonl(tlen + headLen);
204✔
283
  return tlen + headLen;
204✔
284
}
285

286
TEST(td_msg_test, delete_req_codec_secure_delete) {
816✔
287
  SVDeleteReq req = {0};
204✔
288
  req.header.vgId = 123;
204✔
289
  req.sId = 1;
204✔
290
  req.queryId = 2;
204✔
291
  req.taskId = 3;
204✔
292
  req.sql = (char*)"delete from t1";
204✔
293
  req.sqlLen = strlen(req.sql);
204✔
294
  req.msg = (char*)"xyz";
204✔
295
  req.phyLen = 3;
204✔
296
  req.source = 7;
204✔
297
  req.clientId = 9;
204✔
298
  req.secureDelete = 1;
204✔
299

300
  int32_t size = tSerializeSVDeleteReq(NULL, 0, &req);
204✔
301
  ASSERT_GT(size, 0);
204✔
302
  std::vector<char> buf(size, 0);
204✔
303
  ASSERT_EQ(tSerializeSVDeleteReq(buf.data(), size, &req), size);
204✔
304

305
  SVDeleteReq out = {0};
204✔
306
  ASSERT_EQ(tDeserializeSVDeleteReq(buf.data(), size, &out), 0);
204✔
307
  ASSERT_EQ(out.sId, req.sId);
204✔
308
  ASSERT_EQ(out.queryId, req.queryId);
204✔
309
  ASSERT_EQ(out.taskId, req.taskId);
204✔
310
  ASSERT_EQ(out.sqlLen, req.sqlLen);
204✔
311
  ASSERT_STREQ(out.sql, req.sql);
204✔
312
  ASSERT_EQ(out.phyLen, req.phyLen);
204✔
313
  ASSERT_EQ(memcmp(out.msg, req.msg, req.phyLen), 0);
204✔
314
  ASSERT_EQ(out.source, req.source);
204✔
315
  ASSERT_EQ(out.clientId, req.clientId);
204✔
316
  ASSERT_EQ(out.secureDelete, req.secureDelete);
204✔
317

318
  taosMemoryFree(out.sql);
204✔
319
  taosMemoryFree(out.msg);
204✔
320
}
321

322
TEST(td_msg_test, delete_req_codec_backward_compat_without_secure_delete) {
816✔
323
  SVDeleteReq req = {0};
204✔
324
  req.header.vgId = 456;
204✔
325
  req.sId = 11;
204✔
326
  req.queryId = 22;
204✔
327
  req.taskId = 33;
204✔
328
  req.sql = (char*)"delete from t2";
204✔
329
  req.sqlLen = strlen(req.sql);
204✔
330
  req.msg = (char*)"abc";
204✔
331
  req.phyLen = 3;
204✔
332
  req.source = 5;
204✔
333
  req.clientId = 7;
204✔
334
  req.secureDelete = 1;
204✔
335

336
  std::vector<char> oldBuf(512, 0);
204✔
337
  int32_t oldSize = serializeOldSVDeleteReq(oldBuf.data(), (int32_t)oldBuf.size(), &req);
204✔
338
  ASSERT_GT(oldSize, 0);
204✔
339

340
  SVDeleteReq out = {0};
204✔
341
  ASSERT_EQ(tDeserializeSVDeleteReq(oldBuf.data(), oldSize, &out), 0);
204✔
342
  ASSERT_EQ(out.sId, req.sId);
204✔
343
  ASSERT_EQ(out.queryId, req.queryId);
204✔
344
  ASSERT_EQ(out.taskId, req.taskId);
204✔
345
  ASSERT_EQ(out.sqlLen, req.sqlLen);
204✔
346
  ASSERT_STREQ(out.sql, req.sql);
204✔
347
  ASSERT_EQ(out.phyLen, req.phyLen);
204✔
348
  ASSERT_EQ(memcmp(out.msg, req.msg, req.phyLen), 0);
204✔
349
  ASSERT_EQ(out.source, req.source);
204✔
350
  ASSERT_EQ(out.clientId, req.clientId);
204✔
351
  ASSERT_EQ(out.secureDelete, 0);
204✔
352

353
  taosMemoryFree(out.sql);
204✔
354
  taosMemoryFree(out.msg);
204✔
355
}
204✔
356

357

358
void processCommandArgs(int argc, char** argv) {
204✔
359
  for (int i = 1; i < argc; ++i) {
204✔
UNCOV
360
    if (string(argv[i]) == "--output-config") {
×
UNCOV
361
      string configFile = (i + 1 < argc) ? argv[++i] : "./msgTypeTable.ini";
×
UNCOV
362
      generateConfigFile(configFile);
×
UNCOV
363
      exit(0);
×
UNCOV
364
    }
×
365
  }
366
}
204✔
367

368

369
int main(int argc, char **argv) {
204✔
370
  processCommandArgs(argc, argv);
204✔
371

372
  testing::InitGoogleTest(&argc, argv);
204✔
373
  return RUN_ALL_TESTS();
204✔
374
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc