Skip to main content
As capabilities Preview são recursos fornecidos pelo servidor e expostos às extensões antes que suas APIs sejam finalizadas. Uma extensão que declara uma capability Preview requer vsql_allow_preview_extensions = ON para instalar (consulte Habilitando o Nível Preview) — as extensões que não usam capabilities Preview instalam normalmente, independentemente dessa configuração.
As APIs das capabilities Preview não são estáveis. Uma extensão compilada contra uma capability Preview pode falhar ao carregar após uma atualização do servidor. Quando uma capability se estabiliza, seu cabeçalho é movido para um caminho versionado do SDK C++ estável.

Habilitando o Nível Preview

Defina vsql_allow_preview_extensions = ON com SET PERSIST antes de instalar qualquer extensão que use uma capability Preview:
SET GLOBAL é rejeitado para essa variável — o servidor requer SET PERSIST para que a configuração sobreviva à reinicialização. As extensões com capabilities Preview são carregadas na inicialização, então a variável deve estar configurada como ON quando o servidor iniciar. Se você estiver iniciando o mysqld diretamente (por exemplo, a partir de um script de instalação que inicia o servidor pela primeira vez), passe a flag na linha de comando — o mysqld-auto.cnf ainda não existirá para carregar o valor persistido:
Para desabilitar:
Isso falha se qualquer extensão que usa uma capability Preview estiver instalada no momento. Desinstale essas extensões primeiro e então desative a configuração.

Índice de Capabilities

Padrão de Registro

Para usar uma capability Preview, declare um objeto de capability por valor no escopo do arquivo e passe-o por referência para .with() dentro de make_extension(). O servidor popula o pointer abi do objeto durante o registro:
.with(capability) informa ao servidor quais capabilities a extensão requer. Se vsql_allow_preview_extensions estiver em OFF quando a extensão for instalada, o servidor rejeita a instalação com um erro que nomeia a capability.
Todo objeto de capability declarado em uma extensão deve ser passado para .with() exatamente uma vez. No momento do carregamento, o servidor verifica cada instância de capability declarada em relação ao que .with() recebeu e faz o INSTALL EXTENSION falhar se a regra for violada:
  • Declarada, mas nunca passada para .with(): capability '<Type>' was declared but never passed to .with(); every CapabilityBase-derived static must be registered via .with(cap) in the extension builder
  • Mesma instância passada para .with() mais de uma vez: capability '<Type>' passed to .with() more than once
  • Objeto passado para .with() não é uma capability: .with() received an object that does not inherit vsql::detail::CapabilityBase; not a registered capability
O erro completo aparece como: Failed to load VEF extension '<name>': vef_register returned an error: <message above>.

Acesso ao Keyring

A capability keyring (vsql::preview::keyring) permite que as extensões leiam e escrevam segredos armazenados no componente keyring do MySQL. As extensões a usam para coisas como chaves de API, chaves de criptografia ou outros segredos que não deveriam ficar em tabelas SQL. O nome da capability VEF_PREVIEW_KEYRING_NAME é "vsql::preview::keyring". Um componente keyring deve estar instalado no servidor MySQL para que leituras e escritas tenham sucesso. Sem um, as operações retornam KeyringCapability::Status::UNAVAILABLE.

Valores de Status

KeyringCapability::Status é um enum com escopo retornado por read() (dentro de ReadResult) e por write():

Declarando a Capability

Inclua o cabeçalho, declare um objeto de capability no escopo do arquivo e passe-o para .with():
O objeto g_keyring é populado pelo servidor no momento do carregamento. Os métodos read() e write() retornam Status::UNAVAILABLE em tempo de execução quando nenhum componente keyring está instalado — verifique esse status em cada chamada em vez de depender de uma verificação de disponibilidade separada.

Lendo e Escrevendo

data_id é o identificador da chave. auth_id é o usuário proprietário — passe uma string vazia (ou omita-o em read, que assume {} como padrão) para ler ou escrever chaves internas não associadas a um usuário específico. read retorna um ReadResult por valor. Vincule-o com structured bindings:
Em qualquer status diferente de Status::OK, value fica vazio. write retorna Status diretamente e armazena data sob data_id / auth_id.

Exemplo Completo

Esta é uma versão simplificada da extensão de teste vsql_keyring_reader que é distribuída com o servidor. Ela registra 2 VDFs: keyring_read e keyring_store.

Variáveis de Status

A capability status_var (vsql::preview::status_var) permite que uma extensão exponha contadores long long e double como variáveis de status do MySQL. A extensão é dona do armazenamento e escreve nele; o servidor lê através dos pointers cada vez que a variável de status é consultada. Construa a capability com vsql::preview_status_var::make_capability(), passando uma lista entre chaves de descritores de make_int(name, value_ptr) ou make_double(name, value_ptr). O template deduz a contagem a partir da lista entre chaves, então nenhum tamanho explícito é necessário.

Exemplo Completo

make_int requer um long long *; make_double requer um double *. Esses são os únicos dois tipos compatíveis.

Acessando a partir do SQL

Após INSTALL EXTENSION my_ext, a variável fica visível com o nome da extensão como prefixo:
Incrementos concorrentes de múltiplas threads de consulta usando um ++ não-atômico podem ocasionalmente ser perdidos; isso é aceitável para contadores aproximados de chamadas expostos via SHOW STATUS.

Variáveis de Sistema

A capability sys_var (vsql::preview::sys_var) permite que uma extensão registre variáveis de sistema do MySQL respaldadas por armazenamento de propriedade da extensão. Há suporte para três tipos: BOOL (bool *), INT (long long *) e STR (char **). Os descritores INT também carregam limites min_val e max_val; todos os descritores carregam um valor padrão e um comentário. Construa a capability com vsql::preview_sys_var::make_capability() e as funções de fábrica correspondentes make_bool, make_int e make_str. O objeto de capability também expõe get() e set() para acesso programático a partir do código da extensão. Ambos retornam false em caso de sucesso. Para reagir a mudanças de valor, encadeie .on_change<&fn>() em um descritor. O callback recebe um sv::SysVarChange com var_name() e acessadores tipados (as_int(), as_real(), as_str()). O objeto de capability deve ter duração de armazenamento estática. O MySQL escreve diretamente nos pointers de armazenamento quando o usuário define uma variável.

Exemplo Completo

Acessando a partir do SQL

Após INSTALL EXTENSION my_ext, as variáveis ficam acessíveis usando o nome da extensão como prefixo de componente:

Lendo e Escrevendo a partir do Código da Extensão

Para variáveis INT e BOOL, leia o pointer de armazenamento global diretamente — o MySQL atualiza esses valores atomicamente. Para atualizar uma variável através do MySQL (de forma que travamento, validação de intervalo e persistência sejam tratados pelo servidor), chame SYS_VARS.set(extension_name, var_name, scope, value). Tanto set quanto get retornam false em caso de sucesso.
O argumento scope controla a persistência:

Thread de Trabalho

A capability thread worker (vsql::preview::thread_worker) permite que uma extensão execute uma thread em segundo plano conduzida pelo servidor. A thread é iniciada e parada através de uma variável de sistema de controle que o servidor registra no carregamento da extensão; o servidor invoca a função de trabalho da extensão em um temporizador periódico, na prontidão de um descritor de arquivo, ou em resposta a eventos de habilitação/desabilitação. O nome da capability VEF_PREVIEW_THREAD_WORKER_NAME é "vsql::preview::thread_worker".

Declarando a Capability

Inclua o cabeçalho, declare um ThreadWorkerCapability instanciado sobre a sua função de trabalho no escopo do arquivo e passe-o para .with():
A função de trabalho é fornecida como um argumento de template não-tipo (ThreadWorkerCapability<&my_work>), portanto deve ser uma função com a assinatura mostrada abaixo. O primeiro argumento do construtor é o sufixo do nome da thread; o segundo argumento, opcional, sobrescreve o nome da variável de sistema de controle.

Assinatura da Função de Trabalho

reason indica por que o servidor chamou a função. thread é o handle de propriedade do servidor para este worker (NULL na chamada inicial de VEF_WAKEUP_ENABLE — veja abaixo). arg é o pointer opaco registrado no descritor; ele é repassado inalterado.

Ciclo de Vida do Wakeup

O servidor chama a função de trabalho com um de quatro motivos: O parâmetro thread é NULL quando o motivo é VEF_WAKEUP_ENABLE, porque o handle da thread ainda não existe nesse ponto. Para os outros três motivos, thread é não-nulo.

Valor de Retorno do Wakeup

A função de trabalho retorna um vef_next_wakeup_t para atualizar a configuração do próximo wakeup. Um valor zero em qualquer um dos campos significa “manter a configuração atual” — retorne uma struct inicializada por valor (return {};) para deixar ambos inalterados. Para definir um novo descritor de arquivo de poll, retorne seu valor (deve ser maior que zero). Para limpar um descritor de arquivo de poll existente, retorne -1 em poll_fd. O valor de retorno é ignorado quando o motivo é VEF_WAKEUP_DISABLE.

Nome da Thread e Variável de Controle

Dois campos no descritor controlam a nomenclatura:
  • suffix — o sufixo do nome da thread. O servidor antepõe o nome da extensão, produzindo nomes de thread como my_ext/monitor.
  • var_name — opcional. Quando não-nulo, o servidor registra exatamente este nome como a variável de sistema de controle. Quando nulo, o servidor usa o padrão {suffix}_enabled.
A variável de controle é uma variável de sistema registrada pelo servidor. Habilite o worker com SET GLOBAL {suffix}_enabled = ON; defina-a como OFF para pará-lo.

Exemplo Completo

Uma extensão mínima com um único worker periódico que incrementa um contador de heartbeat a cada tique do temporizador.
Com essa extensão instalada (e vsql_allow_preview_extensions = ON), o servidor registra uma variável de sistema heartbeat_enabled. Habilite o worker com:

Consulta SQL

A capability sql_query (vsql::preview::sql_query) permite que uma extensão execute instruções SQL a partir de uma thread em segundo plano. As consultas são executadas dentro do servidor através da vtable da capability — as extensões não são vinculadas a nenhuma biblioteca cliente do MySQL. O nome da capability VEF_PREVIEW_SQL_QUERY_NAME é "vsql::preview::sql_query".
Uma sessão SQL deve ser aberta a partir de um callback de thread worker usando o vef_thread_handle_t * daquele callback. open() não é válido a partir de VDFs ou de threads arbitrárias criadas pela extensão — ele requer o contexto de sessão do worker.

Declarando a Capability

Inclua o cabeçalho, declare um SqlQueryCapability no escopo do arquivo e passe-o para .with(). Ele é tipicamente registrado junto de um ThreadWorkerCapability, já que as sessões são abertas a partir do callback do worker:
g_sql.open(handle) retorna uma Session. Verifique-a com operator bool antes de usá-la; uma Session inválida indica que a vtable da capability não foi vinculada ou que o servidor não conseguiu alocar uma sessão. A Session é move-only e se fecha na destruição.

Executando Consultas

Uma Session produz uma SqlQuery via session.sql(sv). A consulta pode ser executada em dois modos:
  • execute() — executa a instrução e armazena em buffer todo o conjunto de resultados em um Result. Itere as linhas chamando next() no ritmo do chamador.
  • for_each(fn) — executa a instrução e invoca fn uma vez por linha à medida que as linhas são produzidas, sem armazenar em buffer. O Result retornado carrega apenas diagnósticos (nenhuma linha).
Ambos retornam um Result. Um Result não-nulo não significa que a instrução teve sucesso — chame has_error() para descobrir. Em buffer (execute):
column_str() retorna um string_view que é válido apenas até a próxima chamada de next() ou até que o Result seja destruído. Copie-o se um tempo de vida mais longo for necessário. Um string_view com data() == nullptr indica SQL NULL. Em streaming (for_each):
A Row passada para o callback é válida apenas durante a chamada — não armazene referências a ela entre as linhas. O Result retornado por for_each não contém linhas em buffer; next() nele não produzirá dados. Use-o apenas para has_error(), error(), warning_count() e warning(i).

Diagnósticos

Tanto execute() quanto for_each() expõem diagnósticos através do Result retornado. Um diagnóstico é um Diag:
Result expõe:
error() retorna um Diag construído por padrão (errno_ == 0) quando a instrução teve sucesso. warning(i) retorna um Diag construído por padrão quando i >= warning_count(). Os views sqlstate e message apontam para armazenamento de propriedade do Result e se tornam inválidos quando o Result é destruído — copie-os se eles precisarem sobreviver a ele.

Exemplo Completo

Um worker que executa uma consulta em buffer e uma consulta em streaming a cada tique, registrando diagnósticos de ambas:

Armazenamento de Colunas

O column storage permite que uma extensão registre um layout binário personalizado em disco diretamente com o InnoDB para um de seus tipos personalizados, em vez de rotear os bytes do tipo através do payload VARBINARY da linha. Use-o quando o seu tipo precisa de um formato em disco que o VARBINARY não consegue expressar — por exemplo, um array compactado de floats que precisa residir em páginas dedicadas. Este é um recurso de capability: ele habilita novos layouts de armazenamento, não é um botão de ajuste para os existentes.
O column storage é uma ABI Preview — em desenvolvimento ativo e pode mudar entre versões. Atualmente cobre apenas a persistência em nível de linha; a indexação sobre colunas com armazenamento personalizado ainda não está disponível.

Declarando as Capabilities

Duas capabilities Preview trabalham juntas:
  • vsql::preview::storage — abre acesso à infraestrutura de armazenamento do InnoDB (mini-transações, segmentos, páginas). Declare uma StorageCapability no escopo do arquivo.
  • vsql::preview::column_store — vincula uma implementação de armazenamento por tipo a um dos tipos personalizados da extensão. Declare uma ColumnStoreCapability no escopo do arquivo usando make_column_store<Ctx>(TYPE).…build().
Ambas devem ser passadas para .with() em make_extension():
make_column_store<MyCtx>(MY_TYPE) liga a implementação a um tipo personalizado registrado na mesma extensão. Todos os sete slots são obrigatórios no momento do build(), porque cada um mapeia para um ponto distinto no ciclo de vida da coluna que o InnoDB alcançará durante a operação normal.

As Sete Funções de Armazenamento

Toda função recebe storage::Column::StorageCtx<MyCtx>*, cujo acessador user() retorna o estado por coluna da extensão e cujo arena() fornece alocação gerenciada pelo servidor para objetos auxiliares. Toda função retorna false em caso de sucesso e true em caso de erro, escrevendo uma mensagem em error_msg (capacidade error_msg_len) para que as falhas apareçam ao cliente SQL.
mark_delete e purge são distintos porque o MVCC do InnoDB requer que as linhas excluídas permaneçam legíveis por snapshots mais antigos até que o purge seja executado.

Contexto por Coluna e a Arena

O SDK C++ constrói MyCtx por padrão antes de chamar create ou loadctx->user() já está populado quando a sua função é acionada. MyCtx deve ser construível por padrão; o SDK C++ chama T() sem argumentos. Use ctx->user() diretamente para inicializar o estado. Não chame ctx->arena().construct<MyCtx>() — isso aloca uma segunda instância não utilizada e ctx->user() não aponta para ela.
load segue o mesmo padrão — ctx->user() é pré-populado e storage_ref carrega o valor compactado armazenado por ctx->set_ref() em create:
Use ctx->arena() apenas para alocar objetos auxiliares que são grandes demais ou dinâmicos demais para embutir diretamente em MyCtx. O SDK C++ destrói a arena (e chama ~MyCtx()) automaticamente após o retorno de drop, independentemente de drop ter sucesso.

Utilitários de Acesso ao InnoDB

Inclua <villagesql/preview/storage_api.h> para os primitivos do InnoDB. Todas as leituras e escritas de página devem ocorrer dentro de uma mini-transação:
Confirmar a mini-transação libera os latches de página e escreve os registros do redo log que tornam as mudanças duráveis. Segmentos são reservados no momento do create — veja os exemplos de create e load em Contexto por Coluna acima para o padrão completo de configuração. Durante as operações DML, obtenha uma referência de segmento a partir da página raiz para alocar novas páginas:
Páginas são lidas com um latch compartilhado e escritas com um exclusivo. Passe mtr_ref para as chamadas de escrita para que o InnoDB registre a mudança:
Constantes de layout de página: Ler ou escrever dentro das regiões de cabeçalho ou trailer corrompe a página — o InnoDB usa esses intervalos de bytes para sua própria contabilidade e checksum.

Eventos de Instrução

A capability statement event (vsql::preview::statement_event) executa um handler fornecido pela extensão após cada consulta terminar de executar. O servidor invoca o handler sincronamente na própria thread da consulta e passa metadados de execução — o texto da consulta, tempo, contagens de linhas, identidade da conexão e indicadores de qualidade do otimizador. Use-o para registro de consultas lentas, auditoria ou coleta de métricas. O nome da capability VEF_PREVIEW_STATEMENT_EVENT_NAME é "vsql::preview::statement_event".

Declarando a Capability

Declare um StatementEventCapability, instanciado sobre a fase de disparo e a sua função handler, no escopo do arquivo e passe-o para .with():
O primeiro argumento de template é a fase de disparo, um valor vef_statement_event_phase_t. VEF_STATEMENT_EVENT_POSTEXECUTE dispara após uma consulta terminar de executar, em caso de sucesso ou falha, e é a única fase implementada nesta versão. Os outros valores de vef_statement_event_phase_t são reservados; declarar um handler para um deles faz o servidor rejeitar o INSTALL EXTENSION.

Argumentos do Handler

StatementEventArgs é um view somente leitura da consulta concluída; na fase POSTEXECUTE todos os campos estão populados. Acessadores selecionados: Como query() retorna a forma reescrita do servidor quando existe uma, as instruções que carregam credenciais chegam com o segredo ofuscado em vez de em texto claro, correspondendo à forma como os logs geral, lento e binário já os redigem: SET PASSWORD, CREATE/ALTER USER ... IDENTIFIED BY, CHANGE REPLICATION SOURCE ... SOURCE_PASSWORD e CREATE SERVER ... OPTIONS(PASSWORD ...). As instruções sem regra de reescrita são entregues literalmente. Os acessadores de string como query(), sqlstate() e error_message() apontam para armazenamento que é válido apenas durante a chamada do handler — copie os bytes se você precisar deles depois que o handler retornar. StatementEventResult::error_msg(fmt, ...) escreve uma mensagem formatada com printf. Na fase POSTEXECUTE a mensagem é informativa: o servidor a registra, mas não a propaga para o cliente.

Exemplo Completo

Uma forma condensada da extensão de teste vsql_slow_query_log que é distribuída com o servidor. Ela registra cada consulta cujo tempo de execução excede um limiar, combinando a capability statement event com variáveis de sistema para configuração em tempo de execução:

Habilitando a partir do SQL

Com o nível Preview habilitado (consulte Habilitando o Nível Preview), instale a extensão e configure-a através de suas variáveis de sistema:
Cada consulta mais lenta que o limiar é anexada ao arquivo de log configurado: