Skip to main content
Esta página apresenta quatro das extensões de referência do repositório do SDK Rust: um exemplo mínimo que contém apenas uma função, um tipo personalizado completo com aritmética, ordenação e hashing, uma função de agregação e uma função varargs. O diretório 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
Padrões principais:
  • As VDFs recebem &[InValue] e retornam VdfReturn, 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 usam villagesql::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)
Os valores são sempre armazenados em forma reduzida (GCD = 1) com um denominador positivo.

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 recebem InValue::Custom(&[u8]) e decodificam os bytes por conta própria:

Registro

A macro extension! registra tanto o tipo quanto suas funções em uma única declaração:
Padrões principais:
  • villagesql::custom!("name") referencia um tipo personalizado como argumento ou retorno
  • custom_type! registra o tipo junto com suas funções encode/decode/compare/hash
  • default: "0/1" é o padrão intrínseco: o servidor chama encode() nessa string na inicialização do tipo, portanto ela deve ser um valor válido
  • persisted_length deve corresponder ao comprimento em bytes que encode() retorna
  • deterministic: true permite que o otimizador dobre chamadas constantes

vsql_agg_sum — Função de Agregação

Uma VDF de agregação que reimplementa SUM 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:
Adicionar um grupo todo de NULLs mostra que o acumulador é redefinido entre grupos em vez de ser carregado adiante:

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
O flag 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

Padrões principais:
  • 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 implementar Default
  • A lista de parâmetros declarada é a lista de argumentos por linha: [villagesql::Type::Int] é o que accumulate recebe, e o tipo de retorno é o que a função de resultado produz
  • agg_func! também aceita buffer_size: e deterministic: depois de accumulate:, 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 que varargs_func! suporta — com estado e uma prerun, apenas com prerun e básica — além da validação de argumentos de tipos personalizados. Uso:
A mesma função atende às duas aridades. O prefixo #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:
Omitir a prerun significa aceitar toda chamada. 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:
Para varargs, dimensione o buffer na prerun com 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:
O tipo do estado é () 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:
Uma string simples é rejeitada antes da primeira linha, mesmo que seus bytes fossem interpretáveis como um ponto:
A função de linha então faz a correspondência de 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:fn(&mut State, &[InValue]) -> VdfReturn; prerun: sozinha e a forma básica dão ambas fn(&[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: e deterministic: como um par final
  • point2d não registra hash, que é opcional — compare sozinho é suficiente para ORDER 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++:
Gere ou atualize os resultados esperados com --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