Skip to main content
O SDK Rust está em alpha — espere mudanças incompatíveis de API entre versões. Extensões apenas de funções, funções de agregação, funções varargs e tipos personalizados (encode, decode, compare, hash) são compatíveis, assim como as capabilities Preview sys_var, status_var, thread_worker e keyring. A ABI de armazenamento de coluna é exclusiva do C++ hoje — use o SDK C++ se você precisar dela.
Tipos personalizados permitem que você defina novos tipos de coluna, como RATIONAL, VECTOR ou INET, que funcionam com ORDER BY, índices e funções de agregação. O SDK Rust oferece suporte a isso por meio da macro custom_type! e, para tipos cujo tamanho de armazenamento depende de parâmetros de coluna, por meio de parameterized_type! (consulte Tipos parametrizados). Esta página pressupõe que você já tenha trabalhado com Criando Extensões em Rust. A configuração (Cargo.toml, manifest.json, cargo-vsql) é a mesma.

Quando usar um tipo personalizado

Use um tipo personalizado quando:
  • Você precisar de um layout binário em disco que um tipo SQL padrão não consegue expressar (floats compactados, inteiros de largura fixa, identificadores binários)
  • Seu tipo tiver semântica de ordenação própria que difere da ordenação lexicográfica de strings
  • Você quiser que o servidor indexe e faça hash dos valores corretamente para ORDER BY, COUNT(DISTINCT) e operações de conjunto
Se você precisar apenas de funções chamáveis via SQL e seus dados couberem confortavelmente em colunas STRING, INT ou REAL, você não precisa de um tipo personalizado.

A macro custom_type!

Todo tipo personalizado precisa de 4 callbacks (encode, decode, compare, hash) e um valor padrão. Aqui está a assinatura completa da macro:
type_name, persisted_length, max_decode_buffer_length, encode, decode e compare são obrigatórios. hash e default são opcionais, mas recomendados — hash é necessário para que o COUNT(DISTINCT) e as operações de conjunto funcionem corretamente, e default é necessário para a verificação de inicialização do tipo.

Recebendo e retornando valores binários

Funções que recebem ou retornam um tipo personalizado trabalham com bytes brutos. EntradaInValue::Custom(b) carrega o binário armazenado como &[u8]:
SaídaVdfReturn::Binary(bytes) envia bytes binários de volta ao servidor:
Para referenciar um tipo personalizado em uma declaração func!, use villagesql::custom!("type_name"):

Exemplo: tipo de número racional

examples/vsql_rational no repositório do SDK é uma extensão funcional que implementa um tipo RATIONAL. Ela armazena um número racional como um par de valores i64 (numerador, denominador, totalizando 16 bytes) em ordem de bytes little-endian e fornece funções aritméticas. Aqui estão as implementações principais de encode, decode, compare e hash:
O registro custom_type! e as VDFs aritméticas (rational_add, rational_sub, etc.) estão no código-fonte completo em examples/vsql_rational/src/lib.rs. Com a extensão instalada:
rational_to_real(r RATIONAL) -> REAL converte um valor RATIONAL em uma aproximação de ponto flutuante de 64 bits, dividindo o numerador pelo denominador. Útil quando você precisa de um decimal aproximado para exibição ou comparação, mas não quer armazenar a representação com perda na coluna.

Tipos parametrizados

Um tipo parametrizado precisa de um valor lido no momento do CREATE TABLE — como o 3 em VECTOR(3) — para saber seu tamanho de armazenamento. custom_type! não consegue expressar isso: seu persisted_length é uma única constante fixa para toda coluna. parameterized_type! é a contraparte parametrizada: o comprimento persistido é calculado por coluna a partir dos parâmetros declarados, através de int_to_params e resolve_params, em vez de ser fixo.
type_name, max_persisted_length, max_decode_buffer_length, encode, decode, compare, int_to_params, resolve_params, params_type, params_parse e params_to_strings são obrigatórios. hash e default são opcionais; intrinsic_default_fn (uma função que calcula o padrão a partir de &P, para quando o padrão depende dos parâmetros) também é opcional e mutuamente exclusiva com default. Aqui está padint — um i64 armazenado em 8 bytes fixos, com um parâmetro width que controla a largura de exibição preenchida com zeros:
Uma função que recebe um argumento de tipo personalizado parametrizado vê InValue::CustomWithParams { bytes, params } em vez do simples InValue::Custom(bytes)params é um [TypeParams], uma visão somente de leitura e sem cópia sobre os pares key=value declarados da coluna.

O bloco extension! com tipos

Ao registrar tanto funções quanto tipos, o bloco extension! tem duas seções:
Uma extensão que contém apenas funções omite types:. Uma extensão que contém apenas tipos mantém funcs: [] e não omite mais nada.

Próximos passos

Referência da API Rust

Referência completa para InValue, VdfReturn e todas as macros.

Criando Extensões em Rust

Primeiros passos — configuração do Cargo, primeira função, empacotamento e testes.

Tipos Personalizados em C++

Tipos personalizados em C++ — make_type<>, encode/decode/compare/hash, regras de ALTER TABLE.

Arquitetura de Extensões

Como tipos personalizados são resolvidos, armazenados em cache e persistidos.