Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7862707
fix(rbac): rollup database privilege
kailixu Apr 22, 2026
91b110c
fix(rbac): show create database privilege
kailixu Apr 22, 2026
7a3cf72
test(rbac): update show grants test case
kailixu Apr 22, 2026
ec66bb4
fix(rbac): show connections privilege
kailixu Apr 23, 2026
6a96166
Merge branch 'main' into fix/TD-6837020675-main
kailixu Apr 23, 2026
85d41d3
chore(rbac): privilege check optimization
kailixu Apr 24, 2026
17cbc31
chore: refactor sysscanoperator from 3.0
kailixu Apr 24, 2026
bed17a6
Merge branch 'main' into fix/TD-6837020675-main
kailixu Apr 25, 2026
30fe44f
enh(rbac): read system table privileges
kailixu Apr 25, 2026
073bc72
enh(rbac): read system table privileges
kailixu Apr 25, 2026
0fca3e0
enh(rbac): privilege check for show local/cluster variables
kailixu Apr 25, 2026
33349e9
enh(rbac): privilege of show stmt
kailixu Apr 25, 2026
a4c7511
fix(rbac): alter pass/alter self pass privilege
kailixu Apr 26, 2026
d6c8766
fix(rbac): privilege for show tsmas
kailixu Apr 26, 2026
a50cd47
fix(rbac): privilege of show subscriptions
kailixu Apr 26, 2026
c1adda9
enh(test): makefile for specific c file
kailixu Apr 26, 2026
332d850
enh(test): makefile for specific c file
kailixu Apr 26, 2026
86a015c
fix(rbac): test case of priv_control
kailixu Apr 26, 2026
f2b5b8e
chore: fix ci problem
kailixu Apr 26, 2026
0e6ef8f
chore: fix ci problem
kailixu Apr 26, 2026
4484f52
chore: fix ci problem
kailixu Apr 26, 2026
ef51541
chore: fix ci problem
kailixu Apr 27, 2026
ef08c1f
chore: fix ci problem
kailixu Apr 27, 2026
bffc3ca
chore: update totp to totp_secret
kailixu Apr 27, 2026
c48181c
chore: add test case for totp_secret privilege
kailixu Apr 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/common/systable.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ void getPerfDbMeta(const SSysTableMeta** pPerfsTableMeta, size_t* size);
void getVisibleInfosTablesNum(bool sysInfo, size_t* size);
bool invisibleColumn(bool sysInfo, int8_t tableType, int8_t flags);

const SSysTableMeta* getSysTableMeta(const char* dbName, const char* tbName);

#ifdef __cplusplus
}
#endif
Expand Down
22 changes: 22 additions & 0 deletions source/common/src/systable.c
Original file line number Diff line number Diff line change
Expand Up @@ -972,3 +972,25 @@ bool invisibleColumn(bool sysInfo, int8_t tableType, int8_t flags) {
}
return 0 != (flags & COL_IS_SYSINFO);
}

/**
* information_schema or performance_schema
*/
const SSysTableMeta* getSysTableMeta(const char* dbName, const char* tbName) {
const SSysTableMeta* pMeta = NULL;
size_t size = 0;
if (!dbName || !tbName) {
return NULL;
}
if (dbName[0] == 'i' || dbName[0] == 'I') {
getInfosDbMeta(&pMeta, &size);
} else {
getPerfDbMeta(&pMeta, &size);
}
Comment thread
kailixu marked this conversation as resolved.
for (size_t i = 0; i < size; ++i) {
if (strcasecmp(pMeta[i].name, tbName) == 0) {
return pMeta + i;
}
}
return NULL;
}
4 changes: 3 additions & 1 deletion source/dnode/mnode/impl/src/mndDb.c
Original file line number Diff line number Diff line change
Expand Up @@ -2857,7 +2857,9 @@ static int32_t mndProcessTrimDbReq(SRpcMsg *pReq) {
TAOS_CHECK_EXIT(code);
}

TAOS_CHECK_EXIT(mndCheckDbPrivilege(pMnode, RPC_MSG_USER(pReq), RPC_MSG_TOKEN(pReq), MND_OPER_TRIM_DB, pDb));
TAOS_CHECK_EXIT(mndCheckDbPrivilege(pMnode, RPC_MSG_USER(pReq), RPC_MSG_TOKEN(pReq),
trimReq.optrType == TSDB_OPTR_ROLLUP ? MND_OPER_ROLLUP_DB : MND_OPER_TRIM_DB,
pDb));
Comment thread
kailixu marked this conversation as resolved.

if (pDb->cfg.isMount) {
TAOS_CHECK_EXIT(TSDB_CODE_MND_MOUNT_OBJ_NOT_SUPPORT);
Expand Down
186 changes: 115 additions & 71 deletions source/libs/executor/src/sysscanoperator.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "storageapi.h"
#include "tcompare.h"
#include "thash.h"
#include "tref.h"
#include "trpc.h"
#include "ttypes.h"
// RPC timeout for virtual table reference validation (5 seconds)
Expand Down Expand Up @@ -79,6 +80,8 @@ typedef struct SSysTableScanInfo {
SRetrieveTableReq req;
SEpSet epSet;
tsem_t ready;
int64_t self; // ref ID in sysTableScanRefPool (for callback safety)
int32_t rspCode; // error code set by the RPC callback
SReadHandle readHandle;
const char* pUser;
int32_t accountId;
Expand Down Expand Up @@ -124,12 +127,28 @@ typedef struct SSysTableScanInfo {
STableListInfo* pSubTableListInfo;
} SSysTableScanInfo;

typedef struct SSysTableLoadCtx {
tsem_t ready;
SRetrieveMetaTableRsp* pRsp;
int32_t rspCode;
int32_t refs;
} SSysTableLoadCtx;
// Lightweight wrapper passed as RPC callback param; stores only the ref ID so
// the callback can safely acquire the SSysTableScanInfo from the ref pool.
typedef struct SSysTableScanCbParam {
int64_t sysTableScanId;
} SSysTableScanCbParam;

// Per-file ref pool used to decouple the callback lifetime from the operator
// lifetime, following the same pattern as fetchObjRefPool in exchangeoperator.c.
static int32_t sysTableScanRefPool = -1;
static TdThreadOnce sysTableScanRefPoolOnce = PTHREAD_ONCE_INIT;

static void doDestroySysTableScanInfo(void* param);

static void cleanupSysTableScanRefPool(void) {
int32_t ref = atomic_val_compare_exchange_32(&sysTableScanRefPool, sysTableScanRefPool, 0);
taosCloseRef(ref);
}

static void initSysTableScanRefPool(void) {
sysTableScanRefPool = taosOpenRef(64, doDestroySysTableScanInfo);
(void)atexit(cleanupSysTableScanRefPool);
}

typedef struct {
const char* name;
Expand Down Expand Up @@ -208,25 +227,6 @@ static void destroySysScanOperator(void* param);
static int32_t loadSysTableCallback(void* param, SDataBuf* pMsg, int32_t code);
static __optSysFilter optSysGetFilterFunc(int32_t ctype, bool* reverse, bool* equal);

static void freeSysTableLoadCtx(void* param) {
SSysTableLoadCtx* pCtx = (SSysTableLoadCtx*)param;
if (pCtx == NULL) {
return;
}

if (atomic_sub_fetch_32(&pCtx->refs, 1) != 0) {
return;
}

int32_t code = tsem_destroy(&pCtx->ready);
if (code != TSDB_CODE_SUCCESS) {
qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(code));
}

taosMemoryFreeClear(pCtx->pRsp);
taosMemoryFreeClear(pCtx);
}

static int32_t sysTableUserTagsFillOneTableTags(const SSysTableScanInfo* pInfo, SMetaReader* smrSuperTable,
SMetaReader* smrChildTable, const char* dbname, const char* tableName,
int32_t* pNumOfRows, const SSDataBlock* dataBlock);
Expand Down Expand Up @@ -4945,32 +4945,25 @@ static SSDataBlock* sysTableScanFromMNode(SOperatorInfo* pOperator, SSysTableSca
return NULL;
}

SSysTableLoadCtx* pLoadCtx = taosMemoryCalloc(1, sizeof(SSysTableLoadCtx));
if (pLoadCtx == NULL) {
qError("%s prepare callback context failed", GET_TASKID(pTaskInfo));
pTaskInfo->code = terrno;
taosMemoryFree(buf1);
taosMemoryFree(pMsgSendInfo);
return NULL;
}
int32_t msgType = (strcasecmp(name, TSDB_INS_TABLE_DNODE_VARIABLES) == 0) ? TDMT_DND_SYSTABLE_RETRIEVE
: TDMT_MND_SYSTABLE_RETRIEVE;

code = tsem_init(&pLoadCtx->ready, 0, 0);
if (code != TSDB_CODE_SUCCESS) {
qError("%s init callback context failed since %s", GET_TASKID(pTaskInfo), tstrerror(code));
// Allocate a lightweight wrapper that holds only the ref ID; the callback
// frees it via paramFreeFp = taosAutoMemoryFree after the callback returns.
SSysTableScanCbParam* pWrapper = taosMemoryCalloc(1, sizeof(SSysTableScanCbParam));
if (!pWrapper) {
pTaskInfo->code = terrno;
taosMemoryFree(buf1);
taosMemoryFree(pMsgSendInfo);
taosMemoryFree(pLoadCtx);
pTaskInfo->code = code;
return NULL;
T_LONG_JMP(pTaskInfo->env, pTaskInfo->code);
}
pLoadCtx->rspCode = TSDB_CODE_SUCCESS;
pLoadCtx->refs = 2;
pWrapper->sysTableScanId = pInfo->self;

int32_t msgType = (strcasecmp(name, TSDB_INS_TABLE_DNODE_VARIABLES) == 0) ? TDMT_DND_SYSTABLE_RETRIEVE
: TDMT_MND_SYSTABLE_RETRIEVE;
// reset rspCode from the previous iteration
pInfo->rspCode = 0;

pMsgSendInfo->param = pLoadCtx;
pMsgSendInfo->paramFreeFp = freeSysTableLoadCtx;
pMsgSendInfo->param = pWrapper;
pMsgSendInfo->paramFreeFp = taosAutoMemoryFree;
pMsgSendInfo->msgInfo.pData = buf1;
pMsgSendInfo->msgInfo.len = contLen;
pMsgSendInfo->msgType = msgType;
Expand All @@ -4979,31 +4972,24 @@ static SSDataBlock* sysTableScanFromMNode(SOperatorInfo* pOperator, SSysTableSca

code = asyncSendMsgToServer(pInfo->readHandle.pMsgCb->clientRpc, &pInfo->epSet, NULL, pMsgSendInfo);
if (code != TSDB_CODE_SUCCESS) {
freeSysTableLoadCtx(pLoadCtx);
qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(code));
pTaskInfo->code = code;
T_LONG_JMP(pTaskInfo->env, code);
}

code = tsem_timewait(&pLoadCtx->ready, VTB_REF_RPC_TIMEOUT_MS);
// Block this worker thread until the response arrives. qSemWait notifies
// the worker pool and waits, then re-acquires on wake-up.
code = qSemWait((qTaskInfo_t)pTaskInfo, &pInfo->ready);
if (code != TSDB_CODE_SUCCESS) {
freeSysTableLoadCtx(pLoadCtx);
qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(code));
qError("%s tsem_wait failed at line %d since %s", __func__, __LINE__, tstrerror(code));
Comment thread
kailixu marked this conversation as resolved.
pTaskInfo->code = code;
T_LONG_JMP(pTaskInfo->env, code);
}
Comment on lines +4980 to 4987
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sysTableScanFromMNode previously used tsem_timewait(..., VTB_REF_RPC_TIMEOUT_MS) but now blocks with qSemWait(..., &pInfo->ready) which has no timeout (it wraps tsem_wait). If the RPC callback is never invoked (transport error, dropped response, cancellation edge case), this worker thread can block indefinitely.

If a timeout is still required, consider adding a timed wait variant (e.g., qSemTimeWait) or restoring tsem_timewait and handling TSDB_CODE_TIMEOUT_ERROR explicitly.

Copilot uses AI. Check for mistakes.

if (pLoadCtx->rspCode != TSDB_CODE_SUCCESS) {
pTaskInfo->code = pLoadCtx->rspCode;
} else {
pInfo->pRsp = pLoadCtx->pRsp;
pLoadCtx->pRsp = NULL;
}
freeSysTableLoadCtx(pLoadCtx);

if (pTaskInfo->code) {
if (pInfo->rspCode != TSDB_CODE_SUCCESS) {
qError("%s load meta data from mnode failed, totalRows:%" PRIu64 ", code:%s", GET_TASKID(pTaskInfo),
pInfo->loadInfo.totalRows, tstrerror(pTaskInfo->code));
pInfo->loadInfo.totalRows, tstrerror(pInfo->rspCode));
pTaskInfo->code = pInfo->rspCode;
return NULL;
}

Expand Down Expand Up @@ -5187,6 +5173,17 @@ int32_t createSysTableScanOperatorInfo(void* readHandle, SSystemTableScanPhysiNo
}
pInfo->epSet = pScanPhyNode->mgmtEpSet;
pInfo->readHandle = *(SReadHandle*)readHandle;

// Register pInfo in the per-file ref pool so that loadSysTableCallback can
// safely acquire/release it even after the operator has been destroyed.
(void)taosThreadOnce(&sysTableScanRefPoolOnce, initSysTableScanRefPool);
int64_t refId = taosAddRef(sysTableScanRefPool, pInfo);
if (refId < 0) {
qError("%s failed to add ref for sysTableScan since %s", GET_TASKID(pTaskInfo), tstrerror(terrno));
code = terrno;
goto _error;
}
pInfo->self = refId;
}

pInfo->pSubTableListInfo = pTableListInfo;
Expand Down Expand Up @@ -5236,7 +5233,13 @@ void extractTbnameSlotId(SSysTableScanInfo* pInfo, const SScanPhysiNode* pScanNo
}
}

void destroySysScanOperator(void* param) {
// doDestroySysTableScanInfo: actual teardown for SSysTableScanInfo.
// For operators that use the ref pool (MNode path, self > 0), this function
// is the pool destructor invoked automatically when the last ref is dropped
// via taosRemoveRef / taosReleaseRef — do NOT call it directly on those.
// For local-scan operators (self == 0), destroySysScanOperator calls it
// directly since there is no ref pool involved.
static void doDestroySysTableScanInfo(void* param) {
SSysTableScanInfo* pInfo = (SSysTableScanInfo*)param;
int32_t code = tsem_destroy(&pInfo->ready);
if (code != TSDB_CODE_SUCCESS) {
Expand Down Expand Up @@ -5284,32 +5287,73 @@ void destroySysScanOperator(void* param) {
taosMemoryFreeClear(param);
}

int32_t loadSysTableCallback(void* param, SDataBuf* pMsg, int32_t code) {
SSysTableLoadCtx* pCtx = (SSysTableLoadCtx*)param;
if (pCtx == NULL) {
// destroySysScanOperator: operator destroy callback. For operators that use
// the ref pool (MNode path), we just remove our reference — actual cleanup
// happens inside doDestroySysTableScanInfo when all refs are released. For
// local-scan operators (no ref pool entry), do the teardown inline.
static void destroySysScanOperator(void* param) {
SSysTableScanInfo* pInfo = (SSysTableScanInfo*)param;
if (pInfo->self > 0) {
// MNode path: remove the operator's own ref; the pool calls
// doDestroySysTableScanInfo when all refs (operator + any in-flight
// callbacks) are dropped.
int32_t refCode = taosRemoveRef(sysTableScanRefPool, pInfo->self);
if (refCode != TSDB_CODE_SUCCESS) {
qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(refCode));
}
} else {
// Local scan path: no ref pool — destroy directly.
doDestroySysTableScanInfo(pInfo);
}
}

static int32_t loadSysTableCallback(void* param, SDataBuf* pMsg, int32_t code) {
SSysTableScanCbParam* pWrapper = (SSysTableScanCbParam*)param;

// Acquire the SSysTableScanInfo from the ref pool. If it returns NULL the
// operator has already been destroyed — discard the response safely.
SSysTableScanInfo* pInfo = (SSysTableScanInfo*)taosAcquireRef(sysTableScanRefPool, pWrapper->sysTableScanId);
if (pInfo == NULL) {
// Operator is gone; free the response payload and bail out.
taosMemoryFree(pMsg->pData);
taosMemoryFree(pMsg->pEpSet);
return TSDB_CODE_SUCCESS;
}

if (TSDB_CODE_SUCCESS == code) {
pCtx->pRsp = pMsg->pData;
pMsg->pData = NULL;
pCtx->rspCode = TSDB_CODE_SUCCESS;
pInfo->pRsp = pMsg->pData;

SRetrieveMetaTableRsp* pRsp = pCtx->pRsp;
SRetrieveMetaTableRsp* pRsp = pInfo->pRsp;
pRsp->numOfRows = htonl(pRsp->numOfRows);
pRsp->useconds = htobe64(pRsp->useconds);
pRsp->handle = htobe64(pRsp->handle);
pRsp->compLen = htonl(pRsp->compLen);
} else {
Comment thread
kailixu marked this conversation as resolved.
pCtx->rspCode = rpcCvtErrCode(code);
if (pCtx->rspCode != code) {
qError("load systable rsp received, error:%s, cvted error:%s", tstrerror(code), tstrerror(pCtx->rspCode));
int32_t cvtCode = rpcCvtErrCode(code);
if (cvtCode != code) {
qError("load systable rsp received, error:%s, cvted error:%s", tstrerror(code), tstrerror(cvtCode));
} else {
qError("load systable rsp received, error:%s", tstrerror(code));
}
pInfo->rspCode = cvtCode;
taosMemoryFree(pMsg->pData);
}
taosMemoryFree(pMsg->pEpSet);

int32_t res = tsem_post(&pCtx->ready);
// Release our acquired ref BEFORE posting the semaphore.
// If we post first, the waiter can race ahead: task completes → taosRemoveRef
// drops the count to 1, then doDestroyTask frees the task memory pool (which
// owns pInfo). Our subsequent taosReleaseRef would then drop the count to 0
// and call doDestroySysTableScanInfo on already-freed memory.
// By releasing first (count 2→1, destructor not triggered), pInfo remains
// valid for the tsem_post call below, and doDestroySysTableScanInfo is
// called only later, inside destroySysScanOperator, when pInfo is still live.
int32_t refCode = taosReleaseRef(sysTableScanRefPool, pWrapper->sysTableScanId);
if (refCode != TSDB_CODE_SUCCESS) {
qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(refCode));
}

int32_t res = tsem_post(&pInfo->ready);
if (res != TSDB_CODE_SUCCESS) {
qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(res));
}
Comment thread
kailixu marked this conversation as resolved.
Expand Down
7 changes: 6 additions & 1 deletion source/libs/parser/src/parAstParser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1469,7 +1469,12 @@ static int32_t collectMetaKeyFromShowMounts(SCollectMetaKeyCxt* pCxt, SShowStmt*
}

static int32_t collectMetaKeyFromShowCreateDatabase(SCollectMetaKeyCxt* pCxt, SShowCreateDatabaseStmt* pStmt) {
return reserveDbCfgInCache(pCxt->pParseCxt->acctId, pStmt->dbName, pCxt->pMetaCache);
int32_t code = reserveDbCfgInCache(pCxt->pParseCxt->acctId, pStmt->dbName, pCxt->pMetaCache);
if (TSDB_CODE_SUCCESS == code) {
code = reserveUserAuthInCache(pCxt->pParseCxt->acctId, pCxt->pParseCxt->pUser, pStmt->dbName, NULL,
PRIV_CM_SHOW_CREATE, PRIV_OBJ_DB, pCxt->pMetaCache);
}
return code;
}

static int32_t collectMetaKeyFromShowCreateTable(SCollectMetaKeyCxt* pCxt, SShowCreateTableStmt* pStmt) {
Expand Down
5 changes: 4 additions & 1 deletion source/libs/parser/src/parAuthenticator.c
Original file line number Diff line number Diff line change
Expand Up @@ -1132,7 +1132,6 @@ static int32_t authQuery(SAuthCxt* pCxt, SNode* pStmt) {
case QUERY_NODE_SHOW_BACKUP_NODES_STMT:
case QUERY_NODE_SHOW_DB_ALIVE_STMT:
// case QUERY_NODE_SHOW_CLUSTER_ALIVE_STMT:
case QUERY_NODE_SHOW_CREATE_DATABASE_STMT:
case QUERY_NODE_SHOW_TABLE_DISTRIBUTED_STMT: // TODO: check in mnode
// case QUERY_NODE_SHOW_LOCAL_VARIABLES_STMT: // not check local variables
case QUERY_NODE_SHOW_DNODE_VARIABLES_STMT:
Expand All @@ -1143,6 +1142,8 @@ static int32_t authQuery(SAuthCxt* pCxt, SNode* pStmt) {
case QUERY_NODE_SHOW_ENCRYPT_ALGORITHMS_STMT:
case QUERY_NODE_SHOW_ENCRYPT_STATUS_STMT:
return !pCxt->pParseCxt->enableSysInfo ? TSDB_CODE_PAR_PERMISSION_DENIED : TSDB_CODE_SUCCESS;
case QUERY_NODE_SHOW_CREATE_DATABASE_STMT:
return authObjPrivileges(pCxt, ((SShowCreateDatabaseStmt*)pStmt)->dbName, NULL, PRIV_CM_SHOW_CREATE, PRIV_OBJ_DB);
case QUERY_NODE_SHOW_USERS_STMT:
case QUERY_NODE_SHOW_USERS_FULL_STMT:
return authSysPrivileges(pCxt, pStmt, PRIV_USER_SHOW);
Expand Down Expand Up @@ -1253,6 +1254,8 @@ static int32_t authQuery(SAuthCxt* pCxt, SNode* pStmt) {
return authSysPrivileges(pCxt, pStmt, PRIV_TRANS_KILL);
case QUERY_NODE_SHOW_QUERIES_STMT:
return authSysPrivileges(pCxt, pStmt, PRIV_QUERY_SHOW);
case QUERY_NODE_SHOW_CONNECTIONS_STMT:
return authSysPrivileges(pCxt, pStmt, PRIV_CONN_SHOW);
case QUERY_NODE_KILL_QUERY_STMT:
return authSysPrivileges(pCxt, pStmt, PRIV_QUERY_KILL);
case QUERY_NODE_KILL_CONNECTION_STMT:
Expand Down
Loading
Loading