villagesql. Para o tutorial de introdução, consulte Criando Extensões em Rust. Para tipos personalizados, consulte Tipos Personalizados em Rust.
InValue
InValue é o enum que o servidor passa para cada argumento de função. Sua função recebe args: &[InValue] e deve verificar cada argumento antes de usar seu valor.
Sempre faça a correspondência de
Null explicitamente. Chamar .unwrap() ou fazer a correspondência de padrão apenas das variantes de valor é um bug — o NULL do SQL é uma entrada normal, não um erro.
VdfReturn
VdfReturn é o que sua função retorna ao servidor. Construa-o com uma das funções associadas:
Aviso vs. erro:
Use
warning para falhas de validação de entrada do usuário em que faz sentido continuar com o restante do conjunto de resultados. Um aviso não faz a instrução falhar, inclusive INSERT e UPDATE no modo estrito. Use error para condições em que prosseguir é inseguro: dados armazenados corrompidos, violações de invariantes internas. Um erro fatal aborta a instrução inteira.
macro extension!
extension! gera os pontos de entrada VEF que o servidor chama ao carregar seu arquivo VEB. Ele deve aparecer exatamente uma vez no crate.
types: e requires: são opcionais por si só, mas funcs: deve estar sempre presente — escreva funcs: [] para uma extensão apenas de tipos. Uma extensão apenas de funções omite types:. Um bloco extension! com funcs: [] e sem tipos é válido, mas produz uma extensão que não faz nada.
requires: declara as capabilities Preview que a extensão usa, como referências a objetos static de capability. Ela deve vir por último, depois de uma seção funcs: — inclua funcs: [] se a extensão não registrar nenhuma função.
macro func!
func! declara uma função chamável via SQL. Oito formas — quatro sem estado por instrução (sem parâmetros, apenas buffer_size, apenas deterministic, ambos), duas que anexam estado por instrução por meio de uma função prerun e mais duas que adicionam uma postrun: opcional sobre essas. Consulte Estado por instrução abaixo.
Constantes de tipo para uso em
func!:
Estado por instrução
Algumas funções precisam de um estado que abrange todas as linhas de uma única instrução — um contador de chamadas, um acumulador. Declare o tipo do estado comstate: e uma função de configuração com prerun:. A função prerun é executada uma vez, antes da primeira linha; a função de linha então é executada uma vez por linha com acesso &mut a esse estado.
Uma função prerun tem a assinatura fn(PrerunArgs, PrerunResult<T>), e a função de linha que ela alimenta recebe o estado primeiro: fn(state: &mut T, args: &[InValue]) -> VdfReturn. T é o tipo nomeado por state:, e o compilador verifica se a prerun e a função de linha concordam sobre ele.
PrerunArgs::len() é o número de argumentos que cada linha vai receber, e PrerunArgs::is_empty() é verdadeiro quando a função foi chamada sem argumentos.
Você não deve liberar o estado por conta própria: func! sempre gera a postrun que o descarta quando a instrução termina, então liberá-lo não é responsabilidade sua, mesmo quando você fornece a sua própria finalização. Essa etapa de descarte é o oposto do SDK C++, onde sua postrun tem que chamar delete_state<T>() — consulte Estado por Instrução.
Nomeie uma postrun: postrun_fn, fn(&mut StateType), para executar primeiro a sua própria finalização: ela é executada uma vez após a última linha, enquanto o estado ainda pode ser tomado emprestado, e antes do descarte feito pelo próprio SDK. Consulte examples/vsql_postrun_order/src/lib.rs no repositório do SDK Rust, cuja saída de teste registrada inclui a linha prerun,postrun,drop|rows_seen=3, confirmando a ordem.
Uma extensão completa cuja função retorna o próprio índice de chamada dentro da instrução:
call_index() é executada três vezes e retorna 1, depois 2, depois 3 — um valor por linha. SUM soma esses três valores, o que dá 6.
O segundo SELECT retorna o mesmo total do primeiro, não um maior: o contador é alocado para uma instrução e descartado quando ela termina.
macro agg_func!
agg_func! declara uma função SQL de agregação — no estilo de SUM/COUNT, chamada sobre as linhas de cada grupo em vez de uma vez por linha. Duas formas:
O acumulador é alocado uma vez por instrução e descartado quando a instrução termina —
agg_func! gera tanto a prerun que o cria quanto a postrun que o descarta, então você nunca escreve nenhuma das duas. clear_fn é o que dá a você o comportamento por grupo: com GROUP BY, o mesmo acumulador é reutilizado entre os grupos, então qualquer campo que não deva vazar entre grupos tem que ser redefinido ali.
Uma agregação completa equivalente a SUM — o exemplo vsql_agg_sum no repositório do SDK:
accumulate fazer a correspondência apenas de InValue::Int é o que pula os NULLs, correspondendo ao SUM embutido. O flag seen é o que faz um grupo todo de NULLs e um grupo vazio retornarem NULL em vez de 0:
macro varargs_func!
varargs_func! declara uma VDF que aceita qualquer número de argumentos, de qualquer tipo. A lista de parâmetros é escrita [..] — um literal obrigatório, não o [] usado para uma func! de aridade zero.
Oito formas — três formatos, cada um com uma forma abreviada e uma forma completa que adiciona
buffer_size e deterministic juntos (nunca isoladamente). O formato state:/
prerun: tem mais duas sobre essas, adicionando uma postrun: opcional —
mesmo significado que em func!:
A forma básica não tem validação e aceita uma chamada com zero argumentos — uma escolha legítima para uma função que é total sobre toda entrada, mas isso significa que a função de linha sozinha é responsável por toda entrada que pode receber. Apenas a forma com
state: aloca e descarta o estado por instrução; a forma apenas com prerun usa PrerunResult<()> e não armazena nada, então não há postrun para ela — uma prerun dessas usa PrerunResult apenas para error e request_buffer_size, nunca para set_state.
Inspecionando tipos de argumento em uma prerun
Como o servidor não valida nada, uma prerun de varargs precisa ver os tipos dos argumentos antes que a primeira linha seja executada.PrerunArgs::type_at fornece essa visão, junto com len()/is_empty() e os métodos de PrerunResult descritos em Estado por instrução.
Combine
is_custom() com custom_name() para aceitar exatamente um tipo personalizado: is_custom() sozinho aceita todo tipo personalizado no servidor.
O exemplo vsql_varargs no repositório do SDK declara uma função por forma. Uma função varargs com estado, validada na prerun e carregando um contador de chamadas por instrução:
str_join ainda faz a correspondência de InValue na função de linha mesmo que a prerun tenha provado que todo argumento é uma string: a prerun vê tipos declarados, não valores, e uma coluna STRING pode carregar NULL em qualquer linha.
describe (uma função apenas com prerun que rejeita zero argumentos e argumentos não escalares e, em seguida, formata uma lista heterogênea de argumentos) e point_path (que valida com is_custom() e custom_name()). Consulte examples/vsql_varargs/src/lib.rs no repositório do SDK Rust.
macro custom_type!
custom_type! registra um novo tipo de coluna. type_name, persisted_length, max_decode_buffer_length, encode, decode e compare são obrigatórios. hash é opcional, mas recomendado. Forneça default ou intrinsic_default_fn, não ambos.
O campo
default não é um valor padrão de coluna; uma coluna omitida é NULL. Ele fornece a string que o servidor codifica ao inicializar o tipo, e isso acontece na primeira vez em que uma instrução nomeia o tipo, e não quando a extensão é instalada. Se você não fornecer nem default nem intrinsic_default_fn, o servidor codifica a string vazia. Em qualquer caso, um encode que retorna Err faz essa instrução falhar e deixa o tipo inutilizável:
default quanto intrinsic_default_fn é recusado quando a extensão é instalada, e não adiado para o primeiro uso.
macro custom!
villagesql::custom!("type_name") referencia um tipo personalizado pelo nome em uma declaração func!:
villagesql::Type::* apareceria em uma lista de parâmetros ou posição de tipo de retorno. A string deve corresponder ao type_name declarado no custom_type! correspondente.
campos do manifest.json
Toda extensão precisa de ummanifest.json ao lado de seu Cargo.toml:
Regras de validação de
name: deve começar com uma letra, terminar com uma letra ou dígito, no máximo 64 caracteres. Um manifesto inválido faz com que INSTALL EXTENSION falhe.
