결과 검증
결과 검증 (Result Verification)
SQLLogicTest에서 쿼리 결과를 검증하는 방법을 알아볼게요. 테스트가 어떤 결과를 기대하는지 정확히 명시하는 것이 핵심인데요, 여기서 쓰는 문법과 몇 가지 옵션들을 하나씩 살펴보도록 하죠.
출처: 문서
본문
쿼리 결과를 검증하는 표준적인 방법은 query 문을 쓰고, 그 뒤에 기대하는 결과의 컬럼 수만큼 알파벳 I를 붙이는 것이에요. 쿼리 뒤에는 네 개의 대시(----)를 쓰고, 이어서 탭으로 구분된 결과 값들을 나열하면 돼요. 예를 들어 볼까요.
query II
SELECT 42, 84 UNION ALL SELECT 10, 20;
----
42 84
10 20
레거시 호환을 위해 컬럼을 나타내는 문자로 R과 T도 허용돼요.
주의 — DuckDB는 sqllogictest에서 타입 사용을 더 이상 권장하지 않아요. DuckDB 테스트 러너는 이를 내부적으로 사용하지도, 필요로 하지도 않거든요. 따라서 컬럼을 나타낼 땐
I만 사용하면 돼요.
NULL 값과 빈 문자열
SQLLogic 테스트 러너에서 빈 줄은 특별한 의미를 가져요. 현재 문장이나 쿼리의 끝을 나타내는 신호인데요. 그래서 빈 문자열과 NULL 값은 결과 검증에서 특별한 문법을 사용해야 해요. NULL 값은 NULL이라는 문자열을, 빈 문자열은 (empty)라는 문자열을 쓰면 됩니다.
query II
SELECT NULL, ''
----
NULL
(empty)
오류 검증
오류가 발생할 것으로 기대된다는 것을 나타내려면 statement error 표시를 사용할 수 있어요. statement error는 선택적으로 기대 결과를 받기도 하는데, 이는 기대 오류 메시지로 해석돼요. query와 마찬가지로 쿼리 뒤의 네 개의 대시(----) 아래에 기대 오류를 배치하면 되죠. statement error 아래의 텍스트가 오류 메시지에 포함되어 있으면 테스트는 통과해요. 오류 메시지 전체를 제공할 필요는 없답니다. 오류 메시지의 일부만 쓰는 것을 권장하는데, 그 이유는 오류 메시지의 포맷이 바뀌어도 테스트가 불필요하게 깨지지 않도록 하기 위해서예요.
statement error
SELECT * FROM non_existent_table;
----
Table with name non_existent_table does not exist!
정규식 (Regex)
경우에 따라 결과 값이 너무 크거나 복잡해서, 결과에 특정 텍스트 조각이 포함되어 있는지 여부만 관심이 있을 수 있어요. 그럴 땐 <REGEX>: 수정자를 쓰고 뒤에 정규식을 붙이면 됩니다. 결과 값이 정규식과 일치하면 테스트는 통과해요. 주로 쿼리 플랜 분석에 사용돼요.
query II
EXPLAIN SELECT tbl.a FROM 'data/parquet-testing/arrow/alltypes_plain.parquet' tbl(a) WHERE a = 1 OR a = 2
----
physical_plan <REGEX>:.*PARQUET_SCAN.*Filters: a=1 OR a=2.*
반대로 결과가 특정 텍스트 조각을 포함하지 않아야 한다면 <!REGEX>: 수정자를 쓰면 돼요.
파일 (File)
결과가 꽤 커질 수 있고, 여러 파일에서 결과를 재사용하고 싶을 수도 있는데요. 그럴 때는 <FILE> 명령어를 사용해 파일에서 기대 결과를 읽어올 수 있어요. 기대 결과는 주어진 파일에서 읽힙니다. 관례적으로 파일 경로는 GitHub 저장소의 루트를 기준으로 한 상대 경로로 제공해야 해요.
query I
PRAGMA tpch(1)
----
<FILE>:extension/tpch/dbgen/answers/sf1/q01.csv
행 단위 vs 값 단위 결과 정렬
쿼리 결과 값은 행 단위 순서(각 값은 탭으로 구분)로 제공하거나, 값 단위 순서로 제공할 수 있어요. 값 단위 순서에서는 쿼리의 개별 값을 행, 컬럼 순서대로 각각 한 줄에 하나씩 나타내야 합니다. 두 순서 모두를 다음 예시에서 볼게요.
# 행 단위 (row-wise)
query II
SELECT 42, 84 UNION ALL SELECT 10, 20;
----
42 84
10 20
# 값 단위 (value-wise)
query II
SELECT 42, 84 UNION ALL SELECT 10, 20;
----
42
84
10
20
해시와 값 출력
직접 결과를 검증하는 것 외에도, sqllogic 테스트 스위트에는 값을 비교하는 데 MD5 해시를 사용하는 옵션이 있어요. 해시를 사용해 결과를 검증하는 테스트는 이렇게 생겼답니다.
query I
SELECT g, string_agg(x,',') FROM strings GROUP BY g
----
200 values hashing to b8126ea73f21372cdb3f2dc483106a12
이 방식은 결과에 출력 행이 많을 때 테스트 크기를 줄이는 데 유용해요. 다만 가급적 드물게 사용해야 하는데, 해시 값은 테스트가 깨졌을 때 디버깅을 더 어렵게 만들기 때문이에요.
시스템이 올바른 결과를 출력한다는 것이 확인된 뒤에는, 테스트 파일에 mode output_hash를 추가해 그 파일 안 쿼리들의 해시를 계산할 수 있어요. 예를 들어 볼게요.
mode output_hash
query II
SELECT 42, 84 UNION ALL SELECT 10, 20;
----
42 84
10 20
그러면 테스트 파일의 모든 쿼리에 대한 기대 출력 해시가 터미널에 다음과 같이 출력돼요.
================================================================================
SQL Query
SELECT 42, 84 UNION ALL SELECT 10, 20;
================================================================================
4 values hashing to 498c69da8f30c24da3bd5b322a2fd455
================================================================================
비슷한 방식으로, mode output_result는 테스트 파일에서 실행되는 모든 쿼리에 대해 결과를 터미널에 출력하도록 강제할 때 사용할 수 있어요.
결과 정렬
쿼리에는 결과를 특정 방식으로 정렬하라는 선택적 필드가 있을 수 있어요. 이 필드는 연결 라벨(connection label)과 같은 위치에 들어갑니다. 그래서 연결 라벨과 결과 정렬은 함께 쓸 수 없어요.
이 필드에 가능한 값은 nosort, rowsort, valuesort예요. 사용 예시는 다음과 같습니다.
query I rowsort
SELECT 'world' UNION ALL SELECT 'hello'
----
hello
world
일반적으로는 이 필드를 사용하기보다, 쿼리에서 ORDER BY를 사용해 결정적인 쿼리 답변을 만드는 것을 선호해요. 다만 기존 sqllogictest가 이 필드를 광범위하게 사용하고 있으니, 그 존재는 알아두는 게 좋아요.
쿼리 라벨
결과 검증에 쓰이는 또 다른 기능은 **query labels(쿼리 라벨)**예요. 서로 다른 쿼리가 동일한 결과를 제공하는지 검증하는 데 사용할 수 있죠. 논리적으로 동등하지만 다르게 표현된 쿼리들을 비교할 때 유용해요. 쿼리 라벨은 연결 라벨이나 정렬 지정자 뒤에 제공됩니다.
쿼리 라벨이 있는 쿼리는 결과를 제공할 필요가 없어요. 대신, 같은 라벨을 가진 각 쿼리의 결과를 서로 비교하는 거예요. 예를 들어 다음 스크립트는 SELECT 42+1과 SELECT 44-1이 동일한 결과를 제공하는지 검증합니다.
query I nosort r43
SELECT 42+1;
----
query I nosort r43
SELECT 44-1;
----
더 알아보기 (Learn more)
- SQLLogicTest 테스트 구성과 실행 전반은
dev/sqllogictest/test_configuration문서를 참고해 주세요.