참조가 가리킬 수 있는 키 — int32 가정 걷어내기
상태: 설계 · 구현 전
인덱스가 될 수 있는 타입은 넓어졌는데 참조가 따라가지 않았습니다. int가 아닌 키를 잡은
테이블은 읽고 코드를 생성하는 데 문제가 없지만 가리킬 수 없습니다.
1. 문제 — 반만 적용된 규칙
인덱스가 될 수 있는 타입은 「어느 타입인가」가
아니라 「그 값으로 행을 구별할 수 있는가」 하나입니다. int·bigint·string·uuid·enum이
키가 됩니다 — 생성되는 조회가 인덱스 필드 자신의 타입에 대한 사전이기 때문입니다.
참조만 그 규칙 밖에 있습니다.
Field `Clip.Anim` references `Animation`, whose index is `string`. A reference stores
the target's index and the binary format stores that as an `int`, so only a table with
an `int` index can be referenced. Give `Animation` an `int` index, or carry the key here
as a plain `string` and look the row up through `Animation`'s own index.
거부 메시지가 스스로 증거입니다. 마지막 문장이 「키를 그냥 들고 다니면서 직접 찾아보라」인데,
그것은 참조가 해 주는 일을 작성자에게 넘기는 것입니다. 할 수 없는 일이라서가 아니라 형식이
int32로 고정되어 있어서 그렇게 적혀 있습니다.
int32 가정이 고정되어 있는 자리
| 자리 | 무엇 |
|---|---|
| 검증 | 대상 인덱스가 Int32가 아니면 거부 |
| 바이너리 익스포터 | 참조면 값 타입을 Int32로 덮어씀 |
| 형식의 element 매핑 | 참조면 ElementI32 반환 |
| 생성기 13개 | ForeignRecord를 readInt32()로. 파일당 1~7곳, 합계 54곳 |
| 쿠킹 컨텍스트 | ForeignRecord => typeof(int) |
| SQL 익스포터 3종 | INT NOT NULL · integer NOT NULL |
한 곳의 실수가 아니라 한 가정이 파이프라인 전체에 퍼진 것입니다. 그래서 이것은 조건 하나를 지우는 일이 아니라, 「참조가 담는 값의 타입」이라는 사실을 모델이 들고 다니게 만드는 일입니다.
이미 서 있는 쪽 — 가리켜지는 테이블
막힌 것은 가리키는 컬럼 하나뿐입니다. 대상 테이블 쪽 — 키가 string·bigint·uuid·enum인
테이블과 그 조회 — 은 이미 서 있고 게이트도 있습니다.
| 이미 있는 것 | 무엇을 고정하는가 |
|---|---|
string-index 시나리오 | 「생성기는 아무것도 바꿀 필요가 없었다 — 내는 조회가 인덱스 필드 자신의 타입에 대한 사전이기 때문」이라고 그 recipe가 적어 두었습니다 |
key-types 시나리오 | bigint·uuid·enum이 키가 되는 것을 모든 언어의 골든으로 |
그러므로 이 개정이 만들어야 하는 것은 GetByXOrThrow에 넘길 값이 참조 컬럼에서 제 타입으로
나오게 하는 것이고, 받는 쪽 서명은 이미 맞습니다. 6절의 13번이 새 기능의 확인이 아니라
고정을 푼 자리의 확인인 이유가 그것입니다.
2. 결정 — 대상 인덱스의 타입 그대로
foreign 컬럼이 담는 것은 대상의 기본 인덱스 값이고, 그 값의 타입은 대상이 정합니다.
int32는 그중 한 경우이지 규칙이 아닙니다.
| 대상의 인덱스 | 참조 컬럼이 담는 것 |
|---|---|
int | i32 — 지금과 같습니다 |
bigint | i64 |
string | 문자열 |
uuid | uuid가 실리는 방식 그대로 |
enum | 그 enum의 바탕 타입 |
「그 값으로 행을 구별할 수 있는가」가 인덱스의 규칙이므로, 참조의 규칙도 그것 하나입니다. 인덱스가 될 수 있으면 가리킬 수 있습니다. 별도의 목록을 두지 않는 것이 이 개정의 요지입니다.
3. 기존 출력의 무변경
지금 int32가 아닌 키를 가리키는 참조는 존재할 수 없습니다 — 거부되기 때문입니다. 그래서
이 개정은 받아들이는 경우를 늘릴 뿐이고, 통과하던 워크북이 내는 바이트는 그대로입니다.
- 형식 버전이 올라가지 않습니다. 새 element 코드가 필요 없습니다 —
ElementString·ElementI64는 이미 있고, 참조 컬럼이 그중 맞는 것을 고르게 되는 것뿐입니다. - 리더도 그대로입니다. 리더는 파일이 선언한 element를 읽지 참조인지를 가리지 않습니다.
- 골든이 판정합니다. 한 바이트라도 움직이면 그것은 이 개정이 검출한 것이 아니라 부작용이고 diff를 봐야 합니다.
4. 파싱 순서 — 해석 뒤에 얻는 타입
이 개정에서 실제로 어려운 자리는 여기 하나입니다.
참조 셀의 값은 대상이 해석되기 전에 파싱됩니다. 레이아웃이 필드를 읽을 때 아는 것은
「이 컬럼은 Animation을 가리킨다」는 이름뿐이고, Animation이 무슨 키를 쓰는지는 모든
테이블이 읽힌 뒤에야 정해집니다. 그래서 지금은 참조 컬럼의 타입을 int32로 고정해 두고
데이터 패스가 그것으로 파싱합니다.
결정: 참조 셀은 원문으로 들고 있다가, 해석된 뒤에 대상의 키 타입으로 변환합니다.
| 고른 것 | 미룬 것 |
|---|---|
| 참조 컬럼만 파싱을 한 단계 미룹니다. 다른 컬럼은 지금 그대로 | 선언 전체를 먼저 읽고 데이터를 나중에 읽도록 파서를 두 패스로 나누는 것 |
두 번째가 더 깔끔하지만 레이아웃 파서의 구조를 바꿉니다. 첫 번째는 참조 컬럼에 국한되고, 참조의 「없음」이 이미 그 자리를 열어 두었습니다 — 참조 셀의 빈 칸을 값 파서에 넘기지 않기로 한 것이 같은 방향입니다.
변환이 실패하면 그때가 진단 자리입니다. 대상이
int키인데 셀에abc가 적혀 있으면, 그것은 지금 「Int32로 파싱할 수 없습니다」로 나오던 것이고 대상의 키 타입을 적을 수 있게 되므로 더 나은 메시지가 됩니다 — 어느 테이블의 어떤 키를 기대했는지 적을 수 있습니다.
5. 담지 않는 것
bool·float·double·시각·배열 키. 인덱스가 될 수 없는 것은 가리킬 수도 없습니다. 같은 규칙 하나라는 것이 2절의 요지이므로, 참조 쪽에만 예외를 두지 않습니다.- 점 표기(
Table.Field)가 가리키는 필드의 타입. 그것은 이미 대상 필드의 타입을 따릅니다. 이 문서가 고치는 것은 전체 행 참조가 담는 키입니다. - 여러 테이블 중 하나여도 되는 값. 참조가 아니라 검사로 남았습니다 — 키를 담지 않으므로 이 규칙에 걸리지 않습니다(참조가 내는 이름 §6).
int32키의 폭 최적화. 키가int인 참조는 지금처럼 i32로 실립니다. 비트폭 패킹이 이미 그 위에서 처리합니다.