Skip to main content
이 페이지에서는 Rust SDK 리포지토리의 참조 확장 네 가지를 살펴봅니다 — 최소한의 함수 전용 예제, 산술, 정렬, 해싱을 갖춘 완전한 사용자 정의 타입, 집계 함수, 그리고 가변 인자 함수입니다. 리포지토리의 examples/ 디렉터리에는 지원되는 미리보기 기능별 예제도 하나씩 들어 있습니다. 소스: vsql-rust-sdkexamples/

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)로 저장되는 유리수이며, 산술 함수, 정렬, 해싱을 갖춥니다. 사용법:

이진 저장 형식

rational16바이트를 저장합니다(리틀 엔디언):
  • 바이트 0–7: 분자(i64)
  • 바이트 8–15: 분모(i64)
값은 항상 양의 분모와 함께 기약 형태(GCD = 1)로 저장됩니다.

타입 시스템 함수

파일: 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_lengthencode()가 반환하는 바이트 길이와 일치해야 합니다
  • deterministic: true는 최적화기가 상수 호출을 폴딩할 수 있게 합니다

vsql_agg_sum — 집계 함수

INT 컬럼에 대해 SUM을 다시 구현한 집계 VDF입니다. 집계에 필요한 세 가지 훅 — clear, accumulate, 결과 함수 — 과 각 훅이 어떻게 같은 누산기를 보는지 보여줍니다. 사용법:
전부 NULL인 그룹을 추가해 보면, 누산기가 이월되는 것이 아니라 그룹 사이에서 재설정된다는 것을 알 수 있습니다:

누산기 수명 주기

누산기는 문장마다 하나의 값이며, 모든 그룹에서 재사용됩니다. 서버는 정해진 순서로 이를 구동합니다: 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의 거부는 문장 초기화를 실패시킵니다:
prerun을 생략한다는 것은 모든 호출을 받아들인다는 뜻입니다. arg_count는 기본(bare) 형태로 등록되므로 인수가 0개인 호출도 유효합니다:

구현

파일: src/lib.rs prerun은 PrerunArgs와, T가 상태 타입과 일치하는 PrerunResult<T>를 받습니다. PrerunArgs::len()은 인수 개수이고, type_at(i)는 인수 i의 타입을 ArgType으로 반환합니다:
가변 인자에서는 prerun에서 request_buffer_sizeargs.len()에 비례해 버퍼 크기를 정하세요 — 고정된 buffer_size는 인수 개수에 따라 늘어날 수 없습니다. ArgType은 네 가지 조건식 — is_int(), is_real(), is_str(), is_custom() — 을 노출하므로, 모든 인수가 행 함수가 처리하는 모양 중 하나이기만 하면 prerun이 이질적인 호출도 받아들일 수 있습니다. describe는 세 가지 스칼라의 어떤 조합이든 받아들이고 그 외에는 모두 거부합니다:
이 prerun은 아무것도 유지하지 않으므로 상태 타입이 ()입니다: 검증하고 버퍼 크기를 정할 뿐, set_state는 결코 호출하지 않습니다.

사용자 정의 타입 가변 인자

is_custom()만으로는 인수가 어떤 사용자 정의 타입이라는 것만 알 수 있습니다. custom_name()은 어느 타입인지 반환하므로, prerun이 가변 인자 호출을 단일 타입으로 제한할 수 있습니다. 이 확장은 point2d 사용자 정의 타입을 등록하고 가변 개수의 point2d 값을 받아들입니다:
일반 문자열은 그 바이트가 포인트로 파싱될 수 있더라도 첫 번째 행 이전에 거부됩니다:
그다음 행 함수는 다른 사용자 정의 타입 VDF와 마찬가지로 InValue::Custom(b)를 매치하고 바이트를 직접 디코딩합니다.

등록

describe, point_path, 그리고 point2dencode/decode/compare는 위의 str_joinrational에서 이미 보여준 것과 같은 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:도 받습니다
  • point2dhash를 등록하지 않는데, 이는 선택 사항입니다 — ORDER BY에는 compare만으로 충분합니다

핵심 구현 패턴


테스트

네 예제 모두 C++ 확장과 마찬가지로 MTR(MySQL Test Runner)을 사용합니다:
--record로 예상 결과를 생성하거나 업데이트합니다.

다음 단계

Rust에서 확장 만들기

SDK 설치, 빌드, extension! 매크로

Rust 사용자 정의 타입

encode, decode, compare, hash에 대한 심층 분석

Rust API 참조

InValue, VdfReturn, 그리고 매크로 API

예제 소스

네 가지 예제 전체 소스