문자열 안의 NUL 문자

문자열 안의 NUL 문자

SQLite는 데이터베이스에 저장된 문자열 값 중간에 NUL 문자(ASCII 0x00, 유니코드 \u0000)를 허용해요. 하지만 문자열 안에서 NUL을 사용하면 예상 밖의 동작이 생길 수 있어서 주의가 필요해요.

출처: NUL Characters In Strings 문서

본문

1. 소개

SQLite는 데이터베이스에 저장된 문자열 값 중간에 NUL 문자(ASCII 0x00, 유니코드 \u0000)를 허용해요. 하지만 문자열 안에서 NUL을 사용하면 놀라운 동작이 생길 수 있어요.

  1. length() SQL 함수는 첫 번째 NUL 문자까지, 즉 NUL을 제외한 그 앞의 문자까지만 세어요.
  2. quote() SQL 함수는 첫 번째 NUL 문자까지의 문자까지만 보여줘요.
  3. CLI.dump 명령은 생성하는 SQL 출력에서 첫 번째 NUL 문자와 이후의 모든 텍스트를 생략해요. 사실 CLI는 모든 상황에서 첫 번째 NUL 문자 이후의 모든 것을 생략해요.

SQL 텍스트 문자열에서 NUL 문자를 사용하는 것은 권장되지 않아요.

2. 예상 밖의 동작

다음과 같은 SQL을 생각해 봐요.

CREATE TABLE t1(
  a INTEGER PRIMARY KEY,
  b TEXT
);
INSERT INTO t1(a,b) VALUES(1, 'abc'||char(0)||'xyz');

SELECT a, b, length(b) FROM t1;

위의 SELECT 문은 다음과 같은 출력을 보여줘요.

1,'abc',3

(이 문서 전체에서 CLI에 ".mode quote"가 설정되어 있다고 가정해요.) 그런데 만약 다음과 같이 실행하면:

SELECT * FROM t1 WHERE b='abc';

어떤 행도 반환되지 않아요. SQLite는 t1.b 열이 실제로 7문자 문자열을 담고 있다는 것을 알고 있고, 7문자 문자열 'abc'||char(0)||'xyz'는 3문자 문자열 'abc'와 같지 않기 때문에 어떤 행도 반환되지 않아요. 하지만 CLI 출력이 문자열이 3문자만 가진 것처럼 보이기 때문에 사용자는 쉽게 혼란을 겪을 수 있어요. 버그처럼 보이지만, 이게 SQLite가 동작하는 방식이에요.

3. 문자열에 NUL 문자가 있는지 알아내는 방법

문자열을 BLOB로 CAST하면 문자열의 전체 길이가 보여요. 예를 들어:

SELECT a, CAST(b AS BLOB) FROM t1;

이 결과는 다음과 같아요.

1,X'6162630078797a'

BLOB 출력에서 7문자 문자열의 4번째 문자로 NUL 문자를 명확하게 볼 수 있어요.

문자열 값 X에 포함된 NUL 문자를 알아내는 더 자동화된 방법은 다음과 같은 표현식을 사용하는 거예요.

instr(X,char(0))

이 표현식이 0이 아닌 값 N을 반환하면 N번째 문자 위치에 NUL이 포함되어 있다는 뜻이에요. 따라서 NUL이 포함된 행의 수를 세려면:

SELECT count(*) FROM t1 WHERE instr(b,char(0))>0;

4. 텍스트 필드에서 NUL 문자 제거하기

다음 예제는 테이블의 열에서 NUL 문자와 그 뒤의 모든 텍스트를 제거하는 방법을 보여줘요. NUL이 포함된 데이터베이스 파일이 있고 그걸 제거하고 싶다면, 다음과 유사한 UPDATE 문을 실행하면 도움이 될 수 있어요.

UPDATE t1 SET b=substr(b,1,instr(b,char(0)))
 WHERE instr(b,char(0));

더 알아보기 (Learn more)