Repository navigation
fix: read Japanese, Chinese and Korean text in data files, imports and SQL files - #3192
Merged
Merged
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A CSV exported by Excel on Japanese Windows (Shift JIS, code page 932) opened in the Data Files window as Windows-1252 mojibake:
»Þ²º¶ÝظÌÞÝis the half-width katakanaザイコカンリクブンread one byte at a time. The same file imported through CSV import, a Shift JIS.sqlscript opened in the editor, a Shift JIS dump imported through SQL import, and a Shift JIS file passed to the iOS Shortcuts row actions were each wrong in their own way.Root cause
Every place that reads a user's text file picked its encoding by hand, and none of them considered an East Asian encoding:
DelimitedDialectDetector.sniffEncoding)CSVDialect.probeEncoding)FileTextLoader)com.apple.TextEncodingxattr, UTF-8, then ISO Latin-1ImportEncoding)RowPayload)Fixing the fallback alone would not have been enough. The investigation measured each of these:
ポ,ー,ソand表is0x7Cor0x5C, so a comma CSV of Japanese addresses with full-width dashes was split on|, and UTF-16 files were split mid code unit.TabularTextEncoding.big5and.eucKRused the ISO code pages, which reject碁,裏,똠and뷁from Windows exports.髙(FB FC) rewritten, against the documented "rows you did not touch are copied byte for byte".pg_dump -E SJIScarriesSET NAMES cp932/SET client_encoding = 'SJIS'while the driver always sends UTF-8. Measured on PostgreSQL 18: the decoded dump sent as is fails withinvalid byte sequence for encoding "SJIS": 0x9e 0x20; with the statement rewritten the row日本語 髙橋lands intact.Fix
One detector (
TabularEncodingDetector, TableProTabularIO), used by Data Files, CSV import, SQL files, the SQL import preview and iOS Shortcuts:U+3000,一and dense CJK text are read correctly).0x30, which no supported encoding uses inside a character (GB 18030 four-byte sequences hold ASCII digits).NSString.stringEncoding(for:encodingOptions:…)restricted to CP932, EUC-JP, GB 18030, CP950, CP949 and Windows-1252, with lossy ranking, and accepted only when the text read that way holds East Asian characters. Half-width-katakana-only CP932 is recognized by its byte shape.Data Files
U+FFFDon its own line, and the status bar counts those lines.U+00A0 NO-BREAK SPACE).¥and‾save as0x5Cand0x7E, the JIS Roman bytes Japanese Windows shows as those characters.CSV import links TableProTabularIO, detects the encoding (or honours the chosen one) and transcodes to UTF-8 before any byte scanning. A line the encoding cannot read stops the import before any row is deleted or inserted. The escape character now follows the chosen quote. The encoding menu adds UTF-16 LE/BE, Shift JIS, EUC-JP, GB 18030, Big5 and EUC-KR. PluginKit is untouched, so there is no ABI change.
SQL files detect UTF-16 and East Asian encodings after the byte order mark and the xattr; Western text still opens as ISO Latin-1. Restored tabs record their encoding (and byte order mark, checked against the file before it is used), so their baseline is read the same way, and an older tab that held the Latin-1 reading of an East Asian file either adopts the correct reading (unedited) or keeps the Latin-1 reading (edited). Version history reads older commits in the working copy's encoding.
SQL import adds the five East Asian encodings. When the chosen encoding cannot read the preview, the dialog switches to the encoding the dump declares in a top-level
SET NAMES/SET client_encoding, or else to the detected one, without remembering that switch. It never picks Shift JIS, GB 18030 or Big5 unless the dump declares it: in those encodings a byte can swallow the backslash that escapes a quote, and a security review of this branch showed a latin1 dump holding attacker-chosen Japanese-looking values could otherwise be auto-read as Shift JIS, ending a string literal early and running the rest as a statement (SQLDumpEncodingChoiceTestscovers that dump). When the file is read in anything but UTF-8 or ASCII:SET NAMES,SET CHARACTER SETandcharacter_set_clientkeep the session's own character set (character_set_client = @@session.character_set_client), respecting aGLOBAL/PERSISTscope carried across aSETlist. PostgreSQL getsSET client_encoding = 'UTF8'._binary'…'literal in a Latin-1 or Windows-1252 dump becomes the exactX'…'it held; in other encodings, where re-encoding is not exact (measured: 658 CP932 and 7,844 EUC-JP byte pairs do not round-trip), a_binaryliteral holding non-ASCII bytes stops the import at its line. Backslash escapes are decoded only while the dump's ownsql_modeleaves them on; afterNO_BACKSLASH_ESCAPES, or asql_modeset from a variable, a_binaryliteral holding a backslash stops the import too. Text introducers such as_latin1'…'become_utf8mb4, orCONVERT(_utf8mb4'…' USING latin1)when aCOLLATEfollows.JSON Lines import decodes its 256 KB sample back to a character boundary.
iOS Shortcuts read files through the shared detector and name the line they cannot read.
Detection accuracy
Measured on macOS 27 against 110 clean samples and 17 samples with one damaged line (Japanese, Chinese, Korean and Western text in every candidate encoding, from one line to 1 MB), plus short Western files holding
£,©,®,µ,²,½,°, NBSP with€, and accented capitals:The misses are single-line samples whose bytes read as valid text in two encodings: half-width EUC-JP vs CP932, one short Japanese line read as GB 18030, and one line of accented capitals (
Tëst;Ëmïl;Öl;Ïle) read as GB 18030. File Properties and the import encoding menus cover them.Before / After
The same Shift JIS export (CP932, CRLF, half-width katakana in the reading column) opened in each build.
Before, TablePro 0.76.1: every Japanese character shows as Windows-1252, and the status bar reads
Comma, Windows-1252, CRLF.After, a Debug build of this branch: the text reads correctly, and the status bar reads
Comma, Shift JIS, CRLF.Tests
TableProTabularIOTests:TabularEncodingDetectorTests(21),TabularTextTranscoderTests(14).TableProTabularTests/TabularSaveTests: byte-for-byte saves of a quoted Shift JIS export withFB FCandFA 40, deleted, reordered and reversed rows across chunks, UTF-16 files including a stray trailing byte, the yen sign.TableProTests:DataFileControllerTests,CSVImportPluginTests,CSVImportEncodingFailureTests,FileTextLoaderTests,FileTextWriterTests,FileTabBaselineTests,SQLClientEncodingRewriterTests,SQLTranscodedLiteralRewriterTests,SQLDumpEncodingChoiceTests,SqlFileImportSourceEncodingTests,JSONImportPluginTests. 614 cases across the touched and neighbouring suites pass, and the fullTableProCorepackage suite passes.TableProUITests/DataFileWindowUITests: opens a Shift JIS export, checks the status bar names Shift JIS and Row Details shows the Japanese value. 12/12 pass.TableProMobileTests/RowPayloadTests: three new cases. The iOS app and its tests build (build-for-testing, arm64 simulator); this machine has no iOS simulator runtime, so they run in theios-testsjob.CSVImportPluginTests.testQuoteOverrideParsesUTF8(wastestQuoteAndEncodingOverride), because a chosen encoding now applies when the text is prepared and the parser always reads UTF-8.Not changed, and why: ISO-2022-JP (stateful, not a CSV format in practice) and other single-byte code pages such as CP1251 stay out of the detector.