/src/tdengine/source/libs/parser/src/parInsertSql.c
Line | Count | Source |
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 "crypt.h" |
17 | | #include "decimal.h" |
18 | | #include "geosWrapper.h" |
19 | | #include "parInsertUtil.h" |
20 | | #include "parToken.h" |
21 | | #include "query.h" |
22 | | #include "scalar.h" |
23 | | #include "tbase64.h" |
24 | | #include "tglobal.h" |
25 | | #include "ttime.h" |
26 | | |
27 | | // CSV delimiter and quote character definitions |
28 | 0 | #define CSV_DEFAULT_DELIMITER ',' |
29 | 0 | #define CSV_QUOTE_SINGLE '\'' |
30 | 0 | #define CSV_QUOTE_DOUBLE '"' |
31 | 0 | #define CSV_ESCAPE_CHAR '\\' |
32 | | #define CSV_QUOTE_NONE '\0' |
33 | | |
34 | | typedef struct SCsvParser { |
35 | | char delimiter; // Field delimiter (default: ',') |
36 | | char quote; // Quote character (default: '"') |
37 | | char escape; // Escape character (default: '\') |
38 | | bool allowNewlineInField; // Allow newlines in quoted fields |
39 | | char* buffer; // Read buffer |
40 | | size_t bufferSize; // Buffer size |
41 | | size_t bufferPos; // Current position in buffer |
42 | | size_t bufferLen; // Valid data length in buffer |
43 | | bool eof; // End of file reached |
44 | | TdFilePtr pFile; // File pointer |
45 | | // Line buffer for reuse to avoid frequent memory allocation |
46 | | char* lineBuffer; // Reusable line buffer |
47 | | size_t lineBufferCapacity; // Line buffer capacity |
48 | | } SCsvParser; |
49 | | |
50 | | typedef struct SInsertParseContext { |
51 | | SParseContext* pComCxt; |
52 | | SMsgBuf msg; |
53 | | char tmpTokenBuf[TSDB_MAX_BYTES_PER_ROW]; |
54 | | SBoundColInfo tags; // for stmt |
55 | | bool missCache; |
56 | | bool usingDuplicateTable; |
57 | | bool forceUpdate; |
58 | | bool needTableTagVal; |
59 | | bool needRequest; // whether or not request server |
60 | | // bool isStmtBind; // whether is stmt bind |
61 | | uint8_t stmtTbNameFlag; |
62 | | SSHashObj* pParsedValues; // <cid, SColVal> for stmt bind col |
63 | | } SInsertParseContext; |
64 | | |
65 | | typedef int32_t (*_row_append_fn_t)(SMsgBuf* pMsgBuf, const void* value, int32_t len, void* param); |
66 | | static int32_t parseBoundTagsClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt); |
67 | | static int32_t parseTagsClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, bool autoCreate); |
68 | | |
69 | | // CSV parser function declarations |
70 | | static int32_t csvParserInit(SCsvParser* parser, TdFilePtr pFile); |
71 | | static void csvParserDestroy(SCsvParser* parser); |
72 | | static int32_t csvParserFillBuffer(SCsvParser* parser); |
73 | | static int32_t csvParserReadLine(SCsvParser* parser); |
74 | | static void destroySavedCsvParser(SVnodeModifyOpStmt* pStmt); |
75 | | static int32_t csvParserExpandLineBuffer(SCsvParser* parser, size_t requiredLen); |
76 | | |
77 | | static uint8_t TRUE_VALUE = (uint8_t)TSDB_TRUE; |
78 | | static uint8_t FALSE_VALUE = (uint8_t)TSDB_FALSE; |
79 | | |
80 | 0 | static FORCE_INLINE bool isNullValue(int8_t dataType, SToken* pToken) { |
81 | 0 | return TK_NULL == pToken->type || |
82 | 0 | (TK_NK_STRING == pToken->type && !IS_STR_DATA_TYPE(dataType) && IS_NULL_STR(pToken->z, pToken->n)); |
83 | 0 | } |
84 | | |
85 | 0 | static FORCE_INLINE int32_t toDouble(SToken* pToken, double* value, char** endPtr) { |
86 | 0 | SET_ERRNO(0); |
87 | 0 | *value = taosStr2Double(pToken->z, endPtr); |
88 | 0 |
|
89 | 0 | // not a valid integer number, return error |
90 | 0 | if ((*endPtr - pToken->z) != pToken->n) { |
91 | 0 | return TK_NK_ILLEGAL; |
92 | 0 | } |
93 | 0 |
|
94 | 0 | return pToken->type; |
95 | 0 | } |
96 | | |
97 | 5 | static int32_t skipInsertInto(const char** pSql, SMsgBuf* pMsg) { |
98 | 5 | SToken token; |
99 | 5 | NEXT_TOKEN(*pSql, token); |
100 | 5 | if (TK_INSERT != token.type && TK_IMPORT != token.type) { |
101 | 0 | return buildSyntaxErrMsg(pMsg, "keyword INSERT is expected", token.z); |
102 | 0 | } |
103 | 5 | NEXT_TOKEN(*pSql, token); |
104 | 5 | if (TK_INTO != token.type) { |
105 | 5 | return buildSyntaxErrMsg(pMsg, "keyword INTO is expected", token.z); |
106 | 5 | } |
107 | 0 | return TSDB_CODE_SUCCESS; |
108 | 5 | } |
109 | | |
110 | 0 | static int32_t skipParentheses(SInsertParseContext* pCxt, const char** pSql) { |
111 | 0 | SToken token; |
112 | 0 | int32_t expectRightParenthesis = 1; |
113 | 0 | while (1) { |
114 | 0 | NEXT_TOKEN(*pSql, token); |
115 | 0 | if (TK_NK_LP == token.type) { |
116 | 0 | ++expectRightParenthesis; |
117 | 0 | } else if (TK_NK_RP == token.type && 0 == --expectRightParenthesis) { |
118 | 0 | break; |
119 | 0 | } |
120 | 0 | if (0 == token.n) { |
121 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", NULL); |
122 | 0 | } |
123 | 0 | } |
124 | 0 | return TSDB_CODE_SUCCESS; |
125 | 0 | } |
126 | | |
127 | 0 | static int32_t skipTableOptions(SInsertParseContext* pCxt, const char** pSql) { |
128 | 0 | do { |
129 | 0 | int32_t index = 0; |
130 | 0 | SToken token; |
131 | 0 | NEXT_TOKEN_KEEP_SQL(*pSql, token, index); |
132 | 0 | if (TK_TTL == token.type || TK_COMMENT == token.type) { |
133 | 0 | *pSql += index; |
134 | 0 | NEXT_TOKEN_WITH_PREV(*pSql, token); |
135 | 0 | } else { |
136 | 0 | break; |
137 | 0 | } |
138 | 0 | } while (1); |
139 | 0 | return TSDB_CODE_SUCCESS; |
140 | 0 | } |
141 | | |
142 | | // pSql -> stb_name [(tag1_name, ...)] TAGS (tag1_value, ...) |
143 | 0 | static int32_t ignoreUsingClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
144 | 0 | const char** pSql = &pStmt->pSql; |
145 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
146 | 0 | SToken token; |
147 | 0 | NEXT_TOKEN(*pSql, token); |
148 | |
|
149 | 0 | NEXT_TOKEN(*pSql, token); |
150 | 0 | if (TK_NK_LP == token.type) { |
151 | 0 | code = skipParentheses(pCxt, pSql); |
152 | 0 | if (TSDB_CODE_SUCCESS == code) { |
153 | 0 | NEXT_TOKEN(*pSql, token); |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | // pSql -> TAGS (tag1_value, ...) |
158 | 0 | if (TSDB_CODE_SUCCESS == code) { |
159 | 0 | if (TK_TAGS != token.type) { |
160 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "TAGS is expected", token.z); |
161 | 0 | } else { |
162 | 0 | NEXT_TOKEN(*pSql, token); |
163 | 0 | } |
164 | 0 | } |
165 | 0 | if (TSDB_CODE_SUCCESS == code) { |
166 | 0 | if (TK_NK_LP != token.type) { |
167 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "( is expected", token.z); |
168 | 0 | } else { |
169 | 0 | code = skipParentheses(pCxt, pSql); |
170 | 0 | } |
171 | 0 | } |
172 | |
|
173 | 0 | if (TSDB_CODE_SUCCESS == code) { |
174 | 0 | code = skipTableOptions(pCxt, pSql); |
175 | 0 | } |
176 | |
|
177 | 0 | return code; |
178 | 0 | } |
179 | | |
180 | 0 | static int32_t ignoreUsingClauseAndCheckTagValues(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
181 | 0 | const char** pSql = &pStmt->pSql; |
182 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
183 | 0 | code = parseBoundTagsClause(pCxt, pStmt); |
184 | 0 | if (TSDB_CODE_SUCCESS != code) { |
185 | 0 | return code; |
186 | 0 | } |
187 | | // pSql -> TAGS (tag1_value, ...) |
188 | 0 | code = parseTagsClause(pCxt, pStmt, true); |
189 | 0 | if (TSDB_CODE_SUCCESS != code) { |
190 | 0 | return code; |
191 | 0 | } |
192 | | |
193 | 0 | if (TSDB_CODE_SUCCESS == code) { |
194 | 0 | code = skipTableOptions(pCxt, pSql); |
195 | 0 | } |
196 | |
|
197 | 0 | return code; |
198 | 0 | } |
199 | | |
200 | 0 | static int32_t parseDuplicateUsingClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, bool* pDuplicate) { |
201 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
202 | 0 | *pDuplicate = false; |
203 | |
|
204 | 0 | char tbFName[TSDB_TABLE_FNAME_LEN]; |
205 | 0 | code = tNameExtractFullName(&pStmt->targetTableName, tbFName); |
206 | 0 | if (TSDB_CODE_SUCCESS != code) { |
207 | 0 | return code; |
208 | 0 | } |
209 | 0 | STableMeta** pMeta = taosHashGet(pStmt->pSubTableHashObj, tbFName, strlen(tbFName)); |
210 | 0 | if (NULL != pMeta) { |
211 | 0 | *pDuplicate = true; |
212 | 0 | pCxt->missCache = false; |
213 | 0 | code = cloneTableMeta(*pMeta, &pStmt->pTableMeta); |
214 | 0 | if (TSDB_CODE_SUCCESS != code) { |
215 | 0 | return code; |
216 | 0 | } |
217 | 0 | return ignoreUsingClause(pCxt, pStmt); |
218 | 0 | } |
219 | | |
220 | 0 | return code; |
221 | 0 | } |
222 | | |
223 | | typedef enum { BOUND_TAGS, BOUND_COLUMNS, BOUND_ALL_AND_TBNAME } EBoundColumnsType; |
224 | | |
225 | 0 | static int32_t getTbnameSchemaIndex(STableMeta* pTableMeta) { |
226 | 0 | return pTableMeta->tableInfo.numOfTags + pTableMeta->tableInfo.numOfColumns; |
227 | 0 | } |
228 | | |
229 | | // pStmt->pSql -> field1_name, ...) |
230 | | static int32_t parseBoundColumns(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, const char** pSql, |
231 | 0 | EBoundColumnsType boundColsType, STableMeta* pTableMeta, SBoundColInfo* pBoundInfo) { |
232 | 0 | SSchema* pSchema = NULL; |
233 | 0 | if (boundColsType == BOUND_TAGS) { |
234 | 0 | pSchema = getTableTagSchema(pTableMeta); |
235 | 0 | } else if (boundColsType == BOUND_COLUMNS) { |
236 | 0 | pSchema = getTableColumnSchema(pTableMeta); |
237 | 0 | } else { |
238 | 0 | pSchema = pTableMeta->schema; |
239 | 0 | if (pBoundInfo->numOfCols != getTbnameSchemaIndex(pTableMeta) + 1) { |
240 | 0 | parserError("%s failed, invalid columns num:%d, tbname index:%d", __func__, pBoundInfo->numOfCols, getTbnameSchemaIndex(pTableMeta)); |
241 | 0 | return TSDB_CODE_PAR_INVALID_COLUMNS_NUM; |
242 | 0 | } |
243 | 0 | } |
244 | 0 | int32_t tbnameSchemaIndex = getTbnameSchemaIndex(pTableMeta); |
245 | |
|
246 | 0 | bool* pUseCols = taosMemoryCalloc(pBoundInfo->numOfCols, sizeof(bool)); |
247 | 0 | if (NULL == pUseCols) { |
248 | 0 | return terrno; |
249 | 0 | } |
250 | | |
251 | 0 | pBoundInfo->numOfBound = 0; |
252 | 0 | pBoundInfo->hasBoundCols = true; |
253 | |
|
254 | 0 | bool hasPK = pTableMeta->tableInfo.numOfPKs; |
255 | 0 | int16_t numOfBoundPKs = 0; |
256 | 0 | int16_t lastColIdx = -1; // last column found |
257 | 0 | SArray* pPrivCols = pStmt->pPrivCols; |
258 | 0 | int32_t lastPrivColIdx = 0, nPrivCols = taosArrayGetSize(pPrivCols); |
259 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
260 | 0 | while (TSDB_CODE_SUCCESS == code) { |
261 | 0 | SToken token; |
262 | 0 | NEXT_TOKEN(*pSql, token); |
263 | |
|
264 | 0 | if (TK_NK_RP == token.type) { |
265 | 0 | break; |
266 | 0 | } |
267 | | |
268 | 0 | char tmpTokenBuf[TSDB_COL_NAME_LEN + 2] = {0}; // used for deleting Escape character backstick(`) |
269 | 0 | if (token.n >= TSDB_COL_NAME_LEN + 2) { |
270 | 0 | taosMemoryFree(pUseCols); |
271 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_INVALID_COLUMN, token.z); |
272 | 0 | } |
273 | 0 | tstrncpy(tmpTokenBuf, token.z, token.n < sizeof(tmpTokenBuf) ? token.n + 1 : sizeof(tmpTokenBuf)); |
274 | 0 | token.z = tmpTokenBuf; |
275 | 0 | token.n = strdequote(token.z); |
276 | |
|
277 | 0 | if (boundColsType == BOUND_ALL_AND_TBNAME && token.n == strlen("tbname") && (strcasecmp(token.z, "tbname") == 0)) { |
278 | 0 | pBoundInfo->pColIndex[pBoundInfo->numOfBound] = tbnameSchemaIndex; |
279 | 0 | pUseCols[tbnameSchemaIndex] = true; |
280 | 0 | ++pBoundInfo->numOfBound; |
281 | 0 | continue; |
282 | 0 | } |
283 | | |
284 | 0 | int16_t t = lastColIdx + 1; |
285 | 0 | int16_t end = (boundColsType == BOUND_ALL_AND_TBNAME) ? (pBoundInfo->numOfCols - 1) : pBoundInfo->numOfCols; |
286 | 0 | int16_t index = insFindCol(&token, t, end, pSchema); |
287 | 0 | if (index < 0 && t > 0) { |
288 | 0 | index = insFindCol(&token, 0, t, pSchema); |
289 | 0 | } |
290 | 0 | if (index < 0) { |
291 | 0 | code = generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_INVALID_COLUMN, token.z); |
292 | 0 | } else if (pUseCols[index]) { |
293 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "duplicated column name", token.z); |
294 | 0 | } else { |
295 | 0 | lastColIdx = index; |
296 | 0 | pUseCols[index] = true; |
297 | 0 | pBoundInfo->pColIndex[pBoundInfo->numOfBound] = index; |
298 | 0 | ++pBoundInfo->numOfBound; |
299 | 0 | if (hasPK && (pSchema[index].flags & COL_IS_KEY)) ++numOfBoundPKs; |
300 | |
|
301 | 0 | if (pPrivCols) { // check column-level privileges |
302 | 0 | int32_t j = lastPrivColIdx; |
303 | 0 | bool found = false; |
304 | 0 | for (; lastPrivColIdx < nPrivCols; ++lastPrivColIdx) { |
305 | 0 | SColNameFlag* pColNameFlag = (SColNameFlag*)TARRAY_GET_ELEM(pPrivCols, lastPrivColIdx); |
306 | 0 | if (pSchema[index].colId == pColNameFlag->colId) { |
307 | 0 | found = true; |
308 | 0 | break; |
309 | 0 | } |
310 | 0 | } |
311 | 0 | if (!found) { |
312 | 0 | for (int32_t k = 0; k < j; ++k) { |
313 | 0 | SColNameFlag* pColNameFlag = (SColNameFlag*)TARRAY_GET_ELEM(pPrivCols, k); |
314 | 0 | if (pSchema[index].colId == pColNameFlag->colId) { |
315 | 0 | found = true; |
316 | 0 | break; |
317 | 0 | } |
318 | 0 | } |
319 | 0 | } |
320 | 0 | if (!found) { |
321 | 0 | taosMemoryFree(pUseCols); |
322 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_COL_PERMISSION_DENIED, token.z); |
323 | 0 | } |
324 | 0 | } |
325 | 0 | } |
326 | 0 | } |
327 | | |
328 | 0 | if (TSDB_CODE_SUCCESS == code && (BOUND_TAGS != boundColsType)) { |
329 | 0 | if (!pUseCols[0]) { |
330 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "Primary timestamp column should not be null"); |
331 | 0 | } |
332 | 0 | if (numOfBoundPKs != pTableMeta->tableInfo.numOfPKs) { |
333 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "Primary key column should not be none"); |
334 | 0 | } |
335 | 0 | } |
336 | 0 | if (TSDB_CODE_SUCCESS == code && (BOUND_ALL_AND_TBNAME == boundColsType) && !pUseCols[tbnameSchemaIndex]) { |
337 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "tbname column should not be null"); |
338 | 0 | } |
339 | 0 | taosMemoryFree(pUseCols); |
340 | |
|
341 | 0 | return code; |
342 | 0 | } |
343 | | |
344 | | static int32_t parseTimestampOrInterval(const char** end, SToken* pToken, int16_t timePrec, int64_t* ts, |
345 | 0 | int64_t* interval, SMsgBuf* pMsgBuf, bool* isTs, timezone_t tz) { |
346 | 0 | if (pToken->type == TK_NOW) { |
347 | 0 | *isTs = true; |
348 | 0 | *ts = taosGetTimestamp(timePrec); |
349 | 0 | } else if (pToken->type == TK_TODAY) { |
350 | 0 | *isTs = true; |
351 | 0 | *ts = taosGetTimestampToday(timePrec, tz); |
352 | 0 | } else if (pToken->type == TK_NK_INTEGER) { |
353 | 0 | *isTs = true; |
354 | 0 | if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, ts)) { |
355 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
356 | 0 | } |
357 | 0 | } else if (pToken->type == TK_NK_VARIABLE) { |
358 | 0 | char unit = 0; |
359 | 0 | *isTs = false; |
360 | 0 | if (parseAbsoluteDuration(pToken->z, pToken->n, interval, &unit, timePrec) != TSDB_CODE_SUCCESS) { |
361 | 0 | return TSDB_CODE_TSC_INVALID_OPERATION; |
362 | 0 | } |
363 | 0 | } else { // parse the RFC-3339/ISO-8601 timestamp format string |
364 | 0 | *isTs = true; |
365 | 0 | if (taosParseTime(pToken->z, ts, pToken->n, timePrec, tz) != TSDB_CODE_SUCCESS) { |
366 | 0 | if ((pToken->n == 0) || |
367 | 0 | (pToken->type != TK_NK_STRING && pToken->type != TK_NK_HEX && pToken->type != TK_NK_BIN)) { |
368 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
369 | 0 | } |
370 | 0 | if (IS_NOW_STR(pToken->z, pToken->n)) { |
371 | 0 | *isTs = true; |
372 | 0 | *ts = taosGetTimestamp(timePrec); |
373 | 0 | } else if (IS_TODAY_STR(pToken->z, pToken->n)) { |
374 | 0 | *isTs = true; |
375 | 0 | *ts = taosGetTimestampToday(timePrec, tz); |
376 | 0 | } else if (TSDB_CODE_SUCCESS == toIntegerPure(pToken->z, pToken->n, 10, ts)) { |
377 | 0 | *isTs = true; |
378 | 0 | } else { |
379 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
380 | 0 | } |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | 0 | return TSDB_CODE_SUCCESS; |
385 | 0 | } |
386 | | |
387 | | static int parseTime(const char** end, SToken* pToken, int16_t timePrec, int64_t* time, SMsgBuf* pMsgBuf, |
388 | 0 | timezone_t tz) { |
389 | 0 | int32_t index = 0, i = 0; |
390 | 0 | int64_t interval = 0, tempInterval = 0; |
391 | 0 | int64_t ts = 0, tempTs = 0; |
392 | 0 | bool firstIsTS = false, secondIsTs = false; |
393 | 0 | const char* pTokenEnd = *end; |
394 | |
|
395 | 0 | if (TSDB_CODE_SUCCESS != |
396 | 0 | parseTimestampOrInterval(&pTokenEnd, pToken, timePrec, &ts, &interval, pMsgBuf, &firstIsTS, tz)) { |
397 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
398 | 0 | } |
399 | | |
400 | 0 | if (firstIsTS) { |
401 | 0 | *time = ts; |
402 | 0 | } |
403 | |
|
404 | 0 | for (int k = pToken->n; pToken->z[k] != '\0'; k++) { |
405 | 0 | if (pToken->z[k] == ' ' || pToken->z[k] == '\t') continue; |
406 | 0 | if (pToken->z[k] == '(') { // for insert NOW()/TODAY() |
407 | 0 | if (pToken->z[k + 1] == ')') { |
408 | 0 | *end = pTokenEnd = &pToken->z[k + 2]; |
409 | 0 | ++k; |
410 | 0 | continue; |
411 | 0 | } else { |
412 | 0 | char nc = pToken->z[k + 1]; |
413 | 0 | while (nc == ' ' || nc == '\t' || nc == '\n' || nc == '\r' || nc == '\f') { |
414 | 0 | nc = pToken->z[(++k) + 1]; |
415 | 0 | } |
416 | 0 | if (nc == ')') { |
417 | 0 | *end = pTokenEnd = &pToken->z[k + 2]; |
418 | 0 | ++k; |
419 | 0 | continue; |
420 | 0 | } |
421 | 0 | } |
422 | 0 | } |
423 | 0 | if (pToken->z[k] == ',') { |
424 | 0 | *end = pTokenEnd; |
425 | 0 | if (!firstIsTS) { |
426 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
427 | 0 | } |
428 | 0 | *time = ts; |
429 | 0 | return TSDB_CODE_SUCCESS; |
430 | 0 | } |
431 | 0 | break; |
432 | 0 | } |
433 | | |
434 | 0 | while (pTokenEnd[i] != '\0') { |
435 | 0 | if (pTokenEnd[i] == ' ' || pTokenEnd[i] == '\t') { |
436 | 0 | i++; |
437 | 0 | continue; |
438 | 0 | } else if (pTokenEnd[i] == ',' || pTokenEnd[i] == ')') { |
439 | 0 | *end = pTokenEnd + i; |
440 | 0 | if (!firstIsTS) { |
441 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
442 | 0 | } |
443 | 0 | *time = ts; |
444 | 0 | return TSDB_CODE_SUCCESS; |
445 | 0 | } else { |
446 | 0 | break; |
447 | 0 | } |
448 | 0 | } |
449 | 0 | pTokenEnd = pTokenEnd + i; |
450 | |
|
451 | 0 | index = 0; |
452 | 0 | SToken token = tStrGetToken(pTokenEnd, &index, false, NULL); |
453 | |
|
454 | 0 | if (token.type == TK_NK_MINUS || token.type == TK_NK_PLUS) { |
455 | 0 | pTokenEnd += index; |
456 | 0 | index = 0; |
457 | 0 | SToken valueToken = tStrGetToken(pTokenEnd, &index, false, NULL); |
458 | 0 | pTokenEnd += index; |
459 | 0 | char tmpTokenBuf[TSDB_MAX_BYTES_PER_ROW]; |
460 | 0 | if (TK_NK_STRING == valueToken.type) { |
461 | 0 | if (valueToken.n >= TSDB_MAX_BYTES_PER_ROW) { |
462 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", valueToken.z); |
463 | 0 | } |
464 | 0 | int32_t len = trimString(valueToken.z, valueToken.n, tmpTokenBuf, TSDB_MAX_BYTES_PER_ROW); |
465 | 0 | valueToken.z = tmpTokenBuf; |
466 | 0 | valueToken.n = len; |
467 | 0 | } |
468 | | |
469 | 0 | if (TSDB_CODE_SUCCESS != |
470 | 0 | parseTimestampOrInterval(&pTokenEnd, &valueToken, timePrec, &tempTs, &tempInterval, pMsgBuf, &secondIsTs, tz)) { |
471 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
472 | 0 | } |
473 | | |
474 | 0 | if (valueToken.n < 2) { |
475 | 0 | return buildSyntaxErrMsg(pMsgBuf, "value expected in timestamp", token.z); |
476 | 0 | } |
477 | | |
478 | 0 | if (secondIsTs) { |
479 | | // not support operator between tow timestamp, such as today() + now() |
480 | 0 | if (firstIsTS) { |
481 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
482 | 0 | } |
483 | 0 | ts = tempTs; |
484 | 0 | } else { |
485 | | // not support operator between tow interval, such as 2h + 3s |
486 | 0 | if (!firstIsTS) { |
487 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
488 | 0 | } |
489 | 0 | interval = tempInterval; |
490 | 0 | } |
491 | 0 | if (token.type == TK_NK_MINUS) { |
492 | | // not support interval - ts,such as 2h - today() |
493 | 0 | if (secondIsTs) { |
494 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
495 | 0 | } |
496 | 0 | *time = ts - interval; |
497 | 0 | } else { |
498 | 0 | *time = ts + interval; |
499 | 0 | } |
500 | | |
501 | 0 | for (int k = valueToken.n; valueToken.z[k] != '\0'; k++) { |
502 | 0 | if (valueToken.z[k] == ' ' || valueToken.z[k] == '\t') continue; |
503 | 0 | if (valueToken.z[k] == '(' && valueToken.z[k + 1] == ')') { // for insert NOW()/TODAY() |
504 | 0 | *end = pTokenEnd = &valueToken.z[k + 2]; |
505 | 0 | k++; |
506 | 0 | continue; |
507 | 0 | } |
508 | 0 | if (valueToken.z[k] == ',' || valueToken.z[k] == ')') { |
509 | 0 | *end = pTokenEnd; |
510 | 0 | return TSDB_CODE_SUCCESS; |
511 | 0 | } |
512 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z); |
513 | 0 | } |
514 | 0 | } |
515 | | |
516 | 0 | *end = pTokenEnd; |
517 | 0 | return TSDB_CODE_SUCCESS; |
518 | 0 | } |
519 | | |
520 | | // need to call geosFreeBuffer(*output) later |
521 | 0 | static int parseGeometry(SToken* pToken, unsigned char** output, size_t* size) { |
522 | 0 | #ifdef USE_GEOS |
523 | 0 | int32_t code = TSDB_CODE_FAILED; |
524 | | |
525 | | //[ToDo] support to parse WKB as well as WKT |
526 | 0 | if (pToken->type == TK_NK_STRING) { |
527 | 0 | code = initCtxGeomFromText(); |
528 | 0 | if (code != TSDB_CODE_SUCCESS) { |
529 | 0 | return code; |
530 | 0 | } |
531 | | |
532 | 0 | code = doGeomFromText(pToken->z, output, size); |
533 | 0 | if (code != TSDB_CODE_SUCCESS) { |
534 | 0 | return code; |
535 | 0 | } |
536 | 0 | } |
537 | | |
538 | 0 | return code; |
539 | | #else |
540 | | TAOS_RETURN(TSDB_CODE_OPS_NOT_SUPPORT); |
541 | | #endif |
542 | 0 | } |
543 | | |
544 | | static int32_t parseSingleStrParam(SInsertParseContext* pCxt, const char** ppSql, SToken* pToken, SColVal* pVal, |
545 | 0 | char* tokenBuf, int32_t* inputBytes, bool* final) { |
546 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
547 | 0 | if (TK_NK_LP != pToken->type) { |
548 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "( expected", pToken->z); |
549 | 0 | } |
550 | | |
551 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
552 | 0 | if (TK_NULL == pToken->type) { |
553 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
554 | 0 | if (TK_NK_RP == pToken->type) { |
555 | 0 | pVal->flag = CV_FLAG_NULL; |
556 | |
|
557 | 0 | *final = true; |
558 | 0 | return TSDB_CODE_SUCCESS; |
559 | 0 | } else { |
560 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
561 | 0 | } |
562 | 0 | } else if (TK_NK_STRING != pToken->type) { |
563 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "string expected", pToken->z); |
564 | 0 | } |
565 | | |
566 | 0 | *inputBytes = trimString(pToken->z, pToken->n, tokenBuf, TSDB_MAX_BYTES_PER_ROW); |
567 | |
|
568 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
569 | 0 | if (TK_NK_RP != pToken->type) { |
570 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
571 | 0 | } |
572 | | |
573 | 0 | return TSDB_CODE_SUCCESS; |
574 | 0 | } |
575 | | |
576 | | static int32_t parseBinary(SInsertParseContext* pCxt, const char** ppSql, SToken* pToken, SColVal* pVal, |
577 | 0 | SSchema* pSchema) { |
578 | 0 | int32_t code = 0; |
579 | 0 | int32_t bytes = pSchema->bytes; |
580 | 0 | uint8_t** pData = &pVal->value.pData; |
581 | 0 | uint32_t* nData = &pVal->value.nData; |
582 | |
|
583 | 0 | if (pToken->type == TK_FROM_BASE64 || pToken->type == TK_TO_BASE64 || pToken->type == TK_MD5 || |
584 | 0 | pToken->type == TK_SHA || pToken->type == TK_SHA1 || pToken->type == TK_SHA2 || pToken->type == TK_AES_ENCRYPT || |
585 | 0 | pToken->type == TK_AES_DECRYPT || pToken->type == TK_SM4_ENCRYPT || pToken->type == TK_SM4_DECRYPT) { |
586 | 0 | char* input = NULL; |
587 | 0 | int32_t inputBytes = 0; |
588 | 0 | int32_t outputBytes = 0; |
589 | 0 | bool final = false; |
590 | |
|
591 | 0 | char* tmpTokenBuf = taosMemoryMalloc(TSDB_MAX_BYTES_PER_ROW); |
592 | 0 | if (NULL == tmpTokenBuf) { |
593 | 0 | return terrno; |
594 | 0 | } |
595 | | |
596 | 0 | if (pToken->type == TK_FROM_BASE64) { |
597 | 0 | code = parseSingleStrParam(pCxt, ppSql, pToken, pVal, tmpTokenBuf, &inputBytes, &final); |
598 | 0 | if (TSDB_CODE_SUCCESS != code || final) { |
599 | 0 | taosMemoryFree(tmpTokenBuf); |
600 | |
|
601 | 0 | return code; |
602 | 0 | } |
603 | | |
604 | 0 | input = tmpTokenBuf; |
605 | |
|
606 | 0 | outputBytes = tbase64_decode_len(inputBytes); |
607 | 0 | if (outputBytes + VARSTR_HEADER_SIZE > bytes) { |
608 | 0 | taosMemoryFree(tmpTokenBuf); |
609 | |
|
610 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
611 | 0 | } |
612 | | |
613 | 0 | *pData = taosMemoryMalloc(outputBytes); |
614 | 0 | if (NULL == *pData) { |
615 | 0 | taosMemoryFree(tmpTokenBuf); |
616 | |
|
617 | 0 | return terrno; |
618 | 0 | } |
619 | | |
620 | 0 | if (TSDB_CODE_SUCCESS != tbase64_decode(*pData, (const uint8_t*)input, inputBytes, (VarDataLenT*)&outputBytes)) { |
621 | 0 | pVal->flag = CV_FLAG_NULL; |
622 | |
|
623 | 0 | taosMemoryFree(tmpTokenBuf); |
624 | |
|
625 | 0 | return TSDB_CODE_SUCCESS; |
626 | 0 | } |
627 | 0 | *nData = outputBytes; |
628 | 0 | } else if (pToken->type == TK_TO_BASE64) { |
629 | 0 | code = parseSingleStrParam(pCxt, ppSql, pToken, pVal, tmpTokenBuf, &inputBytes, &final); |
630 | 0 | if (TSDB_CODE_SUCCESS != code || final) { |
631 | 0 | taosMemoryFree(tmpTokenBuf); |
632 | |
|
633 | 0 | return code; |
634 | 0 | } |
635 | | |
636 | 0 | input = tmpTokenBuf; |
637 | |
|
638 | 0 | outputBytes = tbase64_encode_len(inputBytes); |
639 | 0 | if (outputBytes + VARSTR_HEADER_SIZE > bytes) { |
640 | 0 | taosMemoryFree(tmpTokenBuf); |
641 | |
|
642 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
643 | 0 | } |
644 | | |
645 | 0 | *pData = taosMemoryMalloc(outputBytes); |
646 | 0 | if (NULL == *pData) { |
647 | 0 | taosMemoryFree(tmpTokenBuf); |
648 | |
|
649 | 0 | return terrno; |
650 | 0 | } |
651 | | |
652 | 0 | tbase64_encode(*pData, input, inputBytes, outputBytes); |
653 | 0 | *nData = outputBytes; |
654 | 0 | } else if (pToken->type == TK_MD5) { |
655 | 0 | code = parseSingleStrParam(pCxt, ppSql, pToken, pVal, tmpTokenBuf, &inputBytes, &final); |
656 | 0 | if (TSDB_CODE_SUCCESS != code || final) { |
657 | 0 | taosMemoryFree(tmpTokenBuf); |
658 | |
|
659 | 0 | return code; |
660 | 0 | } |
661 | | |
662 | 0 | input = tmpTokenBuf; |
663 | |
|
664 | 0 | if (MD5_OUTPUT_LEN + VARSTR_HEADER_SIZE > bytes) { |
665 | 0 | taosMemoryFree(tmpTokenBuf); |
666 | |
|
667 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
668 | 0 | } |
669 | | |
670 | 0 | int32_t bufLen = TMAX(MD5_OUTPUT_LEN + VARSTR_HEADER_SIZE + 1, inputBytes); |
671 | 0 | *pData = taosMemoryMalloc(bufLen); |
672 | 0 | if (NULL == *pData) { |
673 | 0 | taosMemoryFree(tmpTokenBuf); |
674 | |
|
675 | 0 | return terrno; |
676 | 0 | } |
677 | | |
678 | 0 | (void)memcpy(*pData, input, inputBytes); |
679 | 0 | int32_t len = taosCreateMD5Hash(*pData, inputBytes, bufLen); |
680 | 0 | *nData = len; |
681 | 0 | } else if (pToken->type == TK_SHA2) { |
682 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
683 | 0 | if (TK_NK_LP != pToken->type) { |
684 | 0 | taosMemoryFree(tmpTokenBuf); |
685 | |
|
686 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "( expected", pToken->z); |
687 | 0 | } |
688 | | |
689 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
690 | 0 | if (TK_NULL == pToken->type) { |
691 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
692 | 0 | if (TK_NK_COMMA != pToken->type) { |
693 | 0 | taosMemoryFree(tmpTokenBuf); |
694 | |
|
695 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
696 | 0 | } |
697 | | |
698 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
699 | 0 | if (TK_NK_INTEGER != pToken->type) { |
700 | 0 | taosMemoryFree(tmpTokenBuf); |
701 | |
|
702 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "integer [224, 256, 384, 512] expected", pToken->z); |
703 | 0 | } |
704 | | |
705 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
706 | 0 | if (TK_NK_RP == pToken->type) { |
707 | 0 | pVal->flag = CV_FLAG_NULL; |
708 | |
|
709 | 0 | taosMemoryFree(tmpTokenBuf); |
710 | |
|
711 | 0 | return TSDB_CODE_SUCCESS; |
712 | 0 | } |
713 | 0 | } else if (TK_NK_STRING != pToken->type) { |
714 | 0 | taosMemoryFree(tmpTokenBuf); |
715 | |
|
716 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "string expected", pToken->z); |
717 | 0 | } |
718 | | |
719 | 0 | inputBytes = trimString(pToken->z, pToken->n, tmpTokenBuf, TSDB_MAX_BYTES_PER_ROW); |
720 | 0 | input = tmpTokenBuf; |
721 | |
|
722 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
723 | 0 | if (TK_NK_COMMA != pToken->type) { |
724 | 0 | taosMemoryFree(tmpTokenBuf); |
725 | |
|
726 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
727 | 0 | } |
728 | | |
729 | 0 | int64_t digestLen = 224; |
730 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
731 | 0 | if (TK_NK_INTEGER != pToken->type) { |
732 | 0 | taosMemoryFree(tmpTokenBuf); |
733 | |
|
734 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "integer [224, 256, 384, 512] expected", pToken->z); |
735 | 0 | } else { |
736 | 0 | if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, &digestLen)) { |
737 | 0 | taosMemoryFree(tmpTokenBuf); |
738 | |
|
739 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid integer format", pToken->z); |
740 | 0 | } |
741 | | |
742 | 0 | if (224 != digestLen && 256 != digestLen && 384 != digestLen && 512 != digestLen) { |
743 | 0 | taosMemoryFree(tmpTokenBuf); |
744 | |
|
745 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "sha2 digest length must be one of 224, 256, 384, 512", pToken->z); |
746 | 0 | } |
747 | 0 | } |
748 | | |
749 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
750 | 0 | if (TK_NK_RP != pToken->type) { |
751 | 0 | taosMemoryFree(tmpTokenBuf); |
752 | |
|
753 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
754 | 0 | } |
755 | | |
756 | 0 | if (SHA2_OUTPUT_LEN + VARSTR_HEADER_SIZE > bytes) { |
757 | 0 | taosMemoryFree(tmpTokenBuf); |
758 | |
|
759 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
760 | 0 | } |
761 | | |
762 | 0 | int32_t bufLen = TMAX(SHA2_OUTPUT_LEN + VARSTR_HEADER_SIZE + 1, inputBytes); |
763 | 0 | *pData = taosMemoryMalloc(bufLen); |
764 | 0 | if (NULL == *pData) { |
765 | 0 | taosMemoryFree(tmpTokenBuf); |
766 | |
|
767 | 0 | return terrno; |
768 | 0 | } |
769 | | |
770 | 0 | (void)memcpy(*pData, input, inputBytes); |
771 | 0 | int32_t len = taosCreateSHA2Hash((char *)*pData, inputBytes, digestLen, bufLen); |
772 | 0 | *nData = len; |
773 | 0 | } else if (pToken->type == TK_SHA || pToken->type == TK_SHA1) { |
774 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
775 | 0 | if (TK_NK_LP != pToken->type) { |
776 | 0 | taosMemoryFree(tmpTokenBuf); |
777 | |
|
778 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "( expected", pToken->z); |
779 | 0 | } |
780 | | |
781 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
782 | 0 | if (TK_NULL == pToken->type) { |
783 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
784 | 0 | if (TK_NK_RP == pToken->type) { |
785 | 0 | pVal->flag = CV_FLAG_NULL; |
786 | |
|
787 | 0 | taosMemoryFree(tmpTokenBuf); |
788 | |
|
789 | 0 | return TSDB_CODE_SUCCESS; |
790 | 0 | } |
791 | 0 | } else if (TK_NK_STRING != pToken->type) { |
792 | 0 | taosMemoryFree(tmpTokenBuf); |
793 | |
|
794 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "string expected", pToken->z); |
795 | 0 | } |
796 | | |
797 | 0 | inputBytes = trimString(pToken->z, pToken->n, tmpTokenBuf, TSDB_MAX_BYTES_PER_ROW); |
798 | 0 | input = tmpTokenBuf; |
799 | |
|
800 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
801 | 0 | if (TK_NK_RP != pToken->type) { |
802 | 0 | taosMemoryFree(tmpTokenBuf); |
803 | |
|
804 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
805 | 0 | } |
806 | | |
807 | 0 | if (SHA1_OUTPUT_LEN + VARSTR_HEADER_SIZE > bytes) { |
808 | 0 | taosMemoryFree(tmpTokenBuf); |
809 | |
|
810 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
811 | 0 | } |
812 | | |
813 | 0 | int32_t bufLen = TMAX(SHA1_OUTPUT_LEN + VARSTR_HEADER_SIZE + 1, inputBytes); |
814 | 0 | *pData = taosMemoryMalloc(bufLen); |
815 | 0 | if (NULL == *pData) { |
816 | 0 | taosMemoryFree(tmpTokenBuf); |
817 | |
|
818 | 0 | return terrno; |
819 | 0 | } |
820 | | |
821 | 0 | (void)memcpy(*pData, input, inputBytes); |
822 | 0 | int32_t len = taosCreateSHA1Hash((char *)(*pData), inputBytes, bufLen) ; |
823 | 0 | *nData = len; |
824 | 0 | } else if (pToken->type == TK_AES_ENCRYPT) { |
825 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
826 | 0 | if (TK_NK_LP != pToken->type) { |
827 | 0 | taosMemoryFree(tmpTokenBuf); |
828 | |
|
829 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "( expected", pToken->z); |
830 | 0 | } |
831 | | |
832 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
833 | 0 | if (TK_NK_STRING != pToken->type && TK_NULL != pToken->type) { |
834 | 0 | taosMemoryFree(tmpTokenBuf); |
835 | |
|
836 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "string or null expected", pToken->z); |
837 | 0 | } else if (TK_NULL == pToken->type) { |
838 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
839 | 0 | if (TK_NK_COMMA != pToken->type) { |
840 | 0 | taosMemoryFree(tmpTokenBuf); |
841 | |
|
842 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
843 | 0 | } |
844 | | |
845 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
846 | 0 | if (TK_NK_STRING != pToken->type) { |
847 | 0 | taosMemoryFree(tmpTokenBuf); |
848 | |
|
849 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "key string expected", pToken->z); |
850 | 0 | } |
851 | | |
852 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
853 | 0 | if (TK_NK_RP == pToken->type) { |
854 | 0 | pVal->flag = CV_FLAG_NULL; |
855 | |
|
856 | 0 | taosMemoryFree(tmpTokenBuf); |
857 | |
|
858 | 0 | return TSDB_CODE_SUCCESS; |
859 | 0 | } else if (TK_NK_STRING == pToken->type) { |
860 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
861 | 0 | if (TK_NK_RP == pToken->type) { |
862 | 0 | pVal->flag = CV_FLAG_NULL; |
863 | |
|
864 | 0 | taosMemoryFree(tmpTokenBuf); |
865 | |
|
866 | 0 | return TSDB_CODE_SUCCESS; |
867 | 0 | } |
868 | 0 | } else { |
869 | 0 | taosMemoryFree(tmpTokenBuf); |
870 | |
|
871 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "iv string expected", pToken->z); |
872 | 0 | } |
873 | 0 | } else { |
874 | 0 | inputBytes = trimString(pToken->z, pToken->n, tmpTokenBuf, TSDB_MAX_BYTES_PER_ROW); |
875 | 0 | input = tmpTokenBuf; |
876 | 0 | } |
877 | | |
878 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
879 | 0 | if (TK_NK_COMMA != pToken->type) { |
880 | 0 | taosMemoryFree(tmpTokenBuf); |
881 | |
|
882 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
883 | 0 | } |
884 | | |
885 | 0 | char* key = NULL; |
886 | 0 | int32_t keyBytes = 0; |
887 | 0 | char tmpKeyBuf[TSDB_MAX_BYTES_PER_ROW]; |
888 | |
|
889 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
890 | 0 | if (TK_NK_STRING != pToken->type) { |
891 | 0 | taosMemoryFree(tmpTokenBuf); |
892 | |
|
893 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "key expected", pToken->z); |
894 | 0 | } else { |
895 | 0 | keyBytes = trimString(pToken->z, pToken->n, tmpKeyBuf, TSDB_MAX_BYTES_PER_ROW); |
896 | 0 | key = tmpKeyBuf; |
897 | 0 | } |
898 | | |
899 | 0 | char* iv = NULL; |
900 | 0 | int32_t ivBytes = 0; |
901 | 0 | char tmpIvBuf[TSDB_MAX_BYTES_PER_ROW]; |
902 | |
|
903 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
904 | 0 | if (TK_NK_RP != pToken->type && TK_NK_STRING != pToken->type) { |
905 | 0 | taosMemoryFree(tmpTokenBuf); |
906 | |
|
907 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") or iv expected", pToken->z); |
908 | 0 | } else if (TK_NK_STRING == pToken->type) { |
909 | 0 | ivBytes = trimString(pToken->z, pToken->n, tmpIvBuf, TSDB_MAX_BYTES_PER_ROW); |
910 | 0 | iv = tmpIvBuf; |
911 | |
|
912 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
913 | 0 | if (TK_NK_RP != pToken->type) { |
914 | 0 | taosMemoryFree(tmpTokenBuf); |
915 | |
|
916 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
917 | 0 | } |
918 | 0 | } |
919 | | |
920 | 0 | int32_t outputBytes = taes_encrypt_len(inputBytes); |
921 | 0 | if (outputBytes + VARSTR_HEADER_SIZE > bytes) { |
922 | 0 | taosMemoryFree(tmpTokenBuf); |
923 | |
|
924 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
925 | 0 | } |
926 | | |
927 | 0 | int32_t bufLen = outputBytes + VARSTR_HEADER_SIZE + 1; |
928 | 0 | *pData = taosMemoryCalloc(1, bufLen); |
929 | 0 | if (NULL == *pData) { |
930 | 0 | taosMemoryFree(tmpTokenBuf); |
931 | |
|
932 | 0 | return terrno; |
933 | 0 | } |
934 | | |
935 | 0 | (void)memcpy(*pData, input, inputBytes); |
936 | |
|
937 | 0 | int32_t keypaddedlen = taes_encrypt_len(keyBytes); |
938 | 0 | char* pKeyPaddingBuf = taosMemoryMalloc(keypaddedlen); |
939 | 0 | if (!pKeyPaddingBuf) { |
940 | 0 | taosMemoryFree(tmpTokenBuf); |
941 | |
|
942 | 0 | return terrno; |
943 | 0 | } |
944 | 0 | (void)memcpy(pKeyPaddingBuf, key, keyBytes); |
945 | |
|
946 | 0 | int32_t len = taosAesEncrypt(pKeyPaddingBuf, keyBytes, *pData, inputBytes, iv); |
947 | 0 | *nData = len; |
948 | |
|
949 | 0 | taosMemoryFree(pKeyPaddingBuf); |
950 | 0 | } else if (pToken->type == TK_AES_DECRYPT) { |
951 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
952 | 0 | if (TK_NK_LP != pToken->type) { |
953 | 0 | taosMemoryFree(tmpTokenBuf); |
954 | |
|
955 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "( expected", pToken->z); |
956 | 0 | } |
957 | | |
958 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
959 | 0 | if (TK_NK_STRING != pToken->type && TK_NULL != pToken->type) { |
960 | 0 | taosMemoryFree(tmpTokenBuf); |
961 | |
|
962 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "string or null expected", pToken->z); |
963 | 0 | } else if (TK_NULL == pToken->type) { |
964 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
965 | 0 | if (TK_NK_COMMA != pToken->type) { |
966 | 0 | taosMemoryFree(tmpTokenBuf); |
967 | |
|
968 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
969 | 0 | } |
970 | | |
971 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
972 | 0 | if (TK_NK_STRING != pToken->type) { |
973 | 0 | taosMemoryFree(tmpTokenBuf); |
974 | |
|
975 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "key string expected", pToken->z); |
976 | 0 | } |
977 | | |
978 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
979 | 0 | if (TK_NK_RP == pToken->type) { |
980 | 0 | pVal->flag = CV_FLAG_NULL; |
981 | |
|
982 | 0 | taosMemoryFree(tmpTokenBuf); |
983 | |
|
984 | 0 | return TSDB_CODE_SUCCESS; |
985 | 0 | } else if (TK_NK_STRING == pToken->type) { |
986 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
987 | 0 | if (TK_NK_RP == pToken->type) { |
988 | 0 | pVal->flag = CV_FLAG_NULL; |
989 | |
|
990 | 0 | taosMemoryFree(tmpTokenBuf); |
991 | |
|
992 | 0 | return TSDB_CODE_SUCCESS; |
993 | 0 | } |
994 | 0 | } else { |
995 | 0 | taosMemoryFree(tmpTokenBuf); |
996 | |
|
997 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "iv string expected", pToken->z); |
998 | 0 | } |
999 | 0 | } else { |
1000 | 0 | inputBytes = trimString(pToken->z, pToken->n, tmpTokenBuf, TSDB_MAX_BYTES_PER_ROW); |
1001 | 0 | input = tmpTokenBuf; |
1002 | 0 | } |
1003 | | |
1004 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1005 | 0 | if (TK_NK_COMMA != pToken->type) { |
1006 | 0 | taosMemoryFree(tmpTokenBuf); |
1007 | |
|
1008 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
1009 | 0 | } |
1010 | | |
1011 | 0 | char* key = NULL; |
1012 | 0 | int32_t keyBytes = 0; |
1013 | 0 | char tmpKeyBuf[TSDB_MAX_BYTES_PER_ROW]; |
1014 | |
|
1015 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1016 | 0 | if (TK_NK_STRING != pToken->type) { |
1017 | 0 | taosMemoryFree(tmpTokenBuf); |
1018 | |
|
1019 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "key expected", pToken->z); |
1020 | 0 | } else { |
1021 | 0 | keyBytes = trimString(pToken->z, pToken->n, tmpKeyBuf, TSDB_MAX_BYTES_PER_ROW); |
1022 | 0 | key = tmpKeyBuf; |
1023 | 0 | } |
1024 | | |
1025 | 0 | char* iv = NULL; |
1026 | 0 | int32_t ivBytes = 0; |
1027 | 0 | char tmpIvBuf[TSDB_MAX_BYTES_PER_ROW]; |
1028 | |
|
1029 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1030 | 0 | if (TK_NK_RP != pToken->type && TK_NK_STRING != pToken->type) { |
1031 | 0 | taosMemoryFree(tmpTokenBuf); |
1032 | |
|
1033 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") or iv expected", pToken->z); |
1034 | 0 | } else if (TK_NK_STRING == pToken->type) { |
1035 | 0 | ivBytes = trimString(pToken->z, pToken->n, tmpIvBuf, TSDB_MAX_BYTES_PER_ROW); |
1036 | 0 | iv = tmpIvBuf; |
1037 | |
|
1038 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1039 | 0 | if (TK_NK_RP != pToken->type) { |
1040 | 0 | taosMemoryFree(tmpTokenBuf); |
1041 | |
|
1042 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
1043 | 0 | } |
1044 | 0 | } |
1045 | | |
1046 | 0 | int32_t outputBytes = taes_encrypt_len(inputBytes); |
1047 | 0 | if (outputBytes + VARSTR_HEADER_SIZE > bytes) { |
1048 | 0 | taosMemoryFree(tmpTokenBuf); |
1049 | |
|
1050 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
1051 | 0 | } |
1052 | | |
1053 | 0 | int32_t bufLen = outputBytes + VARSTR_HEADER_SIZE + 1; |
1054 | 0 | *pData = taosMemoryMalloc(bufLen); |
1055 | 0 | if (NULL == *pData) { |
1056 | 0 | taosMemoryFree(tmpTokenBuf); |
1057 | |
|
1058 | 0 | return terrno; |
1059 | 0 | } |
1060 | | |
1061 | 0 | (void)memcpy(*pData, input, inputBytes); |
1062 | |
|
1063 | 0 | int32_t keypaddedlen = taes_encrypt_len(keyBytes); |
1064 | 0 | char* pKeyPaddingBuf = taosMemoryMalloc(keypaddedlen); |
1065 | 0 | if (!pKeyPaddingBuf) { |
1066 | 0 | taosMemoryFree(tmpTokenBuf); |
1067 | |
|
1068 | 0 | return terrno; |
1069 | 0 | } |
1070 | 0 | (void)memcpy(pKeyPaddingBuf, key, keyBytes); |
1071 | |
|
1072 | 0 | int32_t len = taosAesDecrypt(pKeyPaddingBuf, keyBytes, *pData, inputBytes, iv); |
1073 | 0 | *nData = len; |
1074 | |
|
1075 | 0 | taosMemoryFree(pKeyPaddingBuf); |
1076 | 0 | } else if (pToken->type == TK_SM4_ENCRYPT) { |
1077 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1078 | 0 | if (TK_NK_LP != pToken->type) { |
1079 | 0 | taosMemoryFree(tmpTokenBuf); |
1080 | |
|
1081 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "( expected", pToken->z); |
1082 | 0 | } |
1083 | | |
1084 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1085 | 0 | if (TK_NK_STRING != pToken->type && TK_NULL != pToken->type) { |
1086 | 0 | taosMemoryFree(tmpTokenBuf); |
1087 | |
|
1088 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "string or null expected", pToken->z); |
1089 | 0 | } else if (TK_NULL == pToken->type) { |
1090 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1091 | 0 | if (TK_NK_COMMA != pToken->type) { |
1092 | 0 | taosMemoryFree(tmpTokenBuf); |
1093 | |
|
1094 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
1095 | 0 | } |
1096 | | |
1097 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1098 | 0 | if (TK_NK_STRING != pToken->type) { |
1099 | 0 | taosMemoryFree(tmpTokenBuf); |
1100 | |
|
1101 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "key string expected", pToken->z); |
1102 | 0 | } |
1103 | | |
1104 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1105 | 0 | if (TK_NK_RP == pToken->type) { |
1106 | 0 | pVal->flag = CV_FLAG_NULL; |
1107 | |
|
1108 | 0 | taosMemoryFree(tmpTokenBuf); |
1109 | |
|
1110 | 0 | return TSDB_CODE_SUCCESS; |
1111 | 0 | } else { |
1112 | 0 | taosMemoryFree(tmpTokenBuf); |
1113 | |
|
1114 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
1115 | 0 | } |
1116 | 0 | } else { |
1117 | 0 | inputBytes = trimString(pToken->z, pToken->n, tmpTokenBuf, TSDB_MAX_BYTES_PER_ROW); |
1118 | 0 | input = tmpTokenBuf; |
1119 | 0 | } |
1120 | | |
1121 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1122 | 0 | if (TK_NK_COMMA != pToken->type) { |
1123 | 0 | taosMemoryFree(tmpTokenBuf); |
1124 | |
|
1125 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
1126 | 0 | } |
1127 | | |
1128 | 0 | char* key = NULL; |
1129 | 0 | int32_t keyBytes = 0; |
1130 | 0 | char tmpKeyBuf[TSDB_MAX_BYTES_PER_ROW]; |
1131 | |
|
1132 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1133 | 0 | if (TK_NK_STRING != pToken->type) { |
1134 | 0 | taosMemoryFree(tmpTokenBuf); |
1135 | |
|
1136 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "key expected", pToken->z); |
1137 | 0 | } else { |
1138 | 0 | keyBytes = trimString(pToken->z, pToken->n, tmpKeyBuf, TSDB_MAX_BYTES_PER_ROW); |
1139 | 0 | key = tmpKeyBuf; |
1140 | 0 | } |
1141 | | |
1142 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1143 | 0 | if (TK_NK_RP != pToken->type) { |
1144 | 0 | taosMemoryFree(tmpTokenBuf); |
1145 | |
|
1146 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
1147 | 0 | } |
1148 | | |
1149 | 0 | int32_t outputBytes = tsm4_encrypt_len(inputBytes); |
1150 | 0 | if (outputBytes + VARSTR_HEADER_SIZE > bytes) { |
1151 | 0 | taosMemoryFree(tmpTokenBuf); |
1152 | |
|
1153 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
1154 | 0 | } |
1155 | | |
1156 | 0 | int32_t bufLen = outputBytes + VARSTR_HEADER_SIZE + 1; |
1157 | 0 | *pData = taosMemoryMalloc(bufLen); |
1158 | 0 | if (NULL == *pData) { |
1159 | 0 | taosMemoryFree(tmpTokenBuf); |
1160 | |
|
1161 | 0 | return terrno; |
1162 | 0 | } |
1163 | | |
1164 | 0 | (void)memcpy(*pData, input, inputBytes); |
1165 | |
|
1166 | 0 | int32_t keypaddedlen = tsm4_encrypt_len(keyBytes); |
1167 | 0 | char* pKeyPaddingBuf = taosMemoryMalloc(keypaddedlen); |
1168 | 0 | if (!pKeyPaddingBuf) { |
1169 | 0 | taosMemoryFree(tmpTokenBuf); |
1170 | |
|
1171 | 0 | return terrno; |
1172 | 0 | } |
1173 | 0 | (void)memcpy(pKeyPaddingBuf, key, keyBytes); |
1174 | |
|
1175 | 0 | int32_t len = taosSm4Encrypt(pKeyPaddingBuf, keyBytes, *pData, inputBytes); |
1176 | 0 | *nData = len; |
1177 | |
|
1178 | 0 | taosMemoryFree(pKeyPaddingBuf); |
1179 | 0 | } else if (pToken->type == TK_SM4_DECRYPT) { |
1180 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1181 | 0 | if (TK_NK_LP != pToken->type) { |
1182 | 0 | taosMemoryFree(tmpTokenBuf); |
1183 | |
|
1184 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "( expected", pToken->z); |
1185 | 0 | } |
1186 | | |
1187 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1188 | 0 | if (TK_NK_STRING != pToken->type && TK_NULL != pToken->type) { |
1189 | 0 | taosMemoryFree(tmpTokenBuf); |
1190 | |
|
1191 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "string or null expected", pToken->z); |
1192 | 0 | } else if (TK_NULL == pToken->type) { |
1193 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1194 | 0 | if (TK_NK_COMMA != pToken->type) { |
1195 | 0 | taosMemoryFree(tmpTokenBuf); |
1196 | |
|
1197 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
1198 | 0 | } |
1199 | | |
1200 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1201 | 0 | if (TK_NK_STRING != pToken->type) { |
1202 | 0 | taosMemoryFree(tmpTokenBuf); |
1203 | |
|
1204 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "key string expected", pToken->z); |
1205 | 0 | } |
1206 | | |
1207 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1208 | 0 | if (TK_NK_RP == pToken->type) { |
1209 | 0 | pVal->flag = CV_FLAG_NULL; |
1210 | |
|
1211 | 0 | taosMemoryFree(tmpTokenBuf); |
1212 | |
|
1213 | 0 | return TSDB_CODE_SUCCESS; |
1214 | 0 | } else { |
1215 | 0 | taosMemoryFree(tmpTokenBuf); |
1216 | |
|
1217 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
1218 | 0 | } |
1219 | 0 | } else { |
1220 | 0 | inputBytes = trimString(pToken->z, pToken->n, tmpTokenBuf, TSDB_MAX_BYTES_PER_ROW); |
1221 | 0 | input = tmpTokenBuf; |
1222 | 0 | } |
1223 | | |
1224 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1225 | 0 | if (TK_NK_COMMA != pToken->type) { |
1226 | 0 | taosMemoryFree(tmpTokenBuf); |
1227 | |
|
1228 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
1229 | 0 | } |
1230 | | |
1231 | 0 | char* key = NULL; |
1232 | 0 | int32_t keyBytes = 0; |
1233 | 0 | char tmpKeyBuf[TSDB_MAX_BYTES_PER_ROW]; |
1234 | |
|
1235 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1236 | 0 | if (TK_NK_STRING != pToken->type) { |
1237 | 0 | taosMemoryFree(tmpTokenBuf); |
1238 | |
|
1239 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "key expected", pToken->z); |
1240 | 0 | } else { |
1241 | 0 | keyBytes = trimString(pToken->z, pToken->n, tmpKeyBuf, TSDB_MAX_BYTES_PER_ROW); |
1242 | 0 | key = tmpKeyBuf; |
1243 | 0 | } |
1244 | | |
1245 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
1246 | 0 | if (TK_NK_RP != pToken->type) { |
1247 | 0 | taosMemoryFree(tmpTokenBuf); |
1248 | |
|
1249 | 0 | return buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
1250 | 0 | } |
1251 | | |
1252 | 0 | int32_t outputBytes = tsm4_encrypt_len(inputBytes); |
1253 | 0 | if (outputBytes + VARSTR_HEADER_SIZE > bytes) { |
1254 | 0 | taosMemoryFree(tmpTokenBuf); |
1255 | |
|
1256 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
1257 | 0 | } |
1258 | | |
1259 | 0 | int32_t bufLen = outputBytes + VARSTR_HEADER_SIZE + 1; |
1260 | 0 | *pData = taosMemoryMalloc(bufLen); |
1261 | 0 | if (NULL == *pData) { |
1262 | 0 | taosMemoryFree(tmpTokenBuf); |
1263 | |
|
1264 | 0 | return terrno; |
1265 | 0 | } |
1266 | | |
1267 | 0 | (void)memcpy(*pData, input, inputBytes); |
1268 | |
|
1269 | 0 | int32_t keypaddedlen = tsm4_encrypt_len(keyBytes); |
1270 | 0 | char* pKeyPaddingBuf = taosMemoryMalloc(keypaddedlen); |
1271 | 0 | if (!pKeyPaddingBuf) { |
1272 | 0 | taosMemoryFree(tmpTokenBuf); |
1273 | |
|
1274 | 0 | return terrno; |
1275 | 0 | } |
1276 | 0 | (void)memcpy(pKeyPaddingBuf, key, keyBytes); |
1277 | |
|
1278 | 0 | int32_t len = taosSm4Decrypt(pKeyPaddingBuf, keyBytes, *pData, inputBytes); |
1279 | 0 | *nData = len; |
1280 | |
|
1281 | 0 | taosMemoryFree(pKeyPaddingBuf); |
1282 | 0 | } else { |
1283 | 0 | taosMemoryFree(tmpTokenBuf); |
1284 | |
|
1285 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid identifier", pToken->z); |
1286 | 0 | } |
1287 | | |
1288 | 0 | taosMemoryFree(tmpTokenBuf); |
1289 | 0 | } else { |
1290 | 0 | if (pToken->n + VARSTR_HEADER_SIZE > bytes) { |
1291 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
1292 | 0 | } |
1293 | | |
1294 | 0 | *pData = taosMemoryMalloc(pToken->n); |
1295 | 0 | if (NULL == *pData) { |
1296 | 0 | return terrno; |
1297 | 0 | } |
1298 | 0 | memcpy(*pData, pToken->z, pToken->n); |
1299 | 0 | *nData = pToken->n; |
1300 | 0 | } |
1301 | | |
1302 | 0 | pVal->flag = CV_FLAG_VALUE; |
1303 | 0 | return TSDB_CODE_SUCCESS; |
1304 | 0 | } |
1305 | | |
1306 | 0 | static int32_t parseVarbinary(SToken* pToken, uint8_t** pData, uint32_t* nData, int32_t bytes) { |
1307 | 0 | if (pToken->type != TK_NK_STRING) { |
1308 | 0 | return TSDB_CODE_PAR_INVALID_VARBINARY; |
1309 | 0 | } |
1310 | | |
1311 | 0 | if (isHex(pToken->z + 1, pToken->n - 2)) { |
1312 | 0 | if (!isValidateHex(pToken->z + 1, pToken->n - 2)) { |
1313 | 0 | return TSDB_CODE_PAR_INVALID_VARBINARY; |
1314 | 0 | } |
1315 | | |
1316 | 0 | void* data = NULL; |
1317 | 0 | uint32_t size = 0; |
1318 | 0 | if (taosHex2Ascii(pToken->z + 1, pToken->n - 2, &data, &size) < 0) { |
1319 | 0 | return TSDB_CODE_OUT_OF_MEMORY; |
1320 | 0 | } |
1321 | | |
1322 | 0 | if (size + VARSTR_HEADER_SIZE > bytes) { |
1323 | 0 | taosMemoryFree(data); |
1324 | 0 | return TSDB_CODE_PAR_VALUE_TOO_LONG; |
1325 | 0 | } |
1326 | 0 | *pData = data; |
1327 | 0 | *nData = size; |
1328 | 0 | } else { |
1329 | 0 | *pData = taosMemoryCalloc(1, pToken->n); |
1330 | 0 | if (!pData) return terrno; |
1331 | 0 | int32_t len = trimString(pToken->z, pToken->n, *pData, pToken->n); |
1332 | 0 | *nData = len; |
1333 | |
|
1334 | 0 | if (*nData + VARSTR_HEADER_SIZE > bytes) { |
1335 | 0 | return TSDB_CODE_PAR_VALUE_TOO_LONG; |
1336 | 0 | } |
1337 | 0 | } |
1338 | 0 | return TSDB_CODE_SUCCESS; |
1339 | 0 | } |
1340 | 0 | static int32_t parseBlob(SToken* pToken, uint8_t** pData, uint32_t* nData, int32_t bytes) { |
1341 | 0 | if (pToken->type != TK_NK_STRING) { |
1342 | 0 | return TSDB_CODE_PAR_INVALID_VARBINARY; |
1343 | 0 | } |
1344 | 0 | int32_t code = 0; |
1345 | 0 | int32_t lino = 0; |
1346 | 0 | uint32_t size = 0; |
1347 | 0 | *pData = NULL; |
1348 | |
|
1349 | 0 | if (isHex(pToken->z + 1, pToken->n - 2)) { |
1350 | 0 | if (!isValidateHex(pToken->z + 1, pToken->n - 2)) { |
1351 | 0 | return TSDB_CODE_PAR_INVALID_VARBINARY; |
1352 | 0 | } |
1353 | 0 | void* data = NULL; |
1354 | |
|
1355 | 0 | if (taosHex2Ascii(pToken->z + 1, pToken->n - 2, &data, &size) < 0) { |
1356 | 0 | TSDB_CHECK_CODE(code, lino, _error); |
1357 | 0 | } |
1358 | 0 | *pData = data; |
1359 | 0 | *nData = size; |
1360 | 0 | } else { |
1361 | 0 | *pData = taosMemoryCalloc(1, pToken->n); |
1362 | 0 | if (!pData) return terrno; |
1363 | 0 | size = trimString(pToken->z, pToken->n, (char*)*pData, pToken->n); |
1364 | 0 | *nData = size; |
1365 | 0 | } |
1366 | | |
1367 | 0 | if (size + BLOBSTR_HEADER_SIZE > TSDB_MAX_BLOB_LEN) { |
1368 | 0 | TSDB_CHECK_CODE(code = TSDB_CODE_BLOB_VALUE_TOO_LONG, lino, _error); |
1369 | 0 | } |
1370 | | |
1371 | 0 | _error: |
1372 | 0 | if (code != 0) { |
1373 | 0 | taosMemoryFree(pData); |
1374 | 0 | uError("parseBlob failed at lino %d code: %s", lino, tstrerror(code)); |
1375 | 0 | return code; |
1376 | 0 | } |
1377 | 0 | *nData = size; |
1378 | 0 | return TSDB_CODE_SUCCESS; |
1379 | 0 | } |
1380 | | |
1381 | | static int32_t parseTagToken(const char** end, SToken* pToken, SSchema* pSchema, int16_t timePrec, STagVal* val, |
1382 | 0 | SMsgBuf* pMsgBuf, timezone_t tz, void* charsetCxt) { |
1383 | 0 | int64_t iv; |
1384 | 0 | uint64_t uv; |
1385 | 0 | char* endptr = NULL; |
1386 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
1387 | |
|
1388 | | #if 0 |
1389 | | if (isNullValue(pSchema->type, pToken)) { |
1390 | | if (TSDB_DATA_TYPE_TIMESTAMP == pSchema->type && PRIMARYKEY_TIMESTAMP_COL_ID == pSchema->colId) { |
1391 | | return buildSyntaxErrMsg(pMsgBuf, "Primary timestamp column can not be null", pToken->z); |
1392 | | } |
1393 | | |
1394 | | return TSDB_CODE_SUCCESS; |
1395 | | } |
1396 | | #endif |
1397 | |
|
1398 | 0 | val->cid = pSchema->colId; |
1399 | 0 | val->type = pSchema->type; |
1400 | |
|
1401 | 0 | switch (pSchema->type) { |
1402 | 0 | case TSDB_DATA_TYPE_BOOL: { |
1403 | 0 | if ((pToken->type == TK_NK_BOOL || pToken->type == TK_NK_STRING) && (pToken->n != 0)) { |
1404 | 0 | if (IS_TRUE_STR(pToken->z, pToken->n)) { |
1405 | 0 | *(int8_t*)(&val->i64) = TRUE_VALUE; |
1406 | 0 | } else if (IS_FALSE_STR(pToken->z, pToken->n)) { |
1407 | 0 | *(int8_t*)(&val->i64) = FALSE_VALUE; |
1408 | 0 | } else if (TSDB_CODE_SUCCESS == toDoubleEx(pToken->z, pToken->n, pToken->type, (double*)&iv)) { |
1409 | 0 | *(int8_t*)(&val->i64) = (*(double*)&iv == 0 ? FALSE_VALUE : TRUE_VALUE); |
1410 | 0 | } else { |
1411 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid bool data", pToken->z); |
1412 | 0 | } |
1413 | 0 | } else if (pToken->type == TK_NK_INTEGER) { |
1414 | 0 | *(int8_t*)(&val->i64) = ((taosStr2Int64(pToken->z, NULL, 10) == 0) ? FALSE_VALUE : TRUE_VALUE); |
1415 | 0 | } else if (pToken->type == TK_NK_FLOAT) { |
1416 | 0 | *(int8_t*)(&val->i64) = ((taosStr2Double(pToken->z, NULL) == 0) ? FALSE_VALUE : TRUE_VALUE); |
1417 | 0 | } else if ((pToken->type == TK_NK_HEX || pToken->type == TK_NK_BIN) && |
1418 | 0 | (TSDB_CODE_SUCCESS == toDoubleEx(pToken->z, pToken->n, pToken->type, (double*)&iv))) { |
1419 | 0 | *(int8_t*)(&val->i64) = (*(double*)&iv == 0 ? FALSE_VALUE : TRUE_VALUE); |
1420 | 0 | } else { |
1421 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid bool data", pToken->z); |
1422 | 0 | } |
1423 | 0 | break; |
1424 | 0 | } |
1425 | | |
1426 | 0 | case TSDB_DATA_TYPE_TINYINT: { |
1427 | 0 | code = toIntegerEx(pToken->z, pToken->n, pToken->type, &iv); |
1428 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1429 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid tinyint data", pToken->z); |
1430 | 0 | } else if (!IS_VALID_TINYINT(iv)) { |
1431 | 0 | return buildSyntaxErrMsg(pMsgBuf, "tinyint data overflow", pToken->z); |
1432 | 0 | } |
1433 | | |
1434 | 0 | *(int8_t*)(&val->i64) = iv; |
1435 | 0 | break; |
1436 | 0 | } |
1437 | | |
1438 | 0 | case TSDB_DATA_TYPE_UTINYINT: { |
1439 | 0 | code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &uv); |
1440 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1441 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned tinyint data", pToken->z); |
1442 | 0 | } else if (uv > UINT8_MAX) { |
1443 | 0 | return buildSyntaxErrMsg(pMsgBuf, "unsigned tinyint data overflow", pToken->z); |
1444 | 0 | } |
1445 | 0 | *(uint8_t*)(&val->i64) = uv; |
1446 | 0 | break; |
1447 | 0 | } |
1448 | | |
1449 | 0 | case TSDB_DATA_TYPE_SMALLINT: { |
1450 | 0 | code = toIntegerEx(pToken->z, pToken->n, pToken->type, &iv); |
1451 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1452 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid smallint data", pToken->z); |
1453 | 0 | } else if (!IS_VALID_SMALLINT(iv)) { |
1454 | 0 | return buildSyntaxErrMsg(pMsgBuf, "smallint data overflow", pToken->z); |
1455 | 0 | } |
1456 | 0 | *(int16_t*)(&val->i64) = iv; |
1457 | 0 | break; |
1458 | 0 | } |
1459 | | |
1460 | 0 | case TSDB_DATA_TYPE_USMALLINT: { |
1461 | 0 | code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &uv); |
1462 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1463 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned smallint data", pToken->z); |
1464 | 0 | } else if (uv > UINT16_MAX) { |
1465 | 0 | return buildSyntaxErrMsg(pMsgBuf, "unsigned smallint data overflow", pToken->z); |
1466 | 0 | } |
1467 | 0 | *(uint16_t*)(&val->i64) = uv; |
1468 | 0 | break; |
1469 | 0 | } |
1470 | | |
1471 | 0 | case TSDB_DATA_TYPE_INT: { |
1472 | 0 | code = toIntegerEx(pToken->z, pToken->n, pToken->type, &iv); |
1473 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1474 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid int data", pToken->z); |
1475 | 0 | } else if (!IS_VALID_INT(iv)) { |
1476 | 0 | return buildSyntaxErrMsg(pMsgBuf, "int data overflow", pToken->z); |
1477 | 0 | } |
1478 | 0 | *(int32_t*)(&val->i64) = iv; |
1479 | 0 | break; |
1480 | 0 | } |
1481 | | |
1482 | 0 | case TSDB_DATA_TYPE_UINT: { |
1483 | 0 | code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &uv); |
1484 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1485 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned int data", pToken->z); |
1486 | 0 | } else if (uv > UINT32_MAX) { |
1487 | 0 | return buildSyntaxErrMsg(pMsgBuf, "unsigned int data overflow", pToken->z); |
1488 | 0 | } |
1489 | 0 | *(uint32_t*)(&val->i64) = uv; |
1490 | 0 | break; |
1491 | 0 | } |
1492 | | |
1493 | 0 | case TSDB_DATA_TYPE_BIGINT: { |
1494 | 0 | code = toIntegerEx(pToken->z, pToken->n, pToken->type, &iv); |
1495 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1496 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid bigint data", pToken->z); |
1497 | 0 | } |
1498 | 0 | val->i64 = iv; |
1499 | 0 | break; |
1500 | 0 | } |
1501 | | |
1502 | 0 | case TSDB_DATA_TYPE_UBIGINT: { |
1503 | 0 | code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &uv); |
1504 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1505 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid unsigned bigint data", pToken->z); |
1506 | 0 | } |
1507 | 0 | *(uint64_t*)(&val->i64) = uv; |
1508 | 0 | break; |
1509 | 0 | } |
1510 | | |
1511 | 0 | case TSDB_DATA_TYPE_FLOAT: { |
1512 | 0 | double dv; |
1513 | 0 | code = toDoubleEx(pToken->z, pToken->n, pToken->type, &dv); |
1514 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1515 | 0 | return buildSyntaxErrMsg(pMsgBuf, "illegal float data", pToken->z); |
1516 | 0 | } |
1517 | 0 | if (dv > FLT_MAX || dv < -FLT_MAX || isinf(dv) || isnan(dv)) { |
1518 | 0 | return buildSyntaxErrMsg(pMsgBuf, "illegal float data", pToken->z); |
1519 | 0 | } |
1520 | 0 | *(float*)(&val->i64) = dv; |
1521 | 0 | break; |
1522 | 0 | } |
1523 | | |
1524 | 0 | case TSDB_DATA_TYPE_DOUBLE: { |
1525 | 0 | double dv; |
1526 | 0 | code = toDoubleEx(pToken->z, pToken->n, pToken->type, &dv); |
1527 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1528 | 0 | return buildSyntaxErrMsg(pMsgBuf, "illegal float data", pToken->z); |
1529 | 0 | } |
1530 | 0 | if (((dv == HUGE_VAL || dv == -HUGE_VAL) && ERRNO == ERANGE) || isinf(dv) || isnan(dv)) { |
1531 | 0 | return buildSyntaxErrMsg(pMsgBuf, "illegal double data", pToken->z); |
1532 | 0 | } |
1533 | | |
1534 | 0 | *(double*)(&val->i64) = dv; |
1535 | 0 | break; |
1536 | 0 | } |
1537 | | |
1538 | 0 | case TSDB_DATA_TYPE_BINARY: { |
1539 | | // Too long values will raise the invalid sql error message |
1540 | 0 | if (pToken->n + VARSTR_HEADER_SIZE > pSchema->bytes) { |
1541 | 0 | return generateSyntaxErrMsg(pMsgBuf, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
1542 | 0 | } |
1543 | 0 | val->pData = taosStrdup(pToken->z); |
1544 | 0 | if (!val->pData) { |
1545 | 0 | return terrno; |
1546 | 0 | } |
1547 | 0 | val->nData = pToken->n; |
1548 | 0 | break; |
1549 | 0 | } |
1550 | 0 | case TSDB_DATA_TYPE_VARBINARY: { |
1551 | 0 | code = parseVarbinary(pToken, &val->pData, &val->nData, pSchema->bytes); |
1552 | 0 | if (code != TSDB_CODE_SUCCESS) { |
1553 | 0 | return generateSyntaxErrMsg(pMsgBuf, code, pSchema->name); |
1554 | 0 | } |
1555 | 0 | break; |
1556 | 0 | } |
1557 | 0 | case TSDB_DATA_TYPE_GEOMETRY: { |
1558 | 0 | unsigned char* output = NULL; |
1559 | 0 | size_t size = 0; |
1560 | |
|
1561 | 0 | code = parseGeometry(pToken, &output, &size); |
1562 | 0 | if (code != TSDB_CODE_SUCCESS) { |
1563 | 0 | code = buildSyntaxErrMsg(pMsgBuf, getGeosErrMsg(code), pToken->z); |
1564 | 0 | } else if (size + VARSTR_HEADER_SIZE > pSchema->bytes) { |
1565 | | // Too long values will raise the invalid sql error message |
1566 | 0 | code = generateSyntaxErrMsg(pMsgBuf, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
1567 | 0 | } else { |
1568 | 0 | val->pData = taosMemoryMalloc(size); |
1569 | 0 | if (NULL == val->pData) { |
1570 | 0 | code = terrno; |
1571 | 0 | } else { |
1572 | 0 | memcpy(val->pData, output, size); |
1573 | 0 | val->nData = size; |
1574 | 0 | } |
1575 | 0 | } |
1576 | |
|
1577 | 0 | geosFreeBuffer(output); |
1578 | 0 | break; |
1579 | 0 | } |
1580 | | |
1581 | 0 | case TSDB_DATA_TYPE_NCHAR: { |
1582 | 0 | int32_t output = 0; |
1583 | 0 | int64_t realLen = pToken->n << 2; |
1584 | 0 | if (realLen > pSchema->bytes - VARSTR_HEADER_SIZE) realLen = pSchema->bytes - VARSTR_HEADER_SIZE; |
1585 | 0 | void* p = taosMemoryMalloc(realLen); |
1586 | 0 | if (p == NULL) { |
1587 | 0 | return terrno; |
1588 | 0 | } |
1589 | 0 | if (!taosMbsToUcs4(pToken->z, pToken->n, (TdUcs4*)(p), realLen, &output, charsetCxt)) { |
1590 | 0 | if (terrno == TAOS_SYSTEM_ERROR(E2BIG)) { |
1591 | 0 | taosMemoryFree(p); |
1592 | 0 | return generateSyntaxErrMsg(pMsgBuf, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
1593 | 0 | } |
1594 | 0 | char buf[512] = {0}; |
1595 | 0 | snprintf(buf, tListLen(buf), " taosMbsToUcs4 error:%s %d %d", strerror(terrno), ERRNO, EILSEQ); |
1596 | 0 | taosMemoryFree(p); |
1597 | 0 | return buildSyntaxErrMsg(pMsgBuf, buf, pToken->z); |
1598 | 0 | } |
1599 | 0 | val->pData = p; |
1600 | 0 | val->nData = output; |
1601 | 0 | break; |
1602 | 0 | } |
1603 | 0 | case TSDB_DATA_TYPE_TIMESTAMP: { |
1604 | 0 | if (parseTime(end, pToken, timePrec, &iv, pMsgBuf, tz) != TSDB_CODE_SUCCESS) { |
1605 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp", pToken->z); |
1606 | 0 | } |
1607 | | |
1608 | 0 | val->i64 = iv; |
1609 | 0 | break; |
1610 | 0 | } |
1611 | 0 | case TSDB_DATA_TYPE_MEDIUMBLOB: |
1612 | 0 | case TSDB_DATA_TYPE_BLOB: { |
1613 | 0 | code = generateSyntaxErrMsg(pMsgBuf, TSDB_CODE_BLOB_NOT_SUPPORT_TAG, pSchema->name); |
1614 | 0 | break; |
1615 | 0 | } |
1616 | 0 | } |
1617 | | |
1618 | 0 | return code; |
1619 | 0 | } |
1620 | | |
1621 | | // input pStmt->pSql: [(tag1_name, ...)] TAGS (tag1_value, ...) ... |
1622 | | // output pStmt->pSql: TAGS (tag1_value, ...) ... |
1623 | 0 | static int32_t parseBoundTagsClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
1624 | 0 | int32_t code = insInitBoundColsInfo(getNumOfTags(pStmt->pTableMeta), &pCxt->tags); |
1625 | 0 | if (TSDB_CODE_SUCCESS != code) { |
1626 | 0 | return code; |
1627 | 0 | } |
1628 | | |
1629 | 0 | SToken token; |
1630 | 0 | int32_t index = 0; |
1631 | 0 | NEXT_TOKEN_KEEP_SQL(pStmt->pSql, token, index); |
1632 | 0 | if (TK_NK_LP != token.type) { |
1633 | 0 | return TSDB_CODE_SUCCESS; |
1634 | 0 | } |
1635 | | |
1636 | 0 | pStmt->pSql += index; |
1637 | 0 | return parseBoundColumns(pCxt, pStmt, &pStmt->pSql, BOUND_TAGS, pStmt->pTableMeta, &pCxt->tags); |
1638 | 0 | } |
1639 | | |
1640 | | int32_t parseTagValue(SMsgBuf* pMsgBuf, const char** pSql, uint8_t precision, SSchema* pTagSchema, SToken* pToken, |
1641 | 0 | SArray* pTagName, SArray* pTagVals, STag** pTag, timezone_t tz, void* charsetCxt) { |
1642 | 0 | bool isNull = isNullValue(pTagSchema->type, pToken); |
1643 | 0 | if (!isNull && pTagName) { |
1644 | 0 | int32_t code = insTagNameAppend(pTagName, pTagSchema->name, pTagSchema->colId); |
1645 | 0 | if (TSDB_CODE_SUCCESS != code) return code; |
1646 | 0 | } |
1647 | | |
1648 | 0 | if (pTagSchema->type == TSDB_DATA_TYPE_JSON) { |
1649 | 0 | if (pToken->n > (TSDB_MAX_JSON_TAG_LEN - VARSTR_HEADER_SIZE) / TSDB_NCHAR_SIZE) { |
1650 | 0 | return buildSyntaxErrMsg(pMsgBuf, "json string too long than 4095", pToken->z); |
1651 | 0 | } |
1652 | | |
1653 | 0 | if (isNull) { |
1654 | 0 | return tTagNew(pTagVals, 1, true, pTag); |
1655 | 0 | } else { |
1656 | 0 | return parseJsontoTagData(pToken->z, pTagVals, pTag, pMsgBuf, charsetCxt); |
1657 | 0 | } |
1658 | 0 | } |
1659 | | |
1660 | 0 | if (isNull) return 0; |
1661 | | |
1662 | 0 | STagVal val = {0}; |
1663 | 0 | int32_t code = parseTagToken(pSql, pToken, pTagSchema, precision, &val, pMsgBuf, tz, charsetCxt); |
1664 | 0 | if (TSDB_CODE_SUCCESS == code) { |
1665 | 0 | if (NULL == taosArrayPush(pTagVals, &val)) { |
1666 | 0 | code = terrno; |
1667 | 0 | } |
1668 | 0 | } |
1669 | |
|
1670 | 0 | if (TSDB_CODE_SUCCESS != code && IS_VAR_DATA_TYPE(val.type)) { |
1671 | 0 | taosMemoryFree(val.pData); |
1672 | 0 | } |
1673 | |
|
1674 | 0 | return code; |
1675 | 0 | } |
1676 | | |
1677 | 0 | static int32_t buildCreateTbReq(SVnodeModifyOpStmt* pStmt, STag* pTag, SArray* pTagName) { |
1678 | 0 | if (pStmt->pCreateTblReq) { |
1679 | 0 | tdDestroySVCreateTbReq(pStmt->pCreateTblReq); |
1680 | 0 | taosMemoryFreeClear(pStmt->pCreateTblReq); |
1681 | 0 | } |
1682 | 0 | pStmt->pCreateTblReq = taosMemoryCalloc(1, sizeof(SVCreateTbReq)); |
1683 | 0 | if (NULL == pStmt->pCreateTblReq) { |
1684 | 0 | return terrno; |
1685 | 0 | } |
1686 | 0 | return insBuildCreateTbReq(pStmt->pCreateTblReq, pStmt->targetTableName.tname, pTag, pStmt->pTableMeta->suid, |
1687 | 0 | pStmt->usingTableName.tname, pTagName, pStmt->pTableMeta->tableInfo.numOfTags, |
1688 | 0 | TSDB_DEFAULT_TABLE_TTL); |
1689 | 0 | } |
1690 | | |
1691 | 0 | int32_t checkAndTrimValue(SToken* pToken, char* tmpTokenBuf, SMsgBuf* pMsgBuf, int8_t type) { |
1692 | 0 | if (pToken->type == TK_NK_QUESTION) { |
1693 | 0 | return buildInvalidOperationMsg(pMsgBuf, "insert into super table syntax is not supported for stmt"); |
1694 | 0 | } |
1695 | 0 | if ((pToken->type != TK_NOW && pToken->type != TK_TODAY && pToken->type != TK_NK_INTEGER && |
1696 | 0 | pToken->type != TK_NK_STRING && pToken->type != TK_NK_FLOAT && pToken->type != TK_NK_BOOL && |
1697 | 0 | pToken->type != TK_NULL && pToken->type != TK_NK_HEX && pToken->type != TK_NK_OCT && pToken->type != TK_NK_BIN && |
1698 | 0 | pToken->type != TK_NK_VARIABLE && |
1699 | 0 | pToken->type != TK_FROM_BASE64 && pToken->type != TK_TO_BASE64 && |
1700 | 0 | pToken->type != TK_MD5 && pToken->type != TK_SHA && pToken->type != TK_SHA1 && pToken->type != TK_SHA2 && |
1701 | 0 | pToken->type != TK_AES_ENCRYPT && pToken->type != TK_AES_DECRYPT && |
1702 | 0 | pToken->type != TK_SM4_ENCRYPT && pToken->type != TK_SM4_DECRYPT) || |
1703 | 0 | (pToken->n == 0) || (pToken->type == TK_NK_RP)) { |
1704 | 0 | return buildSyntaxErrMsg(pMsgBuf, "invalid data or symbol", pToken->z); |
1705 | 0 | } |
1706 | | |
1707 | | // Remove quotation marks |
1708 | 0 | if (TK_NK_STRING == pToken->type && type != TSDB_DATA_TYPE_VARBINARY && !IS_STR_DATA_BLOB(type)) { |
1709 | 0 | if (!IS_STR_DATA_BLOB(type)) { |
1710 | 0 | if (pToken->n >= TSDB_MAX_BYTES_PER_ROW) { |
1711 | 0 | return buildSyntaxErrMsg(pMsgBuf, "too long string", pToken->z); |
1712 | 0 | } |
1713 | | |
1714 | 0 | int32_t len = trimString(pToken->z, pToken->n, tmpTokenBuf, TSDB_MAX_BYTES_PER_ROW); |
1715 | 0 | pToken->z = tmpTokenBuf; |
1716 | 0 | pToken->n = len; |
1717 | 0 | } else { |
1718 | 0 | if (pToken->n >= TSDB_MAX_BLOB_LEN) { |
1719 | 0 | return buildSyntaxErrMsg(pMsgBuf, "too long blob", pToken->z); |
1720 | 0 | } |
1721 | | |
1722 | 0 | int32_t len = trimString(pToken->z, pToken->n, tmpTokenBuf, TSDB_MAX_BLOB_LEN); |
1723 | 0 | pToken->z = tmpTokenBuf; |
1724 | 0 | pToken->n = len; |
1725 | 0 | } |
1726 | 0 | } |
1727 | | |
1728 | 0 | return TSDB_CODE_SUCCESS; |
1729 | 0 | } |
1730 | | |
1731 | | typedef struct SRewriteTagCondCxt { |
1732 | | SArray* pTagVals; |
1733 | | SArray* pTagName; |
1734 | | int32_t code; |
1735 | | } SRewriteTagCondCxt; |
1736 | | |
1737 | 0 | static int32_t rewriteTagCondColumnImpl(STagVal* pVal, SNode** pNode) { |
1738 | 0 | SValueNode* pValue = NULL; |
1739 | 0 | int32_t code = nodesMakeNode(QUERY_NODE_VALUE, (SNode**)&pValue); |
1740 | 0 | if (NULL == pValue) { |
1741 | 0 | return code; |
1742 | 0 | } |
1743 | | |
1744 | 0 | pValue->node.resType = ((SColumnNode*)*pNode)->node.resType; |
1745 | 0 | nodesDestroyNode(*pNode); |
1746 | 0 | *pNode = (SNode*)pValue; |
1747 | |
|
1748 | 0 | switch (pVal->type) { |
1749 | 0 | case TSDB_DATA_TYPE_BOOL: |
1750 | 0 | pValue->datum.b = *(int8_t*)(&pVal->i64); |
1751 | 0 | *(bool*)&pValue->typeData = pValue->datum.b; |
1752 | 0 | break; |
1753 | 0 | case TSDB_DATA_TYPE_TINYINT: |
1754 | 0 | pValue->datum.i = *(int8_t*)(&pVal->i64); |
1755 | 0 | *(int8_t*)&pValue->typeData = pValue->datum.i; |
1756 | 0 | break; |
1757 | 0 | case TSDB_DATA_TYPE_SMALLINT: |
1758 | 0 | pValue->datum.i = *(int16_t*)(&pVal->i64); |
1759 | 0 | *(int16_t*)&pValue->typeData = pValue->datum.i; |
1760 | 0 | break; |
1761 | 0 | case TSDB_DATA_TYPE_INT: |
1762 | 0 | pValue->datum.i = *(int32_t*)(&pVal->i64); |
1763 | 0 | *(int32_t*)&pValue->typeData = pValue->datum.i; |
1764 | 0 | break; |
1765 | 0 | case TSDB_DATA_TYPE_BIGINT: |
1766 | 0 | pValue->datum.i = pVal->i64; |
1767 | 0 | pValue->typeData = pValue->datum.i; |
1768 | 0 | break; |
1769 | 0 | case TSDB_DATA_TYPE_FLOAT: |
1770 | 0 | pValue->datum.d = *(float*)(&pVal->i64); |
1771 | 0 | *(float*)&pValue->typeData = pValue->datum.d; |
1772 | 0 | break; |
1773 | 0 | case TSDB_DATA_TYPE_DOUBLE: |
1774 | 0 | pValue->datum.d = *(double*)(&pVal->i64); |
1775 | 0 | *(double*)&pValue->typeData = pValue->datum.d; |
1776 | 0 | break; |
1777 | 0 | case TSDB_DATA_TYPE_VARCHAR: |
1778 | 0 | case TSDB_DATA_TYPE_VARBINARY: |
1779 | 0 | case TSDB_DATA_TYPE_NCHAR: |
1780 | 0 | pValue->datum.p = taosMemoryCalloc(1, pVal->nData + VARSTR_HEADER_SIZE); |
1781 | 0 | if (NULL == pValue->datum.p) { |
1782 | 0 | return terrno; |
1783 | 0 | } |
1784 | 0 | varDataSetLen(pValue->datum.p, pVal->nData); |
1785 | 0 | memcpy(varDataVal(pValue->datum.p), pVal->pData, pVal->nData); |
1786 | 0 | break; |
1787 | 0 | case TSDB_DATA_TYPE_TIMESTAMP: |
1788 | 0 | pValue->datum.i = pVal->i64; |
1789 | 0 | pValue->typeData = pValue->datum.i; |
1790 | 0 | break; |
1791 | 0 | case TSDB_DATA_TYPE_UTINYINT: |
1792 | 0 | pValue->datum.i = *(uint8_t*)(&pVal->i64); |
1793 | 0 | *(uint8_t*)&pValue->typeData = pValue->datum.i; |
1794 | 0 | break; |
1795 | 0 | case TSDB_DATA_TYPE_USMALLINT: |
1796 | 0 | pValue->datum.i = *(uint16_t*)(&pVal->i64); |
1797 | 0 | *(uint16_t*)&pValue->typeData = pValue->datum.i; |
1798 | 0 | break; |
1799 | 0 | case TSDB_DATA_TYPE_UINT: |
1800 | 0 | pValue->datum.i = *(uint32_t*)(&pVal->i64); |
1801 | 0 | *(uint32_t*)&pValue->typeData = pValue->datum.i; |
1802 | 0 | break; |
1803 | 0 | case TSDB_DATA_TYPE_UBIGINT: |
1804 | 0 | pValue->datum.i = *(uint64_t*)(&pVal->i64); |
1805 | 0 | *(uint64_t*)&pValue->typeData = pValue->datum.i; |
1806 | 0 | break; |
1807 | 0 | case TSDB_DATA_TYPE_JSON: |
1808 | 0 | case TSDB_DATA_TYPE_DECIMAL: |
1809 | 0 | case TSDB_DATA_TYPE_BLOB: |
1810 | 0 | case TSDB_DATA_TYPE_MEDIUMBLOB: |
1811 | 0 | default: |
1812 | 0 | return TSDB_CODE_FAILED; |
1813 | 0 | } |
1814 | 0 | return TSDB_CODE_SUCCESS; |
1815 | 0 | } |
1816 | | |
1817 | 0 | static int32_t rewriteTagCondColumn(SArray* pTagVals, SArray* pTagName, SNode** pNode) { |
1818 | 0 | SColumnNode* pCol = (SColumnNode*)*pNode; |
1819 | 0 | int32_t ntags = taosArrayGetSize(pTagName); |
1820 | 0 | for (int32_t i = 0; i < ntags; ++i) { |
1821 | 0 | char* pTagColName = taosArrayGet(pTagName, i); |
1822 | 0 | if (0 == strcmp(pTagColName, pCol->colName)) { |
1823 | 0 | return rewriteTagCondColumnImpl(taosArrayGet(pTagVals, i), pNode); |
1824 | 0 | } |
1825 | 0 | } |
1826 | 0 | return TSDB_CODE_PAR_PERMISSION_DENIED; |
1827 | 0 | } |
1828 | | |
1829 | 0 | static EDealRes rewriteTagCond(SNode** pNode, void* pContext) { |
1830 | 0 | if (QUERY_NODE_COLUMN == nodeType(*pNode)) { |
1831 | 0 | SRewriteTagCondCxt* pCxt = pContext; |
1832 | 0 | pCxt->code = rewriteTagCondColumn(pCxt->pTagVals, pCxt->pTagName, pNode); |
1833 | 0 | return (TSDB_CODE_SUCCESS == pCxt->code ? DEAL_RES_IGNORE_CHILD : DEAL_RES_ERROR); |
1834 | 0 | } |
1835 | 0 | return DEAL_RES_CONTINUE; |
1836 | 0 | } |
1837 | | |
1838 | 0 | static int32_t setTagVal(SArray* pTagVals, SArray* pTagName, SNode* pCond) { |
1839 | 0 | SRewriteTagCondCxt cxt = {.code = TSDB_CODE_SUCCESS, .pTagVals = pTagVals, .pTagName = pTagName}; |
1840 | 0 | nodesRewriteExpr(&pCond, rewriteTagCond, &cxt); |
1841 | 0 | return cxt.code; |
1842 | 0 | } |
1843 | | |
1844 | 0 | static int32_t checkTagCondResult(SNode* pResult) { |
1845 | 0 | return (QUERY_NODE_VALUE == nodeType(pResult) && ((SValueNode*)pResult)->datum.b) ? TSDB_CODE_SUCCESS |
1846 | 0 | : TSDB_CODE_PAR_PERMISSION_DENIED; |
1847 | 0 | } |
1848 | | |
1849 | 0 | static int32_t checkSubtablePrivilege(SArray* pTagVals, SArray* pTagName, SNode** pCond) { |
1850 | 0 | int32_t code = setTagVal(pTagVals, pTagName, *pCond); |
1851 | 0 | if (TSDB_CODE_SUCCESS == code) { |
1852 | 0 | code = scalarCalculateConstants(*pCond, pCond); |
1853 | 0 | } |
1854 | 0 | if (TSDB_CODE_SUCCESS == code) { |
1855 | 0 | code = checkTagCondResult(*pCond); |
1856 | 0 | } |
1857 | 0 | NODES_DESTORY_NODE(*pCond); |
1858 | 0 | return code; |
1859 | 0 | } |
1860 | | |
1861 | | // pSql -> tag1_value, ...) |
1862 | 0 | static int32_t parseTagsClauseImpl(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, bool autoCreate) { |
1863 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
1864 | 0 | SSchema* pSchema = getTableTagSchema(pStmt->pTableMeta); |
1865 | 0 | SArray* pTagVals = NULL; |
1866 | 0 | SArray* pTagName = NULL; |
1867 | 0 | uint8_t precision = pStmt->pTableMeta->tableInfo.precision; |
1868 | 0 | SToken token; |
1869 | 0 | bool isJson = false; |
1870 | 0 | STag* pTag = NULL; |
1871 | 0 | uint8_t* pTagsIndex; |
1872 | 0 | int32_t numOfTags = 0; |
1873 | |
|
1874 | 0 | if (pCxt->pComCxt->stmtBindVersion == 2) { // only support stmt2 |
1875 | 0 | pTagsIndex = taosMemoryCalloc(pCxt->tags.numOfBound, sizeof(uint8_t)); |
1876 | 0 | } |
1877 | |
|
1878 | 0 | if (!(pTagVals = taosArrayInit(pCxt->tags.numOfBound, sizeof(STagVal))) || |
1879 | 0 | !(pTagName = taosArrayInit(pCxt->tags.numOfBound, TSDB_COL_NAME_LEN + sizeof(col_id_t)))) { |
1880 | 0 | code = terrno; |
1881 | 0 | goto _exit; |
1882 | 0 | } |
1883 | | |
1884 | 0 | for (int i = 0; TSDB_CODE_SUCCESS == code && i < pCxt->tags.numOfBound; ++i) { |
1885 | 0 | NEXT_TOKEN_WITH_PREV(pStmt->pSql, token); |
1886 | |
|
1887 | 0 | if (token.type == TK_NK_QUESTION) { |
1888 | 0 | if (pCxt->pComCxt->stmtBindVersion == 0) { |
1889 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "? only used in stmt", token.z); |
1890 | 0 | break; |
1891 | 0 | } |
1892 | | |
1893 | 0 | continue; |
1894 | 0 | } |
1895 | | |
1896 | 0 | SSchema* pTagSchema = &pSchema[pCxt->tags.pColIndex[i]]; |
1897 | 0 | isJson = pTagSchema->type == TSDB_DATA_TYPE_JSON; |
1898 | 0 | code = checkAndTrimValue(&token, pCxt->tmpTokenBuf, &pCxt->msg, pTagSchema->type); |
1899 | 0 | if (TSDB_CODE_SUCCESS == code && TK_NK_VARIABLE == token.type) { |
1900 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "not expected tags values ", token.z); |
1901 | 0 | } |
1902 | 0 | if (TSDB_CODE_SUCCESS == code) { |
1903 | 0 | code = parseTagValue(&pCxt->msg, &pStmt->pSql, precision, pTagSchema, &token, pTagName, pTagVals, &pTag, |
1904 | 0 | pCxt->pComCxt->timezone, pCxt->pComCxt->charsetCxt); |
1905 | 0 | } |
1906 | 0 | if (pCxt->pComCxt->stmtBindVersion == 2) { |
1907 | 0 | pTagsIndex[numOfTags++] = pCxt->tags.pColIndex[i]; |
1908 | 0 | } |
1909 | 0 | } |
1910 | |
|
1911 | 0 | if (TSDB_CODE_SUCCESS == code && NULL != pStmt->pTagCond) { |
1912 | 0 | code = checkSubtablePrivilege(pTagVals, pTagName, &pStmt->pTagCond); |
1913 | 0 | } |
1914 | |
|
1915 | 0 | if (TSDB_CODE_SUCCESS == code && pCxt->pComCxt->stmtBindVersion == 2) { |
1916 | 0 | if (numOfTags > 0) { |
1917 | 0 | if (pTagVals->size == pCxt->tags.numOfBound) { |
1918 | 0 | pCxt->stmtTbNameFlag |= IS_FIXED_TAG; |
1919 | 0 | } else { |
1920 | 0 | pCxt->stmtTbNameFlag &= ~IS_FIXED_TAG; |
1921 | 0 | pCxt->tags.parseredTags = taosMemoryMalloc(sizeof(STagsInfo)); |
1922 | 0 | if (pCxt->tags.parseredTags == NULL) { |
1923 | 0 | code = terrno; |
1924 | 0 | goto _exit; |
1925 | 0 | } |
1926 | 0 | pCxt->tags.parseredTags->numOfTags = numOfTags; |
1927 | 0 | pCxt->tags.parseredTags->pTagIndex = pTagsIndex; |
1928 | 0 | pCxt->tags.parseredTags->pTagVals = pTagVals; |
1929 | 0 | pCxt->tags.parseredTags->STagNames = pTagName; |
1930 | 0 | } |
1931 | 0 | } else { |
1932 | 0 | goto _exit; |
1933 | 0 | } |
1934 | 0 | } |
1935 | | |
1936 | 0 | if (TSDB_CODE_SUCCESS == code && !isJson) { |
1937 | 0 | taosArraySort(pTagName, tTagNameCompare); |
1938 | 0 | code = tTagNew(pTagVals, 1, false, &pTag); |
1939 | 0 | } |
1940 | |
|
1941 | 0 | if (TSDB_CODE_SUCCESS == code && !autoCreate) { |
1942 | 0 | code = buildCreateTbReq(pStmt, pTag, pTagName); |
1943 | 0 | pTag = NULL; |
1944 | 0 | } |
1945 | |
|
1946 | 0 | _exit: |
1947 | 0 | if (pCxt->tags.parseredTags == NULL) { |
1948 | 0 | for (int32_t i = 0; i < taosArrayGetSize(pTagVals); ++i) { |
1949 | 0 | STagVal* p = (STagVal*)TARRAY_GET_ELEM(pTagVals, i); |
1950 | 0 | if (IS_VAR_DATA_TYPE(p->type)) { |
1951 | 0 | taosMemoryFreeClear(p->pData); |
1952 | 0 | } |
1953 | 0 | } |
1954 | 0 | taosArrayDestroy(pTagVals); |
1955 | 0 | taosArrayDestroy(pTagName); |
1956 | 0 | if (pCxt->pComCxt->stmtBindVersion == 2) { |
1957 | 0 | taosMemoryFreeClear(pTagsIndex); |
1958 | 0 | } |
1959 | 0 | } |
1960 | |
|
1961 | 0 | tTagFree(pTag); |
1962 | 0 | return code; |
1963 | 0 | } |
1964 | | |
1965 | | // input pStmt->pSql: TAGS (tag1_value, ...) [table_options] ... |
1966 | | // output pStmt->pSql: [table_options] ... |
1967 | 0 | static int32_t parseTagsClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, bool autoCreate) { |
1968 | 0 | SToken token; |
1969 | 0 | NEXT_TOKEN(pStmt->pSql, token); |
1970 | 0 | if (TK_TAGS != token.type) { |
1971 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "TAGS is expected", token.z); |
1972 | 0 | } |
1973 | | |
1974 | 0 | NEXT_TOKEN(pStmt->pSql, token); |
1975 | 0 | if (TK_NK_LP != token.type) { |
1976 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "( is expected", token.z); |
1977 | 0 | } |
1978 | | |
1979 | 0 | int32_t code = parseTagsClauseImpl(pCxt, pStmt, autoCreate); |
1980 | 0 | if (TSDB_CODE_SUCCESS == code) { |
1981 | 0 | NEXT_VALID_TOKEN(pStmt->pSql, token); |
1982 | 0 | if (TK_NK_COMMA == token.type) { |
1983 | 0 | code = generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_TAGS_NOT_MATCHED); |
1984 | 0 | } else if (TK_NK_RP != token.type) { |
1985 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, ") is expected", token.z); |
1986 | 0 | } |
1987 | 0 | } |
1988 | 0 | return code; |
1989 | 0 | } |
1990 | | |
1991 | 0 | static int32_t storeChildTableMeta(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
1992 | 0 | pStmt->pTableMeta->suid = pStmt->pTableMeta->uid; |
1993 | 0 | pStmt->pTableMeta->uid = pStmt->totalTbNum; |
1994 | 0 | pStmt->pTableMeta->tableType = TSDB_CHILD_TABLE; |
1995 | |
|
1996 | 0 | STableMeta* pBackup = NULL; |
1997 | 0 | if (TSDB_CODE_SUCCESS != cloneTableMeta(pStmt->pTableMeta, &pBackup)) { |
1998 | 0 | return TSDB_CODE_OUT_OF_MEMORY; |
1999 | 0 | } |
2000 | | |
2001 | 0 | char tbFName[TSDB_TABLE_FNAME_LEN]; |
2002 | 0 | int32_t code = tNameExtractFullName(&pStmt->targetTableName, tbFName); |
2003 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2004 | 0 | taosMemoryFree(pBackup); |
2005 | 0 | return code; |
2006 | 0 | } |
2007 | 0 | code = taosHashPut(pStmt->pSubTableHashObj, tbFName, strlen(tbFName), &pBackup, POINTER_BYTES); |
2008 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2009 | 0 | taosMemoryFree(pBackup); |
2010 | 0 | } |
2011 | 0 | return code; |
2012 | 0 | } |
2013 | | |
2014 | 0 | static int32_t parseTableOptions(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
2015 | 0 | do { |
2016 | 0 | int32_t index = 0; |
2017 | 0 | SToken token; |
2018 | 0 | NEXT_TOKEN_KEEP_SQL(pStmt->pSql, token, index); |
2019 | 0 | if (TK_TTL == token.type) { |
2020 | 0 | pStmt->pSql += index; |
2021 | 0 | NEXT_TOKEN_WITH_PREV(pStmt->pSql, token); |
2022 | 0 | if (TK_NK_INTEGER != token.type) { |
2023 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "Invalid option ttl", token.z); |
2024 | 0 | } |
2025 | 0 | pStmt->pCreateTblReq->ttl = taosStr2Int32(token.z, NULL, 10); |
2026 | 0 | if (pStmt->pCreateTblReq->ttl < 0) { |
2027 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "Invalid option ttl", token.z); |
2028 | 0 | } |
2029 | 0 | } else if (TK_COMMENT == token.type) { |
2030 | 0 | pStmt->pSql += index; |
2031 | 0 | NEXT_TOKEN(pStmt->pSql, token); |
2032 | 0 | if (TK_NK_STRING != token.type) { |
2033 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "Invalid option comment", token.z); |
2034 | 0 | } |
2035 | 0 | if (token.n >= TSDB_TB_COMMENT_LEN) { |
2036 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "comment too long", token.z); |
2037 | 0 | } |
2038 | 0 | int32_t len = trimString(token.z, token.n, pCxt->tmpTokenBuf, TSDB_TB_COMMENT_LEN); |
2039 | 0 | pStmt->pCreateTblReq->comment = taosStrndup(pCxt->tmpTokenBuf, len); |
2040 | 0 | if (NULL == pStmt->pCreateTblReq->comment) { |
2041 | 0 | return terrno; |
2042 | 0 | } |
2043 | 0 | pStmt->pCreateTblReq->commentLen = len; |
2044 | 0 | } else { |
2045 | 0 | break; |
2046 | 0 | } |
2047 | 0 | } while (1); |
2048 | 0 | return TSDB_CODE_SUCCESS; |
2049 | 0 | } |
2050 | | |
2051 | | // input pStmt->pSql: |
2052 | | // 1. [(tag1_name, ...)] ... |
2053 | | // 2. VALUES ... | FILE ... |
2054 | | // output pStmt->pSql: |
2055 | | // 1. [(field1_name, ...)] |
2056 | | // 2. VALUES ... | FILE ... |
2057 | 0 | static int32_t parseUsingClauseBottom(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
2058 | 0 | if (!pStmt->usingTableProcessing || pCxt->usingDuplicateTable) { |
2059 | 0 | return TSDB_CODE_SUCCESS; |
2060 | 0 | } |
2061 | | |
2062 | 0 | int32_t code = parseBoundTagsClause(pCxt, pStmt); |
2063 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2064 | 0 | code = parseTagsClause(pCxt, pStmt, false); |
2065 | 0 | } |
2066 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2067 | 0 | code = parseTableOptions(pCxt, pStmt); |
2068 | 0 | } |
2069 | |
|
2070 | 0 | return code; |
2071 | 0 | } |
2072 | | |
2073 | 0 | static void setUserAuthInfo(SParseContext* pCxt, SName* pTbName, SUserAuthInfo* pInfo) { |
2074 | 0 | snprintf(pInfo->user, sizeof(pInfo->user), "%s", pCxt->pUser); |
2075 | 0 | memcpy(&pInfo->tbName, pTbName, sizeof(SName)); |
2076 | 0 | pInfo->userId = pCxt->userId; |
2077 | 0 | pInfo->privType = PRIV_TBL_INSERT; |
2078 | 0 | pInfo->objType = PRIV_OBJ_TBL; |
2079 | 0 | } |
2080 | | |
2081 | | // Check CREATE TABLE privilege on the database of pTbName. |
2082 | | // Uses the same sync/async/cache-miss logic as checkAuth(). |
2083 | 0 | static int32_t checkCreateTableAuth(SParseContext* pCxt, SName* pTbName, bool* pMissCache) { |
2084 | 0 | SUserAuthInfo authInfo = {.userId = pCxt->userId, |
2085 | 0 | .tbName = {.acctId = pTbName->acctId, .type = TSDB_DB_NAME_T}, |
2086 | 0 | .privType = PRIV_TBL_CREATE, |
2087 | 0 | .objType = PRIV_OBJ_DB}; |
2088 | 0 | (void)snprintf(authInfo.user, sizeof(authInfo.user), "%s", pCxt->pUser); |
2089 | 0 | (void)snprintf(authInfo.tbName.dbname, sizeof(authInfo.tbName.dbname), "%s", pTbName->dbname); |
2090 | |
|
2091 | 0 | SUserAuthRes authRes = {0}; |
2092 | 0 | SUserAuthRsp authRsp = {.exists = 1}; |
2093 | 0 | int32_t code = 0; |
2094 | 0 | if (pCxt->async) { |
2095 | 0 | code = catalogChkAuthFromCache(pCxt->pCatalog, &authInfo, &authRes, &authRsp); |
2096 | 0 | if (TSDB_CODE_SUCCESS == code && 0 == authRsp.exists) { |
2097 | 0 | if(pMissCache) *pMissCache = true; |
2098 | 0 | return TSDB_CODE_SUCCESS; |
2099 | 0 | } |
2100 | 0 | } else { |
2101 | 0 | SRequestConnInfo conn = {.pTrans = pCxt->pTransporter, |
2102 | 0 | .requestId = pCxt->requestId, |
2103 | 0 | .requestObjRefId = pCxt->requestRid, |
2104 | 0 | .mgmtEps = pCxt->mgmtEpSet}; |
2105 | 0 | code = catalogChkAuth(pCxt->pCatalog, &conn, &authInfo, &authRes); |
2106 | 0 | } |
2107 | 0 | if (TSDB_CODE_SUCCESS == code && !authRes.pass[AUTH_RES_BASIC]) { |
2108 | 0 | code = TSDB_CODE_PAR_PERMISSION_DENIED; |
2109 | 0 | } |
2110 | 0 | return code; |
2111 | 0 | } |
2112 | | |
2113 | | static int32_t checkAuth(SParseContext* pCxt, SName* pTbName, bool isAudit, bool* pMissCache, bool* pWithInsertCond, SNode** pTagCond, |
2114 | 0 | SArray** pPrivCols) { |
2115 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
2116 | 0 | SUserAuthInfo authInfo = {0}; |
2117 | 0 | setUserAuthInfo(pCxt, pTbName, &authInfo); |
2118 | 0 | if (isAudit) { |
2119 | 0 | authInfo.privType = PRIV_AUDIT_TBL_INSERT; |
2120 | 0 | authInfo.objType = PRIV_OBJ_CLUSTER; |
2121 | 0 | } |
2122 | 0 | SUserAuthRes authRes = {0}; |
2123 | 0 | SUserAuthRsp authRsp = {.exists = 1}; |
2124 | 0 | if (pCxt->async) { |
2125 | 0 | code = catalogChkAuthFromCache(pCxt->pCatalog, &authInfo, &authRes, &authRsp); |
2126 | 0 | } else { |
2127 | 0 | SRequestConnInfo conn = {.pTrans = pCxt->pTransporter, |
2128 | 0 | .requestId = pCxt->requestId, |
2129 | 0 | .requestObjRefId = pCxt->requestRid, |
2130 | 0 | .mgmtEps = pCxt->mgmtEpSet}; |
2131 | 0 | code = catalogChkAuth(pCxt->pCatalog, &conn, &authInfo, &authRes); |
2132 | 0 | } |
2133 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2134 | 0 | if (0 == authRsp.exists) { |
2135 | 0 | *pMissCache = true; |
2136 | 0 | } else if (!authRes.pass[AUTH_RES_BASIC]) { |
2137 | 0 | code = TSDB_CODE_PAR_PERMISSION_DENIED; |
2138 | 0 | } else { |
2139 | 0 | if (pTagCond && authRes.pCond[AUTH_RES_BASIC]) { |
2140 | 0 | *pTagCond = authRes.pCond[AUTH_RES_BASIC]; |
2141 | 0 | } |
2142 | 0 | if (pPrivCols && authRes.pCols) { |
2143 | 0 | *pPrivCols = authRes.pCols; |
2144 | 0 | } |
2145 | 0 | *pMissCache = false; |
2146 | 0 | } |
2147 | 0 | if (pWithInsertCond) { |
2148 | 0 | *pWithInsertCond = (authRsp.withInsertCond == 1); |
2149 | 0 | } |
2150 | 0 | } |
2151 | 0 | return code; |
2152 | 0 | } |
2153 | | |
2154 | | static int32_t getTableMeta(SInsertParseContext* pCxt, SName* pTbName, STableMeta** pTableMeta, bool* pMissCache, |
2155 | 0 | bool bUsingTable) { |
2156 | 0 | SParseContext* pComCxt = pCxt->pComCxt; |
2157 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
2158 | 0 | if (pComCxt->async) { |
2159 | 0 | if (bUsingTable) { |
2160 | 0 | code = catalogGetCachedSTableMeta(pComCxt->pCatalog, pTbName, pTableMeta); |
2161 | 0 | } else { |
2162 | 0 | code = catalogGetCachedTableMeta(pComCxt->pCatalog, pTbName, pTableMeta); |
2163 | 0 | } |
2164 | 0 | } else { |
2165 | 0 | SRequestConnInfo conn = {.pTrans = pComCxt->pTransporter, |
2166 | 0 | .requestId = pComCxt->requestId, |
2167 | 0 | .requestObjRefId = pComCxt->requestRid, |
2168 | 0 | .mgmtEps = pComCxt->mgmtEpSet}; |
2169 | 0 | if (bUsingTable) { |
2170 | 0 | code = catalogGetSTableMeta(pComCxt->pCatalog, &conn, pTbName, pTableMeta); |
2171 | 0 | } else { |
2172 | 0 | code = catalogGetTableMeta(pComCxt->pCatalog, &conn, pTbName, pTableMeta); |
2173 | 0 | } |
2174 | 0 | } |
2175 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2176 | 0 | if (NULL == *pTableMeta) { |
2177 | 0 | *pMissCache = true; |
2178 | 0 | } else if (bUsingTable && TSDB_SUPER_TABLE != (*pTableMeta)->tableType) { |
2179 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "create table only from super table is allowed"); |
2180 | 0 | } else if (((*pTableMeta)->virtualStb) || TSDB_VIRTUAL_CHILD_TABLE == (*pTableMeta)->tableType || |
2181 | 0 | TSDB_VIRTUAL_NORMAL_TABLE == (*pTableMeta)->tableType) { |
2182 | 0 | code = TSDB_CODE_VTABLE_NOT_SUPPORT_STMT; |
2183 | 0 | } |
2184 | 0 | } |
2185 | 0 | return code; |
2186 | 0 | } |
2187 | | |
2188 | 0 | static int32_t getTargetTableVgroup(SParseContext* pCxt, SVnodeModifyOpStmt* pStmt, bool isStb, bool* pMissCache) { |
2189 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
2190 | 0 | SVgroupInfo vg; |
2191 | 0 | bool exists = true; |
2192 | 0 | if (pCxt->async) { |
2193 | 0 | code = catalogGetCachedTableHashVgroup(pCxt->pCatalog, &pStmt->targetTableName, &vg, &exists); |
2194 | 0 | } else { |
2195 | 0 | SRequestConnInfo conn = {.pTrans = pCxt->pTransporter, |
2196 | 0 | .requestId = pCxt->requestId, |
2197 | 0 | .requestObjRefId = pCxt->requestRid, |
2198 | 0 | .mgmtEps = pCxt->mgmtEpSet}; |
2199 | 0 | code = catalogGetTableHashVgroup(pCxt->pCatalog, &conn, &pStmt->targetTableName, &vg); |
2200 | 0 | } |
2201 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2202 | 0 | if (exists) { |
2203 | 0 | if (isStb) { |
2204 | 0 | pStmt->pTableMeta->vgId = vg.vgId; |
2205 | 0 | } |
2206 | 0 | code = taosHashPut(pStmt->pVgroupsHashObj, (const char*)&vg.vgId, sizeof(vg.vgId), (char*)&vg, sizeof(vg)); |
2207 | 0 | } |
2208 | 0 | *pMissCache = !exists; |
2209 | 0 | } |
2210 | 0 | return code; |
2211 | 0 | } |
2212 | | |
2213 | 0 | static int32_t getTargetTableMetaAndVgroup(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, bool* pMissCache) { |
2214 | 0 | SParseContext* pComCxt = pCxt->pComCxt; |
2215 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
2216 | 0 | if (pComCxt->async) { |
2217 | 0 | { |
2218 | 0 | SVgroupInfo vg; |
2219 | 0 | code = catalogGetCachedTableVgMeta(pComCxt->pCatalog, &pStmt->targetTableName, &vg, &pStmt->pTableMeta); |
2220 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2221 | 0 | if (NULL != pStmt->pTableMeta) { |
2222 | 0 | if (pStmt->pTableMeta->tableType == TSDB_SUPER_TABLE) { |
2223 | 0 | pStmt->stbSyntax = true; |
2224 | 0 | } else { |
2225 | 0 | code = taosHashPut(pStmt->pVgroupsHashObj, (const char*)&vg.vgId, sizeof(vg.vgId), (char*)&vg, sizeof(vg)); |
2226 | 0 | } |
2227 | 0 | } |
2228 | 0 | *pMissCache = (NULL == pStmt->pTableMeta); |
2229 | 0 | } |
2230 | 0 | } |
2231 | 0 | } else { |
2232 | 0 | bool bUsingTable = false; |
2233 | 0 | code = getTableMeta(pCxt, &pStmt->targetTableName, &pStmt->pTableMeta, pMissCache, bUsingTable); |
2234 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->missCache) { |
2235 | 0 | if (TSDB_SUPER_TABLE == pStmt->pTableMeta->tableType) { |
2236 | 0 | pStmt->stbSyntax = true; |
2237 | 0 | } |
2238 | 0 | if (!pStmt->stbSyntax) { |
2239 | 0 | code = getTargetTableVgroup(pCxt->pComCxt, pStmt, false, &pCxt->missCache); |
2240 | 0 | } |
2241 | 0 | } |
2242 | 0 | } |
2243 | 0 | return code; |
2244 | 0 | } |
2245 | | |
2246 | 0 | static int32_t collectUseTable(const SName* pName, SHashObj* pTable) { |
2247 | 0 | char fullName[TSDB_TABLE_FNAME_LEN]; |
2248 | 0 | int32_t code = tNameExtractFullName(pName, fullName); |
2249 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2250 | 0 | return code; |
2251 | 0 | } |
2252 | 0 | return taosHashPut(pTable, fullName, strlen(fullName), pName, sizeof(SName)); |
2253 | 0 | } |
2254 | | |
2255 | 0 | static int32_t collectUseDatabase(const SName* pName, SHashObj* pDbs) { |
2256 | 0 | char dbFName[TSDB_DB_FNAME_LEN] = {0}; |
2257 | 0 | (void)tNameGetFullDbName(pName, dbFName); |
2258 | 0 | return taosHashPut(pDbs, dbFName, strlen(dbFName), dbFName, sizeof(dbFName)); |
2259 | 0 | } |
2260 | | |
2261 | 0 | static int32_t getTargetTableSchema(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
2262 | 0 | if (pCxt->forceUpdate) { |
2263 | 0 | pCxt->missCache = true; |
2264 | 0 | return TSDB_CODE_SUCCESS; |
2265 | 0 | } |
2266 | 0 | SNode* pTagCond = NULL; |
2267 | 0 | SArray* pPrivCols = NULL; |
2268 | 0 | bool withInsertCond = false; |
2269 | 0 | int32_t code = checkAuth(pCxt->pComCxt, &pStmt->targetTableName, false, &pCxt->missCache, &withInsertCond, &pTagCond, |
2270 | 0 | &pPrivCols); |
2271 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->missCache) { |
2272 | 0 | code = getTargetTableMetaAndVgroup(pCxt, pStmt, &pCxt->missCache); |
2273 | 0 | } |
2274 | | #ifdef TD_ENTERPRISE |
2275 | | // MAC NWD+NRU: for INSERT, user.minSecLevel <= table.secLvl <= user.maxSecLevel |
2276 | | // Only enforced when MAC is explicitly activated cluster-wide. |
2277 | | // Logic mirrors macCheckBySecLvl() in parAuthenticator.c (inline here because SInsertParseContext |
2278 | | // does not carry an SAuthCxt). |
2279 | | if (pCxt->pComCxt->macMode && TSDB_CODE_SUCCESS == code && !pCxt->missCache && pStmt->pTableMeta != NULL) { |
2280 | | int8_t secLvl = pStmt->pTableMeta->secLvl; |
2281 | | if (secLvl >= 0) { |
2282 | | if (pCxt->pComCxt->minSecLevel > secLvl) { |
2283 | | code = TSDB_CODE_MAC_NO_WRITE_DOWN; // NWD violation |
2284 | | } else if (pCxt->pComCxt->maxSecLevel < secLvl) { |
2285 | | code = TSDB_CODE_MAC_INSUFFICIENT_LEVEL; // NRU violation |
2286 | | } |
2287 | | } |
2288 | | } |
2289 | | #endif |
2290 | |
|
2291 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2292 | 0 | if (pPrivCols) pStmt->pPrivCols = pPrivCols; |
2293 | | #ifdef TD_ENTERPRISE |
2294 | | #if 0 |
2295 | | if (pStmt->pTableMeta && pStmt->pTableMeta->isAudit) { |
2296 | | // recheck for audit table |
2297 | | code = checkAuth(pCxt->pComCxt, &pStmt->targetTableName, true, &pCxt->missCache, NULL, NULL, NULL); |
2298 | | if (TSDB_CODE_SUCCESS != code) { |
2299 | | nodesDestroyNode(pTagCond); |
2300 | | return code; |
2301 | | } |
2302 | | } |
2303 | | #endif |
2304 | | if (!pCxt->missCache) { |
2305 | | if (TSDB_SUPER_TABLE != pStmt->pTableMeta->tableType) { |
2306 | | pCxt->needTableTagVal = (NULL != pTagCond); |
2307 | | pCxt->missCache = (NULL != pTagCond); |
2308 | | } else { |
2309 | | pStmt->pTagCond = pTagCond; |
2310 | | pTagCond = NULL; |
2311 | | } |
2312 | | } else if (withInsertCond && !pCxt->pComCxt->isSuperUser) { |
2313 | | // If miss cache, always request tag value and reserved for permission check later if pTagCond exists. This may |
2314 | | // lead to redundant request but ensure correctness, and this only happens when cache is invalid or first time |
2315 | | // insert. |
2316 | | pCxt->needTableTagVal = true; |
2317 | | } |
2318 | | #else |
2319 | 0 | pStmt->pTagCond = NULL; |
2320 | 0 | #endif |
2321 | 0 | } |
2322 | 0 | if (pTagCond) nodesDestroyNode(pTagCond); |
2323 | |
|
2324 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->pComCxt->async) { |
2325 | 0 | code = collectUseDatabase(&pStmt->targetTableName, pStmt->pDbFNameHashObj); |
2326 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2327 | 0 | code = collectUseTable(&pStmt->targetTableName, pStmt->pTableNameHashObj); |
2328 | 0 | } |
2329 | 0 | } |
2330 | 0 | return code; |
2331 | 0 | } |
2332 | | |
2333 | 0 | static int32_t preParseUsingTableName(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SToken* pTbName) { |
2334 | 0 | return insCreateSName(&pStmt->usingTableName, pTbName, pCxt->pComCxt->acctId, pCxt->pComCxt->db, &pCxt->msg); |
2335 | 0 | } |
2336 | | |
2337 | 0 | static int32_t getUsingTableSchema(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, bool* ctbCacheHit) { |
2338 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
2339 | 0 | STableMeta* pStableMeta = NULL; |
2340 | 0 | STableMeta* pCtableMeta = NULL; |
2341 | 0 | if (pCxt->forceUpdate) { |
2342 | 0 | pCxt->missCache = true; |
2343 | 0 | return TSDB_CODE_SUCCESS; |
2344 | 0 | } |
2345 | 0 | char stableFName[TSDB_TABLE_FNAME_LEN]; |
2346 | 0 | code = tNameExtractFullName(&pStmt->usingTableName, stableFName); |
2347 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2348 | 0 | return code; |
2349 | 0 | } |
2350 | | |
2351 | 0 | char ctableFName[TSDB_TABLE_FNAME_LEN]; |
2352 | 0 | code = tNameExtractFullName(&pStmt->targetTableName, ctableFName); |
2353 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2354 | 0 | return code; |
2355 | 0 | } |
2356 | | |
2357 | 0 | if (strcmp(stableFName, ctableFName) == 0) { |
2358 | 0 | return TSDB_CODE_TDB_TABLE_IN_OTHER_STABLE; |
2359 | 0 | } |
2360 | 0 | if (!pCxt->missCache) { |
2361 | 0 | char tbFName[TSDB_TABLE_FNAME_LEN]; |
2362 | 0 | code = tNameExtractFullName(&pStmt->usingTableName, tbFName); |
2363 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2364 | 0 | return code; |
2365 | 0 | } |
2366 | 0 | STableMeta** ppStableMeta = taosHashGet(pStmt->pSuperTableHashObj, tbFName, strlen(tbFName)); |
2367 | 0 | if (NULL != ppStableMeta) { |
2368 | 0 | pStableMeta = *ppStableMeta; |
2369 | 0 | } |
2370 | 0 | if (NULL == pStableMeta) { |
2371 | 0 | bool bUsingTable = true; |
2372 | 0 | code = getTableMeta(pCxt, &pStmt->usingTableName, &pStableMeta, &pCxt->missCache, bUsingTable); |
2373 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2374 | 0 | code = taosHashPut(pStmt->pSuperTableHashObj, tbFName, strlen(tbFName), &pStableMeta, POINTER_BYTES); |
2375 | 0 | } else { |
2376 | 0 | taosMemoryFreeClear(pStableMeta); |
2377 | 0 | } |
2378 | 0 | } |
2379 | 0 | } |
2380 | 0 | if (pCxt->pComCxt->stmtBindVersion > 0) { |
2381 | 0 | goto _no_ctb_cache; |
2382 | 0 | } |
2383 | | |
2384 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->missCache) { |
2385 | 0 | bool bUsingTable = false; |
2386 | 0 | code = getTableMeta(pCxt, &pStmt->targetTableName, &pCtableMeta, &pCxt->missCache, bUsingTable); |
2387 | 0 | } |
2388 | |
|
2389 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->missCache) { |
2390 | 0 | code = (pStableMeta->suid == pCtableMeta->suid) ? TSDB_CODE_SUCCESS : TSDB_CODE_TDB_TABLE_IN_OTHER_STABLE; |
2391 | 0 | *ctbCacheHit = true; |
2392 | 0 | } |
2393 | 0 | _no_ctb_cache: |
2394 | | // Check INSERT privilege on the super table. |
2395 | | // For the non-USING path this is done in getTargetTableSchema() → checkAuth(). We mirror it here. |
2396 | | // async second-round coverage: checkAuthFromMetaData() only runs when *pQuery != NULL (second round); |
2397 | | // the first-round sync path (createInsertQuery) performs no auth, so this call is always needed. |
2398 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->missCache) { |
2399 | 0 | bool missCache = false; |
2400 | 0 | SNode* pTagCond = NULL; |
2401 | 0 | SArray* pPrivCols = NULL; |
2402 | 0 | bool withInsertCond = false; |
2403 | 0 | code = checkAuth(pCxt->pComCxt, &pStmt->usingTableName, false, &missCache, &withInsertCond, &pTagCond, &pPrivCols); |
2404 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2405 | 0 | if (!missCache) { |
2406 | | // Propagate row-condition and column-privilege to pStmt, exactly as getTargetTableSchema() does, |
2407 | | // so that tag-value privilege checks (checkSubtablePrivilege) and column-level INSERT |
2408 | | // restrictions are enforced for the super table on the USING write path. |
2409 | 0 | if (pPrivCols) pStmt->pPrivCols = pPrivCols; |
2410 | 0 | pStmt->pTagCond = pTagCond; |
2411 | 0 | if (withInsertCond && !pCxt->pComCxt->isSuperUser && pTagCond == NULL) { |
2412 | 0 | pCxt->needTableTagVal = true; |
2413 | 0 | } |
2414 | 0 | } else { |
2415 | 0 | pCxt->missCache = true; |
2416 | 0 | nodesDestroyNode(pTagCond); |
2417 | 0 | taosArrayDestroy(pPrivCols); |
2418 | 0 | } |
2419 | 0 | } |
2420 | 0 | } |
2421 | | // ctb does not exist: will be auto-created. Check CREATE TABLE privilege on the db. |
2422 | | // !missCache guard: async cache-miss defers to the second round (processTableSchemaFromMetaData). |
2423 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->missCache && !(*ctbCacheHit)) { |
2424 | 0 | code = checkCreateTableAuth(pCxt->pComCxt, &pStmt->targetTableName, &pCxt->missCache); |
2425 | 0 | } |
2426 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2427 | 0 | if (*ctbCacheHit) { |
2428 | 0 | code = cloneTableMeta(pCtableMeta, &pStmt->pTableMeta); |
2429 | 0 | } else { |
2430 | 0 | code = cloneTableMeta(pStableMeta, &pStmt->pTableMeta); |
2431 | 0 | } |
2432 | 0 | } |
2433 | 0 | taosMemoryFree(pCtableMeta); |
2434 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->missCache) { |
2435 | 0 | code = getTargetTableVgroup(pCxt->pComCxt, pStmt, true, &pCxt->missCache); |
2436 | 0 | } |
2437 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->pComCxt->async) { |
2438 | 0 | code = collectUseDatabase(&pStmt->usingTableName, pStmt->pDbFNameHashObj); |
2439 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2440 | 0 | code = collectUseTable(&pStmt->usingTableName, pStmt->pTableNameHashObj); |
2441 | 0 | } |
2442 | 0 | } |
2443 | 0 | return code; |
2444 | 0 | } |
2445 | | |
2446 | 0 | static int32_t parseUsingTableNameImpl(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
2447 | 0 | SToken token; |
2448 | 0 | NEXT_TOKEN(pStmt->pSql, token); |
2449 | 0 | bool ctbCacheHit = false; |
2450 | 0 | int32_t code = preParseUsingTableName(pCxt, pStmt, &token); |
2451 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2452 | 0 | code = getUsingTableSchema(pCxt, pStmt, &ctbCacheHit); |
2453 | 0 | if (TSDB_CODE_SUCCESS == code && ctbCacheHit && !pCxt->missCache) { |
2454 | 0 | pStmt->usingTableProcessing = false; |
2455 | 0 | return ignoreUsingClauseAndCheckTagValues(pCxt, pStmt); |
2456 | 0 | } |
2457 | 0 | } |
2458 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->missCache) { |
2459 | 0 | code = storeChildTableMeta(pCxt, pStmt); |
2460 | 0 | } |
2461 | 0 | return code; |
2462 | 0 | } |
2463 | | |
2464 | | // input pStmt->pSql: |
2465 | | // 1(care). [USING stb_name [(tag1_name, ...)] TAGS (tag1_value, ...) [table_options]] ... |
2466 | | // 2. VALUES ... | FILE ... |
2467 | | // output pStmt->pSql: |
2468 | | // 1. [(tag1_name, ...)] TAGS (tag1_value, ...) [table_options]] ... |
2469 | | // 2. VALUES ... | FILE ... |
2470 | 0 | static int32_t parseUsingTableName(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
2471 | 0 | SToken token; |
2472 | 0 | int32_t index = 0; |
2473 | 0 | NEXT_TOKEN_KEEP_SQL(pStmt->pSql, token, index); |
2474 | 0 | if (TK_USING != token.type) { |
2475 | 0 | return getTargetTableSchema(pCxt, pStmt); |
2476 | 0 | } |
2477 | 0 | pStmt->usingTableProcessing = true; |
2478 | 0 | pCxt->stmtTbNameFlag |= USING_CLAUSE; |
2479 | | // pStmt->pSql -> stb_name [(tag1_name, ...) |
2480 | 0 | pStmt->pSql += index; |
2481 | 0 | int32_t code = parseDuplicateUsingClause(pCxt, pStmt, &pCxt->usingDuplicateTable); |
2482 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->usingDuplicateTable) { |
2483 | 0 | return parseUsingTableNameImpl(pCxt, pStmt); |
2484 | 0 | } |
2485 | 0 | return code; |
2486 | 0 | } |
2487 | | |
2488 | 0 | static int32_t preParseTargetTableName(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SToken* pTbName) { |
2489 | 0 | int32_t code = insCreateSName(&pStmt->targetTableName, pTbName, pCxt->pComCxt->acctId, pCxt->pComCxt->db, &pCxt->msg); |
2490 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2491 | 0 | if (IS_SYS_DBNAME(pStmt->targetTableName.dbname)) { |
2492 | 0 | return TSDB_CODE_PAR_SYSTABLE_NOT_ALLOWED; |
2493 | 0 | } |
2494 | | #ifdef TD_ENTERPRISE |
2495 | | // Fast-path: if ext source cache is already populated (warm connection), |
2496 | | // reject immediately without needing Phase 2 catalog fetch. |
2497 | | if (tsFederatedQueryEnable && pStmt->targetTableName.dbname[0] != '\0' && pCxt->pComCxt->pCatalog) { |
2498 | | bool isExtSrc = false; |
2499 | | int32_t rc = catalogIsExtSource(pCxt->pComCxt->pCatalog, pStmt->targetTableName.dbname, &isExtSrc); |
2500 | | if (TSDB_CODE_SUCCESS == rc && isExtSrc) { |
2501 | | return TSDB_CODE_EXT_SOURCE_WRITE_NOT_SUPPORTED; |
2502 | | } |
2503 | | } |
2504 | | #endif |
2505 | 0 | } |
2506 | | |
2507 | 0 | return code; |
2508 | 0 | } |
2509 | | |
2510 | | // input pStmt->pSql: |
2511 | | // 1(care). [(field1_name, ...)] ... |
2512 | | // 2. [ USING ... ] ... |
2513 | | // 3. VALUES ... | FILE ... |
2514 | | // output pStmt->pSql: |
2515 | | // 1. [ USING ... ] ... |
2516 | | // 2. VALUES ... | FILE ... |
2517 | 0 | static int32_t preParseBoundColumnsClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
2518 | 0 | SToken token; |
2519 | 0 | int32_t index = 0; |
2520 | 0 | NEXT_TOKEN_KEEP_SQL(pStmt->pSql, token, index); |
2521 | 0 | if (TK_NK_LP != token.type) { |
2522 | 0 | return TSDB_CODE_SUCCESS; |
2523 | 0 | } |
2524 | | |
2525 | | // pStmt->pSql -> field1_name, ...) |
2526 | 0 | pStmt->pSql += index; |
2527 | 0 | pStmt->pBoundCols = pStmt->pSql; |
2528 | 0 | return skipParentheses(pCxt, &pStmt->pSql); |
2529 | 0 | } |
2530 | | |
2531 | 0 | static int32_t getTableDataCxt(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, STableDataCxt** pTableCxt) { |
2532 | 0 | if (pCxt->pComCxt->async) { |
2533 | 0 | return insGetTableDataCxt(pStmt->pTableBlockHashObj, &pStmt->pTableMeta->uid, sizeof(pStmt->pTableMeta->uid), |
2534 | 0 | pStmt->pTableMeta, &pStmt->pCreateTblReq, pTableCxt, false, false); |
2535 | 0 | } |
2536 | | |
2537 | 0 | char tbFName[TSDB_TABLE_FNAME_LEN]; |
2538 | 0 | int32_t code = 0; |
2539 | 0 | if ((pCxt->stmtTbNameFlag & NO_DATA_USING_CLAUSE) == USING_CLAUSE) { |
2540 | 0 | tstrncpy(pStmt->targetTableName.tname, pStmt->usingTableName.tname, sizeof(pStmt->targetTableName.tname)); |
2541 | 0 | tstrncpy(pStmt->targetTableName.dbname, pStmt->usingTableName.dbname, sizeof(pStmt->targetTableName.dbname)); |
2542 | 0 | pStmt->targetTableName.type = TSDB_SUPER_TABLE; |
2543 | 0 | pStmt->pTableMeta->tableType = TSDB_SUPER_TABLE; |
2544 | 0 | } |
2545 | |
|
2546 | 0 | code = tNameExtractFullName(&pStmt->targetTableName, tbFName); |
2547 | |
|
2548 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2549 | 0 | return code; |
2550 | 0 | } |
2551 | 0 | if (pStmt->usingTableProcessing) { |
2552 | 0 | pStmt->pTableMeta->uid = 0; |
2553 | 0 | } |
2554 | 0 | return insGetTableDataCxt(pStmt->pTableBlockHashObj, tbFName, strlen(tbFName), pStmt->pTableMeta, |
2555 | 0 | &pStmt->pCreateTblReq, pTableCxt, NULL != pCxt->pComCxt->pStmtCb, false); |
2556 | 0 | } |
2557 | | #ifdef TD_ENTERPRISE |
2558 | | static int32_t parseCheckNoBoundColPriv(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
2559 | | int32_t nPrivs = taosArrayGetSize(pStmt->pPrivCols); |
2560 | | if (nPrivs <= 0) { |
2561 | | return TSDB_CODE_SUCCESS; |
2562 | | } |
2563 | | int32_t nCols = pStmt->pTableMeta->tableInfo.numOfColumns; |
2564 | | /** |
2565 | | * It cann't provide the specific missing column info although with better performance. For better UE, we check each |
2566 | | * column one by one. |
2567 | | */ |
2568 | | // if (nPrivs < nCols) return TSDB_CODE_PAR_COL_PERMISSION_DENIED; |
2569 | | |
2570 | | // both the schema and privCols are ordered by colId asc |
2571 | | SSchema* pSchemas = pStmt->pTableMeta->schema; |
2572 | | int32_t j = 0; |
2573 | | for (int32_t i = 0; i < nCols; ++i) { |
2574 | | SSchema* pSchema = &pSchemas[i]; |
2575 | | bool found = false; |
2576 | | for (; j < nPrivs; ++j) { |
2577 | | SColNameFlag* pColFlag = (SColNameFlag*)TARRAY_GET_ELEM(pStmt->pPrivCols, j); |
2578 | | if (pColFlag->colId == pSchema->colId) { |
2579 | | found = true; |
2580 | | ++j; |
2581 | | break; |
2582 | | } else if (pColFlag->colId > pSchema->colId) { |
2583 | | break; |
2584 | | } |
2585 | | } |
2586 | | |
2587 | | if (!found) { |
2588 | | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_COL_PERMISSION_DENIED, pSchema->name); |
2589 | | } |
2590 | | } |
2591 | | |
2592 | | return TSDB_CODE_SUCCESS; |
2593 | | } |
2594 | | #endif |
2595 | | |
2596 | 0 | static int32_t parseBoundColumnsClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, STableDataCxt* pTableCxt) { |
2597 | 0 | SToken token; |
2598 | 0 | int32_t index = 0; |
2599 | 0 | NEXT_TOKEN_KEEP_SQL(pStmt->pSql, token, index); |
2600 | 0 | if (TK_NK_LP == token.type) { |
2601 | 0 | pStmt->pSql += index; |
2602 | 0 | if (NULL != pStmt->pBoundCols) { |
2603 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "keyword VALUES or FILE is expected", token.z); |
2604 | 0 | } |
2605 | | // pStmt->pSql -> field1_name, ...) |
2606 | 0 | return parseBoundColumns(pCxt, pStmt, &pStmt->pSql, BOUND_COLUMNS, pStmt->pTableMeta, &pTableCxt->boundColsInfo); |
2607 | 0 | } |
2608 | | |
2609 | 0 | if (NULL != pStmt->pBoundCols) { |
2610 | 0 | return parseBoundColumns(pCxt, pStmt, &pStmt->pBoundCols, BOUND_COLUMNS, pStmt->pTableMeta, |
2611 | 0 | &pTableCxt->boundColsInfo); |
2612 | 0 | } else if (pTableCxt->boundColsInfo.hasBoundCols) { |
2613 | 0 | insResetBoundColsInfo(&pTableCxt->boundColsInfo); |
2614 | 0 | } else if (NULL != pStmt->pPrivCols) { |
2615 | | #ifdef TD_ENTERPRISE |
2616 | | PAR_ERR_RET(parseCheckNoBoundColPriv(pCxt, pStmt)); |
2617 | | #endif |
2618 | 0 | } |
2619 | | |
2620 | | |
2621 | 0 | return TSDB_CODE_SUCCESS; |
2622 | 0 | } |
2623 | | |
2624 | 0 | int32_t initTableColSubmitData(STableDataCxt* pTableCxt) { |
2625 | 0 | if (0 == (pTableCxt->pData->flags & SUBMIT_REQ_COLUMN_DATA_FORMAT)) { |
2626 | 0 | return TSDB_CODE_SUCCESS; |
2627 | 0 | } |
2628 | | |
2629 | | /* Clear any existing aCol entries before adding new ones. |
2630 | | * The entry may have been retained across exec cycles (keepTable |
2631 | | * path) and still hold deep-cleared SColData entries whose nVal |
2632 | | * was zeroed by qResetStmtDataBlock. Without this clear, |
2633 | | * taosArrayReserve appends to the existing entries, doubling |
2634 | | * nColData and producing a corrupt submit request. */ |
2635 | 0 | if (pTableCxt->pData->aCol) { |
2636 | 0 | taosArrayClear(pTableCxt->pData->aCol); |
2637 | 0 | } |
2638 | |
|
2639 | 0 | for (int32_t i = 0; i < pTableCxt->boundColsInfo.numOfBound; ++i) { |
2640 | 0 | SSchema* pSchema = &pTableCxt->pMeta->schema[pTableCxt->boundColsInfo.pColIndex[i]]; |
2641 | 0 | SColData* pCol = taosArrayReserve(pTableCxt->pData->aCol, 1); |
2642 | 0 | if (NULL == pCol) { |
2643 | 0 | return terrno; |
2644 | 0 | } |
2645 | 0 | tColDataInit(pCol, pSchema->colId, pSchema->type, pSchema->flags); |
2646 | 0 | } |
2647 | | |
2648 | 0 | return TSDB_CODE_SUCCESS; |
2649 | 0 | } |
2650 | | |
2651 | 0 | int32_t initTableColSubmitDataWithBoundInfo(STableDataCxt* pTableCxt, SBoundColInfo pBoundColsInfo) { |
2652 | 0 | qDestroyBoundColInfo(&(pTableCxt->boundColsInfo)); |
2653 | 0 | pTableCxt->boundColsInfo = pBoundColsInfo; |
2654 | 0 | if (pTableCxt->pData->aCol) { |
2655 | 0 | taosArrayClear(pTableCxt->pData->aCol); |
2656 | 0 | } |
2657 | 0 | for (int32_t i = 0; i < pBoundColsInfo.numOfBound; ++i) { |
2658 | 0 | SSchema* pSchema = &pTableCxt->pMeta->schema[pTableCxt->boundColsInfo.pColIndex[i]]; |
2659 | 0 | SColData* pCol = taosArrayReserve(pTableCxt->pData->aCol, 1); |
2660 | 0 | if (NULL == pCol) { |
2661 | 0 | return terrno; |
2662 | 0 | } |
2663 | 0 | tColDataInit(pCol, pSchema->colId, pSchema->type, pSchema->flags); |
2664 | 0 | } |
2665 | | |
2666 | 0 | return TSDB_CODE_SUCCESS; |
2667 | 0 | } |
2668 | | |
2669 | | // input pStmt->pSql: |
2670 | | // 1. [(tag1_name, ...)] ... |
2671 | | // 2. VALUES ... | FILE ... |
2672 | | // output pStmt->pSql: VALUES ... | FILE ... |
2673 | | static int32_t parseSchemaClauseBottom(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, |
2674 | 0 | STableDataCxt** pTableCxt) { |
2675 | 0 | int32_t code = parseUsingClauseBottom(pCxt, pStmt); |
2676 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2677 | 0 | code = getTableDataCxt(pCxt, pStmt, pTableCxt); |
2678 | 0 | } |
2679 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2680 | 0 | code = parseBoundColumnsClause(pCxt, pStmt, *pTableCxt); |
2681 | 0 | } |
2682 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2683 | 0 | code = initTableColSubmitData(*pTableCxt); |
2684 | 0 | } |
2685 | 0 | return code; |
2686 | 0 | } |
2687 | | |
2688 | | // input pStmt->pSql: [(field1_name, ...)] [ USING ... ] VALUES ... | FILE ... |
2689 | | // output pStmt->pSql: |
2690 | | // 1. [(tag1_name, ...)] ... |
2691 | | // 2. VALUES ... | FILE ... |
2692 | 0 | static int32_t parseSchemaClauseTop(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SToken* pTbName) { |
2693 | 0 | int32_t code = preParseTargetTableName(pCxt, pStmt, pTbName); |
2694 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2695 | | // option: [(field1_name, ...)] |
2696 | 0 | code = preParseBoundColumnsClause(pCxt, pStmt); |
2697 | 0 | } |
2698 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2699 | | // option: [USING stb_name] |
2700 | 0 | code = parseUsingTableName(pCxt, pStmt); |
2701 | 0 | } |
2702 | 0 | return code; |
2703 | 0 | } |
2704 | | |
2705 | | static int32_t parseValueTokenImpl(SInsertParseContext* pCxt, const char** pSql, SToken* pToken, SSchema* pSchema, |
2706 | 0 | const SSchemaExt* pExtSchema, int16_t timePrec, SColVal* pVal) { |
2707 | 0 | switch (pSchema->type) { |
2708 | 0 | case TSDB_DATA_TYPE_BOOL: { |
2709 | 0 | if ((pToken->type == TK_NK_BOOL || pToken->type == TK_NK_STRING) && (pToken->n != 0)) { |
2710 | 0 | if (IS_TRUE_STR(pToken->z, pToken->n)) { |
2711 | 0 | VALUE_SET_TRIVIAL_DATUM(&pVal->value, TRUE_VALUE); |
2712 | 0 | } else if (IS_FALSE_STR(pToken->z, pToken->n)) { |
2713 | 0 | VALUE_SET_TRIVIAL_DATUM(&pVal->value, FALSE_VALUE); |
2714 | 0 | } else if (TSDB_CODE_SUCCESS == |
2715 | 0 | toDoubleEx(pToken->z, pToken->n, pToken->type, (double*)&VALUE_GET_TRIVIAL_DATUM(&pVal->value))) { |
2716 | 0 | int8_t v = (*(double*)&VALUE_GET_TRIVIAL_DATUM(&pVal->value) == 0 ? FALSE_VALUE : TRUE_VALUE); |
2717 | 0 | valueSetDatum(&pVal->value, TSDB_DATA_TYPE_BOOL, &v, tDataTypes[TSDB_DATA_TYPE_BOOL].bytes); |
2718 | 0 | } else { |
2719 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid bool data", pToken->z); |
2720 | 0 | } |
2721 | 0 | } else if (pToken->type == TK_NK_INTEGER) { |
2722 | 0 | int8_t v = ((taosStr2Int64(pToken->z, NULL, 10) == 0) ? FALSE_VALUE : TRUE_VALUE); |
2723 | 0 | VALUE_SET_TRIVIAL_DATUM(&pVal->value, v); |
2724 | 0 | } else if (pToken->type == TK_NK_FLOAT) { |
2725 | 0 | int8_t v = ((taosStr2Double(pToken->z, NULL) == 0) ? FALSE_VALUE : TRUE_VALUE); |
2726 | 0 | VALUE_SET_TRIVIAL_DATUM(&pVal->value, v); |
2727 | 0 | } else if ((pToken->type == TK_NK_HEX || pToken->type == TK_NK_BIN) && |
2728 | 0 | (TSDB_CODE_SUCCESS == |
2729 | 0 | toDoubleEx(pToken->z, pToken->n, pToken->type, (double*)&VALUE_GET_TRIVIAL_DATUM(&pVal->value)))) { |
2730 | 0 | int8_t v = *(double*)&VALUE_GET_TRIVIAL_DATUM(&pVal->value) == 0 ? FALSE_VALUE : TRUE_VALUE; |
2731 | 0 | valueSetDatum(&pVal->value, TSDB_DATA_TYPE_BOOL, &v, tDataTypes[TSDB_DATA_TYPE_BOOL].bytes); |
2732 | 0 | } else { |
2733 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid bool data", pToken->z); |
2734 | 0 | } |
2735 | 0 | break; |
2736 | 0 | } |
2737 | 0 | case TSDB_DATA_TYPE_TINYINT: { |
2738 | 0 | int32_t code = toIntegerEx(pToken->z, pToken->n, pToken->type, &VALUE_GET_TRIVIAL_DATUM(&pVal->value)); |
2739 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2740 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid tinyint data", pToken->z); |
2741 | 0 | } else if (!IS_VALID_TINYINT(VALUE_GET_TRIVIAL_DATUM(&pVal->value))) { |
2742 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "tinyint data overflow", pToken->z); |
2743 | 0 | } |
2744 | 0 | break; |
2745 | 0 | } |
2746 | 0 | case TSDB_DATA_TYPE_UTINYINT: { |
2747 | 0 | int32_t code = |
2748 | 0 | toUIntegerEx(pToken->z, pToken->n, pToken->type, (uint64_t*)&VALUE_GET_TRIVIAL_DATUM(&pVal->value)); |
2749 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2750 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned tinyint data", pToken->z); |
2751 | 0 | } else if (VALUE_GET_TRIVIAL_DATUM(&pVal->value) > UINT8_MAX) { |
2752 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "unsigned tinyint data overflow", pToken->z); |
2753 | 0 | } |
2754 | 0 | break; |
2755 | 0 | } |
2756 | 0 | case TSDB_DATA_TYPE_SMALLINT: { |
2757 | 0 | int32_t code = toIntegerEx(pToken->z, pToken->n, pToken->type, &VALUE_GET_TRIVIAL_DATUM(&pVal->value)); |
2758 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2759 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid smallint data", pToken->z); |
2760 | 0 | } else if (!IS_VALID_SMALLINT(VALUE_GET_TRIVIAL_DATUM(&pVal->value))) { |
2761 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "smallint data overflow", pToken->z); |
2762 | 0 | } |
2763 | 0 | break; |
2764 | 0 | } |
2765 | 0 | case TSDB_DATA_TYPE_USMALLINT: { |
2766 | 0 | int32_t code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &VALUE_GET_TRIVIAL_DATUM(&pVal->value)); |
2767 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2768 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned smallint data", pToken->z); |
2769 | 0 | } else if (VALUE_GET_TRIVIAL_DATUM(&pVal->value) > UINT16_MAX) { |
2770 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "unsigned smallint data overflow", pToken->z); |
2771 | 0 | } |
2772 | 0 | break; |
2773 | 0 | } |
2774 | 0 | case TSDB_DATA_TYPE_INT: { |
2775 | 0 | int32_t code = toIntegerEx(pToken->z, pToken->n, pToken->type, &VALUE_GET_TRIVIAL_DATUM(&pVal->value)); |
2776 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2777 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid int data", pToken->z); |
2778 | 0 | } else if (!IS_VALID_INT(VALUE_GET_TRIVIAL_DATUM(&pVal->value))) { |
2779 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "int data overflow", pToken->z); |
2780 | 0 | } |
2781 | 0 | break; |
2782 | 0 | } |
2783 | 0 | case TSDB_DATA_TYPE_UINT: { |
2784 | 0 | int32_t code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &VALUE_GET_TRIVIAL_DATUM(&pVal->value)); |
2785 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2786 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned int data", pToken->z); |
2787 | 0 | } else if (VALUE_GET_TRIVIAL_DATUM(&pVal->value) > UINT32_MAX) { |
2788 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "unsigned int data overflow", pToken->z); |
2789 | 0 | } |
2790 | 0 | break; |
2791 | 0 | } |
2792 | 0 | case TSDB_DATA_TYPE_BIGINT: { |
2793 | 0 | int32_t code = toIntegerEx(pToken->z, pToken->n, pToken->type, &VALUE_GET_TRIVIAL_DATUM(&pVal->value)); |
2794 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2795 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid bigint data", pToken->z); |
2796 | 0 | } |
2797 | 0 | break; |
2798 | 0 | } |
2799 | 0 | case TSDB_DATA_TYPE_UBIGINT: { |
2800 | 0 | int32_t code = toUIntegerEx(pToken->z, pToken->n, pToken->type, &VALUE_GET_TRIVIAL_DATUM(&pVal->value)); |
2801 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2802 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid unsigned bigint data", pToken->z); |
2803 | 0 | } |
2804 | 0 | break; |
2805 | 0 | } |
2806 | 0 | case TSDB_DATA_TYPE_FLOAT: { |
2807 | 0 | double dv; |
2808 | 0 | int32_t code = toDoubleEx(pToken->z, pToken->n, pToken->type, &dv); |
2809 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2810 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "illegal float data", pToken->z); |
2811 | 0 | } |
2812 | 0 | if (dv > FLT_MAX || dv < -FLT_MAX || isinf(dv) || isnan(dv)) { |
2813 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "illegal float data", pToken->z); |
2814 | 0 | } |
2815 | 0 | float f = dv; |
2816 | 0 | valueSetDatum(&pVal->value, TSDB_DATA_TYPE_FLOAT, &f, sizeof(f)); |
2817 | 0 | break; |
2818 | 0 | } |
2819 | 0 | case TSDB_DATA_TYPE_DOUBLE: { |
2820 | 0 | double dv; |
2821 | 0 | int32_t code = toDoubleEx(pToken->z, pToken->n, pToken->type, &dv); |
2822 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2823 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "illegal float data", pToken->z); |
2824 | 0 | } |
2825 | 0 | if (isinf(dv) || isnan(dv)) { |
2826 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "illegal double data", pToken->z); |
2827 | 0 | } |
2828 | 0 | VALUE_SET_TRIVIAL_DATUM(&pVal->value, (*(int64_t*)&dv)); |
2829 | 0 | break; |
2830 | 0 | } |
2831 | 0 | case TSDB_DATA_TYPE_BINARY: { |
2832 | 0 | return parseBinary(pCxt, pSql, pToken, pVal, pSchema); |
2833 | 0 | } |
2834 | 0 | case TSDB_DATA_TYPE_VARBINARY: { |
2835 | 0 | int32_t code = parseVarbinary(pToken, &pVal->value.pData, &pVal->value.nData, pSchema->bytes); |
2836 | 0 | if (code != TSDB_CODE_SUCCESS) { |
2837 | 0 | return generateSyntaxErrMsg(&pCxt->msg, code, pSchema->name); |
2838 | 0 | } |
2839 | 0 | break; |
2840 | 0 | } |
2841 | 0 | case TSDB_DATA_TYPE_NCHAR: { |
2842 | | // if the converted output len is over than pColumnModel->bytes, return error: 'Argument list too long' |
2843 | 0 | int32_t len = 0; |
2844 | 0 | int64_t realLen = pToken->n << 2; |
2845 | 0 | if (realLen > pSchema->bytes - VARSTR_HEADER_SIZE) realLen = pSchema->bytes - VARSTR_HEADER_SIZE; |
2846 | 0 | char* pUcs4 = taosMemoryMalloc(realLen); |
2847 | 0 | if (NULL == pUcs4) { |
2848 | 0 | return terrno; |
2849 | 0 | } |
2850 | 0 | if (!taosMbsToUcs4(pToken->z, pToken->n, (TdUcs4*)pUcs4, realLen, &len, pCxt->pComCxt->charsetCxt)) { |
2851 | 0 | taosMemoryFree(pUcs4); |
2852 | 0 | if (terrno == TAOS_SYSTEM_ERROR(E2BIG)) { |
2853 | 0 | return generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
2854 | 0 | } |
2855 | 0 | char buf[512] = {0}; |
2856 | 0 | snprintf(buf, tListLen(buf), "%s", strerror(terrno)); |
2857 | 0 | return buildSyntaxErrMsg(&pCxt->msg, buf, pToken->z); |
2858 | 0 | } |
2859 | 0 | pVal->value.pData = pUcs4; |
2860 | 0 | pVal->value.nData = len; |
2861 | 0 | break; |
2862 | 0 | } |
2863 | 0 | case TSDB_DATA_TYPE_JSON: { |
2864 | 0 | if (pToken->n > (TSDB_MAX_JSON_TAG_LEN - VARSTR_HEADER_SIZE) / TSDB_NCHAR_SIZE) { |
2865 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "json string too long than 4095", pToken->z); |
2866 | 0 | } |
2867 | 0 | pVal->value.pData = taosMemoryMalloc(pToken->n); |
2868 | 0 | if (NULL == pVal->value.pData) { |
2869 | 0 | return terrno; |
2870 | 0 | } |
2871 | 0 | memcpy(pVal->value.pData, pToken->z, pToken->n); |
2872 | 0 | pVal->value.nData = pToken->n; |
2873 | 0 | break; |
2874 | 0 | } |
2875 | 0 | case TSDB_DATA_TYPE_GEOMETRY: { |
2876 | 0 | int32_t code = TSDB_CODE_FAILED; |
2877 | 0 | unsigned char* output = NULL; |
2878 | 0 | size_t size = 0; |
2879 | |
|
2880 | 0 | code = parseGeometry(pToken, &output, &size); |
2881 | 0 | if (code != TSDB_CODE_SUCCESS) { |
2882 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, getGeosErrMsg(code), pToken->z); |
2883 | 0 | } |
2884 | | // Too long values will raise the invalid sql error message |
2885 | 0 | else if (size + VARSTR_HEADER_SIZE > pSchema->bytes) { |
2886 | 0 | code = generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_VALUE_TOO_LONG, pSchema->name); |
2887 | 0 | } else { |
2888 | 0 | pVal->value.pData = taosMemoryMalloc(size); |
2889 | 0 | if (NULL == pVal->value.pData) { |
2890 | 0 | code = terrno; |
2891 | 0 | } else { |
2892 | 0 | memcpy(pVal->value.pData, output, size); |
2893 | 0 | pVal->value.nData = size; |
2894 | 0 | } |
2895 | 0 | } |
2896 | |
|
2897 | 0 | geosFreeBuffer(output); |
2898 | 0 | if (code != TSDB_CODE_SUCCESS) { |
2899 | 0 | return code; |
2900 | 0 | } |
2901 | | |
2902 | 0 | break; |
2903 | 0 | } |
2904 | 0 | case TSDB_DATA_TYPE_TIMESTAMP: { |
2905 | 0 | if (parseTime(pSql, pToken, timePrec, &VALUE_GET_TRIVIAL_DATUM(&pVal->value), &pCxt->msg, |
2906 | 0 | pCxt->pComCxt->timezone) != TSDB_CODE_SUCCESS) { |
2907 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid timestamp", pToken->z); |
2908 | 0 | } |
2909 | 0 | break; |
2910 | 0 | } |
2911 | 0 | case TSDB_DATA_TYPE_BLOB: |
2912 | 0 | case TSDB_DATA_TYPE_MEDIUMBLOB: { |
2913 | 0 | int32_t code = parseBlob(pToken, &pVal->value.pData, &pVal->value.nData, pSchema->bytes); |
2914 | 0 | if (code != TSDB_CODE_SUCCESS) { |
2915 | 0 | return generateSyntaxErrMsg(&pCxt->msg, code, pSchema->name); |
2916 | 0 | } |
2917 | 0 | } break; |
2918 | 0 | case TSDB_DATA_TYPE_DECIMAL: { |
2919 | 0 | if (!pExtSchema) { |
2920 | 0 | qError("Decimal type without ext schema info, cannot parse decimal values"); |
2921 | 0 | return TSDB_CODE_DECIMAL_PARSE_ERROR; |
2922 | 0 | } |
2923 | 0 | uint8_t precision = 0, scale = 0; |
2924 | 0 | decimalFromTypeMod(pExtSchema->typeMod, &precision, &scale); |
2925 | 0 | Decimal128 dec = {0}; |
2926 | 0 | int32_t code = decimal128FromStr(pToken->z, pToken->n, precision, scale, &dec); |
2927 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2928 | 0 | return code; |
2929 | 0 | } |
2930 | | |
2931 | | // precision check |
2932 | | // scale auto fit |
2933 | | |
2934 | 0 | code = decimal128ToDataVal(&dec, &pVal->value); |
2935 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2936 | 0 | return code; |
2937 | 0 | } |
2938 | 0 | break; |
2939 | 0 | } |
2940 | 0 | case TSDB_DATA_TYPE_DECIMAL64: { |
2941 | 0 | if (!pExtSchema) { |
2942 | 0 | qError("Decimal type without ext schema info, cannot parse decimal values"); |
2943 | 0 | return TSDB_CODE_DECIMAL_PARSE_ERROR; |
2944 | 0 | } |
2945 | 0 | uint8_t precision = 0, scale = 0; |
2946 | 0 | decimalFromTypeMod(pExtSchema->typeMod, &precision, &scale); |
2947 | 0 | Decimal64 dec = {0}; |
2948 | 0 | int32_t code = decimal64FromStr(pToken->z, pToken->n, precision, scale, &dec); |
2949 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2950 | 0 | return code; |
2951 | 0 | } |
2952 | 0 | code = decimal64ToDataVal(&dec, &pVal->value); |
2953 | 0 | if (TSDB_CODE_SUCCESS != code) { |
2954 | 0 | return code; |
2955 | 0 | } |
2956 | 0 | break; |
2957 | 0 | } |
2958 | 0 | default: |
2959 | |
|
2960 | 0 | return TSDB_CODE_FAILED; |
2961 | 0 | } |
2962 | | |
2963 | 0 | pVal->flag = CV_FLAG_VALUE; |
2964 | 0 | return TSDB_CODE_SUCCESS; |
2965 | 0 | } |
2966 | | |
2967 | | static int32_t parseValueToken(SInsertParseContext* pCxt, const char** pSql, SToken* pToken, SSchema* pSchema, |
2968 | 0 | const SSchemaExt* pExtSchema, int16_t timePrec, SColVal* pVal) { |
2969 | 0 | int32_t code = checkAndTrimValue(pToken, pCxt->tmpTokenBuf, &pCxt->msg, pSchema->type); |
2970 | 0 | if (TSDB_CODE_SUCCESS == code && isNullValue(pSchema->type, pToken)) { |
2971 | 0 | if (TSDB_DATA_TYPE_TIMESTAMP == pSchema->type && PRIMARYKEY_TIMESTAMP_COL_ID == pSchema->colId) { |
2972 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "Primary timestamp column should not be null", pToken->z); |
2973 | 0 | } |
2974 | | |
2975 | 0 | pVal->flag = CV_FLAG_NULL; |
2976 | 0 | return TSDB_CODE_SUCCESS; |
2977 | 0 | } |
2978 | | |
2979 | 0 | if (TSDB_CODE_SUCCESS == code) { |
2980 | 0 | if (pToken->n == 0 && IS_NUMERIC_TYPE(pSchema->type)) { |
2981 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid numeric data", pToken->z); |
2982 | 0 | } |
2983 | 0 | code = parseValueTokenImpl(pCxt, pSql, pToken, pSchema, pExtSchema, timePrec, pVal); |
2984 | 0 | } |
2985 | | |
2986 | 0 | return code; |
2987 | 0 | } |
2988 | | |
2989 | 0 | static void clearColValArray(SArray* pCols) { |
2990 | 0 | int32_t num = taosArrayGetSize(pCols); |
2991 | 0 | for (int32_t i = 0; i < num; ++i) { |
2992 | 0 | SColVal* pCol = taosArrayGet(pCols, i); |
2993 | 0 | if (IS_VAR_DATA_TYPE(pCol->value.type) || pCol->value.type == TSDB_DATA_TYPE_DECIMAL) { |
2994 | 0 | taosMemoryFreeClear(pCol->value.pData); |
2995 | 0 | } |
2996 | 0 | } |
2997 | 0 | } |
2998 | | |
2999 | | typedef struct SStbRowsDataContext { |
3000 | | SName stbName; |
3001 | | |
3002 | | STableMeta* pStbMeta; |
3003 | | SNode* pTagCond; |
3004 | | SBoundColInfo boundColsInfo; |
3005 | | |
3006 | | // the following fields are for each stb row |
3007 | | SArray* aTagVals; |
3008 | | SArray* aColVals; |
3009 | | SArray* aTagNames; |
3010 | | SName ctbName; |
3011 | | STag* pTag; |
3012 | | STableMeta* pCtbMeta; |
3013 | | SVCreateTbReq* pCreateCtbReq; |
3014 | | bool hasTimestampTag; |
3015 | | bool isJsonTag; |
3016 | | } SStbRowsDataContext; |
3017 | | |
3018 | | typedef union SRowsDataContext { |
3019 | | STableDataCxt* pTableDataCxt; |
3020 | | SStbRowsDataContext* pStbRowsCxt; |
3021 | | } SRowsDataContext; |
3022 | | |
3023 | 0 | int32_t parseTbnameToken(SMsgBuf* pMsgBuf, char* tname, SToken* pToken, bool* pFoundCtbName) { |
3024 | 0 | *pFoundCtbName = false; |
3025 | |
|
3026 | 0 | if (isNullValue(TSDB_DATA_TYPE_BINARY, pToken)) { |
3027 | 0 | return buildInvalidOperationMsg(pMsgBuf, "tbname can not be null value"); |
3028 | 0 | } |
3029 | | |
3030 | 0 | if (pToken->n > 0) { |
3031 | 0 | if (pToken->n <= TSDB_TABLE_NAME_LEN - 1) { |
3032 | 0 | for (int i = 0; i < pToken->n; ++i) { |
3033 | 0 | if (pToken->z[i] == '.') { |
3034 | 0 | return buildInvalidOperationMsg(pMsgBuf, "tbname can not contain '.'"); |
3035 | 0 | } else { |
3036 | 0 | tname[i] = pToken->z[i]; |
3037 | 0 | } |
3038 | 0 | } |
3039 | 0 | tname[pToken->n] = '\0'; |
3040 | 0 | *pFoundCtbName = true; |
3041 | 0 | } else { |
3042 | 0 | return buildInvalidOperationMsg(pMsgBuf, "tbname is too long"); |
3043 | 0 | } |
3044 | 0 | } else { |
3045 | 0 | return buildInvalidOperationMsg(pMsgBuf, "tbname can not be empty"); |
3046 | 0 | } |
3047 | 0 | return TSDB_CODE_SUCCESS; |
3048 | 0 | } |
3049 | | |
3050 | | static int32_t processCtbTagsAfterCtbName(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, |
3051 | | SStbRowsDataContext* pStbRowsCxt, bool ctbFirst, const SToken* tagTokens, |
3052 | 0 | SSchema* const* tagSchemas, int numOfTagTokens) { |
3053 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
3054 | 0 | uint8_t precision = pStmt->pTableMeta->tableInfo.precision; |
3055 | |
|
3056 | 0 | if (code == TSDB_CODE_SUCCESS && ctbFirst) { |
3057 | 0 | for (int32_t i = 0; code == TSDB_CODE_SUCCESS && i < numOfTagTokens; ++i) { |
3058 | 0 | SToken* pTagToken = (SToken*)(tagTokens + i); |
3059 | 0 | SSchema* pTagSchema = tagSchemas[i]; |
3060 | 0 | code = checkAndTrimValue(pTagToken, pCxt->tmpTokenBuf, &pCxt->msg, pTagSchema->type); |
3061 | 0 | if (code == TSDB_CODE_SUCCESS && TK_NK_VARIABLE == pTagToken->type) { |
3062 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "not expected tag"); |
3063 | 0 | } |
3064 | |
|
3065 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3066 | 0 | code = parseTagValue(&pCxt->msg, NULL, precision, pTagSchema, pTagToken, pStbRowsCxt->aTagNames, |
3067 | 0 | pStbRowsCxt->aTagVals, &pStbRowsCxt->pTag, pCxt->pComCxt->timezone, |
3068 | 0 | pCxt->pComCxt->charsetCxt); |
3069 | 0 | } |
3070 | 0 | } |
3071 | 0 | if (code == TSDB_CODE_SUCCESS && !pStbRowsCxt->isJsonTag) { |
3072 | 0 | taosArraySort(pStbRowsCxt->aTagNames, tTagNameCompare); |
3073 | 0 | code = tTagNew(pStbRowsCxt->aTagVals, 1, false, &pStbRowsCxt->pTag); |
3074 | 0 | } |
3075 | 0 | } |
3076 | 0 | if (code == TSDB_CODE_SUCCESS && pStbRowsCxt->pTagCond) { |
3077 | 0 | code = checkSubtablePrivilege(pStbRowsCxt->aTagVals, pStbRowsCxt->aTagNames, &pStbRowsCxt->pTagCond); |
3078 | 0 | } |
3079 | 0 | return code; |
3080 | 0 | } |
3081 | | |
3082 | | static int32_t doGetStbRowValues(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, const char** ppSql, |
3083 | | SStbRowsDataContext* pStbRowsCxt, SToken* pToken, const SBoundColInfo* pCols, |
3084 | | const SSchema* pSchemas, const SSchemaExt* pExtSchemas, SToken* tagTokens, |
3085 | | SSchema** tagSchemas, int* pNumOfTagTokens, bool* bFoundTbName, bool* setCtbName, |
3086 | 0 | SBoundColInfo* ctbCols) { |
3087 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
3088 | 0 | SArray* pTagNames = pStbRowsCxt->aTagNames; |
3089 | 0 | SArray* pTagVals = pStbRowsCxt->aTagVals; |
3090 | 0 | bool canParseTagsAfter = !pStbRowsCxt->pTagCond && !pStbRowsCxt->hasTimestampTag; |
3091 | 0 | int32_t numOfCols = getNumOfColumns(pStbRowsCxt->pStbMeta); |
3092 | 0 | int32_t numOfTags = getNumOfTags(pStbRowsCxt->pStbMeta); |
3093 | 0 | int32_t tbnameIdx = getTbnameSchemaIndex(pStbRowsCxt->pStbMeta); |
3094 | 0 | uint8_t precision = getTableInfo(pStbRowsCxt->pStbMeta).precision; |
3095 | 0 | int tag_index = 0; |
3096 | 0 | int col_index = 0; |
3097 | 0 | for (int i = 0; i < pCols->numOfBound && (code) == TSDB_CODE_SUCCESS; ++i) { |
3098 | 0 | const char* pTmpSql = *ppSql; |
3099 | 0 | bool ignoreComma = false; |
3100 | 0 | NEXT_TOKEN_WITH_PREV_EXT(*ppSql, *pToken, &ignoreComma); |
3101 | |
|
3102 | 0 | if (ignoreComma) { |
3103 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "invalid data or symbol", pTmpSql); |
3104 | 0 | break; |
3105 | 0 | } |
3106 | | |
3107 | 0 | if (TK_NK_RP == pToken->type) { |
3108 | 0 | code = generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_INVALID_COLUMNS_NUM); |
3109 | 0 | break; |
3110 | 0 | } |
3111 | | |
3112 | | // cols tags tbname |
3113 | 0 | if (TK_NK_QUESTION == pToken->type) { |
3114 | 0 | if (pCxt->pComCxt->stmtBindVersion != 2) { |
3115 | 0 | return buildInvalidOperationMsg(&pCxt->msg, |
3116 | 0 | "insert into stb(...tbname...)values(...,?,...) only support in stmt2"); |
3117 | 0 | } |
3118 | 0 | if (pCols->pColIndex[i] == tbnameIdx) { |
3119 | 0 | *bFoundTbName = true; |
3120 | 0 | char* tbName = NULL; |
3121 | 0 | if ((*pCxt->pComCxt->pStmtCb->getTbNameFn)(pCxt->pComCxt->pStmtCb->pStmt, &tbName) == TSDB_CODE_SUCCESS) { |
3122 | 0 | tstrncpy(pStbRowsCxt->ctbName.tname, tbName, sizeof(pStbRowsCxt->ctbName.tname)); |
3123 | 0 | tstrncpy(pStmt->usingTableName.tname, pStmt->targetTableName.tname, sizeof(pStmt->usingTableName.tname)); |
3124 | 0 | tstrncpy(pStmt->targetTableName.tname, tbName, sizeof(pStmt->targetTableName.tname)); |
3125 | 0 | tstrncpy(pStmt->usingTableName.dbname, pStmt->targetTableName.dbname, sizeof(pStmt->usingTableName.dbname)); |
3126 | 0 | pStmt->usingTableName.type = 1; |
3127 | 0 | pStmt->pTableMeta->tableType = TSDB_CHILD_TABLE; // set the table type to child table for parse cache |
3128 | 0 | *setCtbName = true; |
3129 | 0 | } |
3130 | 0 | } else if (pCols->pColIndex[i] < numOfCols) { |
3131 | | // bind column |
3132 | 0 | if (ctbCols->pColIndex == NULL) { |
3133 | 0 | ctbCols->pColIndex = taosMemoryCalloc(numOfCols, sizeof(int16_t)); |
3134 | 0 | if (NULL == ctbCols->pColIndex) { |
3135 | 0 | return terrno; |
3136 | 0 | } |
3137 | 0 | } |
3138 | 0 | ctbCols->pColIndex[col_index++] = pCols->pColIndex[i]; |
3139 | 0 | ctbCols->numOfBound++; |
3140 | 0 | ctbCols->numOfCols++; |
3141 | |
|
3142 | 0 | } else if (pCols->pColIndex[i] < tbnameIdx) { |
3143 | 0 | if (pCxt->tags.pColIndex == NULL) { |
3144 | 0 | pCxt->tags.pColIndex = taosMemoryCalloc(numOfTags, sizeof(int16_t)); |
3145 | 0 | if (NULL == pCxt->tags.pColIndex) { |
3146 | 0 | return terrno; |
3147 | 0 | } |
3148 | 0 | } |
3149 | 0 | if (!(tag_index < numOfTags)) { |
3150 | 0 | return buildInvalidOperationMsg(&pCxt->msg, "not expected numOfTags"); |
3151 | 0 | } |
3152 | 0 | pStmt->usingTableProcessing = true; |
3153 | 0 | pCxt->tags.pColIndex[tag_index++] = pCols->pColIndex[i] - numOfCols; |
3154 | 0 | pCxt->tags.mixTagsCols = true; |
3155 | 0 | pCxt->tags.numOfBound++; |
3156 | 0 | pCxt->tags.numOfCols++; |
3157 | 0 | } else { |
3158 | 0 | return buildInvalidOperationMsg(&pCxt->msg, "not expected numOfBound"); |
3159 | 0 | } |
3160 | 0 | } else { |
3161 | 0 | if (pCxt->pComCxt->stmtBindVersion == 2 && pCxt->pComCxt->pStmtCb != NULL) { |
3162 | 0 | if (pCols->pColIndex[i] < numOfCols) { |
3163 | 0 | const SSchema* pSchema = &pSchemas[pCols->pColIndex[i]]; |
3164 | 0 | const SSchemaExt* pExtSchema = pExtSchemas + pCols->pColIndex[i]; |
3165 | 0 | SColVal* pVal = taosArrayGet(pStbRowsCxt->aColVals, pCols->pColIndex[i]); |
3166 | 0 | code = parseValueToken(pCxt, ppSql, pToken, (SSchema*)pSchema, pExtSchema, precision, pVal); |
3167 | 0 | if (TK_NK_VARIABLE == pToken->type) { |
3168 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "not expected row value"); |
3169 | 0 | } |
3170 | |
|
3171 | 0 | if (ctbCols->pColIndex == NULL) { |
3172 | 0 | ctbCols->pColIndex = taosMemoryCalloc(numOfCols, sizeof(int16_t)); |
3173 | 0 | if (NULL == ctbCols->pColIndex) { |
3174 | 0 | return terrno; |
3175 | 0 | } |
3176 | 0 | } |
3177 | 0 | ctbCols->pColIndex[col_index++] = pCols->pColIndex[i]; |
3178 | 0 | ctbCols->numOfBound++; |
3179 | 0 | ctbCols->numOfCols++; |
3180 | |
|
3181 | 0 | if (code == TSDB_CODE_SUCCESS && pCxt->pParsedValues == NULL) { |
3182 | 0 | pCxt->pParsedValues = |
3183 | 0 | tSimpleHashInit(pCols->numOfBound, taosGetDefaultHashFunction(TSDB_DATA_TYPE_SMALLINT)); |
3184 | 0 | if (pCxt->pParsedValues == NULL) { |
3185 | 0 | return terrno; |
3186 | 0 | } |
3187 | 0 | tSimpleHashSetFreeFp(pCxt->pParsedValues, destroyColVal); |
3188 | 0 | } |
3189 | | |
3190 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3191 | 0 | SColVal clonedVal = *pVal; |
3192 | 0 | if (COL_VAL_IS_VALUE(&clonedVal) && IS_VAR_DATA_TYPE(clonedVal.value.type)) { |
3193 | 0 | clonedVal.value.pData = taosMemoryMalloc(clonedVal.value.nData); |
3194 | 0 | if (NULL == clonedVal.value.pData) { |
3195 | 0 | code = terrno; |
3196 | 0 | break; |
3197 | 0 | } |
3198 | 0 | memcpy(clonedVal.value.pData, pVal->value.pData, clonedVal.value.nData); |
3199 | 0 | } |
3200 | | |
3201 | 0 | clonedVal.cid = pSchema->colId; |
3202 | 0 | code = tSimpleHashPut(pCxt->pParsedValues, &pSchema->colId, sizeof(int16_t), &clonedVal, sizeof(SColVal)); |
3203 | 0 | if (code != TSDB_CODE_SUCCESS) { |
3204 | 0 | if (COL_VAL_IS_VALUE(&clonedVal) && IS_VAR_DATA_TYPE(clonedVal.value.type)) { |
3205 | 0 | taosMemoryFree(clonedVal.value.pData); |
3206 | 0 | clonedVal.value.pData = NULL; |
3207 | 0 | } |
3208 | 0 | return code; |
3209 | 0 | } |
3210 | 0 | } |
3211 | 0 | } else if (pCols->pColIndex[i] < tbnameIdx) { |
3212 | 0 | if (pCxt->tags.parseredTags == NULL) { |
3213 | 0 | pCxt->tags.parseredTags = taosMemoryCalloc(1, sizeof(STagsInfo)); |
3214 | 0 | if (pCxt->tags.parseredTags == NULL) { |
3215 | 0 | code = terrno; |
3216 | 0 | } else { |
3217 | 0 | pCxt->tags.parseredTags->STagNames = taosArrayInit(numOfTags, TSDB_COL_NAME_LEN + sizeof(col_id_t)); |
3218 | 0 | pCxt->tags.parseredTags->pTagVals = taosArrayInit(numOfTags, sizeof(STagVal)); |
3219 | 0 | pCxt->tags.parseredTags->pTagIndex = taosMemoryCalloc(numOfTags, sizeof(uint8_t)); |
3220 | 0 | pCxt->tags.parseredTags->numOfTags = 0; |
3221 | |
|
3222 | 0 | if (pCxt->tags.parseredTags->STagNames == NULL || pCxt->tags.parseredTags->pTagVals == NULL || |
3223 | 0 | pCxt->tags.parseredTags->pTagIndex == NULL) { |
3224 | 0 | return terrno; |
3225 | 0 | } |
3226 | 0 | } |
3227 | 0 | } |
3228 | | |
3229 | 0 | const SSchema* pTagSchema = &pSchemas[pCols->pColIndex[i]]; |
3230 | | // if (canParseTagsAfter) { |
3231 | | // tagTokens[(*pNumOfTagTokens)] = *pToken; |
3232 | | // tagSchemas[(*pNumOfTagTokens)] = (SSchema*)pTagSchema; |
3233 | | // ++(*pNumOfTagTokens); |
3234 | | // } else { |
3235 | 0 | code = checkAndTrimValue(pToken, pCxt->tmpTokenBuf, &pCxt->msg, pTagSchema->type); |
3236 | 0 | if (code == TSDB_CODE_SUCCESS && TK_NK_VARIABLE == pToken->type) { |
3237 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "not expected row value"); |
3238 | 0 | } |
3239 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3240 | 0 | code = parseTagValue(&pCxt->msg, ppSql, precision, (SSchema*)pTagSchema, pToken, |
3241 | 0 | pCxt->tags.parseredTags->STagNames, pCxt->tags.parseredTags->pTagVals, |
3242 | 0 | &pStbRowsCxt->pTag, pCxt->pComCxt->timezone, pCxt->pComCxt->charsetCxt); |
3243 | 0 | } |
3244 | |
|
3245 | 0 | pCxt->tags.parseredTags->pTagIndex[pCxt->tags.parseredTags->numOfTags++] = pCols->pColIndex[i] - numOfCols; |
3246 | |
|
3247 | 0 | if (pCxt->tags.pColIndex == NULL) { |
3248 | 0 | pCxt->tags.pColIndex = taosMemoryCalloc(numOfTags, sizeof(int16_t)); |
3249 | 0 | if (NULL == pCxt->tags.pColIndex) { |
3250 | 0 | return terrno; |
3251 | 0 | } |
3252 | 0 | } |
3253 | 0 | if (!(tag_index < numOfTags)) { |
3254 | 0 | return buildInvalidOperationMsg(&pCxt->msg, "not expected numOfTags"); |
3255 | 0 | } |
3256 | 0 | pStmt->usingTableProcessing = true; |
3257 | 0 | pCxt->tags.pColIndex[tag_index++] = pCols->pColIndex[i] - numOfCols; |
3258 | 0 | pCxt->tags.mixTagsCols = true; |
3259 | 0 | pCxt->tags.numOfBound++; |
3260 | 0 | pCxt->tags.numOfCols++; |
3261 | | // } |
3262 | 0 | } else if (pCols->pColIndex[i] == tbnameIdx) { |
3263 | 0 | return buildInvalidOperationMsg(&pCxt->msg, "STMT tbname in bound cols should not be a fixed value"); |
3264 | 0 | } |
3265 | 0 | } else { |
3266 | 0 | if (pCols->pColIndex[i] < numOfCols) { |
3267 | 0 | const SSchema* pSchema = &pSchemas[pCols->pColIndex[i]]; |
3268 | 0 | const SSchemaExt* pExtSchema = pExtSchemas + pCols->pColIndex[i]; |
3269 | 0 | SColVal* pVal = taosArrayGet(pStbRowsCxt->aColVals, pCols->pColIndex[i]); |
3270 | 0 | code = parseValueToken(pCxt, ppSql, pToken, (SSchema*)pSchema, pExtSchema, precision, pVal); |
3271 | 0 | if (TK_NK_VARIABLE == pToken->type) { |
3272 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "not expected row value"); |
3273 | 0 | } |
3274 | 0 | } else if (pCols->pColIndex[i] < tbnameIdx) { |
3275 | 0 | const SSchema* pTagSchema = &pSchemas[pCols->pColIndex[i]]; |
3276 | 0 | if (canParseTagsAfter) { |
3277 | 0 | tagTokens[(*pNumOfTagTokens)] = *pToken; |
3278 | 0 | tagSchemas[(*pNumOfTagTokens)] = (SSchema*)pTagSchema; |
3279 | 0 | ++(*pNumOfTagTokens); |
3280 | 0 | } else { |
3281 | 0 | code = checkAndTrimValue(pToken, pCxt->tmpTokenBuf, &pCxt->msg, pTagSchema->type); |
3282 | 0 | if (code == TSDB_CODE_SUCCESS && TK_NK_VARIABLE == pToken->type) { |
3283 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "not expected row value"); |
3284 | 0 | } |
3285 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3286 | 0 | code = parseTagValue(&pCxt->msg, ppSql, precision, (SSchema*)pTagSchema, pToken, pTagNames, pTagVals, |
3287 | 0 | &pStbRowsCxt->pTag, pCxt->pComCxt->timezone, pCxt->pComCxt->charsetCxt); |
3288 | 0 | } |
3289 | 0 | } |
3290 | 0 | } else if (pCols->pColIndex[i] == tbnameIdx) { |
3291 | 0 | code = checkAndTrimValue(pToken, pCxt->tmpTokenBuf, &pCxt->msg, TSDB_DATA_TYPE_BINARY); |
3292 | 0 | if (TK_NK_VARIABLE == pToken->type) { |
3293 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "not expected tbname"); |
3294 | 0 | } |
3295 | |
|
3296 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3297 | 0 | code = parseTbnameToken(&pCxt->msg, pStbRowsCxt->ctbName.tname, pToken, bFoundTbName); |
3298 | 0 | } |
3299 | 0 | } |
3300 | 0 | } |
3301 | 0 | } |
3302 | | |
3303 | 0 | if (code == TSDB_CODE_SUCCESS && i < pCols->numOfBound - 1) { |
3304 | 0 | NEXT_VALID_TOKEN(*ppSql, *pToken); |
3305 | 0 | if (TK_NK_COMMA != pToken->type) { |
3306 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
3307 | 0 | } |
3308 | 0 | } |
3309 | 0 | } |
3310 | | |
3311 | 0 | return code; |
3312 | 0 | } |
3313 | | |
3314 | | static int32_t getStbRowValues(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, const char** ppSql, |
3315 | | SStbRowsDataContext* pStbRowsCxt, bool* pGotRow, SToken* pToken, bool* pCtbFirst, |
3316 | 0 | bool* setCtbName, SBoundColInfo* ctbCols) { |
3317 | 0 | SBoundColInfo* pCols = &pStbRowsCxt->boundColsInfo; |
3318 | 0 | SSchema* pSchemas = getTableColumnSchema(pStbRowsCxt->pStbMeta); |
3319 | 0 | SSchemaExt* pExtSchemas = getTableColumnExtSchema(pStbRowsCxt->pStbMeta); |
3320 | |
|
3321 | 0 | bool bFoundTbName = false; |
3322 | 0 | const char* pOrigSql = *ppSql; |
3323 | |
|
3324 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
3325 | 0 | SToken tagTokens[TSDB_MAX_TAGS] = {0}; |
3326 | 0 | SSchema* tagSchemas[TSDB_MAX_TAGS] = {0}; |
3327 | 0 | int numOfTagTokens = 0; |
3328 | |
|
3329 | 0 | code = doGetStbRowValues(pCxt, pStmt, ppSql, pStbRowsCxt, pToken, pCols, pSchemas, pExtSchemas, tagTokens, tagSchemas, |
3330 | 0 | &numOfTagTokens, &bFoundTbName, setCtbName, ctbCols); |
3331 | |
|
3332 | 0 | if (code != TSDB_CODE_SUCCESS) { |
3333 | 0 | return code; |
3334 | 0 | } |
3335 | | |
3336 | 0 | if (!bFoundTbName) { |
3337 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "tbname value expected", pOrigSql); |
3338 | 0 | } |
3339 | |
|
3340 | 0 | bool ctbFirst = true; |
3341 | 0 | char ctbFName[TSDB_TABLE_FNAME_LEN]; |
3342 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3343 | 0 | code = tNameExtractFullName(&pStbRowsCxt->ctbName, ctbFName); |
3344 | 0 | } |
3345 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3346 | 0 | STableMeta** pCtbMeta = taosHashGet(pStmt->pSubTableHashObj, ctbFName, strlen(ctbFName)); |
3347 | 0 | ctbFirst = (pCtbMeta == NULL); |
3348 | 0 | if (!ctbFirst) { |
3349 | 0 | pStbRowsCxt->pCtbMeta->uid = (*pCtbMeta)->uid; |
3350 | 0 | pStbRowsCxt->pCtbMeta->vgId = (*pCtbMeta)->vgId; |
3351 | 0 | } |
3352 | 0 | *pCtbFirst = ctbFirst; |
3353 | 0 | } |
3354 | |
|
3355 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3356 | 0 | code = processCtbTagsAfterCtbName(pCxt, pStmt, pStbRowsCxt, ctbFirst, tagTokens, tagSchemas, numOfTagTokens); |
3357 | 0 | } |
3358 | |
|
3359 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3360 | 0 | *pGotRow = true; |
3361 | 0 | } |
3362 | 0 | return code; |
3363 | 0 | } |
3364 | | |
3365 | | static int32_t processCtbAutoCreationAndCtbMeta(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, |
3366 | 0 | SStbRowsDataContext* pStbRowsCxt) { |
3367 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
3368 | 0 | bool stmtNoCreateTbReq = (pCxt->pComCxt->stmtBindVersion == 2 && pCxt->tags.numOfBound == 0); |
3369 | |
|
3370 | 0 | if (!stmtNoCreateTbReq) { |
3371 | 0 | pStbRowsCxt->pCreateCtbReq = taosMemoryCalloc(1, sizeof(SVCreateTbReq)); |
3372 | 0 | if (pStbRowsCxt->pCreateCtbReq == NULL) { |
3373 | 0 | code = terrno; |
3374 | 0 | } |
3375 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3376 | 0 | code = insBuildCreateTbReq(pStbRowsCxt->pCreateCtbReq, pStbRowsCxt->ctbName.tname, pStbRowsCxt->pTag, |
3377 | 0 | pStbRowsCxt->pStbMeta->uid, pStbRowsCxt->stbName.tname, pStbRowsCxt->aTagNames, |
3378 | 0 | getNumOfTags(pStbRowsCxt->pStbMeta), TSDB_DEFAULT_TABLE_TTL); |
3379 | 0 | pStbRowsCxt->pTag = NULL; |
3380 | 0 | } |
3381 | 0 | } |
3382 | |
|
3383 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3384 | 0 | char ctbFName[TSDB_TABLE_FNAME_LEN]; |
3385 | 0 | code = tNameExtractFullName(&pStbRowsCxt->ctbName, ctbFName); |
3386 | 0 | SVgroupInfo vg; |
3387 | 0 | SRequestConnInfo conn = {.pTrans = pCxt->pComCxt->pTransporter, |
3388 | 0 | .requestId = pCxt->pComCxt->requestId, |
3389 | 0 | .requestObjRefId = pCxt->pComCxt->requestRid, |
3390 | 0 | .mgmtEps = pCxt->pComCxt->mgmtEpSet}; |
3391 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3392 | 0 | code = catalogGetTableHashVgroup(pCxt->pComCxt->pCatalog, &conn, &pStbRowsCxt->ctbName, &vg); |
3393 | 0 | } |
3394 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3395 | 0 | code = taosHashPut(pStmt->pVgroupsHashObj, (const char*)(&vg.vgId), sizeof(vg.vgId), &vg, sizeof(vg)); |
3396 | 0 | } |
3397 | 0 | STableMeta* pBackup = NULL; |
3398 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3399 | 0 | if (stmtNoCreateTbReq) { |
3400 | | /* Resolve child uid by db+tbname; must belong to the super table in this INSERT (same suid). */ |
3401 | 0 | STableMeta* pMeta = NULL; |
3402 | 0 | code = catalogGetTableMeta(pCxt->pComCxt->pCatalog, &conn, &pStbRowsCxt->ctbName, &pMeta); |
3403 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3404 | 0 | if (pMeta == NULL) { |
3405 | 0 | code = TSDB_CODE_PAR_TABLE_NOT_EXIST; |
3406 | 0 | } else if (TSDB_CHILD_TABLE != pMeta->tableType || pMeta->suid != pStbRowsCxt->pStbMeta->uid) { |
3407 | 0 | taosMemoryFreeClear(pMeta); |
3408 | 0 | code = generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_TDB_TABLE_IN_OTHER_STABLE); |
3409 | 0 | } else { |
3410 | 0 | pStbRowsCxt->pCtbMeta->uid = pMeta->uid; |
3411 | 0 | pStbRowsCxt->pCtbMeta->sversion = pMeta->sversion; |
3412 | 0 | pStbRowsCxt->pCtbMeta->tversion = pMeta->tversion; |
3413 | 0 | taosMemoryFreeClear(pMeta); |
3414 | 0 | } |
3415 | 0 | } |
3416 | 0 | } else { |
3417 | 0 | pStbRowsCxt->pCtbMeta->uid = taosHashGetSize(pStmt->pSubTableHashObj) + 1; |
3418 | 0 | } |
3419 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3420 | 0 | pStbRowsCxt->pCtbMeta->vgId = vg.vgId; |
3421 | 0 | code = cloneTableMeta(pStbRowsCxt->pCtbMeta, &pBackup); |
3422 | 0 | } |
3423 | 0 | } |
3424 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3425 | 0 | code = taosHashPut(pStmt->pSubTableHashObj, ctbFName, strlen(ctbFName), &pBackup, POINTER_BYTES); |
3426 | 0 | } |
3427 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3428 | 0 | code = collectUseTable(&pStbRowsCxt->ctbName, pStmt->pTableNameHashObj); |
3429 | 0 | } |
3430 | 0 | } |
3431 | 0 | return code; |
3432 | 0 | } |
3433 | | |
3434 | 0 | static void clearStbRowsDataContext(SStbRowsDataContext* pStbRowsCxt) { |
3435 | 0 | if (pStbRowsCxt == NULL) return; |
3436 | | |
3437 | 0 | taosArrayClear(pStbRowsCxt->aTagNames); |
3438 | 0 | for (int i = 0; i < taosArrayGetSize(pStbRowsCxt->aTagVals); ++i) { |
3439 | 0 | STagVal* p = (STagVal*)taosArrayGet(pStbRowsCxt->aTagVals, i); |
3440 | 0 | if (IS_VAR_DATA_TYPE(p->type)) { |
3441 | 0 | taosMemoryFreeClear(p->pData); |
3442 | 0 | } |
3443 | 0 | } |
3444 | 0 | taosArrayClear(pStbRowsCxt->aTagVals); |
3445 | |
|
3446 | 0 | clearColValArray(pStbRowsCxt->aColVals); |
3447 | |
|
3448 | 0 | tTagFree(pStbRowsCxt->pTag); |
3449 | 0 | pStbRowsCxt->pTag = NULL; |
3450 | 0 | tdDestroySVCreateTbReq(pStbRowsCxt->pCreateCtbReq); |
3451 | 0 | taosMemoryFreeClear(pStbRowsCxt->pCreateCtbReq); |
3452 | |
|
3453 | 0 | if (pStbRowsCxt->boundColsInfo.parseredTags != NULL) { |
3454 | 0 | if (pStbRowsCxt->boundColsInfo.parseredTags->STagNames != NULL) { |
3455 | 0 | taosArrayDestroy(pStbRowsCxt->boundColsInfo.parseredTags->STagNames); |
3456 | 0 | } |
3457 | 0 | if (pStbRowsCxt->boundColsInfo.parseredTags->pTagVals != NULL) { |
3458 | 0 | taosArrayDestroy(pStbRowsCxt->boundColsInfo.parseredTags->pTagVals); |
3459 | 0 | } |
3460 | 0 | taosMemoryFreeClear(pStbRowsCxt->boundColsInfo.parseredTags); |
3461 | 0 | } |
3462 | 0 | } |
3463 | | |
3464 | 5 | static void clearInsertParseContext(SInsertParseContext* pCxt) { |
3465 | 5 | if (pCxt == NULL) return; |
3466 | | |
3467 | 5 | if (pCxt->pParsedValues != NULL) { |
3468 | 0 | tSimpleHashCleanup(pCxt->pParsedValues); |
3469 | 0 | pCxt->pParsedValues = NULL; |
3470 | 0 | } |
3471 | 5 | } |
3472 | | |
3473 | | static int32_t parseStbBoundInfo(SVnodeModifyOpStmt* pStmt, SStbRowsDataContext* pStbRowsCxt, |
3474 | 0 | STableDataCxt** ppTableDataCxt) { |
3475 | 0 | char tbFName[TSDB_TABLE_FNAME_LEN]; |
3476 | 0 | int32_t code = tNameExtractFullName(&pStmt->targetTableName, tbFName); |
3477 | 0 | if (TSDB_CODE_SUCCESS != code) { |
3478 | 0 | return code; |
3479 | 0 | } |
3480 | 0 | if (pStmt->usingTableProcessing) { |
3481 | 0 | pStmt->pTableMeta->uid = 0; |
3482 | 0 | } |
3483 | |
|
3484 | 0 | code = insGetTableDataCxt(pStmt->pTableBlockHashObj, tbFName, strlen(tbFName), pStmt->pTableMeta, |
3485 | 0 | &pStmt->pCreateTblReq, ppTableDataCxt, false, true); |
3486 | 0 | if (code != TSDB_CODE_SUCCESS) { |
3487 | 0 | return code; |
3488 | 0 | } |
3489 | | |
3490 | 0 | qDestroyBoundColInfo(&((*ppTableDataCxt)->boundColsInfo)); |
3491 | 0 | (*ppTableDataCxt)->boundColsInfo = pStbRowsCxt->boundColsInfo; |
3492 | |
|
3493 | 0 | (*ppTableDataCxt)->boundColsInfo.pColIndex = taosMemoryCalloc(pStbRowsCxt->boundColsInfo.numOfBound, sizeof(int16_t)); |
3494 | 0 | if (NULL == (*ppTableDataCxt)->boundColsInfo.pColIndex) { |
3495 | 0 | return terrno; |
3496 | 0 | } |
3497 | 0 | (void)memcpy((*ppTableDataCxt)->boundColsInfo.pColIndex, pStbRowsCxt->boundColsInfo.pColIndex, |
3498 | 0 | sizeof(int16_t) * pStmt->pStbRowsCxt->boundColsInfo.numOfBound); |
3499 | |
|
3500 | 0 | int32_t ret = initTableColSubmitData(*ppTableDataCxt); |
3501 | 0 | if (ret != TSDB_CODE_SUCCESS) { |
3502 | 0 | return ret; |
3503 | 0 | } |
3504 | | |
3505 | 0 | return TSDB_CODE_SUCCESS; |
3506 | 0 | } |
3507 | | |
3508 | | static int32_t parseOneStbRow(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, const char** ppSql, |
3509 | | SStbRowsDataContext* pStbRowsCxt, bool* pGotRow, SToken* pToken, |
3510 | 0 | STableDataCxt** ppTableDataCxt) { |
3511 | 0 | bool bFirstTable = false; |
3512 | 0 | bool setCtbName = false; |
3513 | 0 | bool ctbColsTransferred = false; |
3514 | 0 | SBoundColInfo ctbCols = {0}; |
3515 | 0 | int32_t code = getStbRowValues(pCxt, pStmt, ppSql, pStbRowsCxt, pGotRow, pToken, &bFirstTable, &setCtbName, &ctbCols); |
3516 | |
|
3517 | 0 | if (!setCtbName && pCxt->pComCxt->stmtBindVersion == 2) { |
3518 | 0 | taosMemoryFreeClear(ctbCols.pColIndex); |
3519 | 0 | return parseStbBoundInfo(pStmt, pStbRowsCxt, ppTableDataCxt); |
3520 | 0 | } |
3521 | | |
3522 | 0 | if (code != TSDB_CODE_SUCCESS || !*pGotRow) { |
3523 | 0 | goto _return; |
3524 | 0 | } |
3525 | | |
3526 | 0 | if (code == TSDB_CODE_SUCCESS && bFirstTable) { |
3527 | 0 | code = processCtbAutoCreationAndCtbMeta(pCxt, pStmt, pStbRowsCxt); |
3528 | 0 | } |
3529 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3530 | 0 | if (pCxt->pComCxt->stmtBindVersion == 2) { |
3531 | 0 | char ctbFName[TSDB_TABLE_FNAME_LEN]; |
3532 | 0 | code = tNameExtractFullName(&pStbRowsCxt->ctbName, ctbFName); |
3533 | 0 | if (code != TSDB_CODE_SUCCESS) { |
3534 | 0 | goto _return; |
3535 | 0 | } |
3536 | 0 | code = insGetTableDataCxt(pStmt->pTableBlockHashObj, ctbFName, strlen(ctbFName), pStbRowsCxt->pCtbMeta, |
3537 | 0 | &pStbRowsCxt->pCreateCtbReq, ppTableDataCxt, true, true); |
3538 | 0 | } else { |
3539 | 0 | code = |
3540 | 0 | insGetTableDataCxt(pStmt->pTableBlockHashObj, &pStbRowsCxt->pCtbMeta->uid, sizeof(pStbRowsCxt->pCtbMeta->uid), |
3541 | 0 | pStbRowsCxt->pCtbMeta, &pStbRowsCxt->pCreateCtbReq, ppTableDataCxt, false, true); |
3542 | 0 | } |
3543 | 0 | } |
3544 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3545 | 0 | if (pCxt->pComCxt->stmtBindVersion == 2) { |
3546 | 0 | int32_t tbnameIdx = getTbnameSchemaIndex(pStbRowsCxt->pStbMeta); |
3547 | 0 | code = initTableColSubmitDataWithBoundInfo(*ppTableDataCxt, ctbCols); |
3548 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3549 | 0 | ctbColsTransferred = true; |
3550 | 0 | } |
3551 | 0 | } else { |
3552 | 0 | code = initTableColSubmitData(*ppTableDataCxt); |
3553 | 0 | } |
3554 | 0 | } |
3555 | 0 | if (code == TSDB_CODE_SUCCESS && pCxt->pComCxt->stmtBindVersion == 0) { |
3556 | 0 | SRow** pRow = taosArrayReserve((*ppTableDataCxt)->pData->aRowP, 1); |
3557 | 0 | if ((*ppTableDataCxt)->hasBlob) { |
3558 | 0 | SRowBuildScanInfo sinfo = {.hasBlob = 1, .scanType = ROW_BUILD_UPDATE}; |
3559 | 0 | if ((*ppTableDataCxt)->pData->pBlobSet == NULL) { |
3560 | 0 | code = tBlobSetCreate(1024, 0, &(*ppTableDataCxt)->pData->pBlobSet); |
3561 | 0 | TAOS_CHECK_GOTO(code, NULL, _return); |
3562 | 0 | } |
3563 | 0 | code = tRowBuildWithBlob(pStbRowsCxt->aColVals, (*ppTableDataCxt)->pSchema, pRow, |
3564 | 0 | (*ppTableDataCxt)->pData->pBlobSet, &sinfo); |
3565 | 0 | } else { |
3566 | 0 | SRowBuildScanInfo sinfo = {0}; |
3567 | 0 | code = tRowBuild(pStbRowsCxt->aColVals, (*ppTableDataCxt)->pSchema, pRow, &sinfo); |
3568 | 0 | } |
3569 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3570 | 0 | SRowKey key; |
3571 | 0 | tRowGetKey(*pRow, &key); |
3572 | 0 | insCheckTableDataOrder(*ppTableDataCxt, &key); |
3573 | 0 | } |
3574 | 0 | } |
3575 | | |
3576 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3577 | 0 | *pGotRow = true; |
3578 | 0 | } |
3579 | |
|
3580 | 0 | _return: |
3581 | 0 | if (!ctbColsTransferred && ctbCols.pColIndex != NULL) { |
3582 | 0 | qDestroyBoundColInfo(&ctbCols); |
3583 | 0 | } |
3584 | 0 | clearStbRowsDataContext(pStbRowsCxt); |
3585 | |
|
3586 | 0 | return code; |
3587 | 0 | } |
3588 | | |
3589 | | static int parseOneRow(SInsertParseContext* pCxt, const char** pSql, STableDataCxt* pTableCxt, bool* pGotRow, |
3590 | 0 | SToken* pToken) { |
3591 | 0 | SBoundColInfo* pCols = &pTableCxt->boundColsInfo; |
3592 | 0 | SSchema* pSchemas = getTableColumnSchema(pTableCxt->pMeta); |
3593 | 0 | const SSchemaExt* pExtSchemas = getTableColumnExtSchema(pTableCxt->pMeta); |
3594 | |
|
3595 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
3596 | | // 1. set the parsed value from sql string |
3597 | 0 | for (int i = 0; i < pCols->numOfBound && TSDB_CODE_SUCCESS == code; ++i) { |
3598 | 0 | const char* pOrigSql = *pSql; |
3599 | 0 | bool ignoreComma = false; |
3600 | 0 | NEXT_TOKEN_WITH_PREV_EXT(*pSql, *pToken, &ignoreComma); |
3601 | 0 | if (ignoreComma) { |
3602 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "invalid data or symbol", pOrigSql); |
3603 | 0 | break; |
3604 | 0 | } |
3605 | | |
3606 | 0 | SSchema* pSchema = &pSchemas[pCols->pColIndex[i]]; |
3607 | 0 | const SSchemaExt* pExtSchema = pExtSchemas + pCols->pColIndex[i]; |
3608 | 0 | SColVal* pVal = taosArrayGet(pTableCxt->pValues, pCols->pColIndex[i]); |
3609 | |
|
3610 | 0 | if (pToken->type == TK_NK_QUESTION) { |
3611 | 0 | if (pCxt->pComCxt->stmtBindVersion == 0) { |
3612 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "? only used in stmt", pToken->z); |
3613 | 0 | break; |
3614 | 0 | } |
3615 | 0 | } else { |
3616 | 0 | if (TK_NK_RP == pToken->type) { |
3617 | 0 | code = generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_INVALID_COLUMNS_NUM); |
3618 | 0 | break; |
3619 | 0 | } |
3620 | | |
3621 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3622 | 0 | code = parseValueToken(pCxt, pSql, pToken, pSchema, pExtSchema, getTableInfo(pTableCxt->pMeta).precision, pVal); |
3623 | |
|
3624 | 0 | if (TSDB_CODE_SUCCESS == code && pCxt->pComCxt->stmtBindVersion!=0) { |
3625 | 0 | if (NULL == pCxt->pParsedValues) { |
3626 | 0 | pCxt->pParsedValues = |
3627 | 0 | tSimpleHashInit(pCols->numOfBound, taosGetDefaultHashFunction(TSDB_DATA_TYPE_SMALLINT)); |
3628 | 0 | if (NULL == pCxt->pParsedValues) { |
3629 | 0 | code = terrno; |
3630 | 0 | break; |
3631 | 0 | } |
3632 | 0 | tSimpleHashSetFreeFp(pCxt->pParsedValues, destroyColVal); |
3633 | 0 | } |
3634 | | |
3635 | 0 | SColVal clonedVal = *pVal; |
3636 | 0 | if (COL_VAL_IS_VALUE(&clonedVal) && IS_VAR_DATA_TYPE(clonedVal.value.type)) { |
3637 | 0 | clonedVal.value.pData = taosMemoryMalloc(clonedVal.value.nData); |
3638 | 0 | if (NULL == clonedVal.value.pData) { |
3639 | 0 | code = terrno; |
3640 | 0 | break; |
3641 | 0 | } |
3642 | 0 | memcpy(clonedVal.value.pData, pVal->value.pData, clonedVal.value.nData); |
3643 | 0 | } |
3644 | | |
3645 | 0 | code = tSimpleHashPut(pCxt->pParsedValues, &pSchema->colId, sizeof(int16_t), &clonedVal, sizeof(SColVal)); |
3646 | 0 | if (code != TSDB_CODE_SUCCESS) { |
3647 | 0 | if (COL_VAL_IS_VALUE(&clonedVal) && IS_VAR_DATA_TYPE(clonedVal.value.type)) { |
3648 | 0 | taosMemoryFree(clonedVal.value.pData); |
3649 | 0 | clonedVal.value.pData = NULL; |
3650 | 0 | } |
3651 | 0 | break; |
3652 | 0 | } |
3653 | 0 | } |
3654 | 0 | } |
3655 | 0 | } |
3656 | | |
3657 | 0 | if (TSDB_CODE_SUCCESS == code && i < pCols->numOfBound - 1) { |
3658 | 0 | NEXT_VALID_TOKEN(*pSql, *pToken); |
3659 | 0 | if (TK_NK_COMMA != pToken->type) { |
3660 | | // ')' before all columns are consumed means the client has a stale schema with |
3661 | | // more columns than the server (e.g. after DROP COLUMN from another connection). |
3662 | | // Any other unexpected token is a genuine syntax error in the SQL. |
3663 | 0 | if (!pCxt->forceUpdate && TK_NK_RP == pToken->type) { |
3664 | 0 | code = TSDB_CODE_TDB_INVALID_TABLE_SCHEMA_VER; |
3665 | 0 | parserWarn("QID:0x%" PRIx64 ", column number is smaller than %d, need retry", pCxt->pComCxt->requestId, |
3666 | 0 | pCols->numOfBound); |
3667 | 0 | } else { |
3668 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, ", expected", pToken->z); |
3669 | 0 | } |
3670 | 0 | } |
3671 | 0 | } |
3672 | 0 | } |
3673 | |
|
3674 | 0 | if (TSDB_CODE_SUCCESS == code && pCxt->pComCxt->stmtBindVersion == 0) { |
3675 | 0 | SRow** pRow = taosArrayReserve(pTableCxt->pData->aRowP, 1); |
3676 | 0 | if (pTableCxt->hasBlob) { |
3677 | 0 | SRowBuildScanInfo sinfo = {.hasBlob = 1, .scanType = ROW_BUILD_UPDATE}; |
3678 | 0 | if (pTableCxt->pData->pBlobSet == NULL) { |
3679 | 0 | code = tBlobSetCreate(1024, 0, &pTableCxt->pData->pBlobSet); |
3680 | 0 | TAOS_CHECK_RETURN(code); |
3681 | 0 | } |
3682 | 0 | code = tRowBuildWithBlob(pTableCxt->pValues, pTableCxt->pSchema, pRow, pTableCxt->pData->pBlobSet, &sinfo); |
3683 | 0 | } else { |
3684 | 0 | SRowBuildScanInfo sinfo = {0}; |
3685 | 0 | code = tRowBuild(pTableCxt->pValues, pTableCxt->pSchema, pRow, &sinfo); |
3686 | 0 | } |
3687 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3688 | 0 | SRowKey key; |
3689 | 0 | tRowGetKey(*pRow, &key); |
3690 | 0 | insCheckTableDataOrder(pTableCxt, &key); |
3691 | 0 | } |
3692 | 0 | } |
3693 | | |
3694 | 0 | if (TSDB_CODE_SUCCESS == code && pCxt->pComCxt->stmtBindVersion == 0) { |
3695 | 0 | *pGotRow = true; |
3696 | 0 | } |
3697 | |
|
3698 | 0 | clearColValArray(pTableCxt->pValues); |
3699 | |
|
3700 | 0 | return code; |
3701 | 0 | } |
3702 | | |
3703 | | // pSql -> (field1_value, ...) [(field1_value2, ...) ...] |
3704 | | static int32_t parseValues(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SRowsDataContext rowsDataCxt, |
3705 | 0 | int32_t* pNumOfRows, SToken* pToken) { |
3706 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
3707 | |
|
3708 | 0 | (*pNumOfRows) = 0; |
3709 | 0 | while (TSDB_CODE_SUCCESS == code) { |
3710 | 0 | int32_t index = 0; |
3711 | 0 | NEXT_TOKEN_KEEP_SQL(pStmt->pSql, *pToken, index); |
3712 | 0 | if (TK_NK_LP != pToken->type) { |
3713 | 0 | break; |
3714 | 0 | } |
3715 | 0 | pStmt->pSql += index; |
3716 | |
|
3717 | 0 | bool gotRow = false; |
3718 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3719 | 0 | if (!pStmt->stbSyntax) { |
3720 | 0 | code = parseOneRow(pCxt, &pStmt->pSql, rowsDataCxt.pTableDataCxt, &gotRow, pToken); |
3721 | 0 | } else { |
3722 | | // foreach subtable |
3723 | 0 | STableDataCxt* pTableDataCxt = NULL; |
3724 | 0 | code = parseOneStbRow(pCxt, pStmt, &pStmt->pSql, rowsDataCxt.pStbRowsCxt, &gotRow, pToken, &pTableDataCxt); |
3725 | 0 | } |
3726 | 0 | } |
3727 | |
|
3728 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3729 | 0 | NEXT_VALID_TOKEN(pStmt->pSql, *pToken); |
3730 | 0 | if (TK_NK_COMMA == pToken->type) { |
3731 | 0 | code = generateSyntaxErrMsg(&pCxt->msg, TSDB_CODE_PAR_INVALID_COLUMNS_NUM); |
3732 | 0 | } else if (TK_NK_RP != pToken->type) { |
3733 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, ") expected", pToken->z); |
3734 | 0 | } |
3735 | 0 | } |
3736 | |
|
3737 | 0 | if (TSDB_CODE_SUCCESS == code && gotRow) { |
3738 | 0 | (*pNumOfRows)++; |
3739 | 0 | } |
3740 | 0 | } |
3741 | |
|
3742 | 0 | if (TSDB_CODE_SUCCESS == code && 0 == (*pNumOfRows) && |
3743 | 0 | (!TSDB_QUERY_HAS_TYPE(pStmt->insertType, TSDB_QUERY_TYPE_STMT_INSERT))) { |
3744 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "no any data points", NULL); |
3745 | 0 | } |
3746 | 0 | return code; |
3747 | 0 | } |
3748 | | |
3749 | | // VALUES (field1_value, ...) [(field1_value2, ...) ...] |
3750 | | static int32_t parseValuesClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SRowsDataContext rowsDataContext, |
3751 | 0 | SToken* pToken) { |
3752 | 0 | int32_t numOfRows = 0; |
3753 | 0 | int32_t code = parseValues(pCxt, pStmt, rowsDataContext, &numOfRows, pToken); |
3754 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3755 | 0 | pStmt->totalRowsNum += numOfRows; |
3756 | 0 | pStmt->totalTbNum += 1; |
3757 | 0 | TSDB_QUERY_SET_TYPE(pStmt->insertType, TSDB_QUERY_TYPE_INSERT); |
3758 | 0 | } |
3759 | 0 | return code; |
3760 | 0 | } |
3761 | | |
3762 | | // Simplified CSV parser - only handles newlines within quotes |
3763 | 0 | static int32_t csvParserReadLine(SCsvParser* parser) { |
3764 | 0 | if (!parser) { |
3765 | 0 | return TSDB_CODE_INVALID_PARA; |
3766 | 0 | } |
3767 | | |
3768 | 0 | size_t lineLen = 0; |
3769 | 0 | bool inQuotes = false; |
3770 | 0 | char currentQuote = '\0'; // Track which quote character we're inside |
3771 | 0 | bool fieldStart = true; // Track if we're at the start of a field |
3772 | 0 | bool afterQuote = false; // Track if we just closed a quoted field |
3773 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
3774 | |
|
3775 | 0 | while (true) { |
3776 | | // Fill buffer if needed |
3777 | 0 | if (parser->bufferPos >= parser->bufferLen) { |
3778 | 0 | code = csvParserFillBuffer(parser); |
3779 | 0 | if (code != TSDB_CODE_SUCCESS) { |
3780 | 0 | break; |
3781 | 0 | } |
3782 | 0 | if (parser->bufferPos >= parser->bufferLen && parser->eof) { |
3783 | | // End of file |
3784 | 0 | if (lineLen == 0) { |
3785 | 0 | code = TSDB_CODE_TSC_QUERY_CANCELLED; // Use this to indicate EOF |
3786 | 0 | } else if (inQuotes) { |
3787 | | // Unclosed quote at end of file |
3788 | 0 | code = TSDB_CODE_TSC_INVALID_INPUT; |
3789 | 0 | } |
3790 | 0 | break; |
3791 | 0 | } |
3792 | 0 | } |
3793 | | |
3794 | 0 | char ch = parser->buffer[parser->bufferPos++]; |
3795 | | |
3796 | | // Handle quotes - support both single and double quotes |
3797 | 0 | if (!inQuotes && (ch == CSV_QUOTE_SINGLE || ch == CSV_QUOTE_DOUBLE)) { |
3798 | | // Quote can only start a field at the beginning of a field |
3799 | 0 | if (!fieldStart) { |
3800 | | // Invalid: quote appears in the middle of an unquoted field |
3801 | 0 | code = TSDB_CODE_TSC_INVALID_INPUT; |
3802 | 0 | break; |
3803 | 0 | } |
3804 | | // Starting a quoted section |
3805 | 0 | inQuotes = true; |
3806 | 0 | currentQuote = ch; |
3807 | 0 | fieldStart = false; |
3808 | 0 | afterQuote = false; |
3809 | 0 | } else if (inQuotes && ch == currentQuote) { |
3810 | | // Check for escaped quote (double quote) |
3811 | 0 | if (parser->bufferPos < parser->bufferLen && parser->buffer[parser->bufferPos] == currentQuote) { |
3812 | | // Escaped quote - keep both quotes in line for subsequent processing |
3813 | | // Ensure enough space for both quote characters |
3814 | 0 | code = csvParserExpandLineBuffer(parser, lineLen + 2); |
3815 | 0 | if (code != TSDB_CODE_SUCCESS) { |
3816 | 0 | break; |
3817 | 0 | } |
3818 | | |
3819 | | // Add the first quote character to the line |
3820 | 0 | parser->lineBuffer[lineLen++] = ch; |
3821 | | |
3822 | | // Consume and add the second quote character |
3823 | 0 | parser->bufferPos++; |
3824 | 0 | ch = parser->buffer[parser->bufferPos - 1]; // The second quote |
3825 | 0 | parser->lineBuffer[lineLen++] = ch; |
3826 | 0 | continue; |
3827 | 0 | } else { |
3828 | | // End of quoted section |
3829 | 0 | inQuotes = false; |
3830 | 0 | currentQuote = '\0'; |
3831 | 0 | afterQuote = true; // Mark that we just closed a quote |
3832 | 0 | } |
3833 | 0 | } else if (ch == CSV_DEFAULT_DELIMITER && !inQuotes) { |
3834 | | // Comma marks the start of a new field |
3835 | 0 | fieldStart = true; |
3836 | 0 | afterQuote = false; |
3837 | 0 | } else if (ch != '\r' && ch != '\n' && ch != ' ') { |
3838 | | // Non-space, non-newline character processing |
3839 | | // Check if we have invalid characters after closing quote |
3840 | 0 | if (afterQuote) { |
3841 | | // Invalid: non-whitespace character after closing quote |
3842 | | // RFC 4180: after closing quote, only comma, space, or line end is allowed |
3843 | 0 | code = TSDB_CODE_TSC_INVALID_INPUT; |
3844 | 0 | break; |
3845 | 0 | } |
3846 | | // Non-space character means we're no longer at field start |
3847 | 0 | fieldStart = false; |
3848 | 0 | afterQuote = false; |
3849 | 0 | } |
3850 | | |
3851 | | // Handle newlines |
3852 | 0 | if (ch == '\n' && !inQuotes) { |
3853 | | // End of line (not inside quotes) |
3854 | 0 | break; |
3855 | 0 | } |
3856 | | |
3857 | | // Skip \r characters only when outside quotes |
3858 | 0 | if (ch == '\r' && !inQuotes) { |
3859 | 0 | continue; |
3860 | 0 | } |
3861 | | |
3862 | | // Expand buffer if needed |
3863 | 0 | code = csvParserExpandLineBuffer(parser, lineLen + 1); |
3864 | 0 | if (code != TSDB_CODE_SUCCESS) { |
3865 | 0 | break; |
3866 | 0 | } |
3867 | | |
3868 | | // Add character to line |
3869 | 0 | parser->lineBuffer[lineLen++] = ch; |
3870 | 0 | } |
3871 | |
|
3872 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3873 | 0 | parser->lineBuffer[lineLen] = '\0'; |
3874 | 0 | } else if (code == TSDB_CODE_TSC_INVALID_INPUT) { |
3875 | | // Set a more descriptive error for invalid CSV format |
3876 | 0 | parser->lineBuffer[lineLen] = '\0'; // Null-terminate for error reporting |
3877 | 0 | } |
3878 | |
|
3879 | 0 | return code; |
3880 | 0 | } |
3881 | | |
3882 | | static int32_t parseCsvFile(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SRowsDataContext rowsDataCxt, |
3883 | 0 | int32_t* pNumOfRows) { |
3884 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
3885 | 0 | (*pNumOfRows) = 0; |
3886 | | |
3887 | | // Initialize or use existing CSV parser in pStmt |
3888 | 0 | if (pStmt->pCsvParser == NULL) { |
3889 | | // First time - allocate and initialize CSV parser |
3890 | 0 | pStmt->pCsvParser = taosMemoryMalloc(sizeof(SCsvParser)); |
3891 | 0 | if (!pStmt->pCsvParser) { |
3892 | 0 | return terrno; |
3893 | 0 | } |
3894 | 0 | code = csvParserInit(pStmt->pCsvParser, pStmt->fp); |
3895 | 0 | if (code != TSDB_CODE_SUCCESS) { |
3896 | 0 | taosMemoryFree(pStmt->pCsvParser); |
3897 | 0 | pStmt->pCsvParser = NULL; |
3898 | 0 | return code; |
3899 | 0 | } |
3900 | 0 | } |
3901 | | // If pStmt->pCsvParser exists, we continue from where we left off |
3902 | | |
3903 | 0 | bool firstLine = (pStmt->fileProcessing == false); |
3904 | 0 | pStmt->fileProcessing = false; |
3905 | |
|
3906 | 0 | while (TSDB_CODE_SUCCESS == code) { |
3907 | | // Read one line from CSV using the parser in pStmt |
3908 | 0 | code = csvParserReadLine(pStmt->pCsvParser); |
3909 | 0 | if (code == TSDB_CODE_TSC_QUERY_CANCELLED) { |
3910 | | // End of file |
3911 | 0 | code = TSDB_CODE_SUCCESS; |
3912 | 0 | break; |
3913 | 0 | } |
3914 | 0 | if (code == TSDB_CODE_TSC_INVALID_INPUT) { |
3915 | | // Invalid CSV format detected |
3916 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "Invalid CSV format", pStmt->pCsvParser->lineBuffer); |
3917 | 0 | break; |
3918 | 0 | } |
3919 | 0 | if (code != TSDB_CODE_SUCCESS) { |
3920 | 0 | break; |
3921 | 0 | } |
3922 | | |
3923 | | // Skip empty lines |
3924 | 0 | if (!pStmt->pCsvParser->lineBuffer || strlen(pStmt->pCsvParser->lineBuffer) == 0) { |
3925 | 0 | firstLine = false; |
3926 | 0 | continue; |
3927 | 0 | } |
3928 | | |
3929 | 0 | bool gotRow = false; |
3930 | 0 | SToken token; |
3931 | 0 | (void)strtolower(pStmt->pCsvParser->lineBuffer, pStmt->pCsvParser->lineBuffer); |
3932 | 0 | const char* pRow = pStmt->pCsvParser->lineBuffer; |
3933 | |
|
3934 | 0 | if (!pStmt->stbSyntax) { |
3935 | 0 | code = parseOneRow(pCxt, (const char**)&pRow, rowsDataCxt.pTableDataCxt, &gotRow, &token); |
3936 | 0 | } else { |
3937 | 0 | STableDataCxt* pTableDataCxt = NULL; |
3938 | 0 | code = parseOneStbRow(pCxt, pStmt, (const char**)&pRow, rowsDataCxt.pStbRowsCxt, &gotRow, &token, &pTableDataCxt); |
3939 | 0 | if (code == TSDB_CODE_SUCCESS) { |
3940 | 0 | SStbRowsDataContext* pStbRowsCxt = rowsDataCxt.pStbRowsCxt; |
3941 | 0 | void* pData = pTableDataCxt; |
3942 | 0 | code = taosHashPut(pStmt->pTableCxtHashObj, &pStbRowsCxt->pCtbMeta->uid, sizeof(pStbRowsCxt->pCtbMeta->uid), |
3943 | 0 | &pData, POINTER_BYTES); |
3944 | 0 | if (TSDB_CODE_SUCCESS != code) { |
3945 | 0 | break; |
3946 | 0 | } |
3947 | 0 | } |
3948 | 0 | } |
3949 | | |
3950 | 0 | if (code && firstLine) { |
3951 | 0 | firstLine = false; |
3952 | 0 | code = 0; |
3953 | 0 | continue; |
3954 | 0 | } |
3955 | | |
3956 | 0 | if (TSDB_CODE_SUCCESS == code && gotRow) { |
3957 | 0 | (*pNumOfRows)++; |
3958 | 0 | } |
3959 | |
|
3960 | 0 | if (TSDB_CODE_SUCCESS == code && (*pNumOfRows) >= tsMaxInsertBatchRows) { |
3961 | | // Reached batch limit - keep the parser in pStmt for next batch |
3962 | 0 | pStmt->fileProcessing = true; |
3963 | 0 | break; |
3964 | 0 | } |
3965 | 0 | firstLine = false; |
3966 | 0 | } |
3967 | | |
3968 | | // Don't destroy the parser here - it will be cleaned up when file processing is complete |
3969 | |
|
3970 | 0 | parserDebug("QID:0x%" PRIx64 ", %d rows have been parsed", pCxt->pComCxt->requestId, *pNumOfRows); |
3971 | |
|
3972 | 0 | if (TSDB_CODE_SUCCESS == code && 0 == (*pNumOfRows) && 0 == pStmt->totalRowsNum && |
3973 | 0 | (!TSDB_QUERY_HAS_TYPE(pStmt->insertType, TSDB_QUERY_TYPE_STMT_INSERT)) && !pStmt->fileProcessing) { |
3974 | 0 | code = buildSyntaxErrMsg(&pCxt->msg, "no any data points", NULL); |
3975 | 0 | } |
3976 | 0 | return code; |
3977 | 0 | } |
3978 | | |
3979 | | static int32_t parseDataFromFileImpl(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, |
3980 | 0 | SRowsDataContext rowsDataCxt) { |
3981 | | // init only for file |
3982 | 0 | if (NULL == pStmt->pTableCxtHashObj) { |
3983 | 0 | pStmt->pTableCxtHashObj = taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK); |
3984 | 0 | if (!pStmt->pTableCxtHashObj) { |
3985 | 0 | return terrno; |
3986 | 0 | } |
3987 | 0 | } |
3988 | 0 | int32_t numOfRows = 0; |
3989 | 0 | int32_t code = parseCsvFile(pCxt, pStmt, rowsDataCxt, &numOfRows); |
3990 | 0 | if (TSDB_CODE_SUCCESS == code) { |
3991 | 0 | pStmt->totalRowsNum += numOfRows; |
3992 | 0 | pStmt->totalTbNum += 1; |
3993 | 0 | TSDB_QUERY_SET_TYPE(pStmt->insertType, TSDB_QUERY_TYPE_FILE_INSERT); |
3994 | 0 | if (rowsDataCxt.pTableDataCxt && rowsDataCxt.pTableDataCxt->pData) { |
3995 | 0 | rowsDataCxt.pTableDataCxt->pData->flags |= SUBMIT_REQ_FROM_FILE; |
3996 | 0 | } |
3997 | 0 | if (!pStmt->fileProcessing) { |
3998 | | // File processing is complete, clean up saved CSV parser |
3999 | 0 | destroySavedCsvParser(pStmt); |
4000 | 0 | code = taosCloseFile(&pStmt->fp); |
4001 | 0 | if (TSDB_CODE_SUCCESS != code) { |
4002 | 0 | parserWarn("QID:0x%" PRIx64 ", failed to close file.", pCxt->pComCxt->requestId); |
4003 | 0 | } |
4004 | 0 | } else { |
4005 | 0 | parserDebug("QID:0x%" PRIx64 ", insert from csv. File is too large, do it in batches.", pCxt->pComCxt->requestId); |
4006 | 0 | } |
4007 | 0 | if (pStmt->insertType != TSDB_QUERY_TYPE_FILE_INSERT) { |
4008 | 0 | destroySavedCsvParser(pStmt); |
4009 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "keyword VALUES or FILE is exclusive", NULL); |
4010 | 0 | } |
4011 | 0 | } else { |
4012 | | // On error, also clean up saved CSV parser |
4013 | 0 | destroySavedCsvParser(pStmt); |
4014 | 0 | if (code == TSDB_CODE_TSC_SQL_SYNTAX_ERROR) { |
4015 | 0 | return code; |
4016 | 0 | } |
4017 | 0 | return buildInvalidOperationMsg(&pCxt->msg, tstrerror(code)); |
4018 | 0 | } |
4019 | | |
4020 | | // just record pTableCxt whose data come from file |
4021 | 0 | if (!pStmt->stbSyntax && numOfRows > 0) { |
4022 | 0 | void* pData = rowsDataCxt.pTableDataCxt; |
4023 | 0 | code = taosHashPut(pStmt->pTableCxtHashObj, &pStmt->pTableMeta->uid, sizeof(pStmt->pTableMeta->uid), &pData, |
4024 | 0 | POINTER_BYTES); |
4025 | 0 | } |
4026 | |
|
4027 | 0 | return code; |
4028 | 0 | } |
4029 | | |
4030 | | static int32_t parseDataFromFile(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SToken* pFilePath, |
4031 | 0 | SRowsDataContext rowsDataCxt) { |
4032 | 0 | char filePathStr[PATH_MAX + 16] = {0}; |
4033 | 0 | if (TK_NK_STRING == pFilePath->type) { |
4034 | 0 | (void)trimString(pFilePath->z, pFilePath->n, filePathStr, sizeof(filePathStr)); |
4035 | 0 | if (strlen(filePathStr) >= PATH_MAX) { |
4036 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "file path is too long, max length is 4096", pFilePath->z); |
4037 | 0 | } |
4038 | 0 | } else { |
4039 | 0 | if (pFilePath->n >= PATH_MAX) { |
4040 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "file path is too long, max length is 4096", pFilePath->z); |
4041 | 0 | } |
4042 | 0 | tstrncpy(filePathStr, pFilePath->z, pFilePath->n < sizeof(filePathStr) ? pFilePath->n + 1 : sizeof(filePathStr)); |
4043 | 0 | } |
4044 | 0 | pStmt->fp = taosOpenFile(filePathStr, TD_FILE_READ); |
4045 | 0 | if (NULL == pStmt->fp) { |
4046 | 0 | return terrno; |
4047 | 0 | } |
4048 | | |
4049 | 0 | return parseDataFromFileImpl(pCxt, pStmt, rowsDataCxt); |
4050 | 0 | } |
4051 | | |
4052 | | static int32_t parseFileClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SRowsDataContext rowsDataCxt, |
4053 | 0 | SToken* pToken) { |
4054 | 0 | if (tsUseAdapter) { |
4055 | 0 | return buildInvalidOperationMsg(&pCxt->msg, "proxy mode does not support csv loading"); |
4056 | 0 | } |
4057 | | |
4058 | 0 | NEXT_TOKEN(pStmt->pSql, *pToken); |
4059 | 0 | if (0 == pToken->n || (TK_NK_STRING != pToken->type && TK_NK_ID != pToken->type)) { |
4060 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "file path is required following keyword FILE", pToken->z); |
4061 | 0 | } |
4062 | 0 | return parseDataFromFile(pCxt, pStmt, pToken, rowsDataCxt); |
4063 | 0 | } |
4064 | | |
4065 | | // VALUES (field1_value, ...) [(field1_value2, ...) ...] | FILE csv_file_path |
4066 | 0 | static int32_t parseDataClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SRowsDataContext rowsDataCxt) { |
4067 | 0 | SToken token; |
4068 | 0 | NEXT_TOKEN(pStmt->pSql, token); |
4069 | 0 | switch (token.type) { |
4070 | 0 | case TK_VALUES: |
4071 | 0 | if (TSDB_QUERY_HAS_TYPE(pStmt->insertType, TSDB_QUERY_TYPE_FILE_INSERT)) { |
4072 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "keyword VALUES or FILE is exclusive", token.z); |
4073 | 0 | } |
4074 | 0 | return parseValuesClause(pCxt, pStmt, rowsDataCxt, &token); |
4075 | 0 | case TK_FILE: |
4076 | 0 | return parseFileClause(pCxt, pStmt, rowsDataCxt, &token); |
4077 | 0 | default: |
4078 | 0 | break; |
4079 | 0 | } |
4080 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "keyword VALUES or FILE is expected", token.z); |
4081 | 0 | } |
4082 | | |
4083 | 5 | static void destroyStbRowsDataContext(SStbRowsDataContext* pStbRowsCxt) { |
4084 | 5 | if (pStbRowsCxt == NULL) return; |
4085 | 0 | clearStbRowsDataContext(pStbRowsCxt); |
4086 | 0 | taosArrayDestroy(pStbRowsCxt->aColVals); |
4087 | 0 | pStbRowsCxt->aColVals = NULL; |
4088 | 0 | taosArrayDestroy(pStbRowsCxt->aTagVals); |
4089 | 0 | pStbRowsCxt->aTagVals = NULL; |
4090 | 0 | taosArrayDestroy(pStbRowsCxt->aTagNames); |
4091 | 0 | pStbRowsCxt->aTagNames = NULL; |
4092 | 0 | qDestroyBoundColInfo(&pStbRowsCxt->boundColsInfo); |
4093 | 0 | tTagFree(pStbRowsCxt->pTag); |
4094 | 0 | pStbRowsCxt->pTag = NULL; |
4095 | 0 | taosMemoryFreeClear(pStbRowsCxt->pCtbMeta); |
4096 | 0 | tdDestroySVCreateTbReq(pStbRowsCxt->pCreateCtbReq); |
4097 | 0 | taosMemoryFreeClear(pStbRowsCxt->pCreateCtbReq); |
4098 | 0 | } |
4099 | | |
4100 | 0 | static int32_t constructStbRowsDataContext(SVnodeModifyOpStmt* pStmt, SStbRowsDataContext** ppStbRowsCxt) { |
4101 | 0 | SStbRowsDataContext* pStbRowsCxt = taosMemoryCalloc(1, sizeof(SStbRowsDataContext)); |
4102 | 0 | if (!pStbRowsCxt) { |
4103 | 0 | return terrno; |
4104 | 0 | } |
4105 | 0 | tNameAssign(&pStbRowsCxt->stbName, &pStmt->targetTableName); |
4106 | 0 | int32_t code = collectUseTable(&pStbRowsCxt->stbName, pStmt->pTableNameHashObj); |
4107 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4108 | 0 | code = collectUseDatabase(&pStbRowsCxt->stbName, pStmt->pDbFNameHashObj); |
4109 | 0 | } |
4110 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4111 | 0 | pStbRowsCxt->ctbName.type = TSDB_TABLE_NAME_T; |
4112 | 0 | pStbRowsCxt->ctbName.acctId = pStbRowsCxt->stbName.acctId; |
4113 | 0 | memcpy(pStbRowsCxt->ctbName.dbname, pStbRowsCxt->stbName.dbname, sizeof(pStbRowsCxt->stbName.dbname)); |
4114 | |
|
4115 | 0 | pStbRowsCxt->pTagCond = pStmt->pTagCond; |
4116 | 0 | pStbRowsCxt->pStbMeta = pStmt->pTableMeta; |
4117 | |
|
4118 | 0 | code = cloneTableMeta(pStbRowsCxt->pStbMeta, &pStbRowsCxt->pCtbMeta); |
4119 | 0 | } |
4120 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4121 | 0 | pStbRowsCxt->pCtbMeta->tableType = TSDB_CHILD_TABLE; |
4122 | 0 | pStbRowsCxt->pCtbMeta->suid = pStbRowsCxt->pStbMeta->uid; |
4123 | |
|
4124 | 0 | pStbRowsCxt->aTagNames = taosArrayInit(8, TSDB_COL_NAME_LEN + sizeof(col_id_t)); |
4125 | 0 | if (!pStbRowsCxt->aTagNames) { |
4126 | 0 | code = terrno; |
4127 | 0 | } |
4128 | 0 | } |
4129 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4130 | 0 | pStbRowsCxt->aTagVals = taosArrayInit(8, sizeof(STagVal)); |
4131 | 0 | if (!pStbRowsCxt->aTagVals) { |
4132 | 0 | code = terrno; |
4133 | 0 | } |
4134 | 0 | } |
4135 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4136 | | // col values and bound cols info of STableDataContext is not used |
4137 | 0 | pStbRowsCxt->aColVals = taosArrayInit(getNumOfColumns(pStbRowsCxt->pStbMeta), sizeof(SColVal)); |
4138 | 0 | if (!pStbRowsCxt->aColVals) code = terrno; |
4139 | 0 | } |
4140 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4141 | 0 | code = insInitColValues(pStbRowsCxt->pStbMeta, pStbRowsCxt->aColVals); |
4142 | 0 | } |
4143 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4144 | 0 | STableComInfo tblInfo = getTableInfo(pStmt->pTableMeta); |
4145 | 0 | code = insInitBoundColsInfo(tblInfo.numOfColumns + tblInfo.numOfTags + 1, &pStbRowsCxt->boundColsInfo); |
4146 | 0 | } |
4147 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4148 | 0 | *ppStbRowsCxt = pStbRowsCxt; |
4149 | 0 | } else { |
4150 | 0 | clearStbRowsDataContext(pStbRowsCxt); |
4151 | 0 | } |
4152 | 0 | return code; |
4153 | 0 | } |
4154 | | |
4155 | 0 | static int32_t parseInsertStbClauseBottom(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
4156 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
4157 | 0 | if (!pStmt->pBoundCols) { |
4158 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "(...tbname, ts...) bounded cols is expected for supertable insertion", |
4159 | 0 | pStmt->pSql); |
4160 | 0 | } |
4161 | | |
4162 | 0 | SStbRowsDataContext* pStbRowsCxt = NULL; |
4163 | 0 | code = constructStbRowsDataContext(pStmt, &pStbRowsCxt); |
4164 | |
|
4165 | 0 | if (code == TSDB_CODE_SUCCESS) { |
4166 | 0 | code = parseBoundColumns(pCxt, pStmt, &pStmt->pBoundCols, BOUND_ALL_AND_TBNAME, pStmt->pTableMeta, |
4167 | 0 | &pStbRowsCxt->boundColsInfo); |
4168 | 0 | pStbRowsCxt->hasTimestampTag = false; |
4169 | 0 | for (int32_t i = 0; i < pStbRowsCxt->boundColsInfo.numOfBound; ++i) { |
4170 | 0 | int16_t schemaIndex = pStbRowsCxt->boundColsInfo.pColIndex[i]; |
4171 | 0 | if (schemaIndex != getTbnameSchemaIndex(pStmt->pTableMeta) && schemaIndex >= getNumOfColumns(pStmt->pTableMeta)) { |
4172 | 0 | if (pStmt->pTableMeta->schema[schemaIndex].type == TSDB_DATA_TYPE_TIMESTAMP) { |
4173 | 0 | pStbRowsCxt->hasTimestampTag = true; |
4174 | 0 | } |
4175 | 0 | if (pStmt->pTableMeta->schema[schemaIndex].type == TSDB_DATA_TYPE_JSON) { |
4176 | 0 | pStbRowsCxt->isJsonTag = true; |
4177 | 0 | } |
4178 | 0 | } |
4179 | 0 | } |
4180 | 0 | pStmt->pStbRowsCxt = pStbRowsCxt; |
4181 | 0 | } |
4182 | |
|
4183 | 0 | if (code == TSDB_CODE_SUCCESS) { |
4184 | 0 | SRowsDataContext rowsDataCxt; |
4185 | 0 | rowsDataCxt.pStbRowsCxt = pStbRowsCxt; |
4186 | 0 | code = parseDataClause(pCxt, pStmt, rowsDataCxt); |
4187 | 0 | } |
4188 | |
|
4189 | 0 | return code; |
4190 | 0 | } |
4191 | | |
4192 | | // input pStmt->pSql: |
4193 | | // 1. [(tag1_name, ...)] ... |
4194 | | // 2. VALUES ... | FILE ... |
4195 | 0 | static int32_t parseInsertTableClauseBottom(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
4196 | 0 | if (!pStmt->stbSyntax) { |
4197 | 0 | STableDataCxt* pTableCxt = NULL; |
4198 | 0 | int32_t code = parseSchemaClauseBottom(pCxt, pStmt, &pTableCxt); |
4199 | 0 | SRowsDataContext rowsDataCxt; |
4200 | 0 | rowsDataCxt.pTableDataCxt = pTableCxt; |
4201 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4202 | 0 | code = parseDataClause(pCxt, pStmt, rowsDataCxt); |
4203 | 0 | } |
4204 | 0 | return code; |
4205 | 0 | } else { |
4206 | 0 | int32_t code = parseInsertStbClauseBottom(pCxt, pStmt); |
4207 | 0 | return code; |
4208 | 0 | } |
4209 | 0 | } |
4210 | | |
4211 | 0 | static void resetEnvPreTable(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
4212 | 0 | qDestroyBoundColInfo(&pCxt->tags); |
4213 | 0 | clearInsertParseContext(pCxt); |
4214 | 0 | taosMemoryFreeClear(pStmt->pTableMeta); |
4215 | 0 | if (pStmt->pTagCond) nodesDestroyNode(pStmt->pTagCond); |
4216 | 0 | if (pStmt->pPrivCols) taosArrayDestroy(pStmt->pPrivCols); |
4217 | 0 | if (pStmt->pTableTag) taosArrayDestroy(pStmt->pTableTag); |
4218 | 0 | tdDestroySVCreateTbReq(pStmt->pCreateTblReq); |
4219 | 0 | taosMemoryFreeClear(pStmt->pCreateTblReq); |
4220 | 0 | pCxt->missCache = false; |
4221 | 0 | pCxt->usingDuplicateTable = false; |
4222 | 0 | pStmt->pBoundCols = NULL; |
4223 | 0 | pStmt->pPrivCols = NULL; |
4224 | 0 | pStmt->pTagCond = NULL; |
4225 | 0 | pStmt->usingTableProcessing = false; |
4226 | 0 | pStmt->fileProcessing = false; |
4227 | 0 | pStmt->usingTableName.type = 0; |
4228 | |
|
4229 | 0 | destroyStbRowsDataContext(pStmt->pStbRowsCxt); |
4230 | 0 | taosMemoryFreeClear(pStmt->pStbRowsCxt); |
4231 | 0 | pStmt->stbSyntax = false; |
4232 | 0 | } |
4233 | | |
4234 | | // input pStmt->pSql: [(field1_name, ...)] [ USING ... ] VALUES ... | FILE ... |
4235 | 0 | static int32_t parseInsertTableClause(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SToken* pTbName) { |
4236 | 0 | resetEnvPreTable(pCxt, pStmt); |
4237 | 0 | int32_t code = parseSchemaClauseTop(pCxt, pStmt, pTbName); |
4238 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->missCache) { |
4239 | 0 | code = parseInsertTableClauseBottom(pCxt, pStmt); |
4240 | 0 | } |
4241 | |
|
4242 | 0 | return code; |
4243 | 0 | } |
4244 | | |
4245 | | static int32_t checkTableClauseFirstToken(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SToken* pTbName, |
4246 | 0 | bool* pHasData) { |
4247 | | // no data in the sql string anymore. |
4248 | 0 | if (0 == pTbName->n) { |
4249 | 0 | if (0 != pTbName->type && '\0' != pStmt->pSql[0]) { |
4250 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "invalid table name", pTbName->z); |
4251 | 0 | } |
4252 | | |
4253 | 0 | if (0 == pStmt->totalRowsNum && (!TSDB_QUERY_HAS_TYPE(pStmt->insertType, TSDB_QUERY_TYPE_STMT_INSERT))) { |
4254 | 0 | return buildInvalidOperationMsg(&pCxt->msg, "no data in sql"); |
4255 | 0 | } |
4256 | | |
4257 | 0 | *pHasData = false; |
4258 | 0 | return TSDB_CODE_SUCCESS; |
4259 | 0 | } |
4260 | | |
4261 | 0 | if (TSDB_QUERY_HAS_TYPE(pStmt->insertType, TSDB_QUERY_TYPE_STMT_INSERT) && pStmt->totalTbNum > 0) { |
4262 | 0 | return buildInvalidOperationMsg(&pCxt->msg, "single table allowed in one stmt"); |
4263 | 0 | } |
4264 | | |
4265 | 0 | if (TK_NK_QUESTION == pTbName->type) { |
4266 | 0 | pCxt->stmtTbNameFlag &= ~IS_FIXED_VALUE; |
4267 | 0 | if (pCxt->pComCxt->stmtBindVersion == 0) { |
4268 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "? only used in stmt", pTbName->z); |
4269 | 0 | } |
4270 | | |
4271 | 0 | char* tbName = NULL; |
4272 | 0 | int32_t code = (*pCxt->pComCxt->pStmtCb->getTbNameFn)(pCxt->pComCxt->pStmtCb->pStmt, &tbName); |
4273 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4274 | 0 | pCxt->stmtTbNameFlag |= HAS_BIND_VALUE; |
4275 | 0 | pTbName->z = tbName; |
4276 | 0 | pTbName->n = strlen(tbName); |
4277 | 0 | } |
4278 | 0 | if (code == TSDB_CODE_TSC_STMT_TBNAME_ERROR) { |
4279 | 0 | pCxt->stmtTbNameFlag &= ~HAS_BIND_VALUE; |
4280 | 0 | code = TSDB_CODE_SUCCESS; |
4281 | 0 | } |
4282 | 0 | return code; |
4283 | 0 | } |
4284 | | |
4285 | 0 | if (TK_NK_ID != pTbName->type && TK_NK_STRING != pTbName->type && TK_NK_QUESTION != pTbName->type) { |
4286 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "table_name is expected", pTbName->z); |
4287 | 0 | } |
4288 | | |
4289 | | // db.? situation,ensure that the only thing following the '.' mark is '?' |
4290 | 0 | char* tbNameAfterDbName = strnchr(pTbName->z, '.', pTbName->n, true); |
4291 | 0 | if (tbNameAfterDbName != NULL) { |
4292 | 0 | if (*(tbNameAfterDbName + 1) == '?') { |
4293 | 0 | pCxt->stmtTbNameFlag &= ~IS_FIXED_VALUE; |
4294 | 0 | char* tbName = NULL; |
4295 | 0 | if (NULL == pCxt->pComCxt->pStmtCb) { |
4296 | 0 | return buildSyntaxErrMsg(&pCxt->msg, "? only used in stmt", pTbName->z); |
4297 | 0 | } |
4298 | 0 | int32_t code = (*pCxt->pComCxt->pStmtCb->getTbNameFn)(pCxt->pComCxt->pStmtCb->pStmt, &tbName); |
4299 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4300 | 0 | pCxt->stmtTbNameFlag |= HAS_BIND_VALUE; |
4301 | 0 | pTbName->z = tbName; |
4302 | 0 | pTbName->n = strlen(tbName); |
4303 | 0 | } |
4304 | 0 | if (code == TSDB_CODE_TSC_STMT_TBNAME_ERROR) { |
4305 | 0 | pCxt->stmtTbNameFlag &= ~HAS_BIND_VALUE; |
4306 | 0 | code = TSDB_CODE_SUCCESS; |
4307 | 0 | } |
4308 | 0 | } else { |
4309 | 0 | pCxt->stmtTbNameFlag |= IS_FIXED_VALUE; |
4310 | 0 | parserTrace("QID:0x%" PRIx64 ", stmt tbname:%s is specified in sql", pCxt->pComCxt->requestId, pTbName->z); |
4311 | 0 | *pHasData = true; |
4312 | 0 | } |
4313 | 0 | return TSDB_CODE_SUCCESS; |
4314 | 0 | } |
4315 | | |
4316 | 0 | if (TK_NK_ID == pTbName->type) { |
4317 | 0 | pCxt->stmtTbNameFlag |= IS_FIXED_VALUE; |
4318 | 0 | } |
4319 | |
|
4320 | 0 | *pHasData = true; |
4321 | 0 | return TSDB_CODE_SUCCESS; |
4322 | 0 | } |
4323 | | |
4324 | 0 | static int32_t setStmtInfo(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
4325 | 0 | SBoundColInfo* tags = taosMemoryMalloc(sizeof(pCxt->tags)); |
4326 | 0 | if (NULL == tags) { |
4327 | 0 | return terrno; |
4328 | 0 | } |
4329 | 0 | memcpy(tags, &pCxt->tags, sizeof(pCxt->tags)); |
4330 | |
|
4331 | 0 | SStmtCallback* pStmtCb = pCxt->pComCxt->pStmtCb; |
4332 | 0 | int32_t code = (*pStmtCb->setInfoFn)(pStmtCb->pStmt, pStmt->pTableMeta, tags, &pCxt->pParsedValues, |
4333 | 0 | &pStmt->targetTableName, pStmt->usingTableProcessing, pStmt->pVgroupsHashObj, |
4334 | 0 | pStmt->pTableBlockHashObj, pStmt->usingTableName.tname, pCxt->stmtTbNameFlag); |
4335 | |
|
4336 | 0 | memset(&pCxt->tags, 0, sizeof(pCxt->tags)); |
4337 | 0 | pStmt->pVgroupsHashObj = NULL; |
4338 | 0 | pStmt->pTableBlockHashObj = NULL; |
4339 | 0 | return code; |
4340 | 0 | } |
4341 | | |
4342 | 0 | static int32_t parseInsertBodyBottom(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
4343 | 0 | if (TSDB_QUERY_HAS_TYPE(pStmt->insertType, TSDB_QUERY_TYPE_STMT_INSERT)) { |
4344 | 0 | return setStmtInfo(pCxt, pStmt); |
4345 | 0 | } |
4346 | | |
4347 | | // release old array alloced by merge |
4348 | 0 | pStmt->freeArrayFunc(pStmt->pVgDataBlocks); |
4349 | 0 | pStmt->pVgDataBlocks = NULL; |
4350 | |
|
4351 | 0 | bool fileOnly = (pStmt->insertType == TSDB_QUERY_TYPE_FILE_INSERT); |
4352 | 0 | if (fileOnly) { |
4353 | | // none data, skip merge & buildvgdata |
4354 | 0 | if (0 == taosHashGetSize(pStmt->pTableCxtHashObj)) { |
4355 | 0 | pCxt->needRequest = false; |
4356 | 0 | return TSDB_CODE_SUCCESS; |
4357 | 0 | } |
4358 | 0 | } |
4359 | | |
4360 | | // merge according to vgId |
4361 | 0 | int32_t code = insMergeTableDataCxt(fileOnly ? pStmt->pTableCxtHashObj : pStmt->pTableBlockHashObj, |
4362 | 0 | &pStmt->pVgDataBlocks, pStmt->fileProcessing); |
4363 | | // clear tmp hashobj only |
4364 | 0 | taosHashClear(pStmt->pTableCxtHashObj); |
4365 | |
|
4366 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4367 | 0 | code = insBuildVgDataBlocks(pStmt->pVgroupsHashObj, pStmt->pVgDataBlocks, &pStmt->pDataBlocks, false); |
4368 | 0 | } |
4369 | |
|
4370 | 0 | return code; |
4371 | 0 | } |
4372 | | |
4373 | | // tb_name |
4374 | | // [USING stb_name [(tag1_name, ...)] TAGS (tag1_value, ...)] |
4375 | | // [(field1_name, ...)] |
4376 | | // VALUES (field1_value, ...) [(field1_value2, ...) ...] | FILE csv_file_path |
4377 | | // [...]; |
4378 | 0 | static int32_t parseInsertBody(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
4379 | 0 | SToken token; |
4380 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
4381 | 0 | bool hasData = true; |
4382 | | // for each table |
4383 | 0 | while (TSDB_CODE_SUCCESS == code && hasData && !pCxt->missCache && !pStmt->fileProcessing) { |
4384 | | // pStmt->pSql -> tb_name ... |
4385 | 0 | NEXT_TOKEN(pStmt->pSql, token); |
4386 | 0 | code = checkTableClauseFirstToken(pCxt, pStmt, &token, &hasData); |
4387 | 0 | if (TSDB_CODE_SUCCESS == code && hasData) { |
4388 | 0 | code = parseInsertTableClause(pCxt, pStmt, &token); |
4389 | 0 | } |
4390 | |
|
4391 | 0 | if (TSDB_CODE_SUCCESS == code && pStmt->pTableMeta && |
4392 | 0 | (((pStmt->pTableMeta->virtualStb == 1) && (pStmt->pTableMeta->tableType == TSDB_SUPER_TABLE)) || |
4393 | 0 | (pStmt->pTableMeta->tableType == TSDB_VIRTUAL_NORMAL_TABLE || |
4394 | 0 | pStmt->pTableMeta->tableType == TSDB_VIRTUAL_CHILD_TABLE))) { |
4395 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "Virtual table can not be written"); |
4396 | 0 | } |
4397 | 0 | } |
4398 | |
|
4399 | 0 | if (TSDB_CODE_SUCCESS == code && !pCxt->missCache) { |
4400 | 0 | code = parseInsertBodyBottom(pCxt, pStmt); |
4401 | 0 | } |
4402 | 0 | return code; |
4403 | 0 | } |
4404 | | |
4405 | 0 | static void destroySubTableHashElem(void* p) { taosMemoryFree(*(STableMeta**)p); } |
4406 | | |
4407 | 5 | static int32_t createVnodeModifOpStmt(SInsertParseContext* pCxt, bool reentry, SNode** pOutput) { |
4408 | 5 | SVnodeModifyOpStmt* pStmt = NULL; |
4409 | 5 | int32_t code = nodesMakeNode(QUERY_NODE_VNODE_MODIFY_STMT, (SNode**)&pStmt); |
4410 | 5 | if (NULL == pStmt) { |
4411 | 0 | return code; |
4412 | 0 | } |
4413 | | |
4414 | 5 | if (pCxt->pComCxt->pStmtCb) { |
4415 | 0 | TSDB_QUERY_SET_TYPE(pStmt->insertType, TSDB_QUERY_TYPE_STMT_INSERT); |
4416 | 0 | } |
4417 | 5 | pStmt->pSql = pCxt->pComCxt->pSql; |
4418 | | |
4419 | 5 | pStmt->freeHashFunc = insDestroyTableDataCxtHashMap; |
4420 | 5 | pStmt->freeArrayFunc = insDestroyVgroupDataCxtList; |
4421 | 5 | pStmt->freeStbRowsCxtFunc = destroyStbRowsDataContext; |
4422 | 5 | pStmt->pCsvParser = NULL; |
4423 | | |
4424 | 5 | if (!reentry) { |
4425 | 5 | pStmt->pVgroupsHashObj = taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), true, HASH_ENTRY_LOCK); |
4426 | 5 | if (pCxt->pComCxt->pStmtCb) { |
4427 | 0 | pStmt->pTableBlockHashObj = |
4428 | 0 | taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK); |
4429 | 5 | } else { |
4430 | 5 | pStmt->pTableBlockHashObj = |
4431 | 5 | taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BIGINT), true, HASH_NO_LOCK); |
4432 | 5 | } |
4433 | 5 | } |
4434 | 5 | pStmt->pSubTableHashObj = taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_VARCHAR), true, HASH_NO_LOCK); |
4435 | 5 | pStmt->pSuperTableHashObj = taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_VARCHAR), true, HASH_NO_LOCK); |
4436 | 5 | pStmt->pTableNameHashObj = taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_VARCHAR), true, HASH_NO_LOCK); |
4437 | 5 | pStmt->pDbFNameHashObj = taosHashInit(64, taosGetDefaultHashFunction(TSDB_DATA_TYPE_VARCHAR), true, HASH_NO_LOCK); |
4438 | 5 | if ((!reentry && (NULL == pStmt->pVgroupsHashObj || NULL == pStmt->pTableBlockHashObj)) || |
4439 | 5 | NULL == pStmt->pSubTableHashObj || NULL == pStmt->pTableNameHashObj || NULL == pStmt->pDbFNameHashObj) { |
4440 | 0 | nodesDestroyNode((SNode*)pStmt); |
4441 | 0 | return TSDB_CODE_OUT_OF_MEMORY; |
4442 | 0 | } |
4443 | | |
4444 | 5 | taosHashSetFreeFp(pStmt->pSubTableHashObj, destroySubTableHashElem); |
4445 | 5 | taosHashSetFreeFp(pStmt->pSuperTableHashObj, destroySubTableHashElem); |
4446 | | |
4447 | 5 | *pOutput = (SNode*)pStmt; |
4448 | 5 | return TSDB_CODE_SUCCESS; |
4449 | 5 | } |
4450 | | |
4451 | 5 | static int32_t createInsertQuery(SInsertParseContext* pCxt, SQuery** pOutput) { |
4452 | 5 | SQuery* pQuery = NULL; |
4453 | 5 | int32_t code = nodesMakeNode(QUERY_NODE_QUERY, (SNode**)&pQuery); |
4454 | 5 | if (NULL == pQuery) { |
4455 | 0 | return code; |
4456 | 0 | } |
4457 | | |
4458 | 5 | pQuery->execMode = QUERY_EXEC_MODE_SCHEDULE; |
4459 | 5 | pQuery->haveResultSet = false; |
4460 | 5 | pQuery->msgType = TDMT_VND_SUBMIT; |
4461 | | |
4462 | 5 | code = createVnodeModifOpStmt(pCxt, false, &pQuery->pRoot); |
4463 | 5 | if (TSDB_CODE_SUCCESS == code) { |
4464 | 5 | *pOutput = pQuery; |
4465 | 5 | } else { |
4466 | 0 | nodesDestroyNode((SNode*)pQuery); |
4467 | 0 | } |
4468 | 5 | return code; |
4469 | 5 | } |
4470 | | |
4471 | 0 | static int32_t checkAuthFromMetaData(SInsertParseContext* pCxt, const SMetaData* pMetaData, SVnodeModifyOpStmt* pStmt) { |
4472 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
4473 | 0 | if (1 != taosArrayGetSize(pMetaData->pUser)) { |
4474 | 0 | code = TSDB_CODE_INTERNAL_ERROR; |
4475 | 0 | uError("unexpected meta data size: %d since %s", (int32_t)taosArrayGetSize(pMetaData->pUser), tstrerror(code)); |
4476 | 0 | return code; |
4477 | 0 | } |
4478 | | |
4479 | 0 | SMetaRes* pRes = TARRAY_GET_ELEM(pMetaData->pUser, 0); |
4480 | 0 | if (TSDB_CODE_SUCCESS == pRes->code) { |
4481 | 0 | SUserAuthRes* pAuth = pRes->pRes; |
4482 | 0 | pRes->code = nodesCloneNode(pAuth->pCond[AUTH_RES_BASIC], &pStmt->pTagCond); |
4483 | 0 | if (pAuth->pCols && (TSDB_CODE_SUCCESS == pRes->code)) { |
4484 | 0 | if (!(pStmt->pPrivCols = taosArrayDup(pAuth->pCols, NULL))) { |
4485 | 0 | pRes->code = terrno; |
4486 | 0 | } |
4487 | 0 | } |
4488 | 0 | if (TSDB_CODE_SUCCESS == pRes->code) { |
4489 | 0 | return pAuth->pass[AUTH_RES_BASIC] ? TSDB_CODE_SUCCESS : TSDB_CODE_PAR_PERMISSION_DENIED; |
4490 | 0 | } |
4491 | 0 | } |
4492 | 0 | return pRes->code; |
4493 | 0 | } |
4494 | | |
4495 | 0 | static int32_t getTableMetaFromMetaData(const SArray* pTables, STableMeta** pMeta) { |
4496 | 0 | if (1 != taosArrayGetSize(pTables) && 2 != taosArrayGetSize(pTables)) { |
4497 | 0 | return TSDB_CODE_FAILED; |
4498 | 0 | } |
4499 | | |
4500 | 0 | taosMemoryFreeClear(*pMeta); |
4501 | 0 | SMetaRes* pRes = taosArrayGet(pTables, 0); |
4502 | 0 | if (TSDB_CODE_SUCCESS == pRes->code) { |
4503 | 0 | *pMeta = tableMetaDup((const STableMeta*)pRes->pRes); |
4504 | 0 | if (NULL == *pMeta) { |
4505 | 0 | return TSDB_CODE_OUT_OF_MEMORY; |
4506 | 0 | } |
4507 | 0 | } |
4508 | 0 | return pRes->code; |
4509 | 0 | } |
4510 | | |
4511 | 0 | static int32_t checkAuthUseDb(SParseContext* pCxt, SName* pTbName, bool isAudit) { |
4512 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
4513 | 0 | SUserAuthInfo authInfo = {.userId = pCxt->userId, .privType = PRIV_DB_USE, .objType = PRIV_OBJ_DB}; |
4514 | 0 | (void)snprintf(authInfo.user, sizeof(authInfo.user), "%s", pCxt->pUser); |
4515 | 0 | (void)snprintf(authInfo.tbName.dbname, sizeof(authInfo.tbName.dbname), "%s", pTbName->dbname); |
4516 | 0 | authInfo.tbName.acctId = pTbName->acctId; |
4517 | 0 | authInfo.tbName.type = TSDB_DB_NAME_T; |
4518 | 0 | authInfo.useDb = isAudit ? AUTH_OWNED_MASK : (AUTH_AUTHORIZED_MASK | AUTH_OWNED_MASK); |
4519 | |
|
4520 | 0 | if (isAudit) { |
4521 | | // for audit db, recheck insert privilege |
4522 | 0 | authInfo.privType = PRIV_AUDIT_TBL_INSERT; |
4523 | 0 | authInfo.objType = PRIV_OBJ_TBL; |
4524 | 0 | } |
4525 | |
|
4526 | 0 | SUserAuthRes authRes = {0}; |
4527 | 0 | SRequestConnInfo conn = {.pTrans = pCxt->pTransporter, |
4528 | 0 | .requestId = pCxt->requestId, |
4529 | 0 | .requestObjRefId = pCxt->requestRid, |
4530 | 0 | .mgmtEps = pCxt->mgmtEpSet}; |
4531 | 0 | code = catalogChkAuth(pCxt->pCatalog, &conn, &authInfo, &authRes); // cache used firstly inside the function |
4532 | 0 | if (TSDB_CODE_SUCCESS == code && !authRes.pass[AUTH_RES_BASIC]) { |
4533 | 0 | code = TSDB_CODE_PAR_DB_USE_PERMISSION_DENIED; |
4534 | 0 | } |
4535 | 0 | return code; |
4536 | 0 | } |
4537 | | |
4538 | 0 | static int32_t addTableVgroupFromMetaData(const SArray* pTables, SVnodeModifyOpStmt* pStmt, bool isStb) { |
4539 | 0 | if (1 != taosArrayGetSize(pTables)) { |
4540 | 0 | return TSDB_CODE_FAILED; |
4541 | 0 | } |
4542 | | |
4543 | 0 | SMetaRes* pRes = taosArrayGet(pTables, 0); |
4544 | 0 | if (TSDB_CODE_SUCCESS != pRes->code) { |
4545 | 0 | return pRes->code; |
4546 | 0 | } |
4547 | | |
4548 | 0 | SVgroupInfo* pVg = pRes->pRes; |
4549 | 0 | if (isStb) { |
4550 | 0 | pStmt->pTableMeta->vgId = pVg->vgId; |
4551 | 0 | } |
4552 | 0 | return taosHashPut(pStmt->pVgroupsHashObj, (const char*)&pVg->vgId, sizeof(pVg->vgId), (char*)pVg, |
4553 | 0 | sizeof(SVgroupInfo)); |
4554 | 0 | } |
4555 | | |
4556 | 0 | static int32_t buildTagNameFromMeta(STableMeta* pMeta, SArray** pTagName) { |
4557 | 0 | *pTagName = taosArrayInit(pMeta->tableInfo.numOfTags, TSDB_COL_NAME_LEN); |
4558 | 0 | if (NULL == *pTagName) { |
4559 | 0 | return terrno; |
4560 | 0 | } |
4561 | 0 | SSchema* pSchema = getTableTagSchema(pMeta); |
4562 | 0 | int32_t code = 0; |
4563 | 0 | for (int32_t i = 0; i < pMeta->tableInfo.numOfTags; ++i) { |
4564 | 0 | if (NULL == taosArrayPush(*pTagName, pSchema[i].name)) { |
4565 | 0 | code = terrno; |
4566 | 0 | taosArrayDestroy(*pTagName); |
4567 | 0 | *pTagName = NULL; |
4568 | 0 | break; |
4569 | 0 | } |
4570 | 0 | } |
4571 | 0 | return code; |
4572 | 0 | } |
4573 | | |
4574 | 0 | static int32_t checkSubtablePrivilegeForTable(const SArray* pTables, SVnodeModifyOpStmt* pStmt) { |
4575 | | #ifdef TD_ENTERPRISE |
4576 | | if (1 != taosArrayGetSize(pTables)) { |
4577 | | return TSDB_CODE_FAILED; |
4578 | | } |
4579 | | |
4580 | | SMetaRes* pRes = taosArrayGet(pTables, 0); |
4581 | | if (TSDB_CODE_SUCCESS != pRes->code) { |
4582 | | return pRes->code; |
4583 | | } |
4584 | | |
4585 | | SArray* pTagName = NULL; |
4586 | | int32_t code = buildTagNameFromMeta(pStmt->pTableMeta, &pTagName); |
4587 | | if (TSDB_CODE_SUCCESS == code) { |
4588 | | code = checkSubtablePrivilege((SArray*)pRes->pRes, pTagName, &pStmt->pTagCond); |
4589 | | } |
4590 | | taosArrayDestroy(pTagName); |
4591 | | return code; |
4592 | | #else |
4593 | 0 | return TSDB_CODE_SUCCESS; |
4594 | 0 | #endif |
4595 | 0 | } |
4596 | | |
4597 | | static int32_t processTableSchemaFromMetaData(SInsertParseContext* pCxt, const SMetaData* pMetaData, |
4598 | 0 | SVnodeModifyOpStmt* pStmt, bool isStb) { |
4599 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
4600 | 0 | if (!isStb && TSDB_SUPER_TABLE == pStmt->pTableMeta->tableType) { |
4601 | 0 | code = buildInvalidOperationMsg(&pCxt->msg, "insert data into super table is not supported"); |
4602 | 0 | } |
4603 | | |
4604 | | // Async second-round: pTableMeta[1] is ctb meta; absent/error means ctb does not exist yet. |
4605 | 0 | if (TSDB_CODE_SUCCESS == code && isStb && taosArrayGetSize(pMetaData->pTableMeta) >= 2) { |
4606 | 0 | SMetaRes* pCtbRes = TARRAY_GET_ELEM(pMetaData->pTableMeta, 1); |
4607 | 0 | if (pCtbRes->code != TSDB_CODE_SUCCESS || pCtbRes->pRes == NULL) { |
4608 | 0 | code = checkCreateTableAuth(pCxt->pComCxt, &pStmt->targetTableName, NULL); |
4609 | 0 | } |
4610 | 0 | } |
4611 | |
|
4612 | 0 | if (TSDB_CODE_SUCCESS == code && isStb) { |
4613 | 0 | code = storeChildTableMeta(pCxt, pStmt); |
4614 | 0 | } |
4615 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4616 | 0 | code = addTableVgroupFromMetaData(pMetaData->pTableHash, pStmt, isStb); |
4617 | 0 | } |
4618 | 0 | if (TSDB_CODE_SUCCESS == code && !isStb && NULL != pStmt->pTagCond) { |
4619 | 0 | code = checkSubtablePrivilegeForTable(pMetaData->pTableTag, pStmt); |
4620 | 0 | } |
4621 | 0 | return code; |
4622 | 0 | } |
4623 | | |
4624 | 0 | static void destoryTablesReq(void* p) { |
4625 | 0 | STablesReq* pRes = (STablesReq*)p; |
4626 | 0 | taosArrayDestroy(pRes->pTables); |
4627 | 0 | } |
4628 | | |
4629 | 0 | static void clearCatalogReq(SCatalogReq* pCatalogReq) { |
4630 | 0 | if (NULL == pCatalogReq) { |
4631 | 0 | return; |
4632 | 0 | } |
4633 | | |
4634 | 0 | taosArrayDestroyEx(pCatalogReq->pTableMeta, destoryTablesReq); |
4635 | 0 | pCatalogReq->pTableMeta = NULL; |
4636 | 0 | taosArrayDestroyEx(pCatalogReq->pTableHash, destoryTablesReq); |
4637 | 0 | pCatalogReq->pTableHash = NULL; |
4638 | 0 | taosArrayDestroy(pCatalogReq->pUser); |
4639 | 0 | pCatalogReq->pUser = NULL; |
4640 | 0 | taosArrayDestroy(pCatalogReq->pTableTag); |
4641 | 0 | pCatalogReq->pTableTag = NULL; |
4642 | 0 | } |
4643 | | |
4644 | | static int32_t setVnodeModifOpStmt(SInsertParseContext* pCxt, SCatalogReq* pCatalogReq, const SMetaData* pMetaData, |
4645 | 0 | SVnodeModifyOpStmt* pStmt) { |
4646 | 0 | clearCatalogReq(pCatalogReq); |
4647 | 0 | if (pStmt->pPrivCols) { |
4648 | 0 | taosArrayDestroy(pStmt->pPrivCols); |
4649 | 0 | pStmt->pPrivCols = NULL; |
4650 | 0 | } |
4651 | 0 | if (pStmt->pTagCond) { |
4652 | 0 | nodesDestroyNode(pStmt->pTagCond); |
4653 | 0 | pStmt->pTagCond = NULL; |
4654 | 0 | } |
4655 | |
|
4656 | | #ifdef TD_ENTERPRISE |
4657 | | // Federated query: reject INSERT into external source tables (Phase 2 check). |
4658 | | // In Phase 1, the dbname was registered in pCatalogReq->pExtSourceCheck. |
4659 | | // If the catalog resolved it as a valid ext source, reject the write here. |
4660 | | if (tsFederatedQueryEnable && pMetaData != NULL && pMetaData->pExtSourceInfo != NULL) { |
4661 | | int32_t nSrc = (int32_t)taosArrayGetSize(pMetaData->pExtSourceInfo); |
4662 | | for (int32_t i = 0; i < nSrc; ++i) { |
4663 | | SMetaRes* pRes = taosArrayGet(pMetaData->pExtSourceInfo, i); |
4664 | | if (pRes && pRes->code == TSDB_CODE_SUCCESS && pRes->pRes != NULL) { |
4665 | | return TSDB_CODE_EXT_SOURCE_WRITE_NOT_SUPPORTED; |
4666 | | } |
4667 | | } |
4668 | | } |
4669 | | #endif |
4670 | |
|
4671 | 0 | int32_t code = checkAuthFromMetaData(pCxt, pMetaData, pStmt); |
4672 | |
|
4673 | 0 | if (code == TSDB_CODE_SUCCESS) { |
4674 | 0 | code = getTableMetaFromMetaData(pMetaData->pTableMeta, &pStmt->pTableMeta); |
4675 | 0 | } |
4676 | | #ifdef TD_ENTERPRISE |
4677 | | // MAC NWD+NRU: retry path — metadata was just fetched from server, check must be enforced here too. |
4678 | | // (The primary NWD check in getTargetTableSchema is guarded by !missCache and therefore does not run |
4679 | | // when the first parse attempt hits a cache miss, e.g. fresh connections used by "taos -s".) |
4680 | | if (pCxt->pComCxt->macMode && code == TSDB_CODE_SUCCESS && pStmt->pTableMeta != NULL) { |
4681 | | int8_t secLvl = pStmt->pTableMeta->secLvl; |
4682 | | if (secLvl >= 0) { |
4683 | | if (pCxt->pComCxt->minSecLevel > secLvl) { |
4684 | | code = TSDB_CODE_MAC_NO_WRITE_DOWN; // NWD violation |
4685 | | } else if (pCxt->pComCxt->maxSecLevel < secLvl) { |
4686 | | code = TSDB_CODE_MAC_INSUFFICIENT_LEVEL; // NRU violation |
4687 | | } |
4688 | | } |
4689 | | } |
4690 | | #endif |
4691 | 0 | if (code == TSDB_CODE_SUCCESS) { |
4692 | 0 | code = checkAuthUseDb(pCxt->pComCxt, &pStmt->targetTableName, pStmt->pTableMeta->isAudit); |
4693 | 0 | } |
4694 | 0 | if (code == TSDB_CODE_SUCCESS) { |
4695 | 0 | if (pStmt->pTableMeta->tableType == TSDB_SUPER_TABLE && !pStmt->usingTableProcessing) { |
4696 | 0 | pStmt->stbSyntax = true; |
4697 | 0 | } |
4698 | 0 | if (!pStmt->stbSyntax) { |
4699 | 0 | if (pStmt->usingTableProcessing) { |
4700 | 0 | return processTableSchemaFromMetaData(pCxt, pMetaData, pStmt, true); |
4701 | 0 | } |
4702 | 0 | return processTableSchemaFromMetaData(pCxt, pMetaData, pStmt, false); |
4703 | 0 | } |
4704 | 0 | } |
4705 | 0 | return code; |
4706 | 0 | } |
4707 | | |
4708 | 0 | static int32_t resetVnodeModifOpStmt(SInsertParseContext* pCxt, SQuery* pQuery) { |
4709 | 0 | nodesDestroyNode(pQuery->pRoot); |
4710 | |
|
4711 | 0 | int32_t code = createVnodeModifOpStmt(pCxt, true, &pQuery->pRoot); |
4712 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4713 | 0 | SVnodeModifyOpStmt* pStmt = (SVnodeModifyOpStmt*)pQuery->pRoot; |
4714 | |
|
4715 | 0 | code = (*pCxt->pComCxt->pStmtCb->getExecInfoFn)(pCxt->pComCxt->pStmtCb->pStmt, &pStmt->pVgroupsHashObj, |
4716 | 0 | &pStmt->pTableBlockHashObj); |
4717 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4718 | 0 | if (NULL == pStmt->pVgroupsHashObj) { |
4719 | 0 | pStmt->pVgroupsHashObj = taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), true, HASH_NO_LOCK); |
4720 | 0 | } |
4721 | 0 | if (NULL == pStmt->pTableBlockHashObj) { |
4722 | 0 | pStmt->pTableBlockHashObj = |
4723 | 0 | taosHashInit(128, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK); |
4724 | 0 | } |
4725 | 0 | if (NULL == pStmt->pVgroupsHashObj || NULL == pStmt->pTableBlockHashObj) { |
4726 | 0 | code = TSDB_CODE_OUT_OF_MEMORY; |
4727 | 0 | } |
4728 | 0 | } |
4729 | 0 | } |
4730 | |
|
4731 | 0 | return code; |
4732 | 0 | } |
4733 | | |
4734 | | static int32_t initInsertQuery(SInsertParseContext* pCxt, SCatalogReq* pCatalogReq, const SMetaData* pMetaData, |
4735 | 5 | SQuery** pQuery) { |
4736 | 5 | if (NULL == *pQuery) { |
4737 | 5 | return createInsertQuery(pCxt, pQuery); |
4738 | 5 | } |
4739 | | |
4740 | 0 | if (NULL != pCxt->pComCxt->pStmtCb) { |
4741 | 0 | return resetVnodeModifOpStmt(pCxt, *pQuery); |
4742 | 0 | } |
4743 | | |
4744 | 0 | SVnodeModifyOpStmt* pStmt = (SVnodeModifyOpStmt*)(*pQuery)->pRoot; |
4745 | |
|
4746 | 0 | if (!pStmt->fileProcessing) { |
4747 | 0 | return setVnodeModifOpStmt(pCxt, pCatalogReq, pMetaData, pStmt); |
4748 | 0 | } |
4749 | | |
4750 | 0 | return TSDB_CODE_SUCCESS; |
4751 | 0 | } |
4752 | | |
4753 | 0 | static int32_t setRefreshMeta(SQuery* pQuery) { |
4754 | 0 | SVnodeModifyOpStmt* pStmt = (SVnodeModifyOpStmt*)pQuery->pRoot; |
4755 | 0 | int32_t code = 0; |
4756 | |
|
4757 | 0 | if (taosHashGetSize(pStmt->pTableNameHashObj) > 0) { |
4758 | 0 | taosArrayDestroy(pQuery->pTableList); |
4759 | 0 | pQuery->pTableList = taosArrayInit(taosHashGetSize(pStmt->pTableNameHashObj), sizeof(SName)); |
4760 | 0 | if (!pQuery->pTableList) { |
4761 | 0 | code = terrno; |
4762 | 0 | } else { |
4763 | 0 | SName* pTable = taosHashIterate(pStmt->pTableNameHashObj, NULL); |
4764 | 0 | while (NULL != pTable) { |
4765 | 0 | if (NULL == taosArrayPush(pQuery->pTableList, pTable)) { |
4766 | 0 | code = terrno; |
4767 | 0 | taosHashCancelIterate(pStmt->pTableNameHashObj, pTable); |
4768 | 0 | break; |
4769 | 0 | } |
4770 | 0 | pTable = taosHashIterate(pStmt->pTableNameHashObj, pTable); |
4771 | 0 | } |
4772 | 0 | } |
4773 | 0 | } |
4774 | |
|
4775 | 0 | if (TSDB_CODE_SUCCESS == code && taosHashGetSize(pStmt->pDbFNameHashObj) > 0) { |
4776 | 0 | taosArrayDestroy(pQuery->pDbList); |
4777 | 0 | pQuery->pDbList = taosArrayInit(taosHashGetSize(pStmt->pDbFNameHashObj), TSDB_DB_FNAME_LEN); |
4778 | 0 | if (!pQuery->pDbList) { |
4779 | 0 | code = terrno; |
4780 | 0 | } else { |
4781 | 0 | char* pDb = taosHashIterate(pStmt->pDbFNameHashObj, NULL); |
4782 | 0 | while (NULL != pDb) { |
4783 | 0 | if (NULL == taosArrayPush(pQuery->pDbList, pDb)) { |
4784 | 0 | code = terrno; |
4785 | 0 | taosHashCancelIterate(pStmt->pDbFNameHashObj, pDb); |
4786 | 0 | break; |
4787 | 0 | } |
4788 | 0 | pDb = taosHashIterate(pStmt->pDbFNameHashObj, pDb); |
4789 | 0 | } |
4790 | 0 | } |
4791 | 0 | } |
4792 | |
|
4793 | 0 | return code; |
4794 | 0 | } |
4795 | | |
4796 | | // INSERT INTO |
4797 | | // tb_name |
4798 | | // [USING stb_name [(tag1_name, ...)] TAGS (tag1_value, ...) [table_options]] |
4799 | | // [(field1_name, ...)] |
4800 | | // VALUES (field1_value, ...) [(field1_value2, ...) ...] | FILE csv_file_path |
4801 | | // [...]; |
4802 | 5 | static int32_t parseInsertSqlFromStart(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
4803 | 5 | int32_t code = skipInsertInto(&pStmt->pSql, &pCxt->msg); |
4804 | 5 | if (TSDB_CODE_SUCCESS == code) { |
4805 | 0 | code = parseInsertBody(pCxt, pStmt); |
4806 | 0 | } |
4807 | 5 | return code; |
4808 | 5 | } |
4809 | | |
4810 | 0 | static int32_t parseInsertSqlFromCsv(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
4811 | 0 | int32_t code = TSDB_CODE_SUCCESS; |
4812 | 0 | SRowsDataContext rowsDataCxt; |
4813 | |
|
4814 | 0 | if (!pStmt->stbSyntax) { |
4815 | 0 | STableDataCxt* pTableCxt = NULL; |
4816 | 0 | code = getTableDataCxt(pCxt, pStmt, &pTableCxt); |
4817 | 0 | rowsDataCxt.pTableDataCxt = pTableCxt; |
4818 | 0 | } else { |
4819 | 0 | rowsDataCxt.pStbRowsCxt = pStmt->pStbRowsCxt; |
4820 | 0 | } |
4821 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4822 | 0 | code = parseDataFromFileImpl(pCxt, pStmt, rowsDataCxt); |
4823 | 0 | } |
4824 | |
|
4825 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4826 | 0 | if (pStmt->fileProcessing) { |
4827 | 0 | code = parseInsertBodyBottom(pCxt, pStmt); |
4828 | 0 | } else { |
4829 | 0 | code = parseInsertBody(pCxt, pStmt); |
4830 | 0 | } |
4831 | 0 | } |
4832 | |
|
4833 | 0 | return code; |
4834 | 0 | } |
4835 | | |
4836 | 0 | static int32_t parseInsertSqlFromTable(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
4837 | 0 | int32_t code = parseInsertTableClauseBottom(pCxt, pStmt); |
4838 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4839 | 0 | code = parseInsertBody(pCxt, pStmt); |
4840 | 0 | } |
4841 | 0 | return code; |
4842 | 0 | } |
4843 | | |
4844 | 5 | static int32_t parseInsertSqlImpl(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) { |
4845 | 5 | if (pStmt->pSql == pCxt->pComCxt->pSql || NULL != pCxt->pComCxt->pStmtCb) { |
4846 | 5 | return parseInsertSqlFromStart(pCxt, pStmt); |
4847 | 5 | } |
4848 | | |
4849 | 0 | if (pStmt->fileProcessing) { |
4850 | 0 | return parseInsertSqlFromCsv(pCxt, pStmt); |
4851 | 0 | } |
4852 | | |
4853 | 0 | return parseInsertSqlFromTable(pCxt, pStmt); |
4854 | 0 | } |
4855 | | |
4856 | 0 | static int32_t buildUsingInsertTableReq(SName* pSName, SName* pCName, SArray** pTables) { |
4857 | 0 | if (NULL == *pTables) { |
4858 | 0 | *pTables = taosArrayInit(2, sizeof(SName)); |
4859 | 0 | if (NULL == *pTables) { |
4860 | 0 | goto _err; |
4861 | 0 | } |
4862 | 0 | } |
4863 | 0 | if (NULL == taosArrayPush(*pTables, pSName)) { |
4864 | 0 | goto _err; |
4865 | 0 | } |
4866 | 0 | if (NULL == taosArrayPush(*pTables, pCName)) { |
4867 | 0 | goto _err; |
4868 | 0 | } |
4869 | 0 | return TSDB_CODE_SUCCESS; |
4870 | | |
4871 | 0 | _err: |
4872 | 0 | if (NULL != *pTables) { |
4873 | 0 | taosArrayDestroy(*pTables); |
4874 | 0 | *pTables = NULL; |
4875 | 0 | } |
4876 | 0 | return terrno; |
4877 | 0 | } |
4878 | | |
4879 | 0 | static int32_t buildInsertTableReq(SName* pName, SArray** pTables) { |
4880 | 0 | *pTables = taosArrayInit(1, sizeof(SName)); |
4881 | 0 | if (NULL == *pTables) { |
4882 | 0 | return terrno; |
4883 | 0 | } |
4884 | | |
4885 | 0 | if (NULL == taosArrayPush(*pTables, pName)) { |
4886 | 0 | taosArrayDestroy(*pTables); |
4887 | 0 | *pTables = NULL; |
4888 | 0 | return terrno; |
4889 | 0 | } |
4890 | 0 | return TSDB_CODE_SUCCESS; |
4891 | 0 | } |
4892 | | |
4893 | 0 | static int32_t buildInsertUsingDbReq(SName* pSName, SName* pCName, SArray** pDbs) { |
4894 | 0 | if (NULL == *pDbs) { |
4895 | 0 | *pDbs = taosArrayInit(1, sizeof(STablesReq)); |
4896 | 0 | if (NULL == *pDbs) { |
4897 | 0 | return terrno; |
4898 | 0 | } |
4899 | 0 | } |
4900 | | |
4901 | 0 | STablesReq req = {0}; |
4902 | 0 | req.autoCreate = 1; |
4903 | 0 | (void)tNameGetFullDbName(pSName, req.dbFName); |
4904 | 0 | (void)tNameGetFullDbName(pCName, req.dbFName); |
4905 | |
|
4906 | 0 | int32_t code = buildUsingInsertTableReq(pSName, pCName, &req.pTables); |
4907 | 0 | if (TSDB_CODE_SUCCESS == code && NULL == taosArrayPush(*pDbs, &req)) { |
4908 | 0 | code = TSDB_CODE_OUT_OF_MEMORY; |
4909 | 0 | } |
4910 | 0 | return code; |
4911 | 0 | } |
4912 | | |
4913 | 0 | static int32_t buildInsertDbReq(SName* pName, SArray** pDbs) { |
4914 | 0 | if (NULL == *pDbs) { |
4915 | 0 | *pDbs = taosArrayInit(1, sizeof(STablesReq)); |
4916 | 0 | if (NULL == *pDbs) { |
4917 | 0 | return terrno; |
4918 | 0 | } |
4919 | 0 | } |
4920 | | |
4921 | 0 | STablesReq req = {0}; |
4922 | 0 | (void)tNameGetFullDbName(pName, req.dbFName); |
4923 | 0 | int32_t code = buildInsertTableReq(pName, &req.pTables); |
4924 | 0 | if (TSDB_CODE_SUCCESS == code && NULL == taosArrayPush(*pDbs, &req)) { |
4925 | 0 | code = TSDB_CODE_OUT_OF_MEMORY; |
4926 | 0 | } |
4927 | |
|
4928 | 0 | return code; |
4929 | 0 | } |
4930 | | |
4931 | 0 | static int32_t buildInsertUserAuthReq(SParseContext* pCxt, SName* pName, SArray** pUserAuth) { |
4932 | 0 | *pUserAuth = taosArrayInit(1, sizeof(SUserAuthInfo)); |
4933 | 0 | if (NULL == *pUserAuth) { |
4934 | 0 | return terrno; |
4935 | 0 | } |
4936 | | |
4937 | 0 | SUserAuthInfo userAuth = {.privType = PRIV_TBL_INSERT, .objType = PRIV_OBJ_TBL, .userId = pCxt->userId}; |
4938 | 0 | snprintf(userAuth.user, sizeof(userAuth.user), "%s", pCxt->pUser); |
4939 | 0 | memcpy(&userAuth.tbName, pName, sizeof(SName)); |
4940 | 0 | if (NULL == taosArrayPush(*pUserAuth, &userAuth)) { |
4941 | 0 | taosArrayDestroy(*pUserAuth); |
4942 | 0 | *pUserAuth = NULL; |
4943 | 0 | return terrno; |
4944 | 0 | } |
4945 | | |
4946 | 0 | return TSDB_CODE_SUCCESS; |
4947 | 0 | } |
4948 | | |
4949 | 0 | static int32_t buildInsertTableTagReq(SName* pName, SArray** pTables) { return buildInsertTableReq(pName, pTables); } |
4950 | | |
4951 | 0 | static int32_t buildInsertCatalogReq(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt, SCatalogReq* pCatalogReq) { |
4952 | 0 | int32_t code = buildInsertUserAuthReq( |
4953 | 0 | pCxt->pComCxt, (0 == pStmt->usingTableName.type ? &pStmt->targetTableName : &pStmt->usingTableName), |
4954 | 0 | &pCatalogReq->pUser); |
4955 | 0 | if (TSDB_CODE_SUCCESS == code && pCxt->needTableTagVal) { |
4956 | 0 | code = buildInsertTableTagReq(&pStmt->targetTableName, &pCatalogReq->pTableTag); |
4957 | 0 | } |
4958 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4959 | 0 | if (0 == pStmt->usingTableName.type) { |
4960 | 0 | code = buildInsertDbReq(&pStmt->targetTableName, &pCatalogReq->pTableMeta); |
4961 | 0 | } else { |
4962 | 0 | code = buildInsertUsingDbReq(&pStmt->usingTableName, &pStmt->targetTableName, &pCatalogReq->pTableMeta); |
4963 | 0 | } |
4964 | 0 | } |
4965 | 0 | if (TSDB_CODE_SUCCESS == code) { |
4966 | 0 | code = buildInsertDbReq(&pStmt->targetTableName, &pCatalogReq->pTableHash); |
4967 | 0 | } |
4968 | | #ifdef TD_ENTERPRISE |
4969 | | if (TSDB_CODE_SUCCESS == code && tsFederatedQueryEnable && pStmt->targetTableName.dbname[0] != '\0') { |
4970 | | if (NULL == pCatalogReq->pExtSourceCheck) { |
4971 | | pCatalogReq->pExtSourceCheck = taosArrayInit(1, TSDB_TABLE_NAME_LEN); |
4972 | | } |
4973 | | if (NULL != pCatalogReq->pExtSourceCheck) { |
4974 | | char nameBuf[TSDB_TABLE_NAME_LEN] = {0}; |
4975 | | tstrncpy(nameBuf, pStmt->targetTableName.dbname, TSDB_TABLE_NAME_LEN); |
4976 | | taosArrayPush(pCatalogReq->pExtSourceCheck, nameBuf); |
4977 | | } |
4978 | | } |
4979 | | #endif |
4980 | 0 | return code; |
4981 | 0 | } |
4982 | | |
4983 | 0 | static int32_t setNextStageInfo(SInsertParseContext* pCxt, SQuery* pQuery, SCatalogReq* pCatalogReq) { |
4984 | 0 | SVnodeModifyOpStmt* pStmt = (SVnodeModifyOpStmt*)pQuery->pRoot; |
4985 | 0 | if (pCxt->missCache) { |
4986 | 0 | parserDebug("QID:0x%" PRIx64 ", %d rows of %d tables will be inserted before obtain the cache", |
4987 | 0 | pCxt->pComCxt->requestId, pStmt->totalRowsNum, pStmt->totalTbNum); |
4988 | |
|
4989 | 0 | pQuery->execStage = QUERY_EXEC_STAGE_PARSE; |
4990 | 0 | return buildInsertCatalogReq(pCxt, pStmt, pCatalogReq); |
4991 | 0 | } |
4992 | | |
4993 | 0 | parserDebug("QID:0x%" PRIx64 ", %d rows of %d tables will be inserted", pCxt->pComCxt->requestId, pStmt->totalRowsNum, |
4994 | 0 | pStmt->totalTbNum); |
4995 | |
|
4996 | 0 | pQuery->execStage = QUERY_EXEC_STAGE_SCHEDULE; |
4997 | 0 | return TSDB_CODE_SUCCESS; |
4998 | 0 | } |
4999 | | |
5000 | 5 | int32_t parseInsertSql(SParseContext* pCxt, SQuery** pQuery, SCatalogReq* pCatalogReq, const SMetaData* pMetaData) { |
5001 | 5 | SInsertParseContext context = {.pComCxt = pCxt, |
5002 | 5 | .msg = {.buf = pCxt->pMsg, .len = pCxt->msgLen}, |
5003 | 5 | .missCache = false, |
5004 | 5 | .usingDuplicateTable = false, |
5005 | 5 | .needRequest = true, |
5006 | 5 | .forceUpdate = (NULL != pCatalogReq ? pCatalogReq->forceUpdate : false)}; |
5007 | | |
5008 | 5 | int32_t code = initInsertQuery(&context, pCatalogReq, pMetaData, pQuery); |
5009 | 5 | if (TSDB_CODE_SUCCESS == code) { |
5010 | 5 | code = parseInsertSqlImpl(&context, (SVnodeModifyOpStmt*)((*pQuery)->pRoot)); |
5011 | 5 | } |
5012 | | |
5013 | 5 | if (TSDB_CODE_SUCCESS == code) { |
5014 | 0 | code = setNextStageInfo(&context, *pQuery, pCatalogReq); |
5015 | 0 | } |
5016 | 5 | if ((TSDB_CODE_SUCCESS == code || NEED_CLIENT_HANDLE_ERROR(code)) && |
5017 | 0 | QUERY_EXEC_STAGE_SCHEDULE == (*pQuery)->execStage) { |
5018 | 0 | code = setRefreshMeta(*pQuery); |
5019 | 0 | } |
5020 | | |
5021 | 5 | qDestroyBoundColInfo(&context.tags); |
5022 | 5 | clearInsertParseContext(&context); |
5023 | | // if no data to insert, set emptyMode to avoid request server |
5024 | 5 | if (!context.needRequest) { |
5025 | 0 | (*pQuery)->execMode = QUERY_EXEC_MODE_EMPTY_RESULT; |
5026 | 0 | } |
5027 | 5 | return code; |
5028 | 5 | } |
5029 | | |
5030 | | // CSV Parser Implementation |
5031 | 0 | static int32_t csvParserInit(SCsvParser* parser, TdFilePtr pFile) { |
5032 | 0 | if (!parser || !pFile) { |
5033 | 0 | return TSDB_CODE_INVALID_PARA; |
5034 | 0 | } |
5035 | | |
5036 | 0 | memset(parser, 0, sizeof(SCsvParser)); |
5037 | | |
5038 | | // Set default CSV format |
5039 | 0 | parser->delimiter = CSV_DEFAULT_DELIMITER; |
5040 | 0 | parser->quote = CSV_QUOTE_SINGLE; // Default to single quote for TDengine compatibility |
5041 | 0 | parser->escape = CSV_ESCAPE_CHAR; |
5042 | 0 | parser->allowNewlineInField = true; |
5043 | | |
5044 | | // Initialize buffer |
5045 | 0 | parser->bufferSize = 64 * 1024; // 64KB buffer |
5046 | 0 | parser->buffer = taosMemoryMalloc(parser->bufferSize); |
5047 | 0 | if (!parser->buffer) { |
5048 | 0 | return terrno; |
5049 | 0 | } |
5050 | | |
5051 | | // Initialize line buffer for reuse |
5052 | 0 | parser->lineBufferCapacity = 64 * 1024; // Initial 64KB line buffer |
5053 | 0 | parser->lineBuffer = taosMemoryMalloc(parser->lineBufferCapacity); |
5054 | 0 | if (!parser->lineBuffer) { |
5055 | 0 | return terrno; |
5056 | 0 | } |
5057 | | |
5058 | 0 | parser->bufferPos = 0; |
5059 | 0 | parser->bufferLen = 0; |
5060 | 0 | parser->eof = false; |
5061 | 0 | parser->pFile = pFile; |
5062 | | |
5063 | | // Fill initial buffer to detect quote type |
5064 | 0 | int32_t code = csvParserFillBuffer(parser); |
5065 | 0 | if (code != TSDB_CODE_SUCCESS) { |
5066 | 0 | return code; |
5067 | 0 | } |
5068 | | |
5069 | | // Auto-detect quote character by finding the first quote in the file |
5070 | | // Skip the header line and look for the first quote character in data |
5071 | 0 | bool foundFirstQuote = false; |
5072 | 0 | bool inFirstLine = true; |
5073 | |
|
5074 | 0 | for (size_t i = 0; i < parser->bufferLen && !foundFirstQuote; i++) { |
5075 | 0 | char ch = parser->buffer[i]; |
5076 | | |
5077 | | // Skip the first line (header) |
5078 | 0 | if (inFirstLine) { |
5079 | 0 | if (ch == '\n') { |
5080 | 0 | inFirstLine = false; |
5081 | 0 | } |
5082 | 0 | continue; |
5083 | 0 | } |
5084 | | |
5085 | | // Look for the first quote character in data lines |
5086 | 0 | if (ch == CSV_QUOTE_SINGLE) { |
5087 | 0 | parser->quote = CSV_QUOTE_SINGLE; |
5088 | 0 | foundFirstQuote = true; |
5089 | 0 | } else if (ch == CSV_QUOTE_DOUBLE) { |
5090 | 0 | parser->quote = CSV_QUOTE_DOUBLE; |
5091 | 0 | foundFirstQuote = true; |
5092 | 0 | } |
5093 | 0 | } |
5094 | | |
5095 | | // If no quotes found, keep default (single quote for TDengine compatibility) |
5096 | | |
5097 | | // Reset buffer position for actual parsing |
5098 | 0 | parser->bufferPos = 0; |
5099 | |
|
5100 | 0 | return TSDB_CODE_SUCCESS; |
5101 | 0 | } |
5102 | | |
5103 | 0 | static void csvParserDestroy(SCsvParser* parser) { |
5104 | 0 | if (parser) { |
5105 | 0 | taosMemoryFree(parser->buffer); |
5106 | 0 | taosMemoryFree(parser->lineBuffer); |
5107 | 0 | memset(parser, 0, sizeof(SCsvParser)); |
5108 | 0 | } |
5109 | 0 | } |
5110 | | |
5111 | 0 | static int32_t csvParserFillBuffer(SCsvParser* parser) { |
5112 | 0 | if (!parser || parser->eof) { |
5113 | 0 | return TSDB_CODE_SUCCESS; |
5114 | 0 | } |
5115 | | |
5116 | | // Move remaining data to beginning of buffer |
5117 | | // Since this function is only called when bufferPos >= bufferLen, |
5118 | | // we can simplify by always resetting the buffer |
5119 | 0 | parser->bufferLen = 0; |
5120 | 0 | parser->bufferPos = 0; |
5121 | | |
5122 | | // Read more data |
5123 | 0 | size_t spaceLeft = parser->bufferSize - parser->bufferLen; |
5124 | 0 | if (spaceLeft > 0) { |
5125 | 0 | int64_t bytesRead = taosReadFile(parser->pFile, parser->buffer + parser->bufferLen, spaceLeft); |
5126 | 0 | if (bytesRead < 0) { |
5127 | 0 | return TAOS_SYSTEM_ERROR(errno); |
5128 | 0 | } |
5129 | 0 | if (bytesRead == 0) { |
5130 | 0 | parser->eof = true; |
5131 | 0 | } else { |
5132 | 0 | parser->bufferLen += bytesRead; |
5133 | 0 | } |
5134 | 0 | } |
5135 | | |
5136 | 0 | return TSDB_CODE_SUCCESS; |
5137 | 0 | } |
5138 | | |
5139 | | // Destroy saved CSV parser in SVnodeModifyOpStmt |
5140 | 0 | static void destroySavedCsvParser(SVnodeModifyOpStmt* pStmt) { |
5141 | 0 | if (pStmt && pStmt->pCsvParser) { |
5142 | 0 | csvParserDestroy(pStmt->pCsvParser); |
5143 | 0 | taosMemoryFree(pStmt->pCsvParser); |
5144 | 0 | pStmt->pCsvParser = NULL; |
5145 | 0 | } |
5146 | 0 | } |
5147 | | |
5148 | 0 | static int32_t csvParserExpandLineBuffer(SCsvParser* parser, size_t requiredLen) { |
5149 | 0 | if (!parser || requiredLen <= parser->lineBufferCapacity) { |
5150 | 0 | return TSDB_CODE_SUCCESS; |
5151 | 0 | } |
5152 | | |
5153 | 0 | size_t newCapacity = parser->lineBufferCapacity; |
5154 | 0 | while (newCapacity < requiredLen) { |
5155 | 0 | newCapacity *= 2; |
5156 | 0 | } |
5157 | |
|
5158 | 0 | char* newLineBuffer = taosMemoryRealloc(parser->lineBuffer, newCapacity); |
5159 | 0 | if (!newLineBuffer) { |
5160 | 0 | return TSDB_CODE_OUT_OF_MEMORY; |
5161 | 0 | } |
5162 | | |
5163 | 0 | parser->lineBuffer = newLineBuffer; |
5164 | 0 | parser->lineBufferCapacity = newCapacity; |
5165 | 0 | return TSDB_CODE_SUCCESS; |
5166 | 0 | } |