잘못된 UTF

잘못된 UTF (Invalid UTF)

1. 쓰레기가 들어오면 쓰레기가 나간다 (Garbage In, Garbage Out)

잘못된 UTF에 관해 SQLite는 Garbage-In, Garbage-Out (GIGO) 정책을 따르고 있어요. 잘못된 UTF를 SQLite 데이터베이스에 삽입한 다음 그 데이터를 질의하면, 돌려받는 것이 정확히 넣은 것과 같지 않을 수 있어요. 쓰레기를 넣으면, 다른 쓰레기를 돌려받아도 불평하면 안 되겠죠.

출처: Invalid UTF

본문

이 논의의 목적상 "잘못된 UTF"는 다음 상황 중 어느 하나를 의미할 수 있어요:

  • UTF-16의 잘못된 서러게이트 쌍 (surrogate pairs).
  • UTF-8의 잘못된 멀티바이트 시퀀스.
  • 단일 코드 포인트를 표현하는 데 필요한 것보다 더 많은 UTF-8 바이트 사용. (예: 'A'를 단일 0x41 바이트 대신 2바이트 시퀀스 0xc1, 0x01로 인코딩한 것.)
  • 문자열에 포함된 NUL 문자 (U+0000).
  • 잘못된 결합 문자 시퀀스.
  • 정의되지 않은 유니코드 문자가 아닌 숫자를 인코딩하는 UTF-8 또는 UTF-16 바이트 시퀀스.

1.1. 잘못된 UTF는 절대 메모리 오류를 일으키지 않아요

잘못된 UTF를 SQLite 데이터베이스에 삽입하면, SQLite는 어떤 텍스트를 돌려받게 될지에 대해 보장하지 않아요. 하지만 적어도 SQLite의 내장 처리를 위해서는 잘못된 UTF가 절대 메모리 오류(배열 오버런, 초기화되지 않은 메모리의 읽기나 쓰기 등)를 일으키지 않을 것이라고 약속해요. 다시 말해 잘못된 UTF는 SQLite가 크래시하도록 만들지 않아요.

물론 이 약속은 애플리케이션이 제공하는 확장이 아니라 핵심 SQLite 구성 요소에만 적용돼요. 애플리케이션이 새로운 애플리케이션 정의 SQL 함수나 가상 테이블, 정렬 시퀀스 또는 다른 확장을 추가하고 데이터베이스에 잘못된 UTF가 포함되어 있다면, 잘못된 UTF가 그 확장들로 전달될 수 있어요. 잘못된 UTF가 그 확장 중 하나를 크래시하게 만든다면, 그건 SQLite가 아니라 확장의 문제예요.

2. 텍스트 형식 규칙을 강제하지 않아요 (No enforcement of text formatting rules)

SQLite는 UTF 형식 규칙을 강제하려고 하지 않아요. TEXT 필드에 잘못된 UTF를 삽입해도 SQLite는 이에 대해 불평하지 않아요. 가능한 한 그 잘못된 TEXT를 저장해요. SQLite는 자신의 역할을 텍스트 형식 검증 엔진이 아니라 저장 엔진으로 봐요.

3. 텍스트 보존을 위한 최선의 노력 (Best effort to preserve text)

SQLite는 잘못된 UTF를 항상 보존하겠다고 약속하지는 않지만, 노력은 해요. 일반적으로 텍스트를 어떤 방식으로든 변환해 달라고 SQLite에 요청하지 않는 한, 잘못된 UTF를 SQLite에 삽입하면 정확히 같은 바이트 시퀀스를 돌려받게 돼요.

예를 들어, PRAGMA encoding=UTF16LE인 데이터베이스의 테이블 TEXT 컬럼에 잘못된 서러게이트가 있는 UTF-16LE를 삽입한 다음, sqlite3_column_text16()을 사용해 그 컬럼을 질의하면, 아마 정확히 같은 잘못된 UTF-16을 돌려받을 거예요. 하지만 같은 잘못된 UTF-16LE 내용을 PRAGMA encoding=UTF8 데이터베이스에 삽입하면, 그 내용은 저장될 때 UTF8로 변환되어야 하므로 내용에 되돌릴 수 없는 변화가 생길 수 있어요. 또는 그 같은 잘못된 UTF-16LE 내용을 PRAGMA encoding=UTF16LE 데이터베이스에 삽입한 다음 sqlite3_column_text()로 읽어내면, 읽는 도중 UTF16에서 UTF8로의 변환이 일어나야 하며 그 변환이 되돌릴 수 없는 변화를 도입할 수 있어요.

또는 모든 것을 UTF-8로 처리한다고 가정해 보세요 (가장 흔한 경우예요). 잘못된 UTF-8은 보통 바이트 시퀀스에 아무 변화 없이 데이터베이스를 통과해요. 하지만 substr()replace() 같은 SQL 함수로 잘못된 UTF-8을 변환하려고 하거나 LIKE 연산자로 문자열 매칭을 하려고 하면, 예상치 못한 결과를 얻을 수 있어요.

그러니까 다시 말해, SQLite는 여러분의 잘못된 텍스트를 적극적으로 훼손하려 하지 않아요. 하지만 잘못된 UTF의 변환을 요청하면, 그 변환이 되돌릴 수 있거나 심지어 합리적일 것이라는 보장은 없어요.

4. 데이터베이스 스키마의 잘못된 UTF

데이터베이스 스키마에 잘못된 UTF인 이름(테이블 이름, 컬럼 이름, 인덱스 이름 등)이 포함되어 있어도 SQLite는 정상적으로 계속 동작해요. SQLite의 관점에서 그 이름들은 그냥 바이트 시퀀스일 뿐이에요. SQLite는 그것들이 잘못된 UTF인지 아닌지는 신경 쓰지 않아요.

오류 메시지를 생성할 때 (예: sqlite3_errmsg() 사용), 때때로 SQLite는 데이터베이스 스키마의 일부를 오류 메시지에 포함시켜요. 그 포함된 스키마 요소가 잘못된 UTF이면, 결과 오류 메시지도 잘못된 UTF일 수 있어요. 마찬가지로 PRAGMA integrity_check 및 유사한 문의 출력도 때때로 스키마 요소의 이름을 포함해요. 그 스키마 요소 이름이 잘못된 UTF이면, 그 명령의 출력도 잘못된 UTF가 돼요.

더 알아보기 (Learn more)