Skip to content

fix(ios): set the MySQL session character set on iPhone and decode results by their declared charset (#2725) - #2744

Merged
datlechin merged 1 commit into
mainfrom
fix/ios-mysql-charset-2725
Sep 11, 2026
Merged

fix(ios): set the MySQL session character set on iPhone and decode results by their declared charset (#2725)#2744
datlechin merged 1 commit into
mainfrom
fix/ios-mysql-charset-2725

Conversation

@datlechin

Copy link
Copy Markdown
Member

Follows #2740, which fixed #2725 on macOS. The iPhone and iPad driver had the same defect and ignored the new option.

What was wrong

TableProMobile/.../Drivers/MySQLDriver.swift asked for utf8mb4 only through the handshake (MYSQL_SET_CHARSET_NAME) and never checked the result, so a server with init_connect='SET NAMES latin1' for non-admin users, or MariaDB with skip-character-set-client-handshake, left the session latin1 while the driver kept sending UTF-8: every comment and value saved from iOS was stored double-encoded, which is #2725. It also decoded every cell as UTF-8 with a String(cString:) fallback, ignoring the field's declared charset, and IOSDriverFactory passed no additional fields, so the Encoding option a user set on the Mac did nothing on the phone even though it syncs.

Two more defects in the same file, found while reading it:

  • String(cString: field.name) with no nil check: a result field with no name crashed the app.
  • The driver's own type-name table reported every BLOB and BINARY column as TEXT, VARCHAR or CHAR, so DataBrowserViewModel.searchableColumns() put binary columns into LIKE searches and the grid rendered them as text.

The fix

The macOS plugin and the iOS app now share one implementation instead of two:

  • MariaDBCharacterSet.swift (new, compiled by both) holds the session setup (mysql_set_character_set, the SET NAMES utf8 fallback, keeping the server's session if it refuses both), the collation-to-charset-name lookup, the strnlen-bounded column-name decode, and describeColumns, which builds one MySQLResultColumns for a result. MariaDBPluginConnection lost its private copies and calls these, so macOS behaviour is unchanged.
  • mysqlTypeToString moved there too, and the iOS driver's own 25-case type table is gone. BLOB and BINARY columns now report binary type names, which is what routes them to Cell.binary and keeps them out of LIKE searches.
  • The iOS driver decodes cells, column names and error messages through MySQLColumnDecoding and mysqlSessionText, reads mysqlConnectionEncoding from the connection (passed by IOSDriverFactory), and runs its session statements at connect. A nameless field now falls back to column_<n> instead of crashing.
  • The shared files are nonisolated, because the iOS target compiles under SWIFT_DEFAULT_ACTOR_ISOLATION: MainActor.
  • MySQLKillTarget moved out of MySQLServerFlavor.swift so the iOS app does not pull the macOS DDL helpers in with the flavor type.

Streamed binary values become Cell.binary(byteCount:), so a BLOB shows as [BLOB 16 bytes] on iOS exactly as it already does for SQLite and SQL Server. In a plain query result, which carries String?, binary is rendered as 0x… hex, capped at 256 bytes.

Verification

  • macOS build: PASS (build-TablePro-232644.log).
  • macOS tests: PASS, 140 cases across MySQLLatin1Tests, MySQLCharacterSetTests, MySQLColumnDecodingTests, MySQLConnectionEncodingTests, MariaDBFieldClassifierTests, MariaDBTypeNameTests, GeometryWKBParserTests, MySQLServerFlavorTests, DatabendResultShapeTests, DatabendCatalogTests, DatabendLiteralTests, MySQLSelectLimitTests (test-MySQLLatin1Tests-233455.log).
  • Live server: the swiftc harness against a throwaway MySQL 8.4.11 with init_connect='SET NAMES latin1' gives the same results as fix(plugin-mysql): set the session character set on connect and decode MySQL text by its declared charset (#2725) #2740 after the refactor: the session is utf8mb4, a Japanese COMMENT is stored E383A1…, it’s 5€ survives SET NAMES latin1, and UTF-8 via Latin 1 reads the double-encoded comments as Japanese while storing its own writes the legacy way.
  • iOS build: FAIL, blocked and not caused by this change. It stops in OracleNIO with the Xcode-beta error unknown attribute 'usableFromInlinenonisolated' before any TablePro code compiles, the same wall fix(plugin-postgresql): pin client_encoding to UTF8 in the startup packet on macOS and iOS, and declare it in SQL exports #2743 hit. Instead, the whole iOS driver plus the shared files typecheck clean with swiftc -typecheck -swift-version 6 -default-isolation MainActor against the package modules an earlier build produced. That probe caught a real mistake in this branch (two types deleted with the old type table), so it has teeth. CI runs the real iOS build and the iOS tests.
  • Lint: PASS on every changed path.
  • Review: Codex is out of credits, and the review agent hit the account's session limit, so this is a self-review. The riskiest part is the decode boundary, which the new tests and the macOS suites cover.

Tests

TableProMobileTests/Drivers/MySQLSharedDecodingTests.swift covers the field lookup, the legacy repair, the MySQL latin1 table, the per-column kinds, and the binary type names. It also fails to compile if the shared files are ever dropped from TableProMobile/project.yml, which is the way this parity would silently rot. MySQLConnectionEncodingTests gains a case for init(additionalFields:).

@datlechin
datlechin merged commit b2f02e3 into main Sep 11, 2026
7 checks passed
@datlechin
datlechin deleted the fix/ios-mysql-charset-2725 branch September 11, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Characters are incorrectly decoded in table comments

1 participant