Skip to content

Commit b06348e

Browse files
committed
fix(db): convert SQL Server varchar fields from latin-1 to gbk in exec_sql
1 parent 4d5950c commit b06348e

1 file changed

Lines changed: 53 additions & 1 deletion

File tree

‎backend/apps/db/db.py‎

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,49 @@ def is_numeric_type_code(type_code, dialect_name: str) -> bool:
759759
return False
760760

761761

762+
def is_mssql_string_type_code(type_code) -> bool:
763+
"""
764+
判断 SQL Server (pymssql) 游标描述中的 type_code 是否为字符串类型。
765+
766+
pymssql 的 varchar/char/nvarchar/nchar/text 等字符串类型的 type_code 为
767+
STRING(1),而数值/日期等类型分别为 NUMBER(3)、DATETIME(4) 等。
768+
769+
Args:
770+
type_code: cursor.description[col][1] 的值
771+
772+
Returns:
773+
bool: 是否为字符串(varchar 等)类型
774+
"""
775+
try:
776+
return type_code == 1
777+
except Exception:
778+
return False
779+
780+
781+
def convert_gbk_string(value):
782+
"""
783+
将 SQL Server 中以 latin-1 读取出的乱码字符串转换为 GBK 编码的正确文本。
784+
785+
当 varchar 列实际存储的是 GBK 字节(被错误地按 latin-1 解码)时,
786+
通过 str.encode('latin-1').decode('gbk') 还原。转换中任一步失败则返回原值。
787+
"""
788+
if not isinstance(value, str):
789+
return value
790+
try:
791+
return value.encode('latin-1').decode('gbk')
792+
except (UnicodeEncodeError, UnicodeDecodeError):
793+
return value
794+
795+
796+
def convert_mssql_value(value, col_idx, string_col_idx_set, is_mssql):
797+
"""
798+
对 SQL Server 结果集中字符串列的值进行 GBK 编码转换。
799+
"""
800+
if is_mssql and col_idx in string_col_idx_set:
801+
return convert_gbk_string(value)
802+
return value
803+
804+
762805
def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=False):
763806
while sql.endswith(';'):
764807
sql = sql[:-1]
@@ -777,13 +820,19 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=
777820
try:
778821
columns = result.keys()._keys if origin_column else [item.lower() for item in result.keys()._keys]
779822

823+
# SQL Server varchar 等字符串列需做 GBK 编码转换
824+
is_mssql = dialect_name == 'mssql'
825+
string_cols = []
826+
780827
fields_info = []
781828

782829
for col_idx, col_name in enumerate(columns):
783830
is_numeric = False
784831
try:
785832
type_code = result.cursor.description[col_idx][1]
786833
is_numeric = is_numeric_type_code(type_code, dialect_name)
834+
if is_mssql and is_mssql_string_type_code(type_code):
835+
string_cols.append(col_idx)
787836
except (IndexError, AttributeError):
788837
pass
789838

@@ -792,9 +841,12 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=
792841
"is_numeric": is_numeric
793842
})
794843

844+
string_col_idx_set = set(string_cols)
845+
795846
res = result.fetchall()
796847
result_list = [
797-
{str(columns[i]): convert_value(value) for i, value in enumerate(tuple_item)} for tuple_item in
848+
{str(columns[i]): convert_mssql_value(convert_value(value), i, string_col_idx_set, is_mssql)
849+
for i, value in enumerate(tuple_item)} for tuple_item in
798850
res
799851
]
800852
return {"fields": columns, "data": result_list, "fields_info": fields_info,

0 commit comments

Comments
 (0)