/* * Copyright (c) 2019 TAOS Data, Inc. * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include "parInt.h" using namespace std; using namespace testing; class ParserTest : public Test { protected: void setDatabase(const string& acctId, const string& db) { acctId_ = acctId; db_ = db; } void bind(const char* sql) { reset(); cxt_.acctId = atoi(acctId_.c_str()); cxt_.db = db_.c_str(); sqlBuf_ = string(sql); transform(sqlBuf_.begin(), sqlBuf_.end(), sqlBuf_.begin(), ::tolower); cxt_.sqlLen = strlen(sql); cxt_.pSql = sqlBuf_.c_str(); } bool run(int32_t parseCode = TSDB_CODE_SUCCESS, int32_t translateCode = TSDB_CODE_SUCCESS) { query_ = nullptr; bool res = runImpl(parseCode, translateCode); qDestroyQuery(query_); if (1/*!res*/) { dump(); } return res; } private: static const int max_err_len = 1024; bool runImpl(int32_t parseCode, int32_t translateCode) { int32_t code = parse(&cxt_, &query_); if (code != TSDB_CODE_SUCCESS) { parseErrStr_ = string("code:") + tstrerror(code) + string(", msg:") + errMagBuf_; return (terrno == parseCode); } if (TSDB_CODE_SUCCESS != parseCode) { return false; } parsedAstStr_ = toString(query_->pRoot); code = translate(&cxt_, query_); if (code != TSDB_CODE_SUCCESS) { translateErrStr_ = string("code:") + tstrerror(code) + string(", msg:") + errMagBuf_; return (terrno == translateCode); } translatedAstStr_ = toString(query_->pRoot); code = calculateConstant(&cxt_, query_); if (code != TSDB_CODE_SUCCESS) { calcConstErrStr_ = string("code:") + tstrerror(code) + string(", msg:") + errMagBuf_; return false; } calcConstAstStr_ = toString(query_->pRoot); return (TSDB_CODE_SUCCESS == translateCode); } void dump() { cout << "input sql : [" << cxt_.pSql << "]" << endl; if (!parseErrStr_.empty()) { cout << "parse error: " << parseErrStr_ << endl; } if (!parsedAstStr_.empty()) { cout << "parse output: " << endl; cout << parsedAstStr_ << endl; } if (!translateErrStr_.empty()) { cout << "translate error: " << translateErrStr_ << endl; } if (!translatedAstStr_.empty()) { cout << "translate output: " << endl; cout << translatedAstStr_ << endl; } if (!calcConstErrStr_.empty()) { cout << "calculateConstant error: " << calcConstErrStr_ << endl; } if (!calcConstAstStr_.empty()) { cout << "calculateConstant output: " << endl; cout << calcConstAstStr_ << endl; } } string toString(const SNode* pRoot, bool format = false) { char* pStr = NULL; int32_t len = 0; int32_t code = nodesNodeToString(pRoot, format, &pStr, &len); if (code != TSDB_CODE_SUCCESS) { cout << "sql:[" << cxt_.pSql << "] toString code:" << code << ", strerror:" << tstrerror(code) << endl; throw "nodesNodeToString failed!"; } string str(pStr); taosMemoryFreeClear(pStr); return str; } void reset() { memset(&cxt_, 0, sizeof(cxt_)); memset(errMagBuf_, 0, max_err_len); cxt_.pMsg = errMagBuf_; cxt_.msgLen = max_err_len; parseErrStr_.clear(); parsedAstStr_.clear(); translateErrStr_.clear(); translatedAstStr_.clear(); calcConstErrStr_.clear(); calcConstAstStr_.clear(); } string acctId_; string db_; char errMagBuf_[max_err_len]; string sqlBuf_; SParseContext cxt_; SQuery* query_; string parseErrStr_; string parsedAstStr_; string translateErrStr_; string translatedAstStr_; string calcConstErrStr_; string calcConstAstStr_; }; TEST_F(ParserTest, createAccount) { setDatabase("root", "test"); bind("create account ac_wxy pass '123456'"); ASSERT_TRUE(run(TSDB_CODE_PAR_EXPRIE_STATEMENT)); } TEST_F(ParserTest, alterAccount) { setDatabase("root", "test"); bind("alter account ac_wxy pass '123456'"); ASSERT_TRUE(run(TSDB_CODE_PAR_EXPRIE_STATEMENT)); } TEST_F(ParserTest, createUser) { setDatabase("root", "test"); bind("create user wxy pass '123456'"); ASSERT_TRUE(run()); } TEST_F(ParserTest, alterUser) { setDatabase("root", "test"); bind("alter user wxy pass '123456'"); ASSERT_TRUE(run()); bind("alter user wxy privilege 'write'"); ASSERT_TRUE(run()); } TEST_F(ParserTest, dropUser) { setDatabase("root", "test"); bind("drop user wxy"); ASSERT_TRUE(run()); } TEST_F(ParserTest, selectSimple) { setDatabase("root", "test"); bind("SELECT * FROM t1"); ASSERT_TRUE(run()); bind("SELECT * FROM test.t1"); ASSERT_TRUE(run()); bind("SELECT ts, c1 FROM t1"); ASSERT_TRUE(run()); bind("SELECT ts, t.c1 FROM (SELECT * FROM t1) t"); ASSERT_TRUE(run()); bind("SELECT * FROM t1 tt1, t1 tt2 WHERE tt1.c1 = tt2.c1"); ASSERT_TRUE(run()); } TEST_F(ParserTest, selectConstant) { setDatabase("root", "test"); bind("SELECT 123, 20.4, 'abc', \"wxy\", TIMESTAMP '2022-02-09 17:30:20', true, false, 10s FROM t1"); ASSERT_TRUE(run()); bind("SELECT 1234567890123456789012345678901234567890, 20.1234567890123456789012345678901234567890, 'abc', \"wxy\", TIMESTAMP '2022-02-09 17:30:20', true, false, 15s FROM t1"); ASSERT_TRUE(run()); bind("SELECT 123 + 45 FROM t1 where 2 - 1"); ASSERT_TRUE(run()); } TEST_F(ParserTest, selectExpression) { setDatabase("root", "test"); bind("SELECT ts + 10s, c1 + 10, concat(c2, 'abc') FROM t1"); ASSERT_TRUE(run()); bind("SELECT ts > 0, c1 < 20 AND c2 = 'qaz' FROM t1"); ASSERT_TRUE(run()); bind("SELECT ts > 0, c1 BETWEEN 10 AND 20 AND c2 = 'qaz' FROM t1"); ASSERT_TRUE(run()); } TEST_F(ParserTest, selectPseudoColumn) { setDatabase("root", "test"); bind("SELECT _wstartts, _wendts, count(*) FROM t1 interval(10s)"); ASSERT_TRUE(run()); } TEST_F(ParserTest, selectClause) { setDatabase("root", "test"); // GROUP BY clause bind("SELECT count(*) cnt FROM t1 WHERE c1 > 0"); ASSERT_TRUE(run()); bind("SELECT count(*), c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c2"); ASSERT_TRUE(run()); bind("SELECT count(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 HAVING count(c1) > 10"); ASSERT_TRUE(run()); bind("SELECT count(*), c1, c2 + 10, c1 + c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c2, c1"); ASSERT_TRUE(run()); bind("SELECT count(*), c1 + 10, c2 cnt FROM t1 WHERE c1 > 0 GROUP BY c1 + 10, c2"); ASSERT_TRUE(run()); // ORDER BY clause bind("SELECT count(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 ORDER BY cnt"); ASSERT_TRUE(run()); bind("SELECT count(*) cnt FROM t1 WHERE c1 > 0 GROUP BY c2 ORDER BY 1"); ASSERT_TRUE(run()); // DISTINCT clause bind("SELECT DISTINCT c1, c2 FROM t1 WHERE c1 > 0 ORDER BY c1"); ASSERT_TRUE(run()); bind("SELECT DISTINCT c1 + 10, c2 FROM t1 WHERE c1 > 0 ORDER BY c1 + 10, c2"); ASSERT_TRUE(run()); bind("SELECT DISTINCT c1 + 10 cc1, c2 cc2 FROM t1 WHERE c1 > 0 ORDER BY cc1, c2"); ASSERT_TRUE(run()); bind("SELECT DISTINCT count(c2) FROM t1 WHERE c1 > 0 GROUP BY c1 ORDER BY count(c2)"); ASSERT_TRUE(run()); } TEST_F(ParserTest, selectWindow) { setDatabase("root", "test"); bind("SELECT count(*) FROM t1 interval(10s)"); ASSERT_TRUE(run()); } TEST_F(ParserTest, selectSyntaxError) { setDatabase("root", "test"); bind("SELECTT * FROM t1"); ASSERT_TRUE(run(TSDB_CODE_FAILED)); bind("SELECT *"); ASSERT_TRUE(run(TSDB_CODE_FAILED)); bind("SELECT *, * FROM test.t1"); ASSERT_TRUE(run(TSDB_CODE_FAILED)); bind("SELECT * FROM test.t1 t WHER"); ASSERT_TRUE(run(TSDB_CODE_FAILED)); } TEST_F(ParserTest, selectSemanticError) { setDatabase("root", "test"); // TSDB_CODE_PAR_INVALID_COLUMN bind("SELECT c1, cc1 FROM t1"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_INVALID_COLUMN)); bind("SELECT t1.c1, t1.cc1 FROM t1"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_INVALID_COLUMN)); // TSDB_CODE_PAR_TABLE_NOT_EXIST bind("SELECT * FROM t10"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_TABLE_NOT_EXIST)); bind("SELECT * FROM test.t10"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_TABLE_NOT_EXIST)); bind("SELECT t2.c1 FROM t1"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_TABLE_NOT_EXIST)); // TSDB_CODE_PAR_AMBIGUOUS_COLUMN bind("SELECT c2 FROM t1 tt1, t1 tt2 WHERE tt1.c1 = tt2.c1"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_AMBIGUOUS_COLUMN)); // TSDB_CODE_PAR_WRONG_VALUE_TYPE bind("SELECT 10n FROM t1"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_WRONG_VALUE_TYPE)); bind("SELECT TIMESTAMP '2010' FROM t1"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_WRONG_VALUE_TYPE)); // TSDB_CODE_PAR_INVALID_FUNTION bind("SELECT cnt(*) FROM t1"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_INVALID_FUNTION)); // TSDB_CODE_PAR_FUNTION_PARA_NUM // TSDB_CODE_PAR_FUNTION_PARA_TYPE // TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION bind("SELECT c2 FROM t1 tt1 JOIN t1 tt2 ON count(*) > 0"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION)); bind("SELECT c2 FROM t1 where count(*) > 0"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION)); bind("SELECT c2 FROM t1 GROUP BY count(*)"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_ILLEGAL_USE_AGG_FUNCTION)); // TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT bind("SELECT c2 FROM t1 ORDER BY 0"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT)); bind("SELECT c2 FROM t1 ORDER BY 2"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_WRONG_NUMBER_OF_SELECT)); // TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION bind("SELECT count(*) cnt FROM t1 HAVING c1 > 0"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION)); bind("SELECT count(*) cnt FROM t1 GROUP BY c2 HAVING c1 > 0"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION)); bind("SELECT count(*), c1 cnt FROM t1 GROUP BY c2 HAVING c2 > 0"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION)); bind("SELECT count(*) cnt FROM t1 GROUP BY c2 HAVING c2 > 0 ORDER BY c1"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_GROUPBY_LACK_EXPRESSION)); // TSDB_CODE_PAR_NOT_SINGLE_GROUP bind("SELECT count(*), c1 FROM t1"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SINGLE_GROUP)); bind("SELECT count(*) FROM t1 ORDER BY c1"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SINGLE_GROUP)); bind("SELECT c1 FROM t1 ORDER BY count(*)"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SINGLE_GROUP)); // TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION bind("SELECT DISTINCT c1, c2 FROM t1 WHERE c1 > 0 ORDER BY ts"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION)); bind("SELECT DISTINCT c1 FROM t1 WHERE c1 > 0 ORDER BY count(c2)"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION)); bind("SELECT DISTINCT c2 FROM t1 WHERE c1 > 0 ORDER BY count(c2)"); ASSERT_TRUE(run(TSDB_CODE_SUCCESS, TSDB_CODE_PAR_NOT_SELECTED_EXPRESSION)); } TEST_F(ParserTest, showUsers) { setDatabase("root", "test"); bind("show users"); ASSERT_TRUE(run()); } TEST_F(ParserTest, createDnode) { setDatabase("root", "test"); bind("create dnode abc1 port 7000"); ASSERT_TRUE(run()); bind("create dnode 1.1.1.1 port 9000"); ASSERT_TRUE(run()); } TEST_F(ParserTest, showDnodes) { setDatabase("root", "test"); bind("show dnodes"); ASSERT_TRUE(run()); } TEST_F(ParserTest, alterDnode) { setDatabase("root", "test"); bind("alter dnode 1 'resetLog'"); ASSERT_TRUE(run()); bind("alter dnode 1 'debugFlag' '134'"); ASSERT_TRUE(run()); } TEST_F(ParserTest, createDatabase) { setDatabase("root", "test"); bind("create database wxy_db"); ASSERT_TRUE(run()); bind("create database if not exists wxy_db " "BLOCKS 100 " "CACHE 100 " "CACHELAST 2 " "COMP 1 " "DAYS 100 " "FSYNC 100 " "MAXROWS 1000 " "MINROWS 100 " "KEEP 100 " "PRECISION 'ms' " "QUORUM 1 " "REPLICA 3 " "TTL 100 " "WAL 2 " "VGROUPS 100 " "SINGLE_STABLE 0 " "STREAM_MODE 1 " "RETENTIONS '15s:7d,1m:21d,15m:5y'" ); ASSERT_TRUE(run()); bind("create database if not exists wxy_db " "DAYS 100m " "KEEP 200m,300h,400d " ); ASSERT_TRUE(run()); } TEST_F(ParserTest, alterDatabase) { setDatabase("root", "test"); bind("alter database wxy_db BLOCKS 200"); ASSERT_TRUE(run()); bind("alter database wxy_db " "BLOCKS 200 " "CACHELAST 1 " "FSYNC 200 " "KEEP 200 " "QUORUM 2 " "WAL 1 " ); ASSERT_TRUE(run()); } TEST_F(ParserTest, showDatabase) { setDatabase("root", "test"); bind("show databases"); ASSERT_TRUE(run()); } TEST_F(ParserTest, useDatabase) { setDatabase("root", "test"); bind("use wxy_db"); ASSERT_TRUE(run()); } TEST_F(ParserTest, createTable) { setDatabase("root", "test"); bind("create table t1(ts timestamp, c1 int)"); ASSERT_TRUE(run()); bind("create table if not exists test.t1(" "ts TIMESTAMP, c1 INT, c2 INT UNSIGNED, c3 BIGINT, c4 BIGINT UNSIGNED, c5 FLOAT, c6 DOUBLE, c7 BINARY(20), c8 SMALLINT, " "c9 SMALLINT UNSIGNED COMMENT 'test column comment', c10 TINYINT, c11 TINYINT UNSIGNED, c12 BOOL, c13 NCHAR(30), c14 JSON, c15 VARCHAR(50)) " "KEEP 100 TTL 100 COMMENT 'test create table' SMA(c1, c2, c3)" ); ASSERT_TRUE(run()); bind("create table if not exists test.t1(" "ts TIMESTAMP, c1 INT, c2 INT UNSIGNED, c3 BIGINT, c4 BIGINT UNSIGNED, c5 FLOAT, c6 DOUBLE, c7 BINARY(20), c8 SMALLINT, " "c9 SMALLINT UNSIGNED COMMENT 'test column comment', c10 TINYINT, c11 TINYINT UNSIGNED, c12 BOOL, c13 NCHAR(30), c14 JSON, c15 VARCHAR(50)) " "TAGS (tsa TIMESTAMP, a1 INT, a2 INT UNSIGNED, a3 BIGINT, a4 BIGINT UNSIGNED, a5 FLOAT, a6 DOUBLE, a7 BINARY(20), a8 SMALLINT, " "a9 SMALLINT UNSIGNED COMMENT 'test column comment', a10 TINYINT, a11 TINYINT UNSIGNED, a12 BOOL, a13 NCHAR(30), a14 JSON, a15 VARCHAR(50)) " "KEEP 100 TTL 100 COMMENT 'test create table' SMA(c1, c2, c3) ROLLUP (min) FILE_FACTOR 0.1 DELAY 2" ); ASSERT_TRUE(run()); bind("create table if not exists t1 using st1 tags(1, 'wxy')"); ASSERT_TRUE(run()); bind("create table " "if not exists test.t1 using test.st1 (tag1, tag2) tags(1, 'abc') " "if not exists test.t2 using test.st1 (tag1, tag2) tags(2, 'abc') " "if not exists test.t3 using test.st1 (tag1, tag2) tags(3, 'abc') " "if not exists test.t4 using test.st1 (tag1, tag2) tags(3, null) " "if not exists test.t5 using test.st1 (tag1, tag2) tags(null, 'abc') " "if not exists test.t6 using test.st1 (tag1, tag2) tags(null, null)" ); ASSERT_TRUE(run()); bind("create stable t1(ts timestamp, c1 int) TAGS(id int)"); ASSERT_TRUE(run()); bind("create stable if not exists test.t1(" "ts TIMESTAMP, c1 INT, c2 INT UNSIGNED, c3 BIGINT, c4 BIGINT UNSIGNED, c5 FLOAT, c6 DOUBLE, c7 BINARY(20), c8 SMALLINT, " "c9 SMALLINT UNSIGNED COMMENT 'test column comment', c10 TINYINT, c11 TINYINT UNSIGNED, c12 BOOL, c13 NCHAR(30), c14 JSON, c15 VARCHAR(50)) " "TAGS (tsa TIMESTAMP, a1 INT, a2 INT UNSIGNED, a3 BIGINT, a4 BIGINT UNSIGNED, a5 FLOAT, a6 DOUBLE, a7 BINARY(20), a8 SMALLINT, " "a9 SMALLINT UNSIGNED COMMENT 'test column comment', a10 TINYINT, a11 TINYINT UNSIGNED, a12 BOOL, a13 NCHAR(30), a14 JSON, a15 VARCHAR(50)) " "KEEP 100 TTL 100 COMMENT 'test create table' SMA(c1, c2, c3) ROLLUP (min) FILE_FACTOR 0.1 DELAY 2" ); ASSERT_TRUE(run()); } TEST_F(ParserTest, showTables) { setDatabase("root", "test"); bind("show tables"); ASSERT_TRUE(run()); bind("show test.tables"); ASSERT_TRUE(run()); bind("show tables like 'c%'"); ASSERT_TRUE(run()); bind("show test.tables like 'c%'"); ASSERT_TRUE(run()); } TEST_F(ParserTest, showStables) { setDatabase("root", "test"); bind("show stables"); ASSERT_TRUE(run()); bind("show test.stables"); ASSERT_TRUE(run()); bind("show stables like 'c%'"); ASSERT_TRUE(run()); bind("show test.stables like 'c%'"); ASSERT_TRUE(run()); } TEST_F(ParserTest, showVgroups) { setDatabase("root", "test"); bind("show vgroups"); ASSERT_TRUE(run()); bind("show test.vgroups"); ASSERT_TRUE(run()); } TEST_F(ParserTest, showMnodes) { setDatabase("root", "test"); bind("show mnodes"); ASSERT_TRUE(run()); } TEST_F(ParserTest, showModules) { setDatabase("root", "test"); bind("show modules"); ASSERT_TRUE(run()); } TEST_F(ParserTest, showQnodes) { setDatabase("root", "test"); bind("show qnodes"); ASSERT_TRUE(run()); } TEST_F(ParserTest, showFunctions) { setDatabase("root", "test"); bind("show functions"); ASSERT_TRUE(run()); } TEST_F(ParserTest, showIndexes) { setDatabase("root", "test"); bind("show indexes from t1"); ASSERT_TRUE(run()); bind("show indexes from t1 from test"); ASSERT_TRUE(run()); } TEST_F(ParserTest, showStreams) { setDatabase("root", "test"); bind("show streams"); ASSERT_TRUE(run()); } TEST_F(ParserTest, createSmaIndex) { setDatabase("root", "test"); bind("create sma index index1 on t1 function(max(c1), min(c3 + 10), sum(c4)) INTERVAL(10s)"); ASSERT_TRUE(run()); } TEST_F(ParserTest, dropIndex) { setDatabase("root", "test"); bind("drop index index1 on t1"); ASSERT_TRUE(run()); } TEST_F(ParserTest, createQnode) { setDatabase("root", "test"); bind("create qnode on dnode 1"); ASSERT_TRUE(run()); } TEST_F(ParserTest, dropQnode) { setDatabase("root", "test"); bind("drop qnode on dnode 1"); ASSERT_TRUE(run()); } TEST_F(ParserTest, createTopic) { setDatabase("root", "test"); bind("create topic tp1 as select * from t1"); ASSERT_TRUE(run()); bind("create topic if not exists tp1 as select * from t1"); ASSERT_TRUE(run()); bind("create topic tp1 as test"); ASSERT_TRUE(run()); bind("create topic if not exists tp1 as test"); ASSERT_TRUE(run()); } TEST_F(ParserTest, dropTopic) { setDatabase("root", "test"); bind("drop topic tp1"); ASSERT_TRUE(run()); bind("drop topic if exists tp1"); ASSERT_TRUE(run()); } TEST_F(ParserTest, explain) { setDatabase("root", "test"); bind("explain SELECT * FROM t1"); ASSERT_TRUE(run()); bind("explain analyze SELECT * FROM t1"); ASSERT_TRUE(run()); bind("explain analyze verbose true ratio 0.01 SELECT * FROM t1"); ASSERT_TRUE(run()); }