examples/ do repositório também traz um exemplo para cada capability preview suportada.
Código-fonte: examples/ em vsql-rust-sdk
vsql_rot13 — Extensão Apenas com Função
A extensão Rust mais simples possível: uma VDF que recebe uma STRING e retorna uma STRING. Uso:Estrutura de Diretórios
Implementação
Arquivo:src/lib.rs
- As VDFs recebem
&[InValue]e retornamVdfReturn, sendo ambos enums seguros do Rust - NULL é uma variante de primeira classe em ambos os lados; faça o pattern-match diretamente
- A macro
extension!gera os pontos de entrada em C que o servidor chama no momento do carregamento func!declara a assinatura SQL; os tipos de argumento e de retorno usamvillagesql::Type::*
Manifesto
Arquivo:manifest.json
vsql_rational — Tipo Personalizado com Aritmética
Um tipo personalizado completo: números racionais armazenados como(numerator, denominator) em forma reduzida, com funções aritméticas, ordenação e hashing.
Uso:
Formato de Armazenamento Binário
rational armazena 16 bytes (little-endian):
- Bytes 0–7: numerador (
i64) - Bytes 8–15: denominador (
i64)
Funções do Sistema de Tipos
Arquivo:src/lib.rs
O tipo registra quatro operações: encode (string → bytes), decode (bytes → string), compare (para ORDER BY) e hash (para indexação).
Implementações de VDF
As VDFs que recebem um tipo personalizado recebemInValue::Custom(&[u8]) e decodificam os bytes por conta própria:
Registro
A macroextension! registra tanto o tipo quanto suas funções em uma única declaração:
villagesql::custom!("name")referencia um tipo personalizado como argumento ou retornocustom_type!registra o tipo junto com suas funções encode/decode/compare/hashdefault: "0/1"é o padrão intrínseco: o servidor chamaencode()nessa string na inicialização do tipo, portanto ela deve ser um valor válidopersisted_lengthdeve corresponder ao comprimento em bytes queencode()retornadeterministic: truepermite que o otimizador dobre chamadas constantes
vsql_agg_sum — Função de Agregação
Uma VDF de agregação que reimplementaSUM sobre uma coluna INT. Ela mostra os três ganchos de que uma agregação precisa — clear, accumulate e a função de resultado — e como cada um vê o mesmo acumulador.
Uso:
Ciclo de Vida do Acumulador
O acumulador é um valor por instrução, reutilizado em todos os grupos. O servidor o conduz em uma ordem fixa:clear redefine o acumulador no início de cada grupo. Um campo que ele esquecer de redefinir vaza do grupo anterior.
O servidor chama accumulate para cada linha, incluindo as linhas em que o argumento é NULL. Pular os NULLs é trabalho da função: faça a correspondência apenas da variante que você quer e ignore o resto.
Implementação
Arquivo:src/lib.rs
seen é o que distingue um grupo cuja soma deu zero de um grupo sem nada para somar. Sem ele, um grupo vazio ou todo de NULLs retornaria 0 onde o SUM embutido retorna NULL.
Registro
- O primeiro identificador é a função de resultado, não a função de linha — o trabalho por linha de uma agregação vive em
accumulate: state:nomeia o tipo do acumulador, que deve implementarDefault- A lista de parâmetros declarada é a lista de argumentos por linha:
[villagesql::Type::Int]é o queaccumulaterecebe, e o tipo de retorno é o que a função de resultado produz agg_func!também aceitabuffer_size:edeterministic:depois deaccumulate:, fornecidos juntos, nessa ordem
vsql_varargs — Função Varargs
Quatro VDFs que recebem, cada uma, qualquer número de argumentos. Juntas, elas cobrem as três formas de registro quevarargs_func! suporta — com estado e uma prerun, apenas com prerun e básica — além da validação de argumentos de tipos personalizados.
Uso:
#1 é o contador de chamadas por instrução, que sobe ao longo das linhas de uma instrução:
A Prerun É Dona de Toda a Validação de Varargs
Para uma função varargs, o servidor não faz nenhuma verificação de argumentos — nem a contagem, nem os tipos. Uma assinatura declarada é o que normalmente faz o servidor rejeitar uma chamada ruim antes que seu código seja executado, e uma função varargs não tem nenhuma. O que quer que o gancho prerun não rejeite chega à função de linha. A prerun rejeita a chamada, uma vez, antes de qualquer linha: ela vê os tipos de argumento que o otimizador resolveu e faz a instrução falhar. A função de linha ainda tem que tratar cada valor, porque uma coluna cujo tipo passou na validação ainda pode carregar NULL em qualquer linha. Uma rejeição da prerun faz a inicialização da instrução falhar:arg_count é registrada na forma básica, então uma chamada com zero argumentos é legal:
Implementação
Arquivo:src/lib.rs
Uma prerun recebe PrerunArgs e um PrerunResult<T> cujo T corresponde ao tipo do estado. PrerunArgs::len() é a contagem de argumentos, e type_at(i) retorna o tipo do argumento i como um ArgType:
request_buffer_size, escalado por args.len() — um buffer_size fixo não pode crescer com a contagem de argumentos.
ArgType expõe quatro predicados — is_int(), is_real(), is_str() e is_custom() — para que uma prerun possa aceitar uma chamada heterogênea desde que todo argumento seja um dos formatos que a função de linha trata. describe aceita qualquer combinação dos três escalares e rejeita qualquer outra coisa:
() porque essa prerun não guarda nada: ela valida e dimensiona o buffer, e nunca chama set_state.
Varargs com Tipos Personalizados
is_custom() sozinho diz apenas que o argumento é algum tipo personalizado. custom_name() retorna qual deles, então uma prerun pode restringir uma chamada varargs a um único tipo. A extensão registra um tipo personalizado point2d e aceita um número variável de valores point2d:
InValue::Custom(b) e decodifica os bytes por conta própria, como qualquer outra VDF de tipo personalizado.
Registro
describe, point_path e o encode/decode/compare de point2d seguem os mesmos padrões de correspondência de InValue e de codificação de bytes já mostrados para str_join e rational acima — consulte examples/vsql_varargs/src/lib.rs no repositório do SDK Rust para o código-fonte completo.
Padrões principais:
[..]no lugar de uma lista de parâmetros é o que marca a função como varargs- Três formas, cada uma com uma assinatura diferente de função de linha:
state:+prerun:dáfn(&mut State, &[InValue]) -> VdfReturn;prerun:sozinha e a forma básica dão ambasfn(&[InValue]) -> VdfReturn - Apenas a forma com
state:aloca e descarta o estado por instrução - O tipo de retorno ainda é declarado, então apenas a lista de argumentos é variável
- Cada forma também aceita
buffer_size:edeterministic:como um par final point2dnão registrahash, que é opcional —comparesozinho é suficiente paraORDER BY
Padrões Principais de Implementação
Testes
Todos os quatro exemplos usam MTR (o MySQL Test Runner) assim como as extensões em C++:--record.
Próximos Passos
Criando Extensões em Rust
Instalação do SDK, compilação e a macro extension!
Tipos Personalizados em Rust
Aprofundamento em encode, decode, compare e hash
Referência da API Rust
InValue, VdfReturn e a superfície da macro
Código-Fonte dos Exemplos
Código-fonte completo dos quatro exemplos

