未验证 提交 69b82732 编写于 作者: Y Yang Zhao 提交者: GitHub

[TD-10456]<fix>taosdemo normal table + rest (master) (#8082)

上级 3057dab3
...@@ -586,6 +586,7 @@ static int64_t getTSRandTail(int64_t timeStampStep, int32_t seq, ...@@ -586,6 +586,7 @@ static int64_t getTSRandTail(int64_t timeStampStep, int32_t seq,
int disorderRatio, int disorderRange); int disorderRatio, int disorderRange);
static bool getInfoFromJsonFile(char* file); static bool getInfoFromJsonFile(char* file);
static void init_rand_data(); static void init_rand_data();
static int regexMatch(const char *s, const char *reg, int cflags);
/* ************ Global variables ************ */ /* ************ Global variables ************ */
...@@ -803,7 +804,7 @@ static void printHelp() { ...@@ -803,7 +804,7 @@ static void printHelp() {
printf("%s%s%s%s\n", indent, "-q, --query-mode=MODE", "\t\t", printf("%s%s%s%s\n", indent, "-q, --query-mode=MODE", "\t\t",
"Query mode -- 0: SYNC, 1: ASYNC. By default use SYNC."); "Query mode -- 0: SYNC, 1: ASYNC. By default use SYNC.");
printf("%s%s%s%s\n", indent, "-b, --data-type=DATATYPE", "\t", printf("%s%s%s%s\n", indent, "-b, --data-type=DATATYPE", "\t",
"The data_type of columns, By default use: FLOAT, INT, FLOAT."); "The data_type of columns, By default use: FLOAT,INT,FLOAT. NCHAR and BINARY can also use custom length. Eg: NCHAR(16),BINARY(8)");
printf("%s%s%s%s%d\n", indent, "-w, --binwidth=WIDTH", "\t\t", printf("%s%s%s%s%d\n", indent, "-w, --binwidth=WIDTH", "\t\t",
"The width of data_type 'BINARY' or 'NCHAR'. By default use ", "The width of data_type 'BINARY' or 'NCHAR'. By default use ",
g_args.binwidth); g_args.binwidth);
...@@ -1579,9 +1580,8 @@ static void parse_args(int argc, char *argv[], SArguments *arguments) { ...@@ -1579,9 +1580,8 @@ static void parse_args(int argc, char *argv[], SArguments *arguments) {
&& strcasecmp(dataType, "SMALLINT") && strcasecmp(dataType, "SMALLINT")
&& strcasecmp(dataType, "BIGINT") && strcasecmp(dataType, "BIGINT")
&& strcasecmp(dataType, "DOUBLE") && strcasecmp(dataType, "DOUBLE")
&& strcasecmp(dataType, "BINARY")
&& strcasecmp(dataType, "TIMESTAMP") && strcasecmp(dataType, "TIMESTAMP")
&& strcasecmp(dataType, "NCHAR") && !regexMatch(dataType, "^(NCHAR|BINARY)(\\([1-9][0-9]*\\))?$", REG_ICASE | REG_EXTENDED)
&& strcasecmp(dataType, "UTINYINT") && strcasecmp(dataType, "UTINYINT")
&& strcasecmp(dataType, "USMALLINT") && strcasecmp(dataType, "USMALLINT")
&& strcasecmp(dataType, "UINT") && strcasecmp(dataType, "UINT")
...@@ -1603,9 +1603,11 @@ static void parse_args(int argc, char *argv[], SArguments *arguments) { ...@@ -1603,9 +1603,11 @@ static void parse_args(int argc, char *argv[], SArguments *arguments) {
arguments->data_type[0] = TSDB_DATA_TYPE_FLOAT; arguments->data_type[0] = TSDB_DATA_TYPE_FLOAT;
} else if (0 == strcasecmp(dataType, "DOUBLE")) { } else if (0 == strcasecmp(dataType, "DOUBLE")) {
arguments->data_type[0] = TSDB_DATA_TYPE_DOUBLE; arguments->data_type[0] = TSDB_DATA_TYPE_DOUBLE;
} else if (0 == strcasecmp(dataType, "BINARY")) { } else if (1 == regexMatch(dataType, "^BINARY(\\([1-9][0-9]*\\))?$", REG_ICASE |
REG_EXTENDED)) {
arguments->data_type[0] = TSDB_DATA_TYPE_BINARY; arguments->data_type[0] = TSDB_DATA_TYPE_BINARY;
} else if (0 == strcasecmp(dataType, "NCHAR")) { } else if (1 == regexMatch(dataType, "^NCHAR(\\([1-9][0-9]*\\))?$", REG_ICASE |
REG_EXTENDED)) {
arguments->data_type[0] = TSDB_DATA_TYPE_NCHAR; arguments->data_type[0] = TSDB_DATA_TYPE_NCHAR;
} else if (0 == strcasecmp(dataType, "BOOL")) { } else if (0 == strcasecmp(dataType, "BOOL")) {
arguments->data_type[0] = TSDB_DATA_TYPE_BOOL; arguments->data_type[0] = TSDB_DATA_TYPE_BOOL;
...@@ -1638,9 +1640,8 @@ static void parse_args(int argc, char *argv[], SArguments *arguments) { ...@@ -1638,9 +1640,8 @@ static void parse_args(int argc, char *argv[], SArguments *arguments) {
&& strcasecmp(token, "SMALLINT") && strcasecmp(token, "SMALLINT")
&& strcasecmp(token, "BIGINT") && strcasecmp(token, "BIGINT")
&& strcasecmp(token, "DOUBLE") && strcasecmp(token, "DOUBLE")
&& strcasecmp(token, "BINARY")
&& strcasecmp(token, "TIMESTAMP") && strcasecmp(token, "TIMESTAMP")
&& strcasecmp(token, "NCHAR") && !regexMatch(token, "^(NCHAR|BINARY)(\\([1-9][0-9]*\\))?$", REG_ICASE | REG_EXTENDED)
&& strcasecmp(token, "UTINYINT") && strcasecmp(token, "UTINYINT")
&& strcasecmp(token, "USMALLINT") && strcasecmp(token, "USMALLINT")
&& strcasecmp(token, "UINT") && strcasecmp(token, "UINT")
...@@ -1663,9 +1664,11 @@ static void parse_args(int argc, char *argv[], SArguments *arguments) { ...@@ -1663,9 +1664,11 @@ static void parse_args(int argc, char *argv[], SArguments *arguments) {
arguments->data_type[index] = TSDB_DATA_TYPE_DOUBLE; arguments->data_type[index] = TSDB_DATA_TYPE_DOUBLE;
} else if (0 == strcasecmp(token, "TINYINT")) { } else if (0 == strcasecmp(token, "TINYINT")) {
arguments->data_type[index] = TSDB_DATA_TYPE_TINYINT; arguments->data_type[index] = TSDB_DATA_TYPE_TINYINT;
} else if (0 == strcasecmp(token, "BINARY")) { } else if (1 == regexMatch(token, "^BINARY(\\([1-9][0-9]*\\))?$", REG_ICASE |
REG_EXTENDED)) {
arguments->data_type[index] = TSDB_DATA_TYPE_BINARY; arguments->data_type[index] = TSDB_DATA_TYPE_BINARY;
} else if (0 == strcasecmp(token, "NCHAR")) { } else if (1 == regexMatch(token, "^NCHAR(\\([1-9][0-9]*\\))?$", REG_ICASE |
REG_EXTENDED)) {
arguments->data_type[index] = TSDB_DATA_TYPE_NCHAR; arguments->data_type[index] = TSDB_DATA_TYPE_NCHAR;
} else if (0 == strcasecmp(token, "BOOL")) { } else if (0 == strcasecmp(token, "BOOL")) {
arguments->data_type[index] = TSDB_DATA_TYPE_BOOL; arguments->data_type[index] = TSDB_DATA_TYPE_BOOL;
...@@ -2660,117 +2663,127 @@ static int printfInsertMeta() { ...@@ -2660,117 +2663,127 @@ static int printfInsertMeta() {
} }
} }
printf(" super table count: \033[33m%"PRIu64"\033[0m\n",
g_Dbs.db[i].superTblCount);
for (uint64_t j = 0; j < g_Dbs.db[i].superTblCount; j++) {
printf(" super table[\033[33m%"PRIu64"\033[0m]:\n", j);
printf(" stbName: \033[33m%s\033[0m\n", if (g_args.use_metric) {
g_Dbs.db[i].superTbls[j].stbName); printf(" super table count: \033[33m%"PRIu64"\033[0m\n",
g_Dbs.db[i].superTblCount);
if (PRE_CREATE_SUBTBL == g_Dbs.db[i].superTbls[j].autoCreateTable) { for (uint64_t j = 0; j < g_Dbs.db[i].superTblCount; j++) {
printf(" autoCreateTable: \033[33m%s\033[0m\n", "no"); printf(" super table[\033[33m%"PRIu64"\033[0m]:\n", j);
} else if (AUTO_CREATE_SUBTBL ==
g_Dbs.db[i].superTbls[j].autoCreateTable) {
printf(" autoCreateTable: \033[33m%s\033[0m\n", "yes");
} else {
printf(" autoCreateTable: \033[33m%s\033[0m\n", "error");
}
if (TBL_NO_EXISTS == g_Dbs.db[i].superTbls[j].childTblExists) {
printf(" childTblExists: \033[33m%s\033[0m\n", "no");
} else if (TBL_ALREADY_EXISTS == g_Dbs.db[i].superTbls[j].childTblExists) {
printf(" childTblExists: \033[33m%s\033[0m\n", "yes");
} else {
printf(" childTblExists: \033[33m%s\033[0m\n", "error");
}
printf(" childTblCount: \033[33m%"PRId64"\033[0m\n",
g_Dbs.db[i].superTbls[j].childTblCount);
printf(" childTblPrefix: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].childTblPrefix);
printf(" dataSource: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].dataSource);
printf(" iface: \033[33m%s\033[0m\n",
(g_Dbs.db[i].superTbls[j].iface==TAOSC_IFACE)?"taosc":
(g_Dbs.db[i].superTbls[j].iface==REST_IFACE)?"rest":"stmt");
if (g_Dbs.db[i].superTbls[j].childTblLimit > 0) {
printf(" childTblLimit: \033[33m%"PRId64"\033[0m\n",
g_Dbs.db[i].superTbls[j].childTblLimit);
}
if (g_Dbs.db[i].superTbls[j].childTblOffset > 0) {
printf(" childTblOffset: \033[33m%"PRIu64"\033[0m\n",
g_Dbs.db[i].superTbls[j].childTblOffset);
}
printf(" insertRows: \033[33m%"PRId64"\033[0m\n",
g_Dbs.db[i].superTbls[j].insertRows);
/*
if (0 == g_Dbs.db[i].superTbls[j].multiThreadWriteOneTbl) {
printf(" multiThreadWriteOneTbl: \033[33m no\033[0m\n");
}else {
printf(" multiThreadWriteOneTbl: \033[33m yes\033[0m\n");
}
*/
printf(" interlaceRows: \033[33m%u\033[0m\n",
g_Dbs.db[i].superTbls[j].interlaceRows);
if (g_Dbs.db[i].superTbls[j].interlaceRows > 0) { printf(" stbName: \033[33m%s\033[0m\n",
printf(" stable insert interval: \033[33m%"PRIu64"\033[0m\n", g_Dbs.db[i].superTbls[j].stbName);
g_Dbs.db[i].superTbls[j].insertInterval);
}
printf(" disorderRange: \033[33m%d\033[0m\n", if (PRE_CREATE_SUBTBL == g_Dbs.db[i].superTbls[j].autoCreateTable) {
g_Dbs.db[i].superTbls[j].disorderRange); printf(" autoCreateTable: \033[33m%s\033[0m\n", "no");
printf(" disorderRatio: \033[33m%d\033[0m\n", } else if (AUTO_CREATE_SUBTBL ==
g_Dbs.db[i].superTbls[j].disorderRatio); g_Dbs.db[i].superTbls[j].autoCreateTable) {
printf(" maxSqlLen: \033[33m%"PRIu64"\033[0m\n", printf(" autoCreateTable: \033[33m%s\033[0m\n", "yes");
g_Dbs.db[i].superTbls[j].maxSqlLen);
printf(" timeStampStep: \033[33m%"PRId64"\033[0m\n",
g_Dbs.db[i].superTbls[j].timeStampStep);
printf(" startTimestamp: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].startTimestamp);
printf(" sampleFormat: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].sampleFormat);
printf(" sampleFile: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].sampleFile);
printf(" tagsFile: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].tagsFile);
printf(" columnCount: \033[33m%d\033[0m\n",
g_Dbs.db[i].superTbls[j].columnCount);
for (int k = 0; k < g_Dbs.db[i].superTbls[j].columnCount; k++) {
//printf("dataType:%s, dataLen:%d\t", g_Dbs.db[i].superTbls[j].columns[k].dataType, g_Dbs.db[i].superTbls[j].columns[k].dataLen);
if ((0 == strncasecmp(g_Dbs.db[i].superTbls[j].columns[k].dataType,
"binary", 6))
|| (0 == strncasecmp(g_Dbs.db[i].superTbls[j].columns[k].dataType,
"nchar", 5))) {
printf("column[\033[33m%d\033[0m]:\033[33m%s(%d)\033[0m ", k,
g_Dbs.db[i].superTbls[j].columns[k].dataType,
g_Dbs.db[i].superTbls[j].columns[k].dataLen);
} else { } else {
printf("column[%d]:\033[33m%s\033[0m ", k, printf(" autoCreateTable: \033[33m%s\033[0m\n", "error");
g_Dbs.db[i].superTbls[j].columns[k].dataType);
} }
}
printf("\n");
printf(" tagCount: \033[33m%d\033[0m\n ", if (TBL_NO_EXISTS == g_Dbs.db[i].superTbls[j].childTblExists) {
g_Dbs.db[i].superTbls[j].tagCount); printf(" childTblExists: \033[33m%s\033[0m\n", "no");
for (int k = 0; k < g_Dbs.db[i].superTbls[j].tagCount; k++) { } else if (TBL_ALREADY_EXISTS == g_Dbs.db[i].superTbls[j].childTblExists) {
//printf("dataType:%s, dataLen:%d\t", g_Dbs.db[i].superTbls[j].tags[k].dataType, g_Dbs.db[i].superTbls[j].tags[k].dataLen); printf(" childTblExists: \033[33m%s\033[0m\n", "yes");
if ((0 == strncasecmp(g_Dbs.db[i].superTbls[j].tags[k].dataType,
"binary", strlen("binary")))
|| (0 == strncasecmp(g_Dbs.db[i].superTbls[j].tags[k].dataType,
"nchar", strlen("nchar")))) {
printf("tag[%d]:\033[33m%s(%d)\033[0m ", k,
g_Dbs.db[i].superTbls[j].tags[k].dataType,
g_Dbs.db[i].superTbls[j].tags[k].dataLen);
} else { } else {
printf("tag[%d]:\033[33m%s\033[0m ", k, printf(" childTblExists: \033[33m%s\033[0m\n", "error");
g_Dbs.db[i].superTbls[j].tags[k].dataType); }
printf(" childTblCount: \033[33m%"PRId64"\033[0m\n",
g_Dbs.db[i].superTbls[j].childTblCount);
printf(" childTblPrefix: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].childTblPrefix);
printf(" dataSource: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].dataSource);
printf(" iface: \033[33m%s\033[0m\n",
(g_Dbs.db[i].superTbls[j].iface==TAOSC_IFACE)?"taosc":
(g_Dbs.db[i].superTbls[j].iface==REST_IFACE)?"rest":"stmt");
if (g_Dbs.db[i].superTbls[j].childTblLimit > 0) {
printf(" childTblLimit: \033[33m%"PRId64"\033[0m\n",
g_Dbs.db[i].superTbls[j].childTblLimit);
}
if (g_Dbs.db[i].superTbls[j].childTblOffset > 0) {
printf(" childTblOffset: \033[33m%"PRIu64"\033[0m\n",
g_Dbs.db[i].superTbls[j].childTblOffset);
}
printf(" insertRows: \033[33m%"PRId64"\033[0m\n",
g_Dbs.db[i].superTbls[j].insertRows);
/*
if (0 == g_Dbs.db[i].superTbls[j].multiThreadWriteOneTbl) {
printf(" multiThreadWriteOneTbl: \033[33m no\033[0m\n");
}else {
printf(" multiThreadWriteOneTbl: \033[33m yes\033[0m\n");
}
*/
printf(" interlaceRows: \033[33m%u\033[0m\n",
g_Dbs.db[i].superTbls[j].interlaceRows);
if (g_Dbs.db[i].superTbls[j].interlaceRows > 0) {
printf(" stable insert interval: \033[33m%"PRIu64"\033[0m\n",
g_Dbs.db[i].superTbls[j].insertInterval);
}
printf(" disorderRange: \033[33m%d\033[0m\n",
g_Dbs.db[i].superTbls[j].disorderRange);
printf(" disorderRatio: \033[33m%d\033[0m\n",
g_Dbs.db[i].superTbls[j].disorderRatio);
printf(" maxSqlLen: \033[33m%"PRIu64"\033[0m\n",
g_Dbs.db[i].superTbls[j].maxSqlLen);
printf(" timeStampStep: \033[33m%"PRId64"\033[0m\n",
g_Dbs.db[i].superTbls[j].timeStampStep);
printf(" startTimestamp: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].startTimestamp);
printf(" sampleFormat: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].sampleFormat);
printf(" sampleFile: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].sampleFile);
printf(" tagsFile: \033[33m%s\033[0m\n",
g_Dbs.db[i].superTbls[j].tagsFile);
printf(" columnCount: \033[33m%d\033[0m\n ",
g_Dbs.db[i].superTbls[j].columnCount);
for (int k = 0; k < g_Dbs.db[i].superTbls[j].columnCount; k++) {
//printf("dataType:%s, dataLen:%d\t", g_Dbs.db[i].superTbls[j].columns[k].dataType, g_Dbs.db[i].superTbls[j].columns[k].dataLen);
if ((0 == strncasecmp(g_Dbs.db[i].superTbls[j].columns[k].dataType,
"binary", 6))
|| (0 == strncasecmp(g_Dbs.db[i].superTbls[j].columns[k].dataType,
"nchar", 5))) {
printf("column[%d]:\033[33m%s(%d)\033[0m ", k,
g_Dbs.db[i].superTbls[j].columns[k].dataType,
g_Dbs.db[i].superTbls[j].columns[k].dataLen);
} else {
printf("column[%d]:\033[33m%s\033[0m ", k,
g_Dbs.db[i].superTbls[j].columns[k].dataType);
}
} }
printf("\n");
printf(" tagCount: \033[33m%d\033[0m\n ",
g_Dbs.db[i].superTbls[j].tagCount);
for (int k = 0; k < g_Dbs.db[i].superTbls[j].tagCount; k++) {
//printf("dataType:%s, dataLen:%d\t", g_Dbs.db[i].superTbls[j].tags[k].dataType, g_Dbs.db[i].superTbls[j].tags[k].dataLen);
if ((0 == strncasecmp(g_Dbs.db[i].superTbls[j].tags[k].dataType,
"binary", strlen("binary")))
|| (0 == strncasecmp(g_Dbs.db[i].superTbls[j].tags[k].dataType,
"nchar", strlen("nchar")))) {
printf("tag[%d]:\033[33m%s(%d)\033[0m ", k,
g_Dbs.db[i].superTbls[j].tags[k].dataType,
g_Dbs.db[i].superTbls[j].tags[k].dataLen);
} else {
printf("tag[%d]:\033[33m%s\033[0m ", k,
g_Dbs.db[i].superTbls[j].tags[k].dataType);
}
}
printf("\n");
} }
printf("\n"); } else {
printf(" childTblCount: \033[33m%"PRId64"\033[0m\n",
g_args.ntables);
printf(" insertRows: \033[33m%"PRId64"\033[0m\n",
g_args.insertRows);
} }
printf("\n"); printf("\n");
} }
...@@ -4271,6 +4284,10 @@ static int createSuperTable( ...@@ -4271,6 +4284,10 @@ static int createSuperTable(
len += snprintf(tags + len, TSDB_MAX_TAGS_LEN - len, len += snprintf(tags + len, TSDB_MAX_TAGS_LEN - len,
"T%d %s,", tagIndex, "BIGINT UNSIGNED"); "T%d %s,", tagIndex, "BIGINT UNSIGNED");
lenOfTagOfOneRow += superTbl->tags[tagIndex].dataLen + BIGINT_BUFF_LEN; lenOfTagOfOneRow += superTbl->tags[tagIndex].dataLen + BIGINT_BUFF_LEN;
} else if (strcasecmp(dataType, "TIMESTAMP") == 0) {
len += snprintf(tags + len, TSDB_MAX_TAGS_LEN - len,
"T%d %s,", tagIndex, "TIMESTAMP");
lenOfTagOfOneRow += superTbl->tags[tagIndex].dataLen + TIMESTAMP_BUFF_LEN;
} else { } else {
taos_close(taos); taos_close(taos);
free(command); free(command);
...@@ -10282,8 +10299,7 @@ static void startMultiThreadInsertData(int threads, char* db_name, ...@@ -10282,8 +10299,7 @@ static void startMultiThreadInsertData(int threads, char* db_name,
b = ntables % threads; b = ntables % threads;
} }
if ((stbInfo) if (g_args.iface == REST_IFACE || ((stbInfo) && (stbInfo->iface == REST_IFACE))) {
&& (stbInfo->iface == REST_IFACE)) {
if (convertHostToServAddr( if (convertHostToServAddr(
g_Dbs.host, g_Dbs.port, &(g_Dbs.serv_addr)) != 0) { g_Dbs.host, g_Dbs.port, &(g_Dbs.serv_addr)) != 0) {
ERROR_EXIT("convert host to server address"); ERROR_EXIT("convert host to server address");
...@@ -11734,6 +11750,8 @@ static void initOfQueryMeta() { ...@@ -11734,6 +11750,8 @@ static void initOfQueryMeta() {
} }
static void setParaFromArg() { static void setParaFromArg() {
char type[20];
char length[20];
if (g_args.host) { if (g_args.host) {
tstrncpy(g_Dbs.host, g_args.host, MAX_HOSTNAME_SIZE); tstrncpy(g_Dbs.host, g_args.host, MAX_HOSTNAME_SIZE);
} else { } else {
...@@ -11815,7 +11833,17 @@ static void setParaFromArg() { ...@@ -11815,7 +11833,17 @@ static void setParaFromArg() {
g_Dbs.db[0].superTbls[0].columns[i].data_type = data_type[i]; g_Dbs.db[0].superTbls[0].columns[i].data_type = data_type[i];
tstrncpy(g_Dbs.db[0].superTbls[0].columns[i].dataType, tstrncpy(g_Dbs.db[0].superTbls[0].columns[i].dataType,
dataType[i], min(DATATYPE_BUFF_LEN, strlen(dataType[i]) + 1)); dataType[i], min(DATATYPE_BUFF_LEN, strlen(dataType[i]) + 1));
g_Dbs.db[0].superTbls[0].columns[i].dataLen = g_args.binwidth; if (1 == regexMatch(dataType[i], "^(NCHAR|BINARY)(\\([1-9][0-9]*\\))$", REG_ICASE |
REG_EXTENDED)) {
sscanf(dataType[i], "%[^(](%[^)]", type, length);
g_Dbs.db[0].superTbls[0].columns[i].dataLen = atoi(length);
tstrncpy(g_Dbs.db[0].superTbls[0].columns[i].dataType,
type, min(DATATYPE_BUFF_LEN, strlen(type) + 1));
} else {
g_Dbs.db[0].superTbls[0].columns[i].dataLen = g_args.binwidth;
tstrncpy(g_Dbs.db[0].superTbls[0].columns[i].dataType,
dataType[i], min(DATATYPE_BUFF_LEN, strlen(dataType[i]) + 1));
}
g_Dbs.db[0].superTbls[0].columnCount++; g_Dbs.db[0].superTbls[0].columnCount++;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册