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

taosdata / TDengine / #5055

17 May 2026 01:15AM UTC coverage: 73.355% (-0.003%) from 73.358%
#5055

push

travis-ci

web-flow
feat (TDgpt): Dynamic Model Synchronization Enhancements (#35344)

* refactor: do some internal refactor.

* fix: fix multiprocess sync issue.

* feat: add dynamic anomaly detection and forecasting services

* fix: log error message for undeploying model in exception handling

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: handle undeploy when model exists only on disk

Agent-Logs-Url: https://github.com/taosdata/TDengine/sessions/286aafa0-c3ce-4c27-b803-2707571e9dc1

Co-authored-by: hjxilinx <8252296+hjxilinx@users.noreply.github.com>

* fix: guard dynamic registry concurrent access

Agent-Logs-Url: https://github.com/taosdata/TDengine/sessions/5e4db858-6458-40f4-ac28-d1b1b7f97c18

Co-authored-by: hjxilinx <8252296+hjxilinx@users.noreply.github.com>

* fix: tighten service list locking scope

Agent-Logs-Url: https://github.com/taosdata/TDengine/sessions/5e4db858-6458-40f4-ac28-d1b1b7f97c18

Co-authored-by: hjxilinx <8252296+hjxilinx@users.noreply.github.com>

* fix: restore prophet support and update tests per review feedback

Agent-Logs-Url: https://github.com/taosdata/TDengine/sessions/92298ae1-7da6-4d07-b20e-101c7cd0b26b

Co-authored-by: hjxilinx <8252296+hjxilinx@users.noreply.github.com>

* fix: improve test name and move copy inside lock scope

Agent-Logs-Url: https://github.com/taosdata/TDengine/sessions/92298ae1-7da6-4d07-b20e-101c7cd0b26b

Co-authored-by: hjxilinx <8252296+hjxilinx@users.noreply.github.com>

* Potential fix for pull request finding

Co-au... (continued)

281532 of 383795 relevant lines covered (73.35%)

135557734.7 hits per line

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

87.17
/source/client/src/clientSmlJson.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15

16
#include <ctype.h>
17
#include <stdio.h>
18
#include <stdlib.h>
19
#include <string.h>
20
#include "clientSml.h"
21

22
#define OTD_JSON_SUB_FIELDS_NUM 2
23

24
static inline int32_t smlParseMetricFromJSON(SSmlHandle *info, cJSON *metric, SSmlLineInfo *elements) {
475,664✔
25
  elements->measureLen = strlen(metric->valuestring);
475,664✔
26
  if (IS_INVALID_TABLE_LEN(elements->measureLen)) {
475,664✔
27
    uError("SML:0x%" PRIx64 " Metric length is 0 or large than 192", info->id);
1,314✔
28
    return TSDB_CODE_TSC_INVALID_TABLE_ID_LENGTH;
1,314✔
29
  }
30

31
  elements->measure = metric->valuestring;
474,364✔
32
  return TSDB_CODE_SUCCESS;
474,364✔
33
}
34

35
const char    *jsonName[OTD_JSON_FIELDS_NUM] = {"metric", "timestamp", "value", "tags"};
36
static int32_t smlGetJsonElements(cJSON *root, cJSON ***marks) {
475,664✔
37
  for (int i = 0; i < OTD_JSON_FIELDS_NUM; ++i) {
2,378,208✔
38
    cJSON *child = root->child;
1,902,544✔
39
    while (child != NULL) {
4,756,220✔
40
      if (strcasecmp(child->string, jsonName[i]) == 0) {
4,756,248✔
41
        *marks[i] = child;
1,902,572✔
42
        break;
1,902,572✔
43
      }
44
      child = child->next;
2,853,676✔
45
    }
46
    if (*marks[i] == NULL) {
1,902,544✔
47
      uError("SML %s error, not find mark:%d:%s", __FUNCTION__, i, jsonName[i]);
×
48
      return TSDB_CODE_TSC_INVALID_JSON;
×
49
    }
50
  }
51
  return TSDB_CODE_SUCCESS;
475,664✔
52
}
53

54
static int32_t smlConvertJSONBool(SSmlKv *pVal, char *typeStr, cJSON *value) {
456,342✔
55
  if (strcasecmp(typeStr, "bool") != 0) {
456,342✔
56
    uError("SML:invalid type(%s) for JSON Bool", typeStr);
×
57
    return TSDB_CODE_TSC_INVALID_JSON_TYPE;
×
58
  }
59
  pVal->type = TSDB_DATA_TYPE_BOOL;
456,342✔
60
  pVal->length = (int16_t)tDataTypes[pVal->type].bytes;
456,342✔
61
  pVal->i = value->valueint;
456,342✔
62

63
  return TSDB_CODE_SUCCESS;
456,342✔
64
}
65

66
static int32_t smlConvertJSONNumber(SSmlKv *pVal, char *typeStr, cJSON *value) {
684,320✔
67
  // tinyint
68
  if (strcasecmp(typeStr, "i8") == 0 || strcasecmp(typeStr, "tinyint") == 0) {
684,320✔
69
    if (!IS_VALID_TINYINT(value->valuedouble)) {
119,273✔
70
      uError("SML:JSON value(%f) cannot fit in type(tinyint)", value->valuedouble);
2,628✔
71
      return TSDB_CODE_TSC_VALUE_OUT_OF_RANGE;
2,628✔
72
    }
73
    pVal->type = TSDB_DATA_TYPE_TINYINT;
116,659✔
74
    pVal->length = (int16_t)tDataTypes[pVal->type].bytes;
116,659✔
75
    pVal->i = value->valuedouble;
116,659✔
76
    return TSDB_CODE_SUCCESS;
116,659✔
77
  }
78
  // smallint
79
  if (strcasecmp(typeStr, "i16") == 0 || strcasecmp(typeStr, "smallint") == 0) {
565,047✔
80
    if (!IS_VALID_SMALLINT(value->valuedouble)) {
115,359✔
81
      uError("SML:JSON value(%f) cannot fit in type(smallint)", value->valuedouble);
2,628✔
82
      return TSDB_CODE_TSC_VALUE_OUT_OF_RANGE;
2,628✔
83
    }
84
    pVal->type = TSDB_DATA_TYPE_SMALLINT;
112,731✔
85
    pVal->length = (int16_t)tDataTypes[pVal->type].bytes;
112,731✔
86
    pVal->i = value->valuedouble;
112,731✔
87
    return TSDB_CODE_SUCCESS;
112,731✔
88
  }
89
  // int
90
  if (strcasecmp(typeStr, "i32") == 0 || strcasecmp(typeStr, "int") == 0) {
449,688✔
91
    if (!IS_VALID_INT(value->valuedouble)) {
114,064✔
92
      uError("SML:JSON value(%f) cannot fit in type(int)", value->valuedouble);
2,628✔
93
      return TSDB_CODE_TSC_VALUE_OUT_OF_RANGE;
2,628✔
94
    }
95
    pVal->type = TSDB_DATA_TYPE_INT;
111,436✔
96
    pVal->length = (int16_t)tDataTypes[pVal->type].bytes;
111,436✔
97
    pVal->i = value->valuedouble;
111,436✔
98
    return TSDB_CODE_SUCCESS;
111,436✔
99
  }
100
  // bigint
101
  if (strcasecmp(typeStr, "i64") == 0 || strcasecmp(typeStr, "bigint") == 0) {
335,624✔
102
    pVal->type = TSDB_DATA_TYPE_BIGINT;
114,066✔
103
    pVal->length = (int16_t)tDataTypes[pVal->type].bytes;
114,066✔
104
    if (value->valuedouble >= (double)INT64_MAX) {
114,066✔
105
      pVal->i = INT64_MAX;
104,463✔
106
    } else if (value->valuedouble <= (double)INT64_MIN) {
9,603✔
107
      pVal->i = INT64_MIN;
1,314✔
108
    } else {
109
      pVal->i = value->valuedouble;
8,289✔
110
    }
111
    return TSDB_CODE_SUCCESS;
114,066✔
112
  }
113
  // float
114
  if (strcasecmp(typeStr, "f32") == 0 || strcasecmp(typeStr, "float") == 0) {
221,558✔
115
    if (!IS_VALID_FLOAT(value->valuedouble)) {
112,769✔
116
      uError("SML:JSON value(%f) cannot fit in type(float)", value->valuedouble);
2,628✔
117
      return TSDB_CODE_TSC_VALUE_OUT_OF_RANGE;
2,628✔
118
    }
119
    pVal->type = TSDB_DATA_TYPE_FLOAT;
110,141✔
120
    pVal->length = (int16_t)tDataTypes[pVal->type].bytes;
110,141✔
121
    pVal->f = value->valuedouble;
110,141✔
122
    return TSDB_CODE_SUCCESS;
110,141✔
123
  }
124
  // double
125
  if (strcasecmp(typeStr, "f64") == 0 || strcasecmp(typeStr, "double") == 0) {
108,789✔
126
    pVal->type = TSDB_DATA_TYPE_DOUBLE;
108,789✔
127
    pVal->length = (int16_t)tDataTypes[pVal->type].bytes;
108,789✔
128
    pVal->d = value->valuedouble;
108,789✔
129
    return TSDB_CODE_SUCCESS;
108,789✔
130
  }
131

132
  // if reach here means type is unsupported
133
  uError("SML:invalid type(%s) for JSON Number", typeStr);
×
134
  return TSDB_CODE_TSC_INVALID_JSON_TYPE;
×
135
}
136

137
static int32_t smlConvertJSONString(SSmlKv *pVal, char *typeStr, cJSON *value) {
482,546✔
138
  if (strcasecmp(typeStr, "binary") == 0) {
482,546✔
139
    pVal->type = TSDB_DATA_TYPE_BINARY;
351,265✔
140
  } else if (strcasecmp(typeStr, "varbinary") == 0) {
131,281✔
141
    pVal->type = TSDB_DATA_TYPE_VARBINARY;
×
142
  } else if (strcasecmp(typeStr, "nchar") == 0) {
131,281✔
143
    pVal->type = TSDB_DATA_TYPE_NCHAR;
112,228✔
144
  } else {
145
    uError("SML:invalid type(%s) for JSON String", typeStr);
19,053✔
146
    return TSDB_CODE_TSC_INVALID_JSON_TYPE;
19,053✔
147
  }
148
  pVal->length = strlen(value->valuestring);
463,493✔
149

150
  if ((pVal->type == TSDB_DATA_TYPE_BINARY || pVal->type == TSDB_DATA_TYPE_VARBINARY) && pVal->length > TSDB_MAX_BINARY_LEN - VARSTR_HEADER_SIZE) {
463,493✔
151
    return TSDB_CODE_PAR_INVALID_VAR_COLUMN_LEN;
×
152
  }
153
  if (pVal->type == TSDB_DATA_TYPE_NCHAR &&
463,493✔
154
      pVal->length > (TSDB_MAX_NCHAR_LEN - VARSTR_HEADER_SIZE) / TSDB_NCHAR_SIZE) {
112,228✔
155
    return TSDB_CODE_PAR_INVALID_VAR_COLUMN_LEN;
×
156
  }
157

158
  pVal->value = value->valuestring;
463,493✔
159
  return TSDB_CODE_SUCCESS;
463,493✔
160
}
161

162
static int32_t smlParseValueFromJSONObj(cJSON *root, SSmlKv *kv) {
1,414,894✔
163
  int32_t ret = TSDB_CODE_SUCCESS;
1,414,894✔
164
  int32_t size = cJSON_GetArraySize(root);
1,414,894✔
165

166
  if (size != OTD_JSON_SUB_FIELDS_NUM) {
1,414,922✔
167
    return TSDB_CODE_TSC_INVALID_JSON;
×
168
  }
169

170
  cJSON *value = cJSON_GetObjectItem(root, "value");
1,414,922✔
171
  if (value == NULL) {
1,414,922✔
172
    return TSDB_CODE_TSC_INVALID_JSON;
×
173
  }
174

175
  cJSON *type = cJSON_GetObjectItem(root, "type");
1,414,922✔
176
  if (!cJSON_IsString(type)) {
1,414,922✔
177
    return TSDB_CODE_TSC_INVALID_JSON;
×
178
  }
179

180
  switch (value->type) {
1,414,922✔
181
    case cJSON_True:
456,342✔
182
    case cJSON_False: {
183
      ret = smlConvertJSONBool(kv, type->valuestring, value);
456,342✔
184
      if (ret != TSDB_CODE_SUCCESS) {
456,342✔
185
        return ret;
×
186
      }
187
      break;
456,342✔
188
    }
189
    case cJSON_Number: {
684,334✔
190
      ret = smlConvertJSONNumber(kv, type->valuestring, value);
684,334✔
191
      if (ret != TSDB_CODE_SUCCESS) {
684,334✔
192
        return ret;
10,512✔
193
      }
194
      break;
673,822✔
195
    }
196
    case cJSON_String: {
274,246✔
197
      ret = smlConvertJSONString(kv, type->valuestring, value);
274,246✔
198
      if (ret != TSDB_CODE_SUCCESS) {
274,246✔
199
        return ret;
19,053✔
200
      }
201
      break;
255,193✔
202
    }
203
    default:
×
204
      return TSDB_CODE_TSC_INVALID_JSON_TYPE;
×
205
  }
206

207
  return TSDB_CODE_SUCCESS;
1,385,357✔
208
}
209

210
static int32_t smlParseValueFromJSON(cJSON *root, SSmlKv *kv) {
2,398,256✔
211
  switch (root->type) {
2,398,256✔
212
    case cJSON_True:
308,904✔
213
    case cJSON_False: {
214
      kv->type = TSDB_DATA_TYPE_BOOL;
308,904✔
215
      kv->length = (int16_t)tDataTypes[kv->type].bytes;
308,904✔
216
      kv->i = root->valueint;
308,904✔
217
      break;
308,904✔
218
    }
219
    case cJSON_Number: {
464,844✔
220
      kv->type = TSDB_DATA_TYPE_DOUBLE;
464,844✔
221
      kv->length = (int16_t)tDataTypes[kv->type].bytes;
464,844✔
222
      kv->d = root->valuedouble;
464,844✔
223
      break;
464,844✔
224
    }
225
    case cJSON_String: {
208,300✔
226
      int32_t ret = smlConvertJSONString(kv, "binary", root);
208,300✔
227
      if (ret != TSDB_CODE_SUCCESS) {
208,300✔
228
        uError("SML:Failed to parse binary value from JSON Obj");
×
229
        return ret;
×
230
      }
231
      break;
208,300✔
232
    }
233
    case cJSON_Object: {
1,414,894✔
234
      int32_t ret = smlParseValueFromJSONObj(root, kv);
1,414,894✔
235
      if (ret != TSDB_CODE_SUCCESS) {
1,414,922✔
236
        uError("SML:Failed to parse value from JSON Obj");
29,565✔
237
        return ret;
29,565✔
238
      }
239
      break;
1,385,357✔
240
    }
241
    default:
1,314✔
242
      return TSDB_CODE_TSC_INVALID_JSON;
1,314✔
243
  }
244

245
  return TSDB_CODE_SUCCESS;
2,367,405✔
246
}
247

248
static int32_t smlProcessTagJson(SSmlHandle *info, cJSON *tags){
211,417✔
249
  SArray *preLineKV = info->preLineTagKV;
211,417✔
250
  taosArrayClearEx(preLineKV, freeSSmlKv);
211,417✔
251
  int     cnt = 0;
211,417✔
252

253
  int32_t tagNum = cJSON_GetArraySize(tags);
211,417✔
254
  if (unlikely(tagNum == 0)) {
211,417✔
255
    uError("SML:Tag should not be empty");
1,314✔
256
    return TSDB_CODE_TSC_INVALID_JSON;
1,314✔
257
  }
258
  for (int32_t i = 0; i < tagNum; ++i) {
2,116,289✔
259
    cJSON *tag = cJSON_GetArrayItem(tags, i);
1,924,591✔
260
    if (unlikely(tag == NULL)) {
1,924,577✔
261
      return TSDB_CODE_TSC_INVALID_JSON;
×
262
    }
263
    size_t keyLen = strlen(tag->string);
1,924,577✔
264
    if (unlikely(IS_INVALID_COL_LEN(keyLen))) {
1,924,577✔
265
      uError("SML:Tag key length is 0 or too large than 64");
657✔
266
      return TSDB_CODE_TSC_INVALID_COLUMN_LENGTH;
657✔
267
    }
268

269
    // add kv to SSmlKv
270
    SSmlKv kv = {0};
1,923,920✔
271
    kv.key = tag->string;
1,923,920✔
272
    kv.keyLen = keyLen;
1,923,920✔
273

274
    // value
275
    int32_t ret = smlParseValueFromJSON(tag, &kv);
1,923,920✔
276
    if (unlikely(ret != TSDB_CODE_SUCCESS)) {
1,923,920✔
277
      return ret;
13,140✔
278
    }
279
    if (taosArrayPush(preLineKV, &kv) == NULL) {
1,910,752✔
280
      return terrno;
×
281
    }
282

283
    if (info->dataFormat && !isSmlTagAligned(info, cnt, &kv)) {
1,910,752✔
284
      return TSDB_CODE_TSC_INVALID_JSON;
4,580✔
285
    }
286

287
    cnt++;
1,906,186✔
288
  }
289
  return TSDB_CODE_SUCCESS;
191,698✔
290
}
291

292
static int32_t smlParseTagsFromJSON(SSmlHandle *info, cJSON *tags, SSmlLineInfo *elements) {
456,639✔
293
  if (is_same_child_table_telnet(elements, &info->preLine) == 0) {
456,639✔
294
    elements->measureTag = info->preLine.measureTag;
62,197✔
295
    return TSDB_CODE_SUCCESS;
62,197✔
296
  }
297
  int32_t code = 0;
394,428✔
298
  int32_t lino = 0;
394,428✔
299
  if(info->dataFormat){
394,428✔
300
    SML_CHECK_CODE(smlProcessSuperTable(info, elements));
203,452✔
301
  }
302
  SML_CHECK_CODE(smlProcessTagJson(info, tags));
211,403✔
303
  SML_CHECK_CODE(smlJoinMeasureTag(elements));
191,726✔
304
  return smlProcessChildTable(info, elements);
191,726✔
305

306
END:
202,716✔
307
  if(info->reRun){
202,716✔
308
    return TSDB_CODE_SUCCESS;
185,548✔
309
  }
310
  RETURN
17,168✔
311
}
312

313
static int64_t smlParseTSFromJSONObj(SSmlHandle *info, cJSON *root, int32_t toPrecision) {
149,769✔
314
  int32_t size = cJSON_GetArraySize(root);
149,769✔
315
  if (unlikely(size != OTD_JSON_SUB_FIELDS_NUM)) {
149,769✔
316
    smlBuildInvalidDataMsg(&info->msgBuf, "invalidate json", NULL);
×
317
    return TSDB_CODE_TSC_INVALID_JSON;
×
318
  }
319

320
  cJSON *value = cJSON_GetObjectItem(root, "value");
149,769✔
321
  if (unlikely(!cJSON_IsNumber(value))) {
149,769✔
322
    smlBuildInvalidDataMsg(&info->msgBuf, "invalidate json", NULL);
1,971✔
323
    return TSDB_CODE_TSC_INVALID_JSON;
1,971✔
324
  }
325

326
  cJSON *type = cJSON_GetObjectItem(root, "type");
147,798✔
327
  if (unlikely(!cJSON_IsString(type))) {
147,798✔
328
    smlBuildInvalidDataMsg(&info->msgBuf, "invalidate json", NULL);
×
329
    return TSDB_CODE_TSC_INVALID_JSON;
×
330
  }
331

332
  double timeDouble = value->valuedouble;
147,798✔
333
  if (unlikely(smlDoubleToInt64OverFlow(timeDouble))) {
147,798✔
334
    smlBuildInvalidDataMsg(&info->msgBuf, "timestamp is too large", NULL);
×
335
    return TSDB_CODE_TSC_VALUE_OUT_OF_RANGE;
×
336
  }
337

338
  if (timeDouble == 0) {
147,784✔
339
    return taosGetTimestampNs() / smlFactorNS[toPrecision];
×
340
  }
341

342
  if (timeDouble < 0) {
147,784✔
343
    return (int64_t)timeDouble;
×
344
  }
345

346
  int64_t tsInt64 = (int64_t)timeDouble;
147,784✔
347
  size_t  typeLen = strlen(type->valuestring);
147,784✔
348
  if (typeLen == 1 && (type->valuestring[0] == 's' || type->valuestring[0] == 'S')) {
147,784✔
349
    // seconds
350
    if (smlFactorS[toPrecision] < INT64_MAX / tsInt64) {
1,978✔
351
      return tsInt64 * smlFactorS[toPrecision];
1,978✔
352
    }
353
    return TSDB_CODE_TSC_VALUE_OUT_OF_RANGE;
×
354
  } else if (typeLen == 2 && (type->valuestring[1] == 's' || type->valuestring[1] == 'S')) {
145,806✔
355
    switch (type->valuestring[0]) {
145,806✔
356
      case 'm':
22,947✔
357
      case 'M':
358
        // milliseconds
359
        return convertTimePrecision(tsInt64, TSDB_TIME_PRECISION_MILLI, toPrecision);
22,947✔
360
      case 'u':
×
361
      case 'U':
362
        // microseconds
363
        return convertTimePrecision(tsInt64, TSDB_TIME_PRECISION_MICRO, toPrecision);
×
364
      case 'n':
122,859✔
365
      case 'N':
366
        return convertTimePrecision(tsInt64, TSDB_TIME_PRECISION_NANO, toPrecision);
122,859✔
367
      default:
×
368
        return TSDB_CODE_TSC_INVALID_JSON_TYPE;
×
369
    }
370
  } else {
371
    return TSDB_CODE_TSC_INVALID_JSON_TYPE;
×
372
  }
373
}
374

375
uint8_t smlGetTimestampLen(int64_t num) {
98,906✔
376
  uint8_t len = 0;
98,906✔
377
  while ((num /= 10) != 0) {
1,147,556✔
378
    len++;
1,048,650✔
379
  }
380
  len++;
98,906✔
381
  return len;
98,906✔
382
}
383

384
static int64_t smlParseTSFromJSON(SSmlHandle *info, cJSON *timestamp) {
253,923✔
385
  // Timestamp must be the first KV to parse
386
  int32_t toPrecision = info->currSTableMeta ? info->currSTableMeta->tableInfo.precision : TSDB_TIME_PRECISION_NANO;
253,923✔
387
  if (cJSON_IsNumber(timestamp)) {
253,923✔
388
    // timestamp value 0 indicates current system time
389
    double timeDouble = timestamp->valuedouble;
102,183✔
390
    if (unlikely(smlDoubleToInt64OverFlow(timeDouble))) {
102,183✔
391
      smlBuildInvalidDataMsg(&info->msgBuf, "timestamp is too large", NULL);
×
392
      return TSDB_CODE_TSC_VALUE_OUT_OF_RANGE;
×
393
    }
394

395
    if (unlikely(timeDouble < 0)) {
102,183✔
396
      smlBuildInvalidDataMsg(&info->msgBuf, "timestamp is negative", NULL);
×
397
      return (int64_t)timeDouble;
×
398
    } else if (unlikely(timeDouble == 0)) {
102,183✔
399
      return taosGetTimestampNs() / smlFactorNS[toPrecision];
7,926✔
400
    }
401

402
    uint8_t tsLen = smlGetTimestampLen((int64_t)timeDouble);
98,220✔
403

404
    int8_t fromPrecision = smlGetTsTypeByLen(tsLen);
98,220✔
405
    if (unlikely(fromPrecision == -1)) {
98,220✔
406
      smlBuildInvalidDataMsg(&info->msgBuf,
×
407
                             "timestamp precision can only be seconds(10 digits) or milli seconds(13 digits)", NULL);
408
      return TSDB_CODE_SML_INVALID_DATA;
×
409
    }
410
    int64_t tsInt64 = (int64_t)timeDouble;
98,220✔
411
    if (fromPrecision == TSDB_TIME_PRECISION_SECONDS) {
98,220✔
412
      if (smlFactorS[toPrecision] < INT64_MAX / tsInt64) {
43,474✔
413
        return tsInt64 * smlFactorS[toPrecision];
43,474✔
414
      }
415
      return TSDB_CODE_TSC_VALUE_OUT_OF_RANGE;
×
416
    } else {
417
      return convertTimePrecision(timeDouble, fromPrecision, toPrecision);
54,746✔
418
    }
419
  } else if (cJSON_IsObject(timestamp)) {
151,740✔
420
    return smlParseTSFromJSONObj(info, timestamp, toPrecision);
149,769✔
421
  } else {
422
    smlBuildInvalidDataMsg(&info->msgBuf, "invalidate json", NULL);
1,971✔
423
    return TSDB_CODE_TSC_INVALID_JSON;
1,971✔
424
  }
425
}
426

427
static int32_t smlParseJSONStringExt(SSmlHandle *info, cJSON *root, SSmlLineInfo *elements) {
478,292✔
428
  int32_t code = TSDB_CODE_SUCCESS;
478,292✔
429
  int32_t lino = 0;
478,292✔
430

431
  cJSON *metricJson = NULL;
478,292✔
432
  cJSON *tsJson = NULL;
478,292✔
433
  cJSON *valueJson = NULL;
478,292✔
434
  cJSON *tagsJson = NULL;
478,292✔
435

436
  int32_t size = cJSON_GetArraySize(root);
478,292✔
437
  // outmost json fields has to be exactly 4
438
  if (size != OTD_JSON_FIELDS_NUM) {
478,292✔
439
    uError("SML:0x%" PRIx64 " Invalid number of JSON fields in data point %d", info->id, size);
2,628✔
440
    return TSDB_CODE_TSC_INVALID_JSON;
2,628✔
441
  }
442

443
  cJSON **marks[OTD_JSON_FIELDS_NUM] = {&metricJson, &tsJson, &valueJson, &tagsJson};
475,664✔
444
  SML_CHECK_CODE(smlGetJsonElements(root, marks));
475,664✔
445
  // Parse metric
446
  SML_CHECK_CODE(smlParseMetricFromJSON(info, metricJson, elements));
475,664✔
447
  // Parse metric value
448
  SSmlKv kv = {.key = VALUE, .keyLen = VALUE_LEN};
474,350✔
449
  SML_CHECK_CODE(smlParseValueFromJSON(valueJson, &kv));
474,350✔
450

451
  // Parse tags
452
  elements->tags = cJSON_PrintUnformatted(tagsJson);
456,625✔
453
  SML_CHECK_NULL(elements->tags);
456,639✔
454

455
  elements->tagsLen = strlen(elements->tags);
456,639✔
456
  SML_CHECK_CODE(smlParseTagsFromJSON(info, tagsJson, elements));
456,639✔
457

458
  if (unlikely(info->reRun)) {
439,471✔
459
    goto END;
185,548✔
460
  }
461

462
  // Parse timestamp
463
  // notice!!! put ts back to tag to ensure get meta->precision
464
  int64_t ts = smlParseTSFromJSON(info, tsJson);
253,923✔
465
  if (unlikely(ts < 0)) {
253,909✔
466
    char* tmp = cJSON_PrintUnformatted(tsJson);
3,942✔
467
    if (tmp == NULL) {
3,942✔
468
      uError("SML:0x%" PRIx64 " Unable to parse timestamp from JSON payload %s %" PRId64, info->id, info->msgBuf.buf, ts);
×
469
    } else {
470
      uError("SML:0x%" PRIx64 " Unable to parse timestamp from JSON payload %s %s %" PRId64, info->id, info->msgBuf.buf,tmp, ts);
3,942✔
471
      taosMemoryFree(tmp);
3,942✔
472
    }
473
    SML_CHECK_CODE(TSDB_CODE_INVALID_TIMESTAMP);
3,942✔
474
  }
475
  SSmlKv kvTs = {0};
249,967✔
476
  smlBuildTsKv(&kvTs, ts);
249,967✔
477
  if (info->dataFormat){
249,953✔
478
    code = smlParseEndTelnetJsonFormat(info, elements, &kvTs, &kv);
60,482✔
479
  } else {
480
    code = smlParseEndTelnetJsonUnFormat(info, elements, &kvTs, &kv);
189,471✔
481
  }
482
  SML_CHECK_CODE(code);
249,925✔
483
  taosMemoryFreeClear(info->preLine.tags);
249,925✔
484
  info->preLine = *elements;
249,967✔
485
  elements->tags = NULL;
249,967✔
486

487
END:
475,678✔
488
  taosMemoryFree(elements->tags);
475,678✔
489
  RETURN
475,678✔
490
}
491

492
int32_t smlParseJSONExt(SSmlHandle *info, char *payload) {
225,650✔
493
  int32_t payloadNum = 0;
225,650✔
494
  int32_t ret = TSDB_CODE_SUCCESS;
225,650✔
495

496
  info->root = cJSON_Parse(payload);
225,650✔
497
  if (unlikely(info->root == NULL)) {
225,664✔
498
    uError("SML:0x%" PRIx64 " parse json failed:%s", info->id, payload);
2,628✔
499
    return TSDB_CODE_TSC_INVALID_JSON;
2,628✔
500
  }
501

502
  // multiple data points must be sent in JSON array
503
  if (cJSON_IsArray(info->root)) {
223,036✔
504
    payloadNum = cJSON_GetArraySize(info->root);
27,867✔
505
  } else if (cJSON_IsObject(info->root)) {
195,169✔
506
    payloadNum = 1;
195,169✔
507
  } else {
508
    uError("SML:0x%" PRIx64 " Invalid JSON type:%s", info->id, payload);
×
509
    return TSDB_CODE_TSC_INVALID_JSON;
×
510
  }
511

512
  info->lineNum = payloadNum;
223,036✔
513
  cJSON *head = (payloadNum == 1 && cJSON_IsObject(info->root)) ? info->root : info->root->child;
223,036✔
514

515
  int    cnt = 0;
223,036✔
516
  cJSON *dataPoint = head;
223,036✔
517
  while (dataPoint) {
658,537✔
518
    if (info->dataFormat) {
478,292✔
519
      SSmlLineInfo element = {0};
270,425✔
520
      ret = smlParseJSONStringExt(info, dataPoint, &element);
270,425✔
521
      if (element.measureTagsLen != 0) taosMemoryFree(element.measureTag);
270,425✔
522
    } else {
523
      ret = smlParseJSONStringExt(info, dataPoint, info->lines + cnt);
207,867✔
524
    }
525
    if (unlikely(ret != TSDB_CODE_SUCCESS)) {
478,292✔
526
      uError("SML:0x%" PRIx64 " Invalid JSON Payload 2:%s", info->id, payload);
42,791✔
527
      return ret;
42,791✔
528
    }
529

530
    if (unlikely(info->reRun)) {
435,501✔
531
      cnt = 0;
185,548✔
532
      dataPoint = head;
185,548✔
533
      info->lineNum = payloadNum;
185,548✔
534
      ret = smlClearForRerun(info);
185,548✔
535
      if (ret != TSDB_CODE_SUCCESS) {
185,548✔
536
        return ret;
×
537
      }
538
      continue;
185,548✔
539
    }
540
    cnt++;
249,953✔
541
    dataPoint = dataPoint->next;
249,953✔
542
  }
543

544
  return TSDB_CODE_SUCCESS;
180,245✔
545
}
546

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