Skip to content

Commit 1be6c3f

Browse files
authored
fix: set UTF-8 client charset for SQL Server connections (#528)
* fix: set UTF-8 client charset for SQL Server connections * fix: increase dbconvert buffer to handle multi-byte UTF-8 output
1 parent ee95461 commit 1be6c3f

3 files changed

Lines changed: 8 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818

1919
### Fixed
2020

21+
- SQL Server: Unicode characters (Thai, CJK, etc.) in nvarchar/nchar/ntext columns displaying as question marks
2122
- Globe+F (fn+F) fullscreen shortcut not working in SwiftUI lifecycle app
2223

2324
## [0.26.0] - 2026-03-29

Plugins/MSSQLDriverPlugin/CFreeTDS/include/sybdb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ typedef struct loginrec LOGINREC;
4949
#define DBSETUSER 2
5050
#define DBSETPWD 3
5151
#define DBSETAPP 5 // 4 is unused; real FreeTDS DBSETAPP = 5
52+
#define DBSETCHARSET 7 // Client charset for dbsetlname() — controls string encoding
5253

5354
// Convenience macros (match FreeTDS sybdb.h)
5455
#define DBSETLHOST(x, y) dbsetlname((x), (y), DBSETHOST)

Plugins/MSSQLDriverPlugin/MSSQLPlugin.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ private final class FreeTDSConnection: @unchecked Sendable {
196196
_ = dbsetlname(login, user, Int32(DBSETUSER))
197197
_ = dbsetlname(login, password, Int32(DBSETPWD))
198198
_ = dbsetlname(login, "TablePro", Int32(DBSETAPP))
199+
_ = dbsetlname(login, "UTF-8", Int32(DBSETCHARSET))
199200
_ = dbsetlversion(login, UInt8(DBVERSION_74))
200201

201202
freetdsLastError = ""
@@ -371,10 +372,12 @@ private final class FreeTDSConnection: @unchecked Sendable {
371372
return String(bytes: UnsafeBufferPointer(start: ptr, count: Int(srcLen)), encoding: .utf8)
372373
?? String(bytes: UnsafeBufferPointer(start: ptr, count: Int(srcLen)), encoding: .isoLatin1)
373374
case Int32(SYBNCHAR), Int32(SYBNVARCHAR), Int32(SYBNTEXT):
374-
let data = Data(bytes: ptr, count: Int(srcLen))
375-
return String(data: data, encoding: .utf16LittleEndian)
375+
// With client charset UTF-8, FreeTDS converts UTF-16 wire data to UTF-8
376+
// but may still report the original nvarchar type token
377+
return String(bytes: UnsafeBufferPointer(start: ptr, count: Int(srcLen)), encoding: .utf8)
378+
?? String(data: Data(bytes: ptr, count: Int(srcLen)), encoding: .utf16LittleEndian)
376379
default:
377-
let bufSize: DBINT = 64
380+
let bufSize: DBINT = 256
378381
var buf = [BYTE](repeating: 0, count: Int(bufSize))
379382
let converted = buf.withUnsafeMutableBufferPointer { bufPtr in
380383
dbconvert(proc, srcType, ptr, srcLen, Int32(SYBCHAR), bufPtr.baseAddress, bufSize)

0 commit comments

Comments
 (0)