提交 f9e1ca94 编写于 作者: Y yihaoDeng

TD-1589

上级 49413180
...@@ -455,7 +455,7 @@ void tscTableMetaCallBack(void *param, TAOS_RES *res, int code) { ...@@ -455,7 +455,7 @@ void tscTableMetaCallBack(void *param, TAOS_RES *res, int code) {
// in case of insert, redo parsing the sql string and build new submit data block for two reasons: // in case of insert, redo parsing the sql string and build new submit data block for two reasons:
// 1. the table Id(tid & uid) may have been update, the submit block needs to be updated accordingly. // 1. the table Id(tid & uid) may have been update, the submit block needs to be updated accordingly.
// 2. vnode may need the schema information along with submit block to update its local table schema. // 2. vnode may need the schema information along with submit block to update its local table schema.
if (pCmd->command == TSDB_SQL_INSERT || pCmd->command == TSDB_SQL_SELECT) { if (pCmd->command == TSDB_SQL_INSERT || pCmd->command == TSDB_SQL_SELECT || pCmd->command == TSDB_SQL_DELETE) {
tscDebug("%p redo parse sql string and proceed", pSql); tscDebug("%p redo parse sql string and proceed", pSql);
pCmd->parseFinished = false; pCmd->parseFinished = false;
tscResetSqlCmdObj(pCmd, false); tscResetSqlCmdObj(pCmd, false);
...@@ -477,6 +477,8 @@ void tscTableMetaCallBack(void *param, TAOS_RES *res, int code) { ...@@ -477,6 +477,8 @@ void tscTableMetaCallBack(void *param, TAOS_RES *res, int code) {
tscHandleInsertRetry(pSql); tscHandleInsertRetry(pSql);
} else if (pCmd->command == TSDB_SQL_SELECT) { // in case of other query type, continue } else if (pCmd->command == TSDB_SQL_SELECT) { // in case of other query type, continue
tscProcessSql(pSql); tscProcessSql(pSql);
} else if (pCmd->command == TSDB_SQL_DELETE) {
// handle delete
} }
}else { // in all other cases, simple retry }else { // in all other cases, simple retry
tscProcessSql(pSql); tscProcessSql(pSql);
......
...@@ -90,6 +90,8 @@ static int32_t parseWhereClause(SQueryInfo* pQueryInfo, tSQLExpr** pExpr, SSqlOb ...@@ -90,6 +90,8 @@ static int32_t parseWhereClause(SQueryInfo* pQueryInfo, tSQLExpr** pExpr, SSqlOb
static int32_t parseFillClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQuerySQL); static int32_t parseFillClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQuerySQL);
static int32_t parseOrderbyClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQuerySql, SSchema* pSchema); static int32_t parseOrderbyClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQuerySql, SSchema* pSchema);
static int32_t setDelInfo(SSqlObj* pSql, struct SSqlInfo* pInfo);
static int32_t tsRewriteFieldNameIfNecessary(SSqlCmd* pCmd, SQueryInfo* pQueryInfo); static int32_t tsRewriteFieldNameIfNecessary(SSqlCmd* pCmd, SQueryInfo* pQueryInfo);
static int32_t setAlterTableInfo(SSqlObj* pSql, struct SSqlInfo* pInfo); static int32_t setAlterTableInfo(SSqlObj* pSql, struct SSqlInfo* pInfo);
static int32_t validateSqlFunctionInStreamSql(SSqlCmd* pCmd, SQueryInfo* pQueryInfo); static int32_t validateSqlFunctionInStreamSql(SSqlCmd* pCmd, SQueryInfo* pQueryInfo);
...@@ -105,6 +107,7 @@ static bool validateOneTags(SSqlCmd* pCmd, TAOS_FIELD* pTagField); ...@@ -105,6 +107,7 @@ static bool validateOneTags(SSqlCmd* pCmd, TAOS_FIELD* pTagField);
static bool hasTimestampForPointInterpQuery(SQueryInfo* pQueryInfo); static bool hasTimestampForPointInterpQuery(SQueryInfo* pQueryInfo);
static bool hasNormalColumnFilter(SQueryInfo* pQueryInfo); static bool hasNormalColumnFilter(SQueryInfo* pQueryInfo);
static int32_t getTimeFromExpr(tSQLExpr *pExpr, int16_t timePrecision, int64_t *result);
static int32_t parseLimitClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, int32_t index, SQuerySQL* pQuerySql, SSqlObj* pSql); static int32_t parseLimitClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, int32_t index, SQuerySQL* pQuerySql, SSqlObj* pSql);
static int32_t parseCreateDBOptions(SSqlCmd* pCmd, SCreateDBInfo* pCreateDbSql); static int32_t parseCreateDBOptions(SSqlCmd* pCmd, SCreateDBInfo* pCreateDbSql);
static int32_t getColumnIndexByName(SSqlCmd* pCmd, const SStrToken* pToken, SQueryInfo* pQueryInfo, SColumnIndex* pIndex); static int32_t getColumnIndexByName(SSqlCmd* pCmd, const SStrToken* pToken, SQueryInfo* pQueryInfo, SColumnIndex* pIndex);
...@@ -570,7 +573,13 @@ int32_t tscToSQLCmd(SSqlObj* pSql, struct SSqlInfo* pInfo) { ...@@ -570,7 +573,13 @@ int32_t tscToSQLCmd(SSqlObj* pSql, struct SSqlInfo* pInfo) {
pCmd->parseFinished = 1; pCmd->parseFinished = 1;
return TSDB_CODE_SUCCESS; // do not build query message here return TSDB_CODE_SUCCESS; // do not build query message here
} }
case TSDB_SQL_DELETE: {
if ((code = setDelInfo(pSql, pInfo)) != TSDB_CODE_SUCCESS) {
return code;
}
pCmd->parseFinished = 1;
break;
}
case TSDB_SQL_ALTER_TABLE: { case TSDB_SQL_ALTER_TABLE: {
if ((code = setAlterTableInfo(pSql, pInfo)) != TSDB_CODE_SUCCESS) { if ((code = setAlterTableInfo(pSql, pInfo)) != TSDB_CODE_SUCCESS) {
return code; return code;
...@@ -578,6 +587,7 @@ int32_t tscToSQLCmd(SSqlObj* pSql, struct SSqlInfo* pInfo) { ...@@ -578,6 +587,7 @@ int32_t tscToSQLCmd(SSqlObj* pSql, struct SSqlInfo* pInfo) {
break; break;
} }
case TSDB_SQL_KILL_QUERY: case TSDB_SQL_KILL_QUERY:
case TSDB_SQL_KILL_STREAM: case TSDB_SQL_KILL_STREAM:
...@@ -3696,7 +3706,68 @@ static int32_t handleExprInQueryCond(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSQL ...@@ -3696,7 +3706,68 @@ static int32_t handleExprInQueryCond(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSQL
return ret; return ret;
} }
int32_t handleExprInDelCond(SSqlCmd* pCmd, SQueryInfo *pQueryInfo, tSQLExpr* pExpr) {
return TSDB_CODE_SUCCESS;
}
int32_t getDelCond(SSqlCmd* pCmd, SQueryInfo *pQueryInfo, tSQLExpr* pExpr) {
if (pExpr == NULL) {
return TSDB_CODE_SUCCESS;
}
const char* msg1 = "invalid time stamp";
const char* msg2 = "illegal column name";
if (pExpr->nSQLOptr == TK_IN) {
tSQLExpr* pLeft = pExpr->pLeft;
tSQLExpr* pRight = pExpr->pRight;
SColumnIndex index = COLUMN_INDEX_INITIALIZER;
if (getColumnIndexByName(pCmd, &pLeft->colInfo, pQueryInfo, &index) != TSDB_CODE_SUCCESS) {
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2);
}
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, index.tableIndex);
int16_t timePrecision = tscGetTableInfo(pTableMetaInfo->pTableMeta).precision;
if (index.columnIndex == PRIMARYKEY_TIMESTAMP_COL_INDEX) {
if (pRight == NULL || pRight->nSQLOptr != TK_SET || pRight->pParam == NULL) {
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2);
}
int32_t nParam = pRight->pParam->nExpr;
int64_t *tsBuf = malloc(sizeof(int64_t) * nParam);
for (int i = 0; i < nParam; i++) {
int64_t ts;
if (getTimeFromExpr(pRight->pParam->a[i].pNode, timePrecision, &ts) != TSDB_CODE_SUCCESS) {
free(tsBuf);
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1);
} else {
tsBuf[i] = ts;
}
}
qsort(tsBuf, nParam, sizeof(tsBuf[0]), compareInt64Val);
} else {
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2);
}
} else {
return TSDB_CODE_TSC_INVALID_SQL;
}
//const char* msg1 = "del condition must use 'or'";
//tSQLExpr* pLeft = pExpr->pLeft;
//tSQLExpr* pRight = pExpr->pRight;
//int32_t leftType = -1;
//int32_t rightType = -1;
//if (!isExprDirectParentOfLeafNode(pExpr)) {
// int32_t ret = getDelCond(pCmd, pQueryInfo, pExpr->pLeft);
// if (ret != TSDB_CODE_SUCCESS) {
// return ret;
// }
// ret = getDelCond(pCmd, pQueryInfo, pExpr->pRight);
// if (ret != TSDB_CODE_SUCCESS) {
// return ret;
// }
//}
//return handleExprInDelCond(pCmd, pQueryInfo, pExpr);
}
int32_t getQueryCondExpr(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSQLExpr** pExpr, SCondExpr* pCondExpr, int32_t getQueryCondExpr(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, tSQLExpr** pExpr, SCondExpr* pCondExpr,
int32_t* type, int32_t parentOptr) { int32_t* type, int32_t parentOptr) {
if (pExpr == NULL) { if (pExpr == NULL) {
...@@ -4171,52 +4242,39 @@ int32_t parseWhereClause(SQueryInfo* pQueryInfo, tSQLExpr** pExpr, SSqlObj* pSql ...@@ -4171,52 +4242,39 @@ int32_t parseWhereClause(SQueryInfo* pQueryInfo, tSQLExpr** pExpr, SSqlObj* pSql
return ret; return ret;
} }
int32_t getTimeRange(STimeWindow* win, tSQLExpr* pRight, int32_t optr, int16_t timePrecision) { int32_t getTimeFromExpr(tSQLExpr *pExpr, int16_t timePrecision, int64_t *result) {
// this is join condition, do nothing
if (pRight->nSQLOptr == TK_ID) {
return TSDB_CODE_SUCCESS;
}
/*
* filter primary ts filter expression like:
* where ts in ('2015-12-12 4:8:12')
*/
if (pRight->nSQLOptr == TK_SET || optr == TK_IN) {
return TSDB_CODE_TSC_INVALID_SQL;
}
int64_t val = 0; int64_t val = 0;
bool parsed = false; bool parsed = false;
if (pRight->val.nType == TSDB_DATA_TYPE_BINARY) { if (pExpr->val.nType == TSDB_DATA_TYPE_BINARY) {
pRight->val.nLen = strdequote(pRight->val.pz); pExpr->val.nLen = strdequote(pExpr->val.pz);
char* seg = strnchr(pRight->val.pz, '-', pRight->val.nLen, false); char* seg = strnchr(pExpr->val.pz, '-', pExpr->val.nLen, false);
if (seg != NULL) { if (seg != NULL) {
if (taosParseTime(pRight->val.pz, &val, pRight->val.nLen, TSDB_TIME_PRECISION_MICRO, tsDaylight) == TSDB_CODE_SUCCESS) { if (taosParseTime(pExpr->val.pz, &val, pExpr->val.nLen, TSDB_TIME_PRECISION_MICRO, tsDaylight) == TSDB_CODE_SUCCESS) {
parsed = true; parsed = true;
} else { } else {
return TSDB_CODE_TSC_INVALID_SQL; return TSDB_CODE_TSC_INVALID_SQL;
} }
} else { } else {
SStrToken token = {.z = pRight->val.pz, .n = pRight->val.nLen, .type = TK_ID}; SStrToken token = {.z = pExpr->val.pz, .n = pExpr->val.nLen, .type = TK_ID};
int32_t len = tSQLGetToken(pRight->val.pz, &token.type); int32_t len = tSQLGetToken(pExpr->val.pz, &token.type);
if ((token.type != TK_INTEGER && token.type != TK_FLOAT) || len != pRight->val.nLen) { if ((token.type != TK_INTEGER && token.type != TK_FLOAT) || len != pExpr->val.nLen) {
return TSDB_CODE_TSC_INVALID_SQL; return TSDB_CODE_TSC_INVALID_SQL;
} }
} }
} else if (pRight->nSQLOptr == TK_INTEGER && timePrecision == TSDB_TIME_PRECISION_MILLI) { } else if (pExpr->nSQLOptr == TK_INTEGER && timePrecision == TSDB_TIME_PRECISION_MILLI) {
/* /*
* if the pRight->nSQLOptr == TK_INTEGER/TK_FLOAT, the value is adaptive, we * if the pExpr->nSQLOptr == TK_INTEGER/TK_FLOAT, the value is adaptive, we
* need the time precision in metermeta to transfer the value in MICROSECOND * need the time precision in metermeta to transfer the value in MICROSECOND
* *
* Additional check to avoid data overflow * Additional check to avoid data overflow
*/ */
if (pRight->val.i64Key <= INT64_MAX / 1000) { if (pExpr->val.i64Key <= INT64_MAX / 1000) {
pRight->val.i64Key *= 1000; pExpr->val.i64Key *= 1000;
} }
} else if (pRight->nSQLOptr == TK_FLOAT && timePrecision == TSDB_TIME_PRECISION_MILLI) { } else if (pExpr->nSQLOptr == TK_FLOAT && timePrecision == TSDB_TIME_PRECISION_MILLI) {
pRight->val.dKey *= 1000; pExpr->val.dKey *= 1000;
} }
if (!parsed) { if (!parsed) {
...@@ -4224,7 +4282,7 @@ int32_t getTimeRange(STimeWindow* win, tSQLExpr* pRight, int32_t optr, int16_t t ...@@ -4224,7 +4282,7 @@ int32_t getTimeRange(STimeWindow* win, tSQLExpr* pRight, int32_t optr, int16_t t
* failed to parse timestamp in regular formation, try next * failed to parse timestamp in regular formation, try next
* it may be a epoch time in string format * it may be a epoch time in string format
*/ */
tVariantDump(&pRight->val, (char*)&val, TSDB_DATA_TYPE_BIGINT, true); tVariantDump(&pExpr->val, (char*)&val, TSDB_DATA_TYPE_BIGINT, true);
/* /*
* transfer it into MICROSECOND format if it is a string, since for * transfer it into MICROSECOND format if it is a string, since for
...@@ -4232,12 +4290,33 @@ int32_t getTimeRange(STimeWindow* win, tSQLExpr* pRight, int32_t optr, int16_t t ...@@ -4232,12 +4290,33 @@ int32_t getTimeRange(STimeWindow* win, tSQLExpr* pRight, int32_t optr, int16_t t
* *
* additional check to avoid data overflow * additional check to avoid data overflow
*/ */
if (pRight->nSQLOptr == TK_STRING && timePrecision == TSDB_TIME_PRECISION_MILLI) { if (pExpr->nSQLOptr == TK_STRING && timePrecision == TSDB_TIME_PRECISION_MILLI) {
if (val <= INT64_MAX / 1000) { if (val <= INT64_MAX / 1000) {
val *= 1000; val *= 1000;
} }
} }
} }
*result = val;
return TSDB_CODE_SUCCESS;
}
int32_t getTimeRange(STimeWindow* win, tSQLExpr* pRight, int32_t optr, int16_t timePrecision) {
// this is join condition, do nothing
if (pRight->nSQLOptr == TK_ID) {
return TSDB_CODE_SUCCESS;
}
/*
* filter primary ts filter expression like:
* where ts in ('2015-12-12 4:8:12')
*/
if (pRight->nSQLOptr == TK_SET || optr == TK_IN) {
return TSDB_CODE_TSC_INVALID_SQL;
}
int64_t val;
int32_t code = getTimeFromExpr(pRight, timePrecision, &val);
if (code != TSDB_CODE_SUCCESS) {
return code;
}
int32_t delta = 1; int32_t delta = 1;
/* for millisecond, delta is 1ms=1000us */ /* for millisecond, delta is 1ms=1000us */
...@@ -4560,6 +4639,51 @@ int32_t parseOrderbyClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQu ...@@ -4560,6 +4639,51 @@ int32_t parseOrderbyClause(SSqlCmd* pCmd, SQueryInfo* pQueryInfo, SQuerySQL* pQu
return TSDB_CODE_SUCCESS; return TSDB_CODE_SUCCESS;
} }
int32_t setDelInfo(SSqlObj *pSql, struct SSqlInfo* pInfo) {
const char* msg1 = "invalid table name";
const char* msg2 = "invalid delete sql";
const char* msg3 = "delete can not be supported by super table";
int32_t code = TSDB_CODE_SUCCESS;
SDelSQL* pDelSql = pInfo->pDelInfo;
SSqlCmd* pCmd = &pSql->cmd;
SQueryInfo* pQueryInfo = tscGetQueryInfoDetail(pCmd, 0);
STableMetaInfo* pTableMetaInfo = tscGetMetaInfo(pQueryInfo, 0);
SStrToken tblToken = {0};
if (pDelSql->from && pDelSql->from->nExpr > 0) {
tVariant* pVar = &pDelSql->from->a[0].pVar;
SStrToken token = {.z = pVar->pz, .n = pVar->nLen, TK_STRING};
tblToken = token;
} else {
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1);
}
if (tscValidateName(&tblToken) != TSDB_CODE_SUCCESS) {
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg1);
}
code = tscSetTableFullName(pTableMetaInfo, &tblToken, pSql);
if (code != TSDB_CODE_SUCCESS) {
return code;
}
code = tscGetTableMeta(pSql, pTableMetaInfo);
if (code != TSDB_CODE_SUCCESS) {
return code;
}
if (UTIL_TABLE_IS_SUPER_TABLE(pTableMetaInfo)) {
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg3);
}
if (pDelSql->pWhere != NULL) {
if (getDelCond(pCmd, pQueryInfo, pDelSql->pWhere) != TSDB_CODE_SUCCESS) {
return invalidSqlErrMsg(tscGetErrorMsgPayload(pCmd), msg2);
}
}
return TSDB_CODE_SUCCESS;
}
int32_t setAlterTableInfo(SSqlObj* pSql, struct SSqlInfo* pInfo) { int32_t setAlterTableInfo(SSqlObj* pSql, struct SSqlInfo* pInfo) {
const int32_t DEFAULT_TABLE_INDEX = 0; const int32_t DEFAULT_TABLE_INDEX = 0;
......
...@@ -411,6 +411,7 @@ int doProcessSql(SSqlObj *pSql) { ...@@ -411,6 +411,7 @@ int doProcessSql(SSqlObj *pSql) {
pCmd->command == TSDB_SQL_FETCH || pCmd->command == TSDB_SQL_FETCH ||
pCmd->command == TSDB_SQL_RETRIEVE || pCmd->command == TSDB_SQL_RETRIEVE ||
pCmd->command == TSDB_SQL_INSERT || pCmd->command == TSDB_SQL_INSERT ||
pCmd->command == TSDB_SQL_DELETE ||
pCmd->command == TSDB_SQL_CONNECT || pCmd->command == TSDB_SQL_CONNECT ||
pCmd->command == TSDB_SQL_HB || pCmd->command == TSDB_SQL_HB ||
pCmd->command == TSDB_SQL_META || pCmd->command == TSDB_SQL_META ||
...@@ -528,6 +529,18 @@ int tscBuildSubmitMsg(SSqlObj *pSql, SSqlInfo *pInfo) { ...@@ -528,6 +529,18 @@ int tscBuildSubmitMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
return TSDB_CODE_SUCCESS; return TSDB_CODE_SUCCESS;
} }
int tscBuildDelMsg(SSqlObj *pSql, SSqlInfo *pInfo) {
SQueryInfo *pQueryInfo = tscGetQueryInfoDetail(&pSql->cmd, 0);
STableMeta* pTableMeta = tscGetMetaInfo(pQueryInfo, 0)->pTableMeta;
char* pMsg = pSql->cmd.payload;
// NOTE: shell message size should not include SMsgDesc
int32_t size = pSql->cmd.payloadLen - sizeof(SMsgDesc);
int32_t vgId = pTableMeta->vgroupInfo.vgId;
pSql->cmd.msgType = TSDB_MSG_TYPE_DELETE;
return TSDB_CODE_SUCCESS;
}
/* /*
* for table query, simply return the size <= 1k * for table query, simply return the size <= 1k
*/ */
...@@ -2287,7 +2300,9 @@ int tscGetSTableVgroupInfo(SSqlObj *pSql, int32_t clauseIndex) { ...@@ -2287,7 +2300,9 @@ int tscGetSTableVgroupInfo(SSqlObj *pSql, int32_t clauseIndex) {
void tscInitMsgsFp() { void tscInitMsgsFp() {
tscBuildMsg[TSDB_SQL_SELECT] = tscBuildQueryMsg; tscBuildMsg[TSDB_SQL_SELECT] = tscBuildQueryMsg;
tscBuildMsg[TSDB_SQL_INSERT] = tscBuildSubmitMsg; tscBuildMsg[TSDB_SQL_INSERT] = tscBuildSubmitMsg;
tscBuildMsg[TSDB_SQL_DELETE] = tscBuildDelMsg;
tscBuildMsg[TSDB_SQL_FETCH] = tscBuildFetchMsg; tscBuildMsg[TSDB_SQL_FETCH] = tscBuildFetchMsg;
tscBuildMsg[TSDB_SQL_CREATE_DB] = tscBuildCreateDbMsg; tscBuildMsg[TSDB_SQL_CREATE_DB] = tscBuildCreateDbMsg;
tscBuildMsg[TSDB_SQL_CREATE_USER] = tscBuildUserMsg; tscBuildMsg[TSDB_SQL_CREATE_USER] = tscBuildUserMsg;
......
...@@ -35,6 +35,7 @@ enum { ...@@ -35,6 +35,7 @@ enum {
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_SELECT, "select" ) TSDB_DEFINE_SQL_TYPE( TSDB_SQL_SELECT, "select" )
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_FETCH, "fetch" ) TSDB_DEFINE_SQL_TYPE( TSDB_SQL_FETCH, "fetch" )
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_INSERT, "insert" ) TSDB_DEFINE_SQL_TYPE( TSDB_SQL_INSERT, "insert" )
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_DELETE, "delete" )
TSDB_DEFINE_SQL_TYPE( TSDB_SQL_UPDATE_TAGS_VAL, "update-tag-val" ) TSDB_DEFINE_SQL_TYPE( TSDB_SQL_UPDATE_TAGS_VAL, "update-tag-val" )
// the SQL below is for mgmt node // the SQL below is for mgmt node
......
...@@ -42,6 +42,7 @@ enum { ...@@ -42,6 +42,7 @@ enum {
// message from client to dnode // message from client to dnode
TAOS_DEFINE_MESSAGE_TYPE( TSDB_MSG_TYPE_SUBMIT, "submit" ) TAOS_DEFINE_MESSAGE_TYPE( TSDB_MSG_TYPE_SUBMIT, "submit" )
TAOS_DEFINE_MESSAGE_TYPE( TSDB_MSG_TYPE_DELETE, "delete" )
TAOS_DEFINE_MESSAGE_TYPE( TSDB_MSG_TYPE_QUERY, "query" ) TAOS_DEFINE_MESSAGE_TYPE( TSDB_MSG_TYPE_QUERY, "query" )
TAOS_DEFINE_MESSAGE_TYPE( TSDB_MSG_TYPE_FETCH, "fetch" ) TAOS_DEFINE_MESSAGE_TYPE( TSDB_MSG_TYPE_FETCH, "fetch" )
TAOS_DEFINE_MESSAGE_TYPE( TSDB_MSG_TYPE_UPDATE_TAG_VAL, "update-tag-val" ) TAOS_DEFINE_MESSAGE_TYPE( TSDB_MSG_TYPE_UPDATE_TAG_VAL, "update-tag-val" )
......
...@@ -16,212 +16,212 @@ ...@@ -16,212 +16,212 @@
#ifndef TDENGINE_TTOKENDEF_H #ifndef TDENGINE_TTOKENDEF_H
#define TDENGINE_TTOKENDEF_H #define TDENGINE_TTOKENDEF_H
#define TK_ID 1
#define TK_ID 1 #define TK_BOOL 2
#define TK_BOOL 2 #define TK_TINYINT 3
#define TK_TINYINT 3 #define TK_SMALLINT 4
#define TK_SMALLINT 4 #define TK_INTEGER 5
#define TK_INTEGER 5 #define TK_BIGINT 6
#define TK_BIGINT 6 #define TK_FLOAT 7
#define TK_FLOAT 7 #define TK_DOUBLE 8
#define TK_DOUBLE 8 #define TK_STRING 9
#define TK_STRING 9 #define TK_TIMESTAMP 10
#define TK_TIMESTAMP 10 #define TK_BINARY 11
#define TK_BINARY 11 #define TK_NCHAR 12
#define TK_NCHAR 12 #define TK_OR 13
#define TK_OR 13 #define TK_AND 14
#define TK_AND 14 #define TK_NOT 15
#define TK_NOT 15 #define TK_EQ 16
#define TK_EQ 16 #define TK_NE 17
#define TK_NE 17 #define TK_ISNULL 18
#define TK_ISNULL 18 #define TK_NOTNULL 19
#define TK_NOTNULL 19 #define TK_IS 20
#define TK_IS 20 #define TK_LIKE 21
#define TK_LIKE 21 #define TK_GLOB 22
#define TK_GLOB 22 #define TK_BETWEEN 23
#define TK_BETWEEN 23 #define TK_IN 24
#define TK_IN 24 #define TK_GT 25
#define TK_GT 25 #define TK_GE 26
#define TK_GE 26 #define TK_LT 27
#define TK_LT 27 #define TK_LE 28
#define TK_LE 28 #define TK_BITAND 29
#define TK_BITAND 29 #define TK_BITOR 30
#define TK_BITOR 30 #define TK_LSHIFT 31
#define TK_LSHIFT 31 #define TK_RSHIFT 32
#define TK_RSHIFT 32 #define TK_PLUS 33
#define TK_PLUS 33 #define TK_MINUS 34
#define TK_MINUS 34 #define TK_DIVIDE 35
#define TK_DIVIDE 35 #define TK_TIMES 36
#define TK_TIMES 36 #define TK_STAR 37
#define TK_STAR 37 #define TK_SLASH 38
#define TK_SLASH 38 #define TK_REM 39
#define TK_REM 39 #define TK_CONCAT 40
#define TK_CONCAT 40 #define TK_UMINUS 41
#define TK_UMINUS 41 #define TK_UPLUS 42
#define TK_UPLUS 42 #define TK_BITNOT 43
#define TK_BITNOT 43 #define TK_SHOW 44
#define TK_SHOW 44 #define TK_DATABASES 45
#define TK_DATABASES 45 #define TK_MNODES 46
#define TK_MNODES 46 #define TK_DNODES 47
#define TK_DNODES 47 #define TK_ACCOUNTS 48
#define TK_ACCOUNTS 48 #define TK_USERS 49
#define TK_USERS 49 #define TK_MODULES 50
#define TK_MODULES 50 #define TK_QUERIES 51
#define TK_QUERIES 51 #define TK_CONNECTIONS 52
#define TK_CONNECTIONS 52 #define TK_STREAMS 53
#define TK_STREAMS 53 #define TK_VARIABLES 54
#define TK_VARIABLES 54 #define TK_SCORES 55
#define TK_SCORES 55 #define TK_GRANTS 56
#define TK_GRANTS 56 #define TK_VNODES 57
#define TK_VNODES 57 #define TK_IPTOKEN 58
#define TK_IPTOKEN 58 #define TK_DOT 59
#define TK_DOT 59 #define TK_CREATE 60
#define TK_CREATE 60 #define TK_TABLE 61
#define TK_TABLE 61 #define TK_DATABASE 62
#define TK_DATABASE 62 #define TK_TABLES 63
#define TK_TABLES 63 #define TK_STABLES 64
#define TK_STABLES 64 #define TK_VGROUPS 65
#define TK_VGROUPS 65 #define TK_DROP 66
#define TK_DROP 66 #define TK_DNODE 67
#define TK_DNODE 67 #define TK_USER 68
#define TK_USER 68 #define TK_ACCOUNT 69
#define TK_ACCOUNT 69 #define TK_USE 70
#define TK_USE 70 #define TK_DESCRIBE 71
#define TK_DESCRIBE 71 #define TK_ALTER 72
#define TK_ALTER 72 #define TK_PASS 73
#define TK_PASS 73 #define TK_PRIVILEGE 74
#define TK_PRIVILEGE 74 #define TK_LOCAL 75
#define TK_LOCAL 75 #define TK_IF 76
#define TK_IF 76 #define TK_EXISTS 77
#define TK_EXISTS 77 #define TK_PPS 78
#define TK_PPS 78 #define TK_TSERIES 79
#define TK_TSERIES 79 #define TK_DBS 80
#define TK_DBS 80 #define TK_STORAGE 81
#define TK_STORAGE 81 #define TK_QTIME 82
#define TK_QTIME 82 #define TK_CONNS 83
#define TK_CONNS 83 #define TK_STATE 84
#define TK_STATE 84 #define TK_KEEP 85
#define TK_KEEP 85 #define TK_CACHE 86
#define TK_CACHE 86 #define TK_REPLICA 87
#define TK_REPLICA 87 #define TK_QUORUM 88
#define TK_QUORUM 88 #define TK_DAYS 89
#define TK_DAYS 89 #define TK_MINROWS 90
#define TK_MINROWS 90 #define TK_MAXROWS 91
#define TK_MAXROWS 91 #define TK_BLOCKS 92
#define TK_BLOCKS 92 #define TK_CTIME 93
#define TK_CTIME 93 #define TK_WAL 94
#define TK_WAL 94 #define TK_FSYNC 95
#define TK_FSYNC 95 #define TK_COMP 96
#define TK_COMP 96 #define TK_PRECISION 97
#define TK_PRECISION 97 #define TK_LP 98
#define TK_LP 98 #define TK_RP 99
#define TK_RP 99
#define TK_TAGS 100 #define TK_TAGS 100
#define TK_USING 101 #define TK_USING 101
#define TK_AS 102 #define TK_AS 102
#define TK_COMMA 103 #define TK_COMMA 103
#define TK_NULL 104 #define TK_NULL 104
#define TK_SELECT 105 #define TK_DELETE 105
#define TK_UNION 106 #define TK_SELECT 106
#define TK_ALL 107 #define TK_UNION 107
#define TK_FROM 108 #define TK_ALL 108
#define TK_VARIABLE 109 #define TK_FROM 109
#define TK_INTERVAL 110 #define TK_VARIABLE 110
#define TK_FILL 111 #define TK_INTERVAL 111
#define TK_SLIDING 112 #define TK_FILL 112
#define TK_ORDER 113 #define TK_SLIDING 113
#define TK_BY 114 #define TK_ORDER 114
#define TK_ASC 115 #define TK_BY 115
#define TK_DESC 116 #define TK_ASC 116
#define TK_GROUP 117 #define TK_DESC 117
#define TK_HAVING 118 #define TK_GROUP 118
#define TK_LIMIT 119 #define TK_HAVING 119
#define TK_OFFSET 120 #define TK_LIMIT 120
#define TK_SLIMIT 121 #define TK_OFFSET 121
#define TK_SOFFSET 122 #define TK_SLIMIT 122
#define TK_WHERE 123 #define TK_SOFFSET 123
#define TK_NOW 124 #define TK_WHERE 124
#define TK_RESET 125 #define TK_NOW 125
#define TK_QUERY 126 #define TK_RESET 126
#define TK_ADD 127 #define TK_QUERY 127
#define TK_COLUMN 128 #define TK_ADD 128
#define TK_TAG 129 #define TK_COLUMN 129
#define TK_CHANGE 130 #define TK_TAG 130
#define TK_SET 131 #define TK_CHANGE 131
#define TK_KILL 132 #define TK_SET 132
#define TK_CONNECTION 133 #define TK_KILL 133
#define TK_STREAM 134 #define TK_CONNECTION 134
#define TK_COLON 135 #define TK_STREAM 135
#define TK_ABORT 136 #define TK_COLON 136
#define TK_AFTER 137 #define TK_ABORT 137
#define TK_ATTACH 138 #define TK_AFTER 138
#define TK_BEFORE 139 #define TK_ATTACH 139
#define TK_BEGIN 140 #define TK_BEFORE 140
#define TK_CASCADE 141 #define TK_BEGIN 141
#define TK_CLUSTER 142 #define TK_CASCADE 142
#define TK_CONFLICT 143 #define TK_CLUSTER 143
#define TK_COPY 144 #define TK_CONFLICT 144
#define TK_DEFERRED 145 #define TK_COPY 145
#define TK_DELIMITERS 146 #define TK_DEFERRED 146
#define TK_DETACH 147 #define TK_DELIMITERS 147
#define TK_EACH 148 #define TK_DETACH 148
#define TK_END 149 #define TK_EACH 149
#define TK_EXPLAIN 150 #define TK_END 150
#define TK_FAIL 151 #define TK_EXPLAIN 151
#define TK_FOR 152 #define TK_FAIL 152
#define TK_IGNORE 153 #define TK_FOR 153
#define TK_IMMEDIATE 154 #define TK_IGNORE 154
#define TK_INITIALLY 155 #define TK_IMMEDIATE 155
#define TK_INSTEAD 156 #define TK_INITIALLY 156
#define TK_MATCH 157 #define TK_INSTEAD 157
#define TK_KEY 158 #define TK_MATCH 158
#define TK_OF 159 #define TK_KEY 159
#define TK_RAISE 160 #define TK_OF 160
#define TK_REPLACE 161 #define TK_RAISE 161
#define TK_RESTRICT 162 #define TK_REPLACE 162
#define TK_ROW 163 #define TK_RESTRICT 163
#define TK_STATEMENT 164 #define TK_ROW 164
#define TK_TRIGGER 165 #define TK_STATEMENT 165
#define TK_VIEW 166 #define TK_TRIGGER 166
#define TK_COUNT 167 #define TK_VIEW 167
#define TK_SUM 168 #define TK_COUNT 168
#define TK_AVG 169 #define TK_SUM 169
#define TK_MIN 170 #define TK_AVG 170
#define TK_MAX 171 #define TK_MIN 171
#define TK_FIRST 172 #define TK_MAX 172
#define TK_LAST 173 #define TK_FIRST 173
#define TK_TOP 174 #define TK_LAST 174
#define TK_BOTTOM 175 #define TK_TOP 175
#define TK_STDDEV 176 #define TK_BOTTOM 176
#define TK_PERCENTILE 177 #define TK_STDDEV 177
#define TK_APERCENTILE 178 #define TK_PERCENTILE 178
#define TK_LEASTSQUARES 179 #define TK_APERCENTILE 179
#define TK_HISTOGRAM 180 #define TK_LEASTSQUARES 180
#define TK_DIFF 181 #define TK_HISTOGRAM 181
#define TK_SPREAD 182 #define TK_DIFF 182
#define TK_TWA 183 #define TK_SPREAD 183
#define TK_INTERP 184 #define TK_TWA 184
#define TK_LAST_ROW 185 #define TK_INTERP 185
#define TK_RATE 186 #define TK_LAST_ROW 186
#define TK_IRATE 187 #define TK_RATE 187
#define TK_SUM_RATE 188 #define TK_IRATE 188
#define TK_SUM_IRATE 189 #define TK_SUM_RATE 189
#define TK_AVG_RATE 190 #define TK_SUM_IRATE 190
#define TK_AVG_IRATE 191 #define TK_AVG_RATE 191
#define TK_TBID 192 #define TK_AVG_IRATE 192
#define TK_SEMI 193 #define TK_TBID 193
#define TK_NONE 194 #define TK_SEMI 194
#define TK_PREV 195 #define TK_NONE 195
#define TK_LINEAR 196 #define TK_PREV 196
#define TK_IMPORT 197 #define TK_LINEAR 197
#define TK_METRIC 198 #define TK_IMPORT 198
#define TK_TBNAME 199 #define TK_METRIC 199
#define TK_JOIN 200 #define TK_TBNAME 200
#define TK_METRICS 201 #define TK_JOIN 201
#define TK_STABLE 202 #define TK_METRICS 202
#define TK_INSERT 203 #define TK_STABLE 203
#define TK_INTO 204 #define TK_INSERT 204
#define TK_VALUES 205 #define TK_INTO 205
#define TK_VALUES 206
#define TK_SPACE 300 #define TK_SPACE 300
#define TK_COMMENT 301 #define TK_COMMENT 301
......
...@@ -85,6 +85,11 @@ typedef struct SQuerySQL { ...@@ -85,6 +85,11 @@ typedef struct SQuerySQL {
SStrToken selectToken; // sql string SStrToken selectToken; // sql string
} SQuerySQL; } SQuerySQL;
typedef struct SDelSQL {
tVariantList* from;
struct tSQLExpr* pWhere;
} SDelSQL;
typedef struct SCreateTableSQL { typedef struct SCreateTableSQL {
struct SStrToken name; // meter name, create table [meterName] xxx struct SStrToken name; // meter name, create table [meterName] xxx
bool existCheck; bool existCheck;
...@@ -188,6 +193,7 @@ typedef struct SSqlInfo { ...@@ -188,6 +193,7 @@ typedef struct SSqlInfo {
SCreateTableSQL *pCreateTableInfo; SCreateTableSQL *pCreateTableInfo;
SAlterTableSQL * pAlterInfo; SAlterTableSQL * pAlterInfo;
tDCLSQL * pDCLInfo; tDCLSQL * pDCLInfo;
SDelSQL * pDelInfo;
}; };
SSubclauseInfo subclauseInfo; SSubclauseInfo subclauseInfo;
...@@ -268,12 +274,15 @@ SQuerySQL *tSetQuerySQLElems(SStrToken *pSelectToken, tSQLExprList *pSelection, ...@@ -268,12 +274,15 @@ SQuerySQL *tSetQuerySQLElems(SStrToken *pSelectToken, tSQLExprList *pSelection,
tVariantList *pGroupby, tVariantList *pSortOrder, SIntervalVal *pInterval, tVariantList *pGroupby, tVariantList *pSortOrder, SIntervalVal *pInterval,
SStrToken *pSliding, tVariantList *pFill, SLimitVal *pLimit, SLimitVal *pGLimit); SStrToken *pSliding, tVariantList *pFill, SLimitVal *pLimit, SLimitVal *pGLimit);
SCreateTableSQL *tSetCreateSQLElems(tFieldList *pCols, tFieldList *pTags, SStrToken *pMetricName, SCreateTableSQL *tSetCreateSQLElems(tFieldList *pCols, tFieldList *pTags, SStrToken *pMetricName,
tVariantList *pTagVals, SQuerySQL *pSelect, int32_t type); tVariantList *pTagVals, SQuerySQL *pSelect, int32_t type);
void tSQLExprNodeDestroy(tSQLExpr *pExpr); void tSQLExprNodeDestroy(tSQLExpr *pExpr);
tSQLExpr *tSQLExprNodeClone(tSQLExpr *pExpr); tSQLExpr *tSQLExprNodeClone(tSQLExpr *pExpr);
SDelSQL *tSetDelSQLElems(tVariantList *pFrom, tSQLExpr *pWhere);
SAlterTableSQL *tAlterTableSQLElems(SStrToken *pMeterName, tFieldList *pCols, tVariantList *pVals, int32_t type); SAlterTableSQL *tAlterTableSQLElems(SStrToken *pMeterName, tFieldList *pCols, tVariantList *pVals, int32_t type);
tSQLExprListList *tSQLListListAppend(tSQLExprListList *pList, tSQLExprList *pExprList); tSQLExprListList *tSQLListListAppend(tSQLExprListList *pList, tSQLExprList *pExprList);
...@@ -281,6 +290,8 @@ tSQLExprListList *tSQLListListAppend(tSQLExprListList *pList, tSQLExprList *pExp ...@@ -281,6 +290,8 @@ tSQLExprListList *tSQLListListAppend(tSQLExprListList *pList, tSQLExprList *pExp
void destroyAllSelectClause(SSubclauseInfo *pSql); void destroyAllSelectClause(SSubclauseInfo *pSql);
void doDestroyQuerySql(SQuerySQL *pSql); void doDestroyQuerySql(SQuerySQL *pSql);
void doDestroyDelSql(SDelSQL *pSql);
SSqlInfo * setSQLInfo(SSqlInfo *pInfo, void *pSqlExprInfo, SStrToken *pMeterName, int32_t type); SSqlInfo * setSQLInfo(SSqlInfo *pInfo, void *pSqlExprInfo, SStrToken *pMeterName, int32_t type);
SSubclauseInfo *setSubclause(SSubclauseInfo *pClause, void *pSqlExprInfo); SSubclauseInfo *setSubclause(SSubclauseInfo *pClause, void *pSqlExprInfo);
......
...@@ -376,6 +376,16 @@ tagitem(A) ::= PLUS(X) FLOAT(Y). { ...@@ -376,6 +376,16 @@ tagitem(A) ::= PLUS(X) FLOAT(Y). {
tVariantCreate(&A, &X); tVariantCreate(&A, &X);
} }
///////////////////////////////////DELETE TABLE statement//////////////////////////////////
%type delete {SDelSQL*}
%destructor delete { doDestroyDelSql($$); }
delete(A) ::= DELETE from(X) where_opt(Y). {
A = tSetDelSQLElems(X, Y);
}
cmd ::= delete(A). { setSQLInfo(pInfo, A, NULL, TSDB_SQL_DELETE); }
//////////////////////// The SELECT statement ///////////////////////////////// //////////////////////// The SELECT statement /////////////////////////////////
%type select {SQuerySQL*} %type select {SQuerySQL*}
%destructor select {doDestroyQuerySql($$);} %destructor select {doDestroyQuerySql($$);}
...@@ -700,6 +710,7 @@ cmd ::= ALTER TABLE ids(X) cpxName(F) SET TAG ids(Y) EQ tagitem(Z). { ...@@ -700,6 +710,7 @@ cmd ::= ALTER TABLE ids(X) cpxName(F) SET TAG ids(Y) EQ tagitem(Z). {
setSQLInfo(pInfo, pAlterTable, NULL, TSDB_SQL_ALTER_TABLE); setSQLInfo(pInfo, pAlterTable, NULL, TSDB_SQL_ALTER_TABLE);
} }
////////////////////////////////////////kill statement/////////////////////////////////////// ////////////////////////////////////////kill statement///////////////////////////////////////
cmd ::= KILL CONNECTION INTEGER(Y). {setKillSQL(pInfo, TSDB_SQL_KILL_CONNECTION, &Y);} cmd ::= KILL CONNECTION INTEGER(Y). {setKillSQL(pInfo, TSDB_SQL_KILL_CONNECTION, &Y);}
cmd ::= KILL STREAM INTEGER(X) COLON(Z) INTEGER(Y). {X.n += (Z.n + Y.n); setKillSQL(pInfo, TSDB_SQL_KILL_STREAM, &X);} cmd ::= KILL STREAM INTEGER(X) COLON(Z) INTEGER(Y). {X.n += (Z.n + Y.n); setKillSQL(pInfo, TSDB_SQL_KILL_STREAM, &X);}
......
...@@ -578,6 +578,30 @@ void doDestroyQuerySql(SQuerySQL *pQuerySql) { ...@@ -578,6 +578,30 @@ void doDestroyQuerySql(SQuerySQL *pQuerySql) {
free(pQuerySql); free(pQuerySql);
} }
/*
* extract the del info out of sql string
*/
SDelSQL *tSetDelSQLElems(tVariantList *pFrom, tSQLExpr *pWhere) {
//assert(pSelection != NULL);
SDelSQL *pDelSql = calloc(1, sizeof(SDelSQL));
pDelSql->from = pFrom;
pDelSql->pWhere = pWhere;
return pDelSql;
}
void doDestroyDelSql(SDelSQL *pDelSql) {
if (pDelSql == NULL) {
return;
}
tSQLExprDestroy(pDelSql->pWhere);
pDelSql->pWhere = NULL;
tVariantListDestroy(pDelSql->from);
pDelSql->from = NULL;
free(pDelSql);
}
void destroyAllSelectClause(SSubclauseInfo *pClause) { void destroyAllSelectClause(SSubclauseInfo *pClause) {
if (pClause == NULL || pClause->numOfClause == 0) { if (pClause == NULL || pClause->numOfClause == 0) {
return; return;
...@@ -662,8 +686,9 @@ void SQLInfoDestroy(SSqlInfo *pInfo) { ...@@ -662,8 +686,9 @@ void SQLInfoDestroy(SSqlInfo *pInfo) {
} else if (pInfo->type == TSDB_SQL_ALTER_TABLE) { } else if (pInfo->type == TSDB_SQL_ALTER_TABLE) {
tVariantListDestroy(pInfo->pAlterInfo->varList); tVariantListDestroy(pInfo->pAlterInfo->varList);
tFieldListDestroy(pInfo->pAlterInfo->pAddColumns); tFieldListDestroy(pInfo->pAlterInfo->pAddColumns);
taosTFree(pInfo->pAlterInfo); taosTFree(pInfo->pAlterInfo);
} else if (pInfo->type == TSDB_SQL_DELETE) {
doDestroyDelSql(pInfo->pDelInfo);
} else { } else {
if (pInfo->pDCLInfo != NULL && pInfo->pDCLInfo->nAlloc > 0) { if (pInfo->pDCLInfo != NULL && pInfo->pDCLInfo->nAlloc > 0) {
free(pInfo->pDCLInfo->a); free(pInfo->pDCLInfo->a);
...@@ -702,6 +727,8 @@ SSqlInfo* setSQLInfo(SSqlInfo *pInfo, void *pSqlExprInfo, SStrToken *pMeterName, ...@@ -702,6 +727,8 @@ SSqlInfo* setSQLInfo(SSqlInfo *pInfo, void *pSqlExprInfo, SStrToken *pMeterName,
if (type == TSDB_SQL_SELECT) { if (type == TSDB_SQL_SELECT) {
pInfo->subclauseInfo = *(SSubclauseInfo*) pSqlExprInfo; pInfo->subclauseInfo = *(SSubclauseInfo*) pSqlExprInfo;
free(pSqlExprInfo); free(pSqlExprInfo);
} else if (type == TSDB_SQL_DELETE){
pInfo->pDelInfo = pSqlExprInfo;
} else { } else {
pInfo->pCreateTableInfo = pSqlExprInfo; pInfo->pCreateTableInfo = pSqlExprInfo;
} }
......
...@@ -237,6 +237,7 @@ static SKeyword keywordTable[] = { ...@@ -237,6 +237,7 @@ static SKeyword keywordTable[] = {
{"SUM_IRATE", TK_SUM_IRATE}, {"SUM_IRATE", TK_SUM_IRATE},
{"AVG_RATE", TK_AVG_RATE}, {"AVG_RATE", TK_AVG_RATE},
{"AVG_IRATE", TK_AVG_IRATE}, {"AVG_IRATE", TK_AVG_IRATE},
{"DELETE", TK_DELETE},
}; };
static const char isIdChar[] = { static const char isIdChar[] = {
...@@ -661,4 +662,4 @@ bool isKeyWord(const char* z, int32_t len) { return (tSQLKeywordCode((char*)z, l ...@@ -661,4 +662,4 @@ bool isKeyWord(const char* z, int32_t len) { return (tSQLKeywordCode((char*)z, l
void taosCleanupKeywordsTable() { void taosCleanupKeywordsTable() {
taosHashCleanup(KeywordHashTable); taosHashCleanup(KeywordHashTable);
} }
\ No newline at end of file
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册