Fix TD-27403: insert error about use today + now as timestamp value

This commit is contained in:
fullhonest 2023-11-29 20:07:40 +08:00
parent bf81ea1a06
commit 72d6614b7b
4 changed files with 194 additions and 29 deletions

View File

@ -646,7 +646,7 @@ int32_t parseAbsoluteDuration(const char* token, int32_t tokenlen, int64_t* dura
/* get the basic numeric value */
int64_t timestamp = taosStr2Int64(token, &endPtr, 10);
if (timestamp < 0 || errno != 0) {
if ((timestamp == 0 && token[0] != '0') || errno != 0) {
return -1;
}

View File

@ -267,28 +267,49 @@ static int32_t parseBoundColumns(SInsertParseContext* pCxt, const char** pSql, E
return code;
}
static int parseTime(const char** end, SToken* pToken, int16_t timePrec, int64_t* time, SMsgBuf* pMsgBuf) {
int32_t index = 0;
int64_t interval;
int64_t ts = 0;
const char* pTokenEnd = *end;
static int parseTimestampOrInterval(const char** end, SToken* pToken, int16_t timePrec, int64_t* ts, int64_t* interval, SMsgBuf* pMsgBuf, bool* isTs) {
if (pToken->type == TK_NOW) {
ts = taosGetTimestamp(timePrec);
*isTs = true;
*ts = taosGetTimestamp(timePrec);
} else if (pToken->type == TK_TODAY) {
ts = taosGetTimestampToday(timePrec);
*isTs = true;
*ts = taosGetTimestampToday(timePrec);
} else if (pToken->type == TK_NK_INTEGER) {
if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, &ts)) {
*isTs = true;
if (TSDB_CODE_SUCCESS != toInteger(pToken->z, pToken->n, 10, ts)) {
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z);
}
} else if (pToken->type == TK_NK_VARIABLE) {
char unit = 0;
*isTs = false;
if (parseAbsoluteDuration(pToken->z, pToken->n, interval, &unit, timePrec) != TSDB_CODE_SUCCESS) {
return TSDB_CODE_TSC_INVALID_OPERATION;
}
} else { // parse the RFC-3339/ISO-8601 timestamp format string
if (taosParseTime(pToken->z, time, pToken->n, timePrec, tsDaylight) != TSDB_CODE_SUCCESS) {
*isTs = true;
if (taosParseTime(pToken->z, ts, pToken->n, timePrec, tsDaylight) != TSDB_CODE_SUCCESS) {
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z);
}
}
return TSDB_CODE_SUCCESS;
}
static int parseTime(const char** end, SToken* pToken, int16_t timePrec, int64_t* time, SMsgBuf* pMsgBuf) {
int32_t index = 0, i = 0;
int64_t interval = 0, tempInterval = 0;
int64_t ts = 0, tempTs = 0;
bool firstIsTS = false, secondIsTs = false;
const char* pTokenEnd = *end;
if (TSDB_CODE_SUCCESS != parseTimestampOrInterval(&pTokenEnd, pToken, timePrec, &ts, &interval, pMsgBuf, &firstIsTS)) {
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z);
}
if (firstIsTS) {
*time = ts;
}
for (int k = pToken->n; pToken->z[k] != '\0'; k++) {
if (pToken->z[k] == ' ' || pToken->z[k] == '\t') continue;
if (pToken->z[k] == '(' && pToken->z[k + 1] == ')') { // for insert NOW()/TODAY()
@ -298,45 +319,98 @@ static int parseTime(const char** end, SToken* pToken, int16_t timePrec, int64_t
}
if (pToken->z[k] == ',') {
*end = pTokenEnd;
*time = ts;
return 0;
if (!firstIsTS) {
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z);
}
*time = ts;
return TSDB_CODE_SUCCESS;
}
break;
}
/*
* time expression:
* e.g., now+12a, now-5h
*/
while (pTokenEnd[i] != '\0') {
if (pTokenEnd[i] == ' ' || pTokenEnd[i] == '\t') {
i++;
continue;
}
else if (pTokenEnd[i] == ',' || pTokenEnd[i] == ')') {
*end = pTokenEnd + i;
if (!firstIsTS) {
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z);
}
*time = ts;
return TSDB_CODE_SUCCESS;
} else {
break;
}
}
pTokenEnd = pTokenEnd + i;
index = 0;
SToken token = tStrGetToken(pTokenEnd, &index, false, NULL);
pTokenEnd += index;
if (token.type == TK_NK_MINUS || token.type == TK_NK_PLUS) {
pTokenEnd += index;
index = 0;
SToken valueToken = tStrGetToken(pTokenEnd, &index, false, NULL);
pTokenEnd += index;
char tmpTokenBuf[TSDB_MAX_BYTES_PER_ROW];
if (TK_NK_STRING == valueToken.type) {
if (valueToken.n >= TSDB_MAX_BYTES_PER_ROW) {
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", valueToken.z);
}
int32_t len = trimString(valueToken.z, valueToken.n, tmpTokenBuf, TSDB_MAX_BYTES_PER_ROW);
valueToken.z = tmpTokenBuf;
valueToken.n = len;
}
if (TSDB_CODE_SUCCESS != parseTimestampOrInterval(&pTokenEnd, &valueToken, timePrec, &tempTs, &tempInterval, pMsgBuf, &secondIsTs)) {
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z);
}
if (valueToken.n < 2) {
return buildSyntaxErrMsg(pMsgBuf, "value expected in timestamp", token.z);
}
char unit = 0;
if (parseAbsoluteDuration(valueToken.z, valueToken.n, &interval, &unit, timePrec) != TSDB_CODE_SUCCESS) {
return TSDB_CODE_TSC_INVALID_OPERATION;
if (secondIsTs) {
// not support operator between tow timestamp, such as today() + now()
if (firstIsTS) {
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z);
}
ts = tempTs;
}else {
// not support operator between tow interval, such as 2h + 3s
if (!firstIsTS) {
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z);
}
interval = tempInterval;
}
if (token.type == TK_NK_MINUS) {
// not support interval - ts,such as 2h - today()
if (secondIsTs) {
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z);
}
*time = ts - interval;
} else {
*time = ts + interval;
}
if (token.type == TK_NK_PLUS) {
ts += interval;
} else {
ts = ts - interval;
for (int k = valueToken.n; valueToken.z[k] != '\0'; k++) {
if (valueToken.z[k] == ' ' || valueToken.z[k] == '\t') continue;
if (valueToken.z[k] == '(' && valueToken.z[k + 1] == ')') { // for insert NOW()/TODAY()
*end = pTokenEnd = &valueToken.z[k + 2];
k++;
continue;
}
if (valueToken.z[k] == ',' || valueToken.z[k] == ')') {
*end = pTokenEnd;
return TSDB_CODE_SUCCESS;
}
return buildSyntaxErrMsg(pMsgBuf, "invalid timestamp format", pToken->z);
}
}
*end = pTokenEnd;
}
*time = ts;
return TSDB_CODE_SUCCESS;
}
@ -666,7 +740,7 @@ static int32_t checkAndTrimValue(SToken* pToken, char* tmpTokenBuf, SMsgBuf* pMs
if ((pToken->type != TK_NOW && pToken->type != TK_TODAY && pToken->type != TK_NK_INTEGER &&
pToken->type != TK_NK_STRING && pToken->type != TK_NK_FLOAT && pToken->type != TK_NK_BOOL &&
pToken->type != TK_NULL && pToken->type != TK_NK_HEX && pToken->type != TK_NK_OCT &&
pToken->type != TK_NK_BIN) ||
pToken->type != TK_NK_BIN && pToken->type != TK_NK_VARIABLE) ||
(pToken->n == 0) || (pToken->type == TK_NK_RP)) {
return buildSyntaxErrMsg(pMsgBuf, "invalid data or symbol", pToken->z);
}
@ -845,6 +919,9 @@ static int32_t parseTagsClauseImpl(SInsertParseContext* pCxt, SVnodeModifyOpStmt
SSchema* pTagSchema = &pSchema[pCxt->tags.pColIndex[i]];
isJson = pTagSchema->type == TSDB_DATA_TYPE_JSON;
code = checkAndTrimValue(&token, pCxt->tmpTokenBuf, &pCxt->msg);
if (TK_NK_VARIABLE == token.type) {
code = buildSyntaxErrMsg(&pCxt->msg, "not expected tags values ", token.z);
}
if (TSDB_CODE_SUCCESS == code) {
code = parseTagValue(pCxt, pStmt, &pStmt->pSql, pTagSchema, &token, pTagName, pTagVals, &pTag);
}
@ -1535,6 +1612,10 @@ static int32_t parseValueToken(SInsertParseContext* pCxt, const char** pSql, STo
if (TSDB_DATA_TYPE_TIMESTAMP == pSchema->type && PRIMARYKEY_TIMESTAMP_COL_ID == pSchema->colId) {
return buildSyntaxErrMsg(&pCxt->msg, "primary timestamp should not be null", pToken->z);
}
if (TK_NK_VARIABLE == pToken->type && pSchema->type != TSDB_DATA_TYPE_TIMESTAMP) {
return buildSyntaxErrMsg(&pCxt->msg, "invalid values", pToken->z);
}
pVal->flag = CV_FLAG_NULL;
return TSDB_CODE_SUCCESS;
}
@ -1587,6 +1668,9 @@ typedef union SRowsDataContext{
static int32_t parseTbnameToken(SInsertParseContext* pCxt, SStbRowsDataContext* pStbRowsCxt, SToken* pToken, bool* pFoundCtbName) {
*pFoundCtbName = false;
int32_t code = checkAndTrimValue(pToken, pCxt->tmpTokenBuf, &pCxt->msg);
if (TK_NK_VARIABLE == pToken->type) {
code = buildInvalidOperationMsg(&pCxt->msg, "not expected tbname");
}
if (code == TSDB_CODE_SUCCESS){
if (isNullValue(TSDB_DATA_TYPE_BINARY, pToken)) {
return buildInvalidOperationMsg(&pCxt->msg, "tbname can not be null value");
@ -1624,6 +1708,10 @@ static int32_t processCtbTagsAfterCtbName(SInsertParseContext* pCxt, SVnodeModif
SToken* pTagToken = (SToken*)(tagTokens + i);
SSchema* pTagSchema = tagSchemas[i];
code = checkAndTrimValue(pTagToken, pCxt->tmpTokenBuf, &pCxt->msg);
if (TK_NK_VARIABLE == pTagToken->type) {
code = buildInvalidOperationMsg(&pCxt->msg, "not expected tag");
}
if (code == TSDB_CODE_SUCCESS) {
code = parseTagValue(pCxt, pStmt, NULL, pTagSchema, pTagToken, pStbRowsCxt->aTagNames, pStbRowsCxt->aTagVals,
&pStbRowsCxt->pTag);
@ -1668,6 +1756,9 @@ static int32_t doGetStbRowValues(SInsertParseContext* pCxt, SVnodeModifyOpStmt*
const SSchema* pSchema = &pSchemas[pCols->pColIndex[i]];
SColVal* pVal = taosArrayGet(pStbRowsCxt->aColVals, pCols->pColIndex[i]);
code = parseValueToken(pCxt, ppSql, pToken, (SSchema*)pSchema, getTableInfo(pStbRowsCxt->pStbMeta).precision, pVal);
if (TK_NK_VARIABLE == pToken->type) {
code = buildInvalidOperationMsg(&pCxt->msg, "not expected row value");
}
} else if (pCols->pColIndex[i] < tbnameIdx) {
const SSchema* pTagSchema = &pSchemas[pCols->pColIndex[i]];
if (canParseTagsAfter) {
@ -1676,6 +1767,9 @@ static int32_t doGetStbRowValues(SInsertParseContext* pCxt, SVnodeModifyOpStmt*
++(*pNumOfTagTokens);
} else {
code = checkAndTrimValue(pToken, pCxt->tmpTokenBuf, &pCxt->msg);
if (TK_NK_VARIABLE == pToken->type) {
code = buildInvalidOperationMsg(&pCxt->msg, "not expected row value");
}
if (code == TSDB_CODE_SUCCESS) {
code = parseTagValue(pCxt, pStmt, ppSql, (SSchema*)pTagSchema, pToken, pTagNames, pTagVals, &pStbRowsCxt->pTag);
}

View File

@ -302,6 +302,7 @@ e
,,y,system-test,./pytest.sh python3 ./test.py -f 1-insert/test_ts4219.py
,,y,system-test,./pytest.sh python3 ./test.py -f 1-insert/test_ts4295.py
,,y,system-test,./pytest.sh python3 ./test.py -f 1-insert/test_td27388.py
,,y,system-test,./pytest.sh python3 ./test.py -f 1-insert/insert_timestamp.py
,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/show.py
,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/show_tag_index.py
,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/information_schema.py

View File

@ -0,0 +1,70 @@
import sys
from util.log import *
from util.cases import *
from util.sql import *
from util.dnodes import tdDnodes
from math import inf
class TDTestCase:
def init(self, conn, logSql, replicaVer=1):
tdLog.debug("start to execute %s" % __file__)
tdSql.init(conn.cursor(), True)
#def prepare_data(self):
def run(self):
tdSql.execute("create database test_insert_timestamp;")
tdSql.execute("use test_insert_timestamp;")
tdSql.execute("create stable st(ts timestamp, c1 int) tags(id int);")
tdSql.execute("create table test_t using st tags(1);")
tdSql.error("insert into test_t values(now + today(), 1 ); ")
tdSql.error("insert into test_t values(now - today(), 1 ); ")
tdSql.error("insert into test_t values(today() + now(), 1 ); ")
tdSql.error("insert into test_t values(today() - now(), 1 ); ")
tdSql.error("insert into test_t values(2h - now(), 1 ); ")
tdSql.error("insert into test_t values(2h - today(), 1 ); ")
tdSql.error("insert into test_t values(2h - 1h, 1 ); ")
tdSql.error("insert into test_t values(2h + 1h, 1 ); ")
tdSql.error("insert into test_t values('2023-11-28 00:00:00.000' + '2023-11-28 00:00:00.000', 1 ); ")
tdSql.error("insert into test_t values('2023-11-28 00:00:00.000' + 1701111600000, 1 ); ")
tdSql.error("insert into test_t values(1701111500000 + 1701111600000, 1 ); ")
tdSql.error("insert into test_insert_timestamp.test_t values(1701111600000 + 1h + 1s, 4); ")
tdSql.execute("insert into test_insert_timestamp.test_t values(1701111600000 + 1h, 4); ")
tdSql.execute("insert into test_insert_timestamp.test_t values(2h + 1701111600000, 5); ")
tdSql.execute("insert into test_insert_timestamp.test_t values('2023-11-28 00:00:00.000' + 1h, 1); ")
tdSql.execute("insert into test_insert_timestamp.test_t values(3h + '2023-11-28 00:00:00.000', 3); ")
tdSql.execute("insert into test_insert_timestamp.test_t values(1701111600000 - 1h, 2); ")
tdSql.execute("insert into test_insert_timestamp.test_t values(1701122400000, 6); ")
tdSql.execute("insert into test_insert_timestamp.test_t values('2023-11-28 07:00:00.000', 7); ")
tdSql.query(f'select ts, c1 from test_t order by ts;')
tdSql.checkRows(7)
tdSql.checkEqual(tdSql.queryResult[0][0], datetime.datetime(2023, 11, 28, 1, 0, 0) )
tdSql.checkEqual(tdSql.queryResult[0][1], 1)
tdSql.checkEqual(tdSql.queryResult[1][0], datetime.datetime(2023, 11, 28, 2, 0, 0) )
tdSql.checkEqual(tdSql.queryResult[1][1], 2)
tdSql.checkEqual(tdSql.queryResult[2][0], datetime.datetime(2023, 11, 28, 3, 0, 0) )
tdSql.checkEqual(tdSql.queryResult[2][1], 3)
tdSql.checkEqual(tdSql.queryResult[3][0], datetime.datetime(2023, 11, 28, 4, 0, 0) )
tdSql.checkEqual(tdSql.queryResult[3][1], 4)
tdSql.checkEqual(tdSql.queryResult[4][0], datetime.datetime(2023, 11, 28, 5, 0, 0) )
tdSql.checkEqual(tdSql.queryResult[4][1], 5)
tdSql.checkEqual(tdSql.queryResult[5][0], datetime.datetime(2023, 11, 28, 6, 0, 0) )
tdSql.checkEqual(tdSql.queryResult[5][1], 6)
tdSql.checkEqual(tdSql.queryResult[6][0], datetime.datetime(2023, 11, 28, 7, 0, 0) )
tdSql.checkEqual(tdSql.queryResult[6][1], 7)
tdSql.execute("drop table if exists test_t ;")
tdSql.execute("drop stable if exists st;")
tdSql.execute("drop database if exists test_insert_timestamp;")
def stop(self):
tdSql.close()
tdLog.success("%s successfully executed" % __file__)
tdCases.addWindows(__file__, TDTestCase())
tdCases.addLinux(__file__, TDTestCase())