Skip to main content
O Protocol 3 do VEF é estável a partir da v0.0.4. O Protocol 4 está em desenvolvimento e disponível apenas por meio de cabeçalhos opcionais da dev ABI (-DVSQL_USE_DEV_ABI=ON). Extensões compiladas com o antigo Protocol 2 são rejeitadas pelo servidor e precisam ser recompiladas.

Visão geral

O framework de extensões do VillageSQL (VEF) permite adicionar funcionalidades personalizadas ao servidor de banco de dados. Este guia percorre a criação de uma extensão em C++ usando o SDK C++ e o template de extensão. Para escrever implementações de VDF em profundidade (como tipos de argumento e de retorno, agregados, variáveis de sistema e tipos parametrizados), consulte o Guia de Desenvolvimento.
Se você prefere Rust, consulte Criando Extensões em Rust.

O que é uma Extensão do VillageSQL?

Uma extensão do VillageSQL é empacotada como um arquivo VEB (VillageSQL Extension Bundle) contendo:
  • Manifesto - Metadados sobre a extensão (nome, versão, descrição)
  • Biblioteca compartilhada - Código C++ compilado que implementa a funcionalidade
  • Metadados opcionais - Recursos ou configurações adicionais
Extensões em C++ são compiladas usando o SDK C++, a ligação C++ para o VEF (o VillageSQL Extension Framework). Ele fornece:
  • API C++ para definir tipos e funções
  • Registro automático sem scripts SQL
  • Tipos de argumento e de retorno com segurança de tipos
  • Padrão de builder para a definição da extensão
VDFs vs UDFs tradicionais: Funções registradas por meio do VEF são chamadas de VDFs (VillageSQL Defined Functions). O VillageSQL também oferece suporte a UDFs tradicionais do MySQL registradas via CREATE FUNCTION ... SONAME, mas VDFs são recomendadas para novas extensões.

Chamando VDFs em SQL

VDFs podem ser chamadas com ou sem o prefixo da extensão:
Ordem de resolução de funções: Quando você chama uma função sem qualificação, o VillageSQL a resolve nesta ordem:
  1. Funções de sistema (funções nativas do MySQL como NOW(), CONCAT())
  2. UDFs (funções tradicionais definidas pelo usuário do MySQL)
  3. VDFs (funções de extensão) - apenas se houver exatamente uma função com esse nome
  4. Funções armazenadas (criadas com CREATE FUNCTION)
Quando usar nomes qualificados:
  • Use extension.function_name quando várias extensões fornecem funções com o mesmo nome
  • Use nomes não qualificados para um código mais limpo quando não houver ambiguidade
  • A qualificação nunca é obrigatória se apenas uma extensão fornecer aquele nome de função
Extensões podem adicionar:
  • Funções personalizadas (VDFs) - Funções SQL com verificação e validação de tipos automáticas
  • Tipos de dados personalizados - Novos tipos de coluna como COMPLEX, UUID ou VECTOR que funcionam com ORDER BY e índices
  • Operações de tipo - Funções de encode, decode, compare e hash para tipos personalizados

Pré-requisitos

Antes de começar, compile o VillageSQL a partir do código-fonte — extensões são vinculadas aos cabeçalhos do SDK e à árvore de compilação do servidor. Siga primeiro o guia Compilar a Partir do Código-Fonte. Você também precisa de:
  • Git - Para clonagem e controle de versão
  • CMake 3.18 ou superior - Sistema de compilação
  • Compilador C++ - GCC 8+, Clang 8+ ou MSVC 2019+ com suporte a C++17
  • Conhecimento básico de C++ - Compreensão de C++ e pointers de função
Compilando com um agente de IA? A skill vsql-extension-builder automatiza todo esse fluxo de trabalho, da estruturação aos testes, usando Claude Code, Gemini ou outros agentes compatíveis. Instale-a com:

Passo 1: Obtenha o Template de Extensão

Você pode começar com o template de extensão de duas maneiras:

Opção A: Usar o Template a Partir do Código-Fonte do VillageSQL

Se você tem o código-fonte do VillageSQL, o template já está incluído:

Opção B: Fazer um Fork no GitHub

Comece fazendo um fork do repositório do template de extensão do VillageSQL:
  1. Acesse o repositório do template no GitHub:
  2. Clique no botão “Fork” para criar sua própria cópia
  3. Clone seu fork localmente:
Como alternativa, use o botão “Use this template” no GitHub para criar um novo repositório baseado no template sem o histórico do fork.

Passo 2: Atualize o Manifesto

Edite manifest.json para definir os metadados da sua extensão:
O campo $schema é opcional, mas habilita o autocompletar da IDE e a validação inline para todos os campos do manifesto.

Esquema do manifest.json

Regras de validação:
  • name: Deve começar com uma letra e terminar com uma letra ou dígito. Pode conter letras minúsculas, dígitos, sublinhados e hifens. Máximo de 64 caracteres. Use sublinhados — hifens exigem uso de crases (backticks) em SQL.
  • version: Deve seguir o versionamento semântico (ex.: 1.0.0, 0.2.1)
  • Um manifesto inválido fará com que INSTALL EXTENSION falhe
Exemplo:
Para a convenção de nomenclatura completa em SQL, nomes de arquivo e nomes de repositório, consulte Convenções de Nomenclatura de Extensões.

Passo 3: Implemente Sua Extensão com o SDK C++

O SDK C++ fornece uma API C++ para definir extensões usando um padrão de builder fluente:
  • Definições de função com segurança de tipos e verificação em tempo de compilação
  • Validação automática de argumentos e conversão de tipos
  • Suporte a tipos personalizados com funções compare/hash (habilita ORDER BY e índices)

Inclua os Cabeçalhos do VillageSQL

Crie o arquivo principal da sua extensão (ex.: src/extension.cc) e inclua os cabeçalhos do SDK C++:
O cabeçalho <villagesql/vsql.h> traz o builder de tipos, o builder de funções e o builder de extensão, e reexporta símbolos comumente usados para o namespace vsql.

Defina Sua Extensão

Use a macro VEF_GENERATE_ENTRY_POINTS() para definir sua extensão:
Métodos do Builder de Função:
  • make_func<&impl>("name") - Cria a função com o pointer de implementação
  • .returns(type) - Define o tipo de retorno (STRING, INT, REAL ou nome de tipo personalizado)
  • .param(type) - Adiciona um parâmetro (máximo de 8 parâmetros)
  • .buffer_size(size_t) - Solicita um tamanho de buffer de saída específico para retornos STRING/CUSTOM
  • .max_result_length(size_t) - Dimensiona a coluna de resultado para um retorno STRING para que um resultado materializado não seja truncado na largura do argumento. Valores acima de VEF_MAX_RESULT_LENGTH (16 MiB) são limitados pelo servidor. Somente STRING; chame .returns(STRING) primeiro. Requer o Protocol 4 (dev ABI).
  • .deterministic(bool = true) - Declara que esta função sempre retorna a mesma saída para as mesmas entradas e não tem efeitos colaterais. O padrão é não determinístico.
  • .prerun<func>() - Define a função de preparação por instrução (opcional)
  • .postrun<func>() - Define a função de limpeza por instrução (opcional)
  • .build() - Finaliza o registro da função
Limite de Parâmetros: As funções oferecem suporte a no máximo 8 parâmetros (definido por kMaxParams). Se você precisar de mais, considere usar tipos estruturados ou várias funções.

VDFs com Argumentos e Valores de Retorno de Tipo Personalizado

Uma VDF pode receber e retornar valores de tipo personalizado usando .param(TYPE_NAME) e .returns(TYPE_NAME) no builder. A implementação usa CustomArg para entrada e CustomResult para saída — os mesmos tipos de argumento e resultado usados nas operações de tipo:
Registre com .param(COMPLEX) e .returns(COMPLEX):
Consulte o guia de desenvolvimento para a API completa de CustomArg/CustomResult, incluindo CustomArgWith<P> e CustomResultWith<P> para tipos parametrizados.

Funções Determinísticas

Por padrão, VDFs são registradas como não determinísticas. Uma função não determinística é bloqueada em três contextos SQL: colunas geradas, restrições CHECK e valores padrão de expressão (DEFAULT (expr) em uma coluna) — usar qualquer um desses recursos com uma VDF não determinística retorna um erro. Se sua função sempre produz a mesma saída para as mesmas entradas e não tem efeitos colaterais, você pode declará-la determinística adicionando .deterministic() à cadeia do builder. O otimizador pode usar essa informação para avaliar a função uma vez por instrução e reutilizar o valor entre as linhas, em vez de chamá-la por linha. Marcar incorretamente uma função não determinística como determinística pode, portanto, fazer com que o servidor retorne o mesmo resultado para entradas que deveriam produzir saídas diferentes. Adicione .deterministic() apenas quando sua função realmente não tiver dependência de estado externo, aleatoriedade ou tempo. Assinatura do builder: .deterministic(bool d = true) — a forma sem argumentos assume true como padrão. Exemplo:
Como complex_add é declarada determinística, ela pode ser usada em uma definição de coluna gerada:

Tamanhos de Buffer Personalizados

Para funções que retornam dados de comprimento variável, solicite um tamanho de buffer específico:
Quando você mesmo constrói o valor com buffer() e set_length(), o buffer tem tamanho fixo de buffer().size() bytes — escrever além dele causa estouro de memória, então proteja-se contra isso:
Para resultados STRING, out.set(sv) segue um contrato de estouro no estilo snprintf: ele copia quantos bytes couberem e informa o tamanho total do valor via set_length. Quando o tamanho informado excede o buffer, o servidor aumenta o buffer de resultado e reinvoca a função, de modo que um valor STRING maior que o buffer solicitado deixa de ser truncado. Esse contrato se aplica no momento da linha. Um resultado STRING materializado (em uma tabela temporária de GROUP BY/DISTINCT, CREATE TABLE ... SELECT ou UNION) ainda é truncado na largura do argumento, a menos que a função declare .max_result_length(n), que dimensiona a coluna de resultado (em caracteres, limitada a VEF_MAX_RESULT_LENGTH, 16 MiB):
Solicite um tamanho de buffer suficiente via .buffer_size() com base no tamanho máximo de saída da sua função. Dimensionar o buffer corretamente evita o custo de um ciclo de aumento-e-nova-tentativa.
Antes de implementar suas funções, revise a Referência da API C++ para os contratos completos de VDF: verificação de null, tipos de resultado, dimensionamento de buffer e tratamento de erros.

Passo 4: Criando Tipos Personalizados

Tipos personalizados permitem definir novos tipos de coluna — como COMPLEX, UUID ou VECTOR — que funcionam com ORDER BY, índices e funções de agregação. Se sua extensão registra apenas funções, pule para o Passo 5. Consulte Tipos Personalizados em C++ para o passo a passo completo da implementação. Se seu tipo recebe parâmetros (ex.: VECTOR(1536)), consulte Tipos Parametrizados.

Passo 5: Atualize a Configuração de Compilação

Edite CMakeLists.txt para compilar sua extensão como um arquivo VEB:
Notas de configuração:
  • VillageSQLExtensionFramework fornece helpers do CMake para compilar extensões
  • VEF_CREATE_VEB() empacota sua biblioteca, manifesto e metadados em um arquivo .veb
  • O framework detecta automaticamente as flags de compilação do MySQL/VillageSQL
  • O nome do target da biblioteca é normalmente extension (pode ser qualquer um)
  • O nome do VEB deve corresponder ao nome no seu manifest.json
  • Por padrão, extensões são compiladas com os cabeçalhos da ABI estável. Defina -DVSQL_USE_DEV_ABI=ON para compilar com os cabeçalhos instáveis da dev ABI

Passo 6: Crie um Diretório de Compilação

Crie um diretório de compilação separado:

Passo 7: Compile com CMake e Make

Configure e compile sua extensão:
Isso cria:
  • Biblioteca compartilhada compilada (arquivo .so)
  • Pacote VEB (arquivo .veb) - um arquivo tar contendo manifesto e biblioteca

Verifique a Compilação

Verifique o conteúdo do seu arquivo VEB:
Você deverá ver:

Passo 8: Instale e Teste

Opção A: Instalar no Diretório de Extensões do VillageSQL

Use o target de instalação para copiar o VEB para sua instalação do VillageSQL:
Isso copia o arquivo .veb para o diretório configurado via VillageSQL_VEB_INSTALL_DIR.

Opção B: Instalação Manual

Copie o arquivo VEB manualmente:

Teste Sua Extensão

  1. Conecte-se ao VillageSQL:
  2. Instale a extensão:
  3. Verifique a instalação:
  4. Teste suas funções:

Criando Testes

Adicione arquivos de teste para validar que sua extensão funciona corretamente:
  1. Crie um arquivo de teste em mysql-test/t/:
  2. Gere os resultados esperados:
  3. Execute os testes:

Solução de Problemas

A Extensão Não Carrega

Verifique o log de erros e confira o conteúdo do VEB:

Função Não Encontrada

Verifique a instalação e o registro:

Erros de Compilação

Extensões de Exemplo

Aprenda com extensões existentes do VillageSQL:

vsql_complex

Implementação do tipo de dado de número complexo

vsql_extension_template

Template mínimo para criar extensões

Próximos Passos

Instalando Extensões

Aprenda a instalar e gerenciar extensões

Guia de Desenvolvimento

Tipos de argumento e de retorno, agregados, variáveis de sistema e testes

Arquitetura de Extensões

Ciclo de vida, cache, desempenho e modelo de segurança

Compilar a Partir do Código-Fonte

Compile o VillageSQL a partir do código-fonte

Recursos