villagesql 크레이트 API에 대한 참조입니다. 시작하기 가이드는 Rust로 확장 만들기를 참조하세요. 사용자 정의 타입에 대해서는 Rust에서의 사용자 정의 타입을 참조하세요.
InValue
InValue는 서버가 각 함수 인수에 대해 전달하는 열거형입니다. 함수는 args: &[InValue]를 받으며, 값을 사용하기 전에 각 인수를 확인해야 합니다.
항상
Null을 명시적으로 매치해야 합니다. .unwrap()을 호출하거나 값 배리언트만 패턴 매칭하는 것은 버그입니다 — SQL NULL은 오류가 아닌 정상적인 입력입니다.
VdfReturn
VdfReturn은 함수가 서버에 반환하는 것입니다. 연관 함수 중 하나로 구성합니다:
경고 vs 오류:
사용자 입력 검증 실패 시 결과 집합의 나머지 부분을 계속 처리하는 것이 합리적인 경우
warning을 사용하세요. 경고는 문을 실패시키지 않으며, 엄격 모드의 INSERT 및 UPDATE에서도 마찬가지입니다. 데이터가 손상되거나 내부 불변 조건 위반과 같은 안전하지 않은 상황에서는 error를 사용하세요. 치명적 오류는 전체 문을 중단합니다.
extension! 매크로
extension!은 서버가 VEB 파일을 로드할 때 호출하는 VEF 엔트리 포인트를 생성합니다. 이는 크레이트 내에서 정확히 한 번만 나타나야 합니다.
types:와 requires:는 각각 단독으로는 선택 사항이지만, funcs:는 항상 존재해야 합니다 — 타입만 있는 확장에는 funcs: []를 작성하세요. 순수 함수 확장은 types:를 생략합니다. funcs: []가 있고 타입이 없는 extension! 블록은 유효하지만 아무 작업도 하지 않는 확장을 생성합니다.
requires:는 확장이 사용하는 Rust에서의 미리보기 기능을 static 기능 객체에 대한 참조로 선언합니다. 이는 funcs: 섹션 뒤에 마지막으로 와야 합니다 — 확장이 함수를 등록하지 않는다면 funcs: []를 포함하세요.
func! 매크로
func!는 SQL 호출 가능한 함수를 선언합니다. 여덟 가지 형태가 있습니다 — 문장별 상태가 없는 네 가지(매개변수 없음, buffer_size만, deterministic만, 둘 다), prerun 함수를 통해 문장별 상태를 붙이는 두 가지, 그리고 그 위에 선택적 postrun:을 추가하는 두 가지입니다. 아래의 문장별 상태를 참조하세요.
타입 상수 (
func!에서 사용):
문장별 상태
일부 함수는 단일 문장의 모든 행에 걸치는 상태를 필요로 합니다 — 호출 카운터, 누산기 같은 것입니다. 상태 타입은state:로, 설정 함수는 prerun:으로 선언합니다. prerun 함수는 첫 번째 행 이전에 한 번 실행되고, 그다음 행 함수가 그 상태에 대한 &mut 접근 권한을 가지고 행마다 한 번씩 실행됩니다.
prerun 함수의 서명은 fn(PrerunArgs, PrerunResult<T>)이며, 이 함수가 공급하는 행 함수는 상태를 먼저 받습니다: fn(state: &mut T, args: &[InValue]) -> VdfReturn. T는 state:가 지정한 타입이며, 컴파일러는 prerun 함수와 행 함수가 그 타입에 대해 일치하는지 확인합니다.
PrerunArgs::len()은 각 행이 받게 될 인수의 개수이며, PrerunArgs::is_empty()는 함수가 인수 없이 호출되었을 때 참입니다.
상태를 직접 해제해서는 안 됩니다: func!는 문장이 끝날 때 상태를 드롭하는 postrun을 항상 생성하므로, 직접 정리 함수를 제공하더라도 상태 해제는 여러분의 책임이 아닙니다. 이 드롭 단계는 postrun에서 delete_state<T>()를 호출해야 하는 C++ SDK와는 반대입니다 — 문장별 상태를 참조하세요.
자체 정리 작업을 먼저 실행하려면 postrun: postrun_fn(fn(&mut StateType))을 지정하세요: 이 함수는 마지막 행 이후, 상태를 아직 빌릴 수 있는 동안, 그리고 SDK 자체의 드롭 전에 한 번 실행됩니다. Rust SDK 리포지토리의 examples/vsql_postrun_order/src/lib.rs를 참조하세요. 그 기록된 테스트 출력에는 순서를 확인해 주는 prerun,postrun,drop|rows_seen=3 줄이 포함되어 있습니다.
문장 내에서 자신의 호출 인덱스를 반환하는 함수를 가진 완전한 확장입니다:
call_index()는 세 번 실행되어 1, 그다음 2, 그다음 3을 반환합니다 — 행마다 하나의 값입니다. SUM은 이 세 값을 더하여 6을 만듭니다.
두 번째 SELECT는 첫 번째보다 큰 값이 아니라 동일한 합계를 반환합니다: 카운터는 하나의 문장에 대해 할당되고 그 문장이 끝나면 삭제됩니다.
agg_func! 매크로
agg_func!는 집계 SQL 함수를 선언합니다 — 행마다 한 번씩이 아니라 각 그룹의 행 전체에 대해 호출되는 SUM/COUNT 스타일의 함수입니다. 두 가지 형태가 있습니다:
누산기는 문장마다 한 번 할당되고 문장이 끝나면 드롭됩니다 —
agg_func!가 누산기를 생성하는 prerun과 드롭하는 postrun을 모두 생성하므로, 둘 중 어느 것도 직접 작성하지 않습니다. 그룹별 동작을 만들어 주는 것이 clear_fn입니다: GROUP BY에서는 같은 누산기가 그룹 사이에서 재사용되므로, 그룹 간에 누출되어서는 안 되는 필드는 반드시 여기에서 재설정해야 합니다.
SDK 리포지토리의 vsql_agg_sum 예제인, 완전한 SUM 대응 집계입니다:
accumulate가 InValue::Int만 매치하는 것이 NULL을 건너뛰게 하며, 이는 내장 SUM과 동일한 동작입니다. seen 플래그는 전부 NULL인 그룹과 빈 그룹이 0이 아니라 NULL을 반환하게 만듭니다:
varargs_func! 매크로
varargs_func!는 임의 개수의, 임의 타입의 인수를 받는 VDF를 선언합니다. 매개변수 목록은 [..]로 씁니다 — 이는 필수 리터럴이며, 인수가 없는 func!에 쓰는 []와는 다릅니다.
여덟 가지 형태 — 세 가지 모양이 있고, 각각 축약형과 buffer_size 및 deterministic을 (단독이 아니라) 함께 추가하는 전체형이 있습니다. state:/prerun: 모양에는 그 위에 선택적 postrun:을 추가하는 두 가지 형태가 더 있으며, 의미는 func!에서와 같습니다:
기본(bare) 형태는 검증이 없고 인수가 0개인 호출도 받아들입니다 — 모든 입력에 대해 정의되는 함수라면 정당한 선택이지만, 이는 전달될 수 있는 모든 입력을 행 함수 혼자 책임진다는 뜻입니다. 문장별 상태를 할당하고 드롭하는 것은
state: 형태뿐입니다. prerun 전용 형태는 PrerunResult<()>를 사용하고 아무것도 저장하지 않으므로 그에 대한 postrun도 없습니다 — 그런 prerun은 PrerunResult를 error와 request_buffer_size에만 사용하며, set_state는 절대 호출하지 않습니다.
prerun에서의 인수 타입 검사
서버가 아무것도 검증하지 않으므로, 가변 인자 prerun은 첫 번째 행이 실행되기 전에 인수 타입을 볼 수 있어야 합니다.PrerunArgs::type_at이 그 시야를 제공하며, len()/is_empty() 및 문장별 상태에서 설명한 PrerunResult 메서드와 함께 사용합니다.
정확히 하나의 사용자 정의 타입만 받아들이려면
is_custom()을 custom_name()과 짝지으세요: is_custom()만으로는 서버의 모든 사용자 정의 타입을 받아들입니다.
SDK 리포지토리의 vsql_varargs 예제는 형태마다 함수를 하나씩 선언합니다. prerun에서 검증하고 문장별 호출 카운터를 유지하는, 상태 있는 가변 인자 함수입니다:
str_join은 여전히 행 함수에서 InValue를 매치합니다: prerun이 보는 것은 값이 아니라 선언된 타입이며, STRING 컬럼은 어느 행에서든 NULL을 담을 수 있습니다.
describe(인수가 0개이거나 스칼라가 아닌 인수를 거부한 다음, 이질적인 인수 목록을 형식화하는 prerun 전용 함수)와 point_path(is_custom()과 custom_name()으로 검증)도 선언합니다. Rust SDK 리포지토리의 examples/vsql_varargs/src/lib.rs를 참조하세요.
custom_type! 매크로
custom_type!은 새로운 열 타입을 등록합니다. type_name, persisted_length, max_decode_buffer_length, encode, decode, compare는 필수입니다. hash는 선택적이지만 권장됩니다. default와 intrinsic_default_fn 중 하나만 지정하고, 둘 다 지정하지 마세요.
default 필드는 열 기본값이 아니며, 생략된 열은 NULL입니다. 이 필드는 서버가 타입을 초기화할 때 인코딩하는 문자열을 제공하며, 초기화는 확장 프로그램이 설치될 때가 아니라 어떤 문이 그 타입을 처음 지정할 때 일어납니다. default와 intrinsic_default_fn을 둘 다 지정하지 않으면 서버는 대신 빈 문자열을 인코딩합니다. 어느 경우든 encode가 Err를 반환하면 그 문은 실패하고 타입은 사용할 수 없는 상태로 남습니다:
default와 intrinsic_default_fn을 둘 다 지정하면 첫 사용 시점으로 미뤄지지 않고 확장 프로그램 설치 시 거부됩니다.
custom! 매크로
villagesql::custom!("type_name")은 func! 선언에서 사용자 정의 타입을 이름으로 참조합니다:
villagesql::Type::*이 매개변수 목록이나 반환 타입 위치에 나타나는 곳이라면 어디서든 사용할 수 있습니다. 문자열은 해당 custom_type!에서 선언된 type_name과 일치해야 합니다.
manifest.json 필드
모든 확장 프로그램은Cargo.toml과 함께 manifest.json이 필요합니다:
name 검증 규칙: 첫 글자는 알파벳, 마지막 글자는 알파벳 또는 숫자여야 하며, 최대 64자입니다. 유효하지 않은 매니페스트는 INSTALL EXTENSION을 실패시킵니다.
