Skip to main content
Este guia é a referência aprofundada para escrever implementações de VDF em C++. Ele é o complemento de Criando Extensões em C++, que cobre as etapas de compilação de ponta a ponta, e de Testes em C++, que cobre o ciclo de teste e iteração.
O Protocol 3 do VEF é estável desde a v0.0.4. O Protocol 4 está em desenvolvimento e está disponível apenas por meio de cabeçalhos de ABI de desenvolvimento opcionais (-DVSQL_USE_DEV_ABI=ON). Extensões compiladas com o antigo Protocol 2 são rejeitadas pelo servidor e precisam ser recompiladas.

Escrevendo Funções de Extensão

As funções de extensão são escritas em C++ e registradas com o VEF. Inclua um único cabeçalho para acessar o SDK completo:

Tipos de Argumento e Resultado

Os parâmetros e resultados de VDF são passados como tipos de argumento e resultado com segurança de tipo. O framework os detecta a partir da assinatura da sua função e se adapta automaticamente — a sintaxe de registro make_func permanece inalterada. Tipos de argumento: IntArg, RealArg, StringArg, CustomArg — cada um fornece is_null() e value(). Para tipos personalizados parametrizados, CustomArgWith<P> adiciona um acessador params() que retorna a struct de params analisada e armazenada em cache (consulte Tipos Parametrizados). Tipos de resultado: IntResult, RealResult, StringResult, CustomResult — cada um fornece set_null(), warning(msg) e error(msg). Os resultados escalares também fornecem set(value). Os resultados de buffer fornecem buffer() e set_length(len). StringResult também fornece set(std::string_view), que copia até buffer().size() bytes da view e define o comprimento em uma única chamada. Para tipos personalizados parametrizados, CustomResultWith<P> adiciona um acessador params(). Tipo Span: value() e buffer() nos tipos de argumento e resultado orientados a bytes retornam um vsql::Span<T> — uma view não proprietária sobre uma sequência contígua de T com data(), size(), empty(), begin()/end() e operator[]. No C++20 ele é um alias para std::span<T>; no C++17, este SDK fornece uma implementação mínima compatível, de modo que o mesmo código compila em qualquer um dos padrões. Ele está disponível através de <villagesql/vsql.h>. warning(msg) retorna SQL NULL para a linha e anexa um aviso SQL. No modo estrito (STRICT_TRANS_TABLES), o MySQL o promove a um erro de instrução em INSERT/UPDATE, então ele se comporta como error(msg) em contextos estritos. Use-o para entrada inválida recuperável, como uma string que não pode ser analisada em uma função de codificação. Use error(msg) para dados armazenados corrompidos ou qualquer condição em que continuar seja inseguro. A mensagem de ambos é truncada para caber no buffer de erro interno do servidor, se necessário. Exemplo escalar — soma dois inteiros:
Exemplo binário — transforma um buffer de tipo personalizado no local:
Para StringResult e CustomResult, escreva em buffer() e, em seguida, chame set_length() com o número de bytes escritos. buffer().size() é a capacidade máxima. Para VDFs que retornam um tipo personalizado (returns(CUSTOM(MYTYPE))), o servidor dimensiona o buffer de resultado para o persisted_length do tipo de retorno resolvido automaticamente — os autores de extensões não precisam declarar .buffer_size(...) no construtor da função nesse caso. Se prerun aumentar ainda mais o buffer, esse tamanho maior é preservado. É isso que permite, por exemplo, que SVECTOR::from_string('[…1024 floats…]') codifique um vetor amplo sem que o buffer de resultado fique sem espaço. Você pode usar estilos diferentes entre funções na mesma extensão — o estilo de cada função é determinado por sua própria assinatura.

VDFs Agregadas

As VDFs agregadas acumulam estado entre linhas dentro de cada grupo GROUP BY e retornam um único resultado por grupo, como SUM ou COUNT do SQL. Use make_aggregate_func<State, &result_fn>("name") para registrar uma. O tipo State é o buffer de acumulação por grupo; prerun e postrun são gerados automaticamente para alocá-lo e removê-lo. A função de resultado deve ter a assinatura void(const State&, ResultType) onde ResultType é um dentre IntResult, RealResult, StringResult, CustomResult ou CustomResultWith<P>. Chame out.set(value) para retornar um valor ou out.set_null() para retornar SQL NULL. Tanto .clear<>() quanto .accumulate<>() são obrigatórios. O construtor impõe isso em tempo de compilação (via build()), e o servidor valida novamente no momento do INSTALL EXTENSIONclear redefine o estado, accumulate acumula as linhas e a função de resultado lê o estado final.
Como funcionam os métodos do construtor:
  • make_aggregate_func<State, &result_fn>() gera automaticamente prerun e postrun (inicializa por valor e remove o State).
  • .clear<&fn>() registra sua função de redefinição void(State&).
  • .accumulate<&fn>() registra sua função de acumulação void(State&, TypedArgs...). Os TypedArgs são deduzidos da assinatura da função (IntArg, StringArg, etc.).
  • O tipo de resultado (IntResult, RealResult, etc.) é deduzido da assinatura da função de resultado.
Para um contador que nunca retorna NULL, use um tipo de estado simples:
Uma VDF agregada com StringResult retorna texto: o resultado informa o charset e a collation utf8mb4_bin, de modo que os clientes o exibem como caracteres em vez de hexadecimal — o mesmo que o caminho STRING da VDF escalar. Ela também respeita .max_result_length(n) da mesma forma, dimensionando um resultado agregado materializado (uma tabela temporária de GROUP BY/DISTINCT, CREATE TABLE ... SELECT ou UNION) de modo que ele não seja truncado na largura do argumento. Consulte Tamanhos de Buffer Personalizados para as regras de dimensionamento e o limite.

Estado por Instrução (Prerun e Postrun)

Algumas VDFs precisam de estado que abranja cada linha que uma única consulta toca — um contador de chamadas, um resultado em cache, um recurso aberto. Aloque-o em um gancho prerun, acesse-o a partir do corpo da VDF e libere-o em um gancho postrun. Ambos os ganchos são executados uma vez por instrução; o corpo da VDF é executado uma vez por linha. Registre-os com .prerun<&Hook>() e .postrun<&Hook>(). As assinaturas obrigatórias são: Use PrerunResult::set_user_data(void*) para armazenar o estado; use PostrunArgs::delete_state<T>() para liberá-lo. Se prerun chamar set_user_data(new T{}), postrun deve chamar delete_state<T>() — o SDK não libera automaticamente. PrerunArgs::type_at(i) expõe o tipo SQL declarado de cada argumento antes que qualquer linha seja lida; os predicados is_int(), is_real(), is_str(), is_custom() no PrerunArgType retornado espelham os tipos de coluna. Use isto em prerun para validar os tipos de argumento ou chame PrerunResult::request_buffer_size(n) para dimensionar o buffer de resultado.

VDFs com Varargs

Uma VDF com varargs aceita qualquer número de argumentos de qualquer tipo SQL. Declare uma com .varargs() no construtor da função, o que é mutuamente exclusivo com .no_params() e .param(TYPE). O corpo recebe um argumento vsql::VarArgs em vez dos tipos de argumento usuais de aridade fixa.
O registro de varargs requer o Protocol 3 do VEF. Servidores mais antigos rejeitam a extensão no momento da instalação.
O framework não pode validar a contagem ou os tipos de argumento para VDFs com varargs. Combine cada registro de varargs com um gancho prerun que chame PrerunResult::error() em entrada inválida ou PrerunResult::request_buffer_size(n) para dimensionar o buffer de resultado. Itere sobre os argumentos com range-for. Cada elemento AnyArg requer uma verificação de tipo antes de ler seu valor: Verifique is_null() antes de qualquer acessador — todos os quatro são indefinidos em um argumento nulo.

VEF_GENERATE_REGISTRATION

VEF_GENERATE_REGISTRATION cria um auxiliar interno _vef_do_register() que realiza o registro da extensão, mas não define os pontos de entrada extern "C". Use-o quando precisar personalizar o comportamento de vef_register — por exemplo, para corrigir descritores após o registro em uma compilação de teste. Para extensões normais, use VEF_GENERATE_ENTRY_POINTS em vez disso.

Operações de Tipos Personalizados

Para a referência completa do construtor de operações de tipo — codificação, decodificação, comparação, hash, padrões intrínsecos e tipos parametrizados — consulte Operações de Tipo.

Capabilities Preview

As seguintes capabilities do VEF estão disponíveis como cabeçalhos Preview opcionais. A ABI e a API ainda estão em desenvolvimento ativo; consulte Capabilities Preview para a referência completa.

Inspecionando os Metadados de Registro da Extensão

INFORMATION_SCHEMA.EXTENSION_REGISTRATION expõe a struct de registro do VEF em memória para cada extensão carregada como um documento JSON. Use-o para verificar se o servidor analisou corretamente as funções, os tipos e as variáveis de sistema da sua extensão após o INSTALL EXTENSION.

Veja Também