Fix TD-27403: insert error about use today + now as timestamp value
This commit is contained in:
parent
bf81ea1a06
commit
72d6614b7b
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
Loading…
Reference in New Issue