Skip to main content
Esta página é a referência dos construtores de operações de tipo em C++. Para a introdução em nível de tutorial aos tipos personalizados, consulte Tipos Personalizados em C++. Os tipos personalizados exigem três operações que o mecanismo chama internamente: encode (string para binário), decode (binário para string) e compare. O hash é opcional. Implemente-as de acordo com estas assinaturas em C++ (todas disponíveis via <villagesql/vsql.h>):

Tipos de Comprimento Fixo

Omitir uma função de hash personalizada faz com que o hash recaia sobre os bytes brutos armazenados do tipo. Isso não é seguro para um tipo cuja função de encode possa produzir mais de uma representação binária de um mesmo valor lógico — o caso comum é um campo de ponto flutuante, em que -0.0 e +0.0 são iguais na comparação mas diferem bit a bit, de modo que GROUP BY e COUNT(DISTINCT) os tratariam como diferentes. Registre essas operações usando vsql::make_type<kTypeName>(). O nome do tipo é passado como um parâmetro de template que não é de tipo (NTTP) — um array static constexpr const char[]. O construtor gera automaticamente nomes de VDF no formato TYPE::method (por exemplo, "MYTYPE::from_string") a partir desse NTTP, de modo que nenhuma correspondência manual de strings seja necessária. Passe o objeto de tipo construído para .type() no construtor da extensão; chamadas .func() separadas para operações de tipo não são necessárias.
O nome do tipo deve ser uma variável static constexpr const char[] — um literal de string não pode ser usado como parâmetro de template que não é de tipo. Passar "MYTYPE" diretamente produz um erro de compilação como:
Declare o nome como um array nomeado, como mostrado abaixo.
build() falha em tempo de compilação se from_string, to_string ou compare estiver ausente. Cada método de template verifica a assinatura do pointer de função via static_assert. Um tipo personalizado pode declarar no máximo 65532 bytes de armazenamento por valor. Uma coluna personalizada é sustentada por um campo VARBINARY, e o orçamento de 65535 bytes por linha do MySQL também paga pelos dois bytes de comprimento desse campo e pelo byte de nulo da linha; como a decisão de uma coluna ser nullable é tomada por coluna em CREATE TABLE, e nullable é o padrão, o limite precisa deixar espaço para o byte de nulo. Tanto persisted_length quanto max_persisted_length são verificados contra ele, e a verificação ocorre em INSTALL EXTENSION em vez de em CREATE TABLE, de modo que um tipo largo demais é rejeitado junto com a extensão inteira:
Ficar abaixo do limite não garante que uma coluna caiba. Todas as demais colunas da tabela usam o mesmo orçamento de 65535 bytes por linha, que somente CREATE TABLE pode avaliar, portanto um tipo no limite ou perto dele ainda pode falhar lá com ER_TOO_BIG_ROWSIZE. Um VARBINARY simples da mesma largura se comporta da mesma forma.

Padrão Intrínseco

Quando uma coluna de tipo personalizado NOT NULL recebe NULL no modo IGNORE (por exemplo, INSERT IGNORE ou UPDATE IGNORE), o servidor chama o padrão intrínseco para produzir um valor alternativo em vez de gerar um erro. O padrão intrínseco fornece uma representação em string; o servidor a converte para binário usando a função from_string do tipo.
Se você omitir tanto .intrinsic_default_str() quanto .intrinsic_default_vdf(), o servidor chama from_string("") como alternativa. Isso acontece quando o tipo é usado pela primeira vez (na criação da tabela), não em INSTALL EXTENSION. Se a sua função de encode rejeitar a string vazia (ou codificá-la para o número errado de bytes), a inicialização do tipo falha com um erro visível no cliente SQL:
Para tipos de comprimento fixo, a string padrão deve codificar para exatamente persisted_length bytes. Defina um padrão explícito para qualquer tipo em que a string vazia não seja uma entrada válida.
Forneça o padrão intrínseco de uma de duas formas. As duas são mutuamente exclusivas: declarar tanto .intrinsic_default_str() quanto .intrinsic_default_vdf() é um erro de compilação. Literal de string: .intrinsic_default_str() Para um padrão constante, passe a string diretamente no construtor do tipo (como mostrado no exemplo de comprimento fixo acima, com .intrinsic_default_str("0")). Baseado em VDF: .intrinsic_default_vdf() + make_intrinsic_default Quando o valor padrão depende dos parâmetros do tipo, implemente uma função de acordo com uma destas assinaturas (disponíveis via <villagesql/vsql.h>):
Mudança incompatível: IntrinsicDefaultFunc e IntrinsicDefaultWithParamsFunc retornam std::string em vez de const char*. Atualize quaisquer implementações de padrão intrínseco existentes para retornar std::string diretamente.
Retorne uma representação std::string do valor padrão. Em caso de erro, escreva uma mensagem em error_msg e retorne qualquer valor (o SDK verifica error_msg[0] != '\0' para detectar erros). Registre com make_intrinsic_default<&fn>("vdf_name") (um argumento: o nome da VDF) e referencie esse nome no construtor do tipo com .intrinsic_default_vdf(). O exemplo de tipos parametrizados abaixo mostra o padrão de registro completo.

Tipos Parametrizados

Os tipos parametrizados precisam dos parâmetros declarados da coluna nos momentos de encode, decode, compare e hash para determinar os tamanhos de alocação e o layout. Defina uma struct de parâmetros com uma função de parse e uma função inversa to_strings, registre ambas no construtor do tipo com .params<P, &ParseFunc, &ToStringsFunc>(), e use const P& como o primeiro argumento das suas funções de operação de tipo. O SDK armazena em cache o resultado do parse por combinação única de parâmetros, de modo que a função de parse é executada no máximo uma vez por instanciação de tipo. A função to_strings é a inversa de parse: ela escreve um P tipado de volta na forma canônica de string chave/valor para que o servidor possa publicar os parâmetros inferidos no mesmo formato que parse consome.
Registre .params<>() no construtor do tipo. Use .int_to_params<&mytype_int_to_params_fn>() para tratar a sintaxe de inteiro MYTYPE(N) e .resolve_params<&mytype_resolve_params_fn>() para validar os parâmetros e calcular os tamanhos de armazenamento. Chame .max_persisted_length(N) com um limite superior do tamanho persistido em bytes em todas as parametrizações válidas; o servidor usa isso apenas no caminho de inferência de parâmetros de tipo, onde ele ainda não inferiu os parâmetros e, portanto, não pode consultar resolve_params para dimensionar o buffer de encode. Para um padrão intrínseco baseado em VDF, use .intrinsic_default_vdf() com o nome da VDF e registre a VDF separadamente via make_intrinsic_default<&mytype_default>(). build() exige .resolve_params<>() sempre que .params<>() ou .int_to_params<>() estiver registrado: int_to_params apenas reescreve a forma abreviada MYTYPE(N) em uma string de parâmetros, enquanto resolve_params é o portão que valida os parâmetros e resolve o tamanho de armazenamento. Omiti-lo é um erro de compilação.
.max_persisted_length() requer o VEF Protocol 3 ou superior. Um tipo que o utiliza não pode ser carregado por servidores anteriores ao Protocol 3. Ele é válido apenas para tipos de comprimento variável ou parametrizados; um tipo de comprimento fixo não parametrizado que o define falha ao compilar.
Os tamanhos que resolve_params reporta também são validados no momento do CREATE TABLE, uma vez que os parâmetros de uma coluna sejam conhecidos. Para um tipo de comprimento fixo, o persisted_length resolvido não pode exceder o max_persisted_length declarado: uma parametrização cujo footprint calculado (por exemplo, um dimensionamento dimension * 4) ultrapasse esse limite é rejeitada com um erro de sintaxe. Independentemente do tipo de comprimento, resolve_params deve resolver um max_decode_buffer_length maior que zero, já que esse valor dimensiona o buffer de trabalho do decode; um resultado não positivo é rejeitado com a mesma classe de erro.
As variantes parametrizadas (TypeEncodeWithParamsFunc<P>, TypeDecodeWithParamsFunc<P>, TypeCompareWithParamsFunc<P> e TypeHashWithParamsFunc<P>) juntamente com ParamsToStringsFunc<P> (void fn(const P&, std::map<std::string,std::string>&)) estão disponíveis via <villagesql/vsql.h>. Os métodos de template vsql::make_type detectam o argumento de parâmetros e roteiam através do cache de parâmetros automaticamente. As funções de encode recebem vsql::MaybeParams<P> & como primeiro argumento; is_known() é sempre verdadeiro em tempo de execução, e value() retorna const P&. As variantes de decode, compare e hash recebem vsql::CustomArgWith<P>, cujo acessador params() retorna const P&. Fornecendo parâmetros em SQL. Duas sintaxes chegam a resolve_params:
  • InteiroMYTYPE(N). O servidor roteia N através de int_to_params para construir o mapa de parâmetros. Requer .int_to_params<>().
  • StringMYTYPE('key=value,...'). O servidor normaliza a string e chama resolve_params diretamente; int_to_params não é envolvido. Disponível sempre que .resolve_params<>() estiver registrado — sem chamada extra do construtor.
Um tipo que registra apenas .resolve_params<>() aceita a forma de string e rejeita MYTYPE(N). SHOW CREATE TABLE não repete o que o autor escreveu. O servidor mantém o mapa de parâmetros resolvido e reconstrói uma forma de exibição a partir dele, preferindo MYTYPE(N) quando int_to_params(N) reproduz o mapa inteiro e recorrendo à forma canônica MYTYPE('key=value,...') com as chaves ordenadas. Uma coluna escrita como MYTYPE('dimension=8') portanto é lida de volta como MYTYPE(8).
A string de parâmetros serializada key=value,... que int_to_params produz e resolve_params consome é limitada a VEF_MAX_TYPE_PARAMS_STRING_LEN (1024 bytes). Uma parametrização cuja string canônica exceda esse limite é rejeitada com um erro definido em vez de ser truncada silenciosamente — mantenha os nomes e valores de parâmetros combinados de um único tipo dentro de 1024 bytes.

Reescrevendo parâmetros e fornecendo padrões

resolve_params tem uma segunda sobrecarga, mutável: ela recebe o mapa de parâmetros por referência não constante, de modo que o tipo pode reescrevê-lo — normalmente para preencher padrões que o autor omitiu. Registre-a da mesma forma (.resolve_params<&fn>() aceita qualquer forma; registre apenas uma):
O mapa reescrito torna-se a string de parâmetros canônica que o servidor persiste e SHOW CREATE TABLE imprime, portanto a reescrita deve ser idempotente. Uma declaração simples (MYTYPE, sem comprimento ou parâmetros) agora chama resolve_params com um mapa vazio em vez de ignorá-la, de modo que um tipo que fornece padrões dá a cada coluna parâmetros explícitos — o BITFIELD de vsql_bitfield_test resolve uma coluna simples para max_number_of_bits=4096:

Tipos de Comprimento Variável

Um tipo personalizado de comprimento variável decide seu tamanho persistido por valor, em vez de usar um único footprint fixo. Declare um chamando .variable_length_type() no construtor do tipo, o que define a flag variable_length do tipo.
.variable_length_type() eleva o protocol exigido pelo tipo para o VEF Protocol 4. O servidor lê a flag variable_length apenas no protocol 4 ou superior. Compile com os cabeçalhos opcionais da dev ABI (-DVSQL_USE_DEV_ABI=ON); servidores mais antigos não leem a flag.
Os tipos de comprimento variável também devem chamar .max_persisted_length(N). build() falha em tempo de compilação se ele for omitido — o servidor precisa do limite superior para alocar um buffer para o campo de suporte. Um tipo de comprimento variável não deve também chamar .persisted_length(): seu tamanho é decidido por valor, portanto build() rejeita a combinação em tempo de compilação. .variable_length_type() é monotônico: chamá-lo antes ou depois dos setters do Protocol 3 (max_persisted_length(), params(), int_to_params()) não reduz a exigência de protocol de volta para abaixo do Protocol 4.
Como todo tipo personalizado, um tipo de comprimento variável deve produzir um padrão intrínseco utilizável. O padrão é codificado dentro da capacidade máxima do campo, e qualquer resultado não vazio de 1 a max_persisted_length bytes é aceito. Um tipo cujo encode de string vazia produz zero bytes (um array ou conjunto de bits vazio, digamos) não tem um padrão utilizável, portanto declare um explícito que codifique para um valor não vazio:
Caso contrário, o tipo falha ao inicializar na primeira vez que uma coluna NOT NULL o referencia, em CREATE TABLE — o mesmo que um tipo de comprimento fixo que não consegue codificar from_string("").

Tipos Personalizados em Stored Procedures

Tipos personalizados de extensão podem ser usados como tipos de parâmetro de stored procedure e em declarações de variáveis DECLARE. O servidor resolve o tipo personalizado no momento da execução da rotina usando os metadados de tipo da extensão instalada.

Veja Também