examples/ 디렉터리에는 지원되는 미리보기 기능별 예제도 하나씩 들어 있습니다.
소스: vsql-rust-sdk의 examples/
vsql_rot13 — 함수 전용 확장
가능한 한 가장 간단한 Rust 확장: STRING을 받아 STRING을 반환하는 VDF 하나입니다. 사용법:디렉터리 구조
구현
파일:src/lib.rs
- VDF는
&[InValue]를 받고VdfReturn을 반환합니다 — 둘 다 안전한 Rust 열거형입니다 - NULL은 양쪽 모두에서 일급 배리언트(variant)입니다; 직접 패턴 매치하세요
extension!매크로는 서버가 로드 시 호출하는 C 엔트리 포인트를 생성합니다func!는 SQL 시그니처를 선언합니다; 인수 및 반환 타입은villagesql::Type::*를 사용합니다
매니페스트
파일:manifest.json
vsql_rational — 산술을 갖춘 사용자 정의 타입
완전한 사용자 정의 타입: 기약 형태의(numerator, denominator)로 저장되는 유리수이며, 산술 함수, 정렬, 해싱을 갖춥니다.
사용법:
이진 저장 형식
rational은 16바이트를 저장합니다(리틀 엔디언):
- 바이트 0–7: 분자(
i64) - 바이트 8–15: 분모(
i64)
타입 시스템 함수
파일:src/lib.rs
이 타입은 네 가지 연산을 등록합니다: encode(문자열 → 바이트), decode(바이트 → 문자열), compare(ORDER BY용), hash(인덱싱용).
VDF 구현
사용자 정의 타입을 받는 VDF는InValue::Custom(&[u8])를 받아 바이트를 직접 디코딩합니다:
등록
extension! 매크로는 타입과 그 함수를 단일 선언으로 등록합니다:
villagesql::custom!("name")는 사용자 정의 타입을 인수 또는 반환으로 참조합니다custom_type!은 타입을 그 encode/decode/compare/hash 함수와 함께 등록합니다default: "0/1"은 내재적 기본값입니다 — 서버는 타입 초기화 시 이 문자열에 대해encode()를 호출하므로 유효한 값이어야 합니다persisted_length는encode()가 반환하는 바이트 길이와 일치해야 합니다deterministic: true는 최적화기가 상수 호출을 폴딩할 수 있게 합니다
vsql_agg_sum — 집계 함수
INT 컬럼에 대해 SUM을 다시 구현한 집계 VDF입니다. 집계에 필요한 세 가지 훅 — clear, accumulate, 결과 함수 — 과 각 훅이 어떻게 같은 누산기를 보는지 보여줍니다.
사용법:
누산기 수명 주기
누산기는 문장마다 하나의 값이며, 모든 그룹에서 재사용됩니다. 서버는 정해진 순서로 이를 구동합니다:clear는 각 그룹이 시작될 때 누산기를 재설정합니다. 재설정을 빠뜨린 필드는 이전 그룹에서 누출됩니다.
서버는 인수가 NULL인 행을 포함해 모든 행에 대해 accumulate를 호출합니다. NULL을 건너뛰는 것은 함수의 몫입니다: 원하는 variant만 매치하고 나머지는 무시하세요.
구현
파일:src/lib.rs
seen 플래그는 합계가 0인 그룹과 합산할 것이 없는 그룹을 구분해 줍니다. 이 플래그가 없으면 빈 그룹이나 전부 NULL인 그룹은 내장 SUM이 NULL을 반환하는 곳에서 0을 반환하게 됩니다.
등록
- 첫 번째 식별자는 행 함수가 아니라 결과 함수입니다 — 집계의 행별 작업은
accumulate:에 있습니다 state:는 누산기 타입을 지정하며, 이 타입은Default를 구현해야 합니다- 선언된 매개변수 목록은 행별 인수 목록입니다:
[villagesql::Type::Int]는accumulate가 받는 것이고, 반환 타입은 결과 함수가 만들어 내는 것입니다 agg_func!는accumulate:뒤에buffer_size:와deterministic:도 받습니다 — 함께, 그 순서로 제공해야 합니다
vsql_varargs — 가변 인자 함수
각각 임의 개수의 인수를 받는 네 개의 VDF입니다. 이들은varargs_func!가 지원하는 세 가지 등록 형태 — prerun이 있는 상태형, prerun 전용, 기본(bare) — 에 더해 사용자 정의 타입 인수의 검증까지 함께 보여줍니다.
사용법:
#1 접두사는 문장별 호출 카운터로, 한 문장의 행들을 거치며 증가합니다:
가변 인자 검증은 모두 prerun의 몫
가변 인자 함수에 대해 서버는 인수 검사를 전혀 하지 않습니다 — 개수도, 타입도 검사하지 않습니다. 보통은 선언된 시그니처가 있어서 잘못된 호출이 코드가 실행되기 전에 서버에서 거부되지만, 가변 인자 함수에는 선언된 시그니처가 없습니다. prerun 훅이 거부하지 않은 것은 무엇이든 행 함수에 도달합니다. prerun은 어떤 행보다도 먼저, 한 번, 호출을 거부합니다: 최적화기가 결정한 인수 타입을 보고 문장을 실패시킵니다. 행 함수는 여전히 각 값을 처리해야 합니다 — 타입 검증을 통과한 컬럼도 어느 행에서든 NULL을 담을 수 있기 때문입니다. prerun의 거부는 문장 초기화를 실패시킵니다:arg_count는 기본(bare) 형태로 등록되므로 인수가 0개인 호출도 유효합니다:
구현
파일:src/lib.rs
prerun은 PrerunArgs와, T가 상태 타입과 일치하는 PrerunResult<T>를 받습니다. PrerunArgs::len()은 인수 개수이고, type_at(i)는 인수 i의 타입을 ArgType으로 반환합니다:
request_buffer_size로 args.len()에 비례해 버퍼 크기를 정하세요 — 고정된 buffer_size는 인수 개수에 따라 늘어날 수 없습니다.
ArgType은 네 가지 조건식 — is_int(), is_real(), is_str(), is_custom() — 을 노출하므로, 모든 인수가 행 함수가 처리하는 모양 중 하나이기만 하면 prerun이 이질적인 호출도 받아들일 수 있습니다. describe는 세 가지 스칼라의 어떤 조합이든 받아들이고 그 외에는 모두 거부합니다:
()입니다: 검증하고 버퍼 크기를 정할 뿐, set_state는 결코 호출하지 않습니다.
사용자 정의 타입 가변 인자
is_custom()만으로는 인수가 어떤 사용자 정의 타입이라는 것만 알 수 있습니다. custom_name()은 어느 타입인지 반환하므로, prerun이 가변 인자 호출을 단일 타입으로 제한할 수 있습니다. 이 확장은 point2d 사용자 정의 타입을 등록하고 가변 개수의 point2d 값을 받아들입니다:
InValue::Custom(b)를 매치하고 바이트를 직접 디코딩합니다.
등록
describe, point_path, 그리고 point2d의 encode/decode/compare는 위의 str_join과 rational에서 이미 보여준 것과 같은 InValue 매칭 및 바이트 인코딩 패턴을 따릅니다 — 전체 소스는 Rust SDK 리포지토리의 examples/vsql_varargs/src/lib.rs를 참조하세요.
핵심 패턴:
- 매개변수 목록 자리의
[..]가 함수를 가변 인자로 표시합니다 - 세 가지 형태가 각기 다른 행 함수 시그니처를 가집니다:
state:+prerun:은fn(&mut State, &[InValue]) -> VdfReturn,prerun:단독과 기본(bare) 형태는 둘 다fn(&[InValue]) -> VdfReturn - 문장별 상태를 할당하고 드롭하는 것은
state:형태뿐입니다 - 반환 타입은 여전히 선언되므로, 가변적인 것은 인수 목록뿐입니다
- 각 형태는 후행 쌍으로
buffer_size:와deterministic:도 받습니다 point2d는hash를 등록하지 않는데, 이는 선택 사항입니다 —ORDER BY에는compare만으로 충분합니다
핵심 구현 패턴
테스트
네 예제 모두 C++ 확장과 마찬가지로 MTR(MySQL Test Runner)을 사용합니다:--record로 예상 결과를 생성하거나 업데이트합니다.
다음 단계
Rust에서 확장 만들기
SDK 설치, 빌드, extension! 매크로
Rust 사용자 정의 타입
encode, decode, compare, hash에 대한 심층 분석
Rust API 참조
InValue, VdfReturn, 그리고 매크로 API
예제 소스
네 가지 예제 전체 소스

