From f81adb659178a0d56c69d051f4db20d412a9b585 Mon Sep 17 00:00:00 2001 From: yuxinghang Date: Fri, 17 Jul 2026 10:53:40 +0800 Subject: [PATCH] fix(function): reject SM4 functions in community edition --- source/libs/function/src/builtins.c | 15 ++ source/libs/parser/src/parInsertSql.c | 7 + .../01-Scalar/test_scalar_crypto.py | 174 +++++++++++------- .../09-NoFrom/test_fun_no_from_all.py | 23 ++- 4 files changed, 144 insertions(+), 75 deletions(-) diff --git a/source/libs/function/src/builtins.c b/source/libs/function/src/builtins.c index 5bb7e293802f..20384847a781 100644 --- a/source/libs/function/src/builtins.c +++ b/source/libs/function/src/builtins.c @@ -49,6 +49,13 @@ static int32_t invaildFuncParaValueErrMsg(char* pErrBuf, int32_t len, const char return buildFuncErrMsg(pErrBuf, len, TSDB_CODE_FUNC_FUNTION_PARA_VALUE, "Invalid parameter value : %s", pFuncName); } +#if !defined(TD_ENTERPRISE) && !defined(TD_ASTRA) +static int32_t enterpriseOnlyFuncErrMsg(char* pErrBuf, int32_t len, const char* pFuncName) { + return buildFuncErrMsg(pErrBuf, len, TSDB_CODE_OPS_NOT_SUPPORT, "%s is only supported in Enterprise edition", + pFuncName); +} +#endif + static int32_t validateTimeUnitParam(uint8_t dbPrec, const SValueNode* pVal) { if (!IS_DURATION_VAL(pVal->flag)) { return TSDB_CODE_FUNC_TIME_UNIT_INVALID; @@ -1793,6 +1800,10 @@ static int32_t translateOutAesDe(SFunctionNode* pFunc, char* pErrBuf, int32_t le } static int32_t translateOutSm4(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { +#if !defined(TD_ENTERPRISE) && !defined(TD_ASTRA) + return enterpriseOnlyFuncErrMsg(pErrBuf, len, pFunc->functionName); +#endif + FUNC_ERR_RET(validateParam(pFunc, pErrBuf, len)); uint8_t orgType = getSDataTypeFromNode(nodesListGetNode(pFunc->pParameterList, 0))->type; @@ -1806,6 +1817,10 @@ static int32_t translateOutSm4(SFunctionNode* pFunc, char* pErrBuf, int32_t len) } static int32_t translateOutSm4De(SFunctionNode* pFunc, char* pErrBuf, int32_t len) { +#if !defined(TD_ENTERPRISE) && !defined(TD_ASTRA) + return enterpriseOnlyFuncErrMsg(pErrBuf, len, pFunc->functionName); +#endif + FUNC_ERR_RET(validateParam(pFunc, pErrBuf, len)); uint8_t orgType = getSDataTypeFromNode(nodesListGetNode(pFunc->pParameterList, 0))->type; diff --git a/source/libs/parser/src/parInsertSql.c b/source/libs/parser/src/parInsertSql.c index 02701afc0a07..255aa33de2d2 100644 --- a/source/libs/parser/src/parInsertSql.c +++ b/source/libs/parser/src/parInsertSql.c @@ -580,6 +580,13 @@ static int32_t parseBinary(SInsertParseContext* pCxt, const char** ppSql, SToken uint8_t** pData = &pVal->value.pData; uint32_t* nData = &pVal->value.nData; +#if !defined(TD_ENTERPRISE) && !defined(TD_ASTRA) + if (pToken->type == TK_SM4_ENCRYPT || pToken->type == TK_SM4_DECRYPT) { + return generateSyntaxErrMsgExt(&pCxt->msg, TSDB_CODE_OPS_NOT_SUPPORT, "%s is only supported in Enterprise edition", + pToken->type == TK_SM4_ENCRYPT ? "SM4_ENCRYPT" : "SM4_DECRYPT"); + } +#endif + if (pToken->type == TK_FROM_BASE64 || pToken->type == TK_TO_BASE64 || pToken->type == TK_MD5 || pToken->type == TK_SHA || pToken->type == TK_SHA1 || pToken->type == TK_SHA2 || pToken->type == TK_AES_ENCRYPT || pToken->type == TK_AES_DECRYPT || pToken->type == TK_SM4_ENCRYPT || pToken->type == TK_SM4_DECRYPT) { diff --git a/test/cases/11-Functions/01-Scalar/test_scalar_crypto.py b/test/cases/11-Functions/01-Scalar/test_scalar_crypto.py index cf32cc22b002..e8ca636cb0c9 100644 --- a/test/cases/11-Functions/01-Scalar/test_scalar_crypto.py +++ b/test/cases/11-Functions/01-Scalar/test_scalar_crypto.py @@ -26,6 +26,14 @@ def test_crypto(self): """ + self.sm4_supported = tdSql.query( + "SELECT sm4_encrypt('probe', 'mykeystring')", + queryTimes=1, + exit=False, + ) is not False + if not self.sm4_supported: + self.sm4_community_rejection() + self.smoking() tdStream.dropAllStreamsAndDbs() self.null() @@ -37,6 +45,34 @@ def test_crypto(self): self.error() tdStream.dropAllStreamsAndDbs() + def sm4_community_rejection(self): + err = "only supported in Enterprise edition" + tdSql.error( + "SELECT sm4_encrypt('plaintext', 'mykeystring')", + expectErrInfo=err, + fullMatched=False, + ) + tdSql.error( + "SELECT sm4_decrypt('ciphertext', 'mykeystring')", + expectErrInfo=err, + fullMatched=False, + ) + + tdSql.execute("drop database if exists db_sm4_unsupported") + tdSql.execute("create database db_sm4_unsupported") + tdSql.execute("create table db_sm4_unsupported.tb(ts timestamp, v varchar(64))") + tdSql.error( + "insert into db_sm4_unsupported.tb values(now, sm4_encrypt('plaintext', 'mykeystring'))", + expectErrInfo=err, + fullMatched=False, + ) + tdSql.error( + "insert into db_sm4_unsupported.tb values(now, sm4_decrypt('ciphertext', 'mykeystring'))", + expectErrInfo=err, + fullMatched=False, + ) + tdSql.execute("drop database db_sm4_unsupported") + def smoking(self): vgroups = 1 dbName = "db" @@ -65,27 +101,28 @@ def smoking(self): tdSql.checkRows(1) tdSql.checkData(0, 0, 'mytext') - tdLog.info(f"====> sm4") - f = 'sm4_ENCRYPT' - fi = 'sm4_decrypt' - - tdSql.query(f"SELECT {fi}({f}('mytext','mykeystring'), 'mykeystring')") - tdSql.checkRows(1) - tdSql.checkData(0, 0, 'mytext') - - tdSql.execute(f"drop table {dbName}.{tbName}") - tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(32))") - tdSql.execute(f"insert into {dbName}.{tbName} values(now, {f}('mytext','mykeystring'))") - tdSql.query(f"SELECT {fi}(f1, 'mykeystring') from {dbName}.{tbName}") - tdSql.checkRows(1) - tdSql.checkData(0, 0, 'mytext') - - tdSql.execute(f"drop table {dbName}.{tbName}") - tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(32))") - tdSql.execute(f"insert into {dbName}.{tbName} SELECT now, {fi}({f}('mytext','mykeystring'), 'mykeystring')") - tdSql.query(f"SELECT f1 from {dbName}.{tbName}") - tdSql.checkRows(1) - tdSql.checkData(0, 0, 'mytext') + if self.sm4_supported: + tdLog.info(f"====> sm4") + f = 'sm4_ENCRYPT' + fi = 'sm4_decrypt' + + tdSql.query(f"SELECT {fi}({f}('mytext','mykeystring'), 'mykeystring')") + tdSql.checkRows(1) + tdSql.checkData(0, 0, 'mytext') + + tdSql.execute(f"drop table {dbName}.{tbName}") + tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(32))") + tdSql.execute(f"insert into {dbName}.{tbName} values(now, {f}('mytext','mykeystring'))") + tdSql.query(f"SELECT {fi}(f1, 'mykeystring') from {dbName}.{tbName}") + tdSql.checkRows(1) + tdSql.checkData(0, 0, 'mytext') + + tdSql.execute(f"drop table {dbName}.{tbName}") + tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(32))") + tdSql.execute(f"insert into {dbName}.{tbName} SELECT now, {fi}({f}('mytext','mykeystring'), 'mykeystring')") + tdSql.query(f"SELECT f1 from {dbName}.{tbName}") + tdSql.checkRows(1) + tdSql.checkData(0, 0, 'mytext') tdLog.info(f"====> base64") f = 'to_base64' @@ -258,31 +295,32 @@ def null(self): tdSql.checkRows(1) tdSql.checkData(0, 0, None) - f = 'sm4_encrypt' - - tdSql.query(f"SELECT {f}(null, 'mykeystring')") - tdSql.checkRows(1) - tdSql.checkData(0, 0, None) + if self.sm4_supported: + f = 'sm4_encrypt' - tdSql.execute(f"drop table if exists {dbName}.{tbName}") - tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(256))") - tdSql.execute(f"insert into {dbName}.{tbName} values(now, {f}(NULL, 'mykeystring'))") - tdSql.query(f"SELECT f1 from {dbName}.{tbName}") - tdSql.checkRows(1) - tdSql.checkData(0, 0, None) + tdSql.query(f"SELECT {f}(null, 'mykeystring')") + tdSql.checkRows(1) + tdSql.checkData(0, 0, None) - f = 'sm4_decrypt' - - tdSql.query(f"SELECT {f}(null, 'mykeystring')") - tdSql.checkRows(1) - tdSql.checkData(0, 0, None) + tdSql.execute(f"drop table if exists {dbName}.{tbName}") + tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(256))") + tdSql.execute(f"insert into {dbName}.{tbName} values(now, {f}(NULL, 'mykeystring'))") + tdSql.query(f"SELECT f1 from {dbName}.{tbName}") + tdSql.checkRows(1) + tdSql.checkData(0, 0, None) - tdSql.execute(f"drop table if exists {dbName}.{tbName}") - tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(256))") - tdSql.execute(f"insert into {dbName}.{tbName} values(now, {f}(NULL, 'mykeystring'))") - tdSql.query(f"SELECT f1 from {dbName}.{tbName}") - tdSql.checkRows(1) - tdSql.checkData(0, 0, None) + f = 'sm4_decrypt' + + tdSql.query(f"SELECT {f}(null, 'mykeystring')") + tdSql.checkRows(1) + tdSql.checkData(0, 0, None) + + tdSql.execute(f"drop table if exists {dbName}.{tbName}") + tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(256))") + tdSql.execute(f"insert into {dbName}.{tbName} values(now, {f}(NULL, 'mykeystring'))") + tdSql.query(f"SELECT f1 from {dbName}.{tbName}") + tdSql.checkRows(1) + tdSql.checkData(0, 0, None) tdLog.info(f"====> masking") @@ -597,19 +635,20 @@ def empty(self): tdSql.checkRows(1) tdSql.checkData(0, 0, '') - f = 'sm4_encrypt' - fi = 'sm4_decrypt' - - tdSql.query(f"SELECT {fi}({f}('', 'mykeystring'), 'mykeystring')") - tdSql.checkRows(1) - tdSql.checkData(0, 0, '') + if self.sm4_supported: + f = 'sm4_encrypt' + fi = 'sm4_decrypt' - tdSql.execute(f"drop table if exists {dbName}.{tbName}") - tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(256))") - tdSql.execute(f"insert into {dbName}.{tbName} values(now, {f}('', 'mykeystring'))") - tdSql.query(f"SELECT {fi}(f1, 'mykeystring') from {dbName}.{tbName}") - tdSql.checkRows(1) - tdSql.checkData(0, 0, '') + tdSql.query(f"SELECT {fi}({f}('', 'mykeystring'), 'mykeystring')") + tdSql.checkRows(1) + tdSql.checkData(0, 0, '') + + tdSql.execute(f"drop table if exists {dbName}.{tbName}") + tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(256))") + tdSql.execute(f"insert into {dbName}.{tbName} values(now, {f}('', 'mykeystring'))") + tdSql.query(f"SELECT {fi}(f1, 'mykeystring') from {dbName}.{tbName}") + tdSql.checkRows(1) + tdSql.checkData(0, 0, '') def single(self): vgroups = 1 @@ -790,19 +829,20 @@ def single(self): tdSql.checkRows(1) tdSql.checkData(0, 0, 'a') - f = 'sm4_encrypt' - fi = 'sm4_decrypt' - - tdSql.query(f"SELECT {fi}({f}('a', 'mykeystring'), 'mykeystring')") - tdSql.checkRows(1) - tdSql.checkData(0, 0, 'a') + if self.sm4_supported: + f = 'sm4_encrypt' + fi = 'sm4_decrypt' - tdSql.execute(f"drop table if exists {dbName}.{tbName}") - tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(256))") - tdSql.execute(f"insert into {dbName}.{tbName} values(now, {f}('a', 'mykeystring'))") - tdSql.query(f"SELECT {fi}(f1, 'mykeystring') from {dbName}.{tbName}") - tdSql.checkRows(1) - tdSql.checkData(0, 0, 'a') + tdSql.query(f"SELECT {fi}({f}('a', 'mykeystring'), 'mykeystring')") + tdSql.checkRows(1) + tdSql.checkData(0, 0, 'a') + + tdSql.execute(f"drop table if exists {dbName}.{tbName}") + tdSql.execute(f"create table {dbName}.{tbName}(ts timestamp, f1 varchar(256))") + tdSql.execute(f"insert into {dbName}.{tbName} values(now, {f}('a', 'mykeystring'))") + tdSql.query(f"SELECT {fi}(f1, 'mykeystring') from {dbName}.{tbName}") + tdSql.checkRows(1) + tdSql.checkData(0, 0, 'a') def error(self): vgroups = 1 diff --git a/test/cases/11-Functions/09-NoFrom/test_fun_no_from_all.py b/test/cases/11-Functions/09-NoFrom/test_fun_no_from_all.py index 71fb10a25a14..fff431318676 100644 --- a/test/cases/11-Functions/09-NoFrom/test_fun_no_from_all.py +++ b/test/cases/11-Functions/09-NoFrom/test_fun_no_from_all.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from new_test_framework.utils import tdLog, tdSql, etool +from new_test_framework.utils import tdLog, tdSql, tdCom, etool import subprocess @@ -87,8 +87,6 @@ class TestFunctionNoFromAll: ("sha2", "select sha2('abc', 256);"), ("sign", "select sign(1);"), ("sin", "select sin(1);"), - ("sm4_decrypt", "select sm4_decrypt(sm4_encrypt('abc', 'key123456789012'), 'key123456789012');"), - ("sm4_encrypt", "select sm4_encrypt('abc', 'key123456789012');"), ("spread", "select spread(1);"), ("sqrt", "select sqrt(1);"), ("st_astext", "select st_astext(st_geomfromtext('POINT(1 2)'));"), @@ -136,6 +134,11 @@ class TestFunctionNoFromAll: ("weekofyear", "select weekofyear('2024-01-01 00:00:00');"), ] + SM4_CASES = [ + ("sm4_decrypt", "select sm4_decrypt(sm4_encrypt('abc', 'key123456789012'), 'key123456789012');"), + ("sm4_encrypt", "select sm4_encrypt('abc', 'key123456789012');"), + ] + # Unsupported in query without FROM. These calls must fail. NEGATIVE_CASES = [ # count(*) requires a table to resolve the '*' column; only count(1) is valid without FROM. @@ -155,9 +158,8 @@ class TestFunctionNoFromAll: def _run_by_taos_cli(self, sql): taos_file = etool.taosFile() - escaped_sql = sql.replace('"', '\\"') - cmd = f'"{taos_file}" -s "{escaped_sql}"' - proc = subprocess.run(cmd, shell=True, text=True, capture_output=True) + cmd = [taos_file, "-c", tdCom.getClientCfgPath(), "-s", sql] + proc = subprocess.run(cmd, text=True, capture_output=True) return proc.returncode, (proc.stdout or "") + (proc.stderr or "") def _is_sql_failed(self, code, output): @@ -194,8 +196,13 @@ def test_fun_no_from_all(self): - 2026-04-02 wpan Add full no-from function tests """ + sm4_code, sm4_output = self._run_by_taos_cli(self.SM4_CASES[0][1]) + sm4_supported = not self._is_sql_failed(sm4_code, sm4_output) + positive_cases = self.CASES + (self.SM4_CASES if sm4_supported else []) + negative_cases = self.NEGATIVE_CASES + ([] if sm4_supported else self.SM4_CASES) + failed = [] - for name, sql in self.CASES: + for name, sql in positive_cases: code, output = self._run_by_taos_cli(sql) if self._is_sql_failed(code, output): tail = output.strip().splitlines() @@ -212,7 +219,7 @@ def test_fun_no_from_all(self): ) unexpected_success = [] - for name, sql in self.NEGATIVE_CASES: + for name, sql in negative_cases: code, output = self._run_by_taos_cli(sql) if not self._is_sql_failed(code, output): tail = output.strip().splitlines()