Estendendo o MariaDB - Databases - Software - Computers

Estendendo o MariaDB

Índice

MySQL Internals
Threads MySQL
Pacotes de Teste do MariaDB
Adicionando Novas Funções ao MySQL
Sintaxe CREATE FUNCTION/DROP FUNCTION
Adicionando Novas Funções Definidas Por Usuário
Adicionando uma Nova Função Nativa
Adicionado Novos Procedimentos ao MySQL
Análise de Procedimento
Escrevendo um Procedimento

MySQL Internals

Threads MySQL
Pacotes de Teste do MariaDB

Este descreve várias coisas que você precisa saber ao trabalhar no código do MariaDB. Se você planeja contribuir com o desenvolvimento do MariaDB, quiser ter acesso ao código entre versões, ou apenas deseja acompanhar o desenvolvimento, siga as instruções em 'Instalando pela árvore de fontes do desenvolvimento'. Se você está interessada nos MariaDB internals, você também deve se inscrever na nossa lista de emails internals. Esta lista é relativamente de baixo tráfico. Para detalhes de como se inscrever, por favor veja 'As Listas de Discussão do MariaDB'. Todos os desenvolvedores na MariaDB Foundation estão na lista internals e nós ajudamos outras pessoal que estão trabalhando no código MariaDB. Esteja a vontade de utilizar esta tanto para perguntas sobre o código qunato para enviar patches com os auqis você gostaria de contribui no projeto MySQL!

Threads MariaDB

O servidor MariaDB cria as seguintes threads:

mysqladmin processlist mostra apenas a thread da conexão, do INSERT DELAYED, e da replicação.

Pacotes de Teste do MariaDB

Executando o Pacote de Testes do MariaDB
Extendendo o Pacote de Teste do MariaDB
Relatando Bugs no Pacote de Teste do MariaDB

Até pouco tempo, o nosso principal pacote de teste com cobertura total era baseado em dados proprietários de clientes e por esta razão não era disponível publicamente. A única parte disponível publicamente de nosso processo de teste consistia de um teste crash-me, um benchamrk Perl DBI/DBD encontrado no diretório sql-bench e testes variadaos localizadaos no diretório tests. A falta de um um pacote de teste padronizado disponível publicamente tem criado dificuldade para nosso usuários e para nossos desenvolvedores de fazer teste de regressão no código do MariaDB. Para resolver este problema, nós criamos um novo sistema de teste que é incluído nas distribuições fonte e binária do Unix a partir da versão 3.23.29. Os testes podem ser executados no Unix ou no Windows usando um ambiente Cygwin. Eles não podem ser executados em um ambiente Windows nativo.

O conjunto de testes de atual não testa tudo no MySQL, mas deve pegar os bugs mais óbvios no código de processamento SQL, detalhes de SO/biblioteca, e é bem compleo em teste de replicações. Nosso objetivo eventual é ter os testes cobrindo 100% do código. Contibuições para o nosso pacote de teste são benvindas. Você pode desejar contribuir com testes que examinam a funcionalidade critica ao seu sistema, o que irá assegurar que todas as futuras versões do MariaDB irão funcionar bem com suas aplicações.

Executando o Pacote de Testes do MariaDB

O sistema de teste consiste de um interpretador de linguagem de teste (mysqltest), um script shell para executar todos os testes (mysql-test-run), os casos de teste atual escritos em uma linguagem de teste especial e seus resultados esperados. Para executar o pacote de teste em seu sistema depois de uma construção, digite make test ou mysql-test/mysql-test-run da raiz do fonte. Se você tiver uma distribuição binária instalada, digite cd para a raíz de instalação. (ex. /usr/local/mysql), e faça scripts/mysql-test-run. Todos os testes devem dar certo. Se não, você deve tentar encontrar o porque e relatar o problema se este é um bug n MySQL. Leia Seção 14.1.2.3, 'Relatando Bugs no Pacote de Teste do MariaDB'.

Se você tiver uma cópia de mysqld executando ná máquina onde você deseja executar o teste, você não tem de pará-lo, desde que não esteja usando as portas 9306 e 9307. Se uma destas portas forem tomadas, você deve editar mysql-test-run e alterar os valores da porta do master e/ou slave para uma disponível.

Você pode executar um cado de teste individual com mysql-test/mysql-test-run test_name.

Se um teste falhar, você de testar executando mysql-test-run com a opção --force para verificar se nenhum outro teste falhou.

Extendendo o Pacote de Teste do MariaDB

Você pode utilizar a linguagem mysqltest para escrever o seu próprio caso de teste. Infelizmente nós ainda não escrevemos a documentação completa para ela. Você pode, no entanto, olhar os nosso casos de teste atuais e usá-los como um exemplo. O seguintes pontos devem ajudá-lo a começar:

Relatando Bugs no Pacote de Teste do MariaDB

Se a sua versão não passar no pacote de teste você deve fazer o seguinte:

Adicionando Novas Funções ao MariaDB

Sintaxe CREATE FUNCTION/DROP FUNCTION
Adicionando Novas Funções Definidas Por Usuário
Adicionando uma Nova Função Nativa

Existem dois modos de se adicionar novas funções ao MySQL:

Cada método tem suas vantagens e desvantagens:

Seja qual for o método que você utilizou para adicionar novas funções, eles podem ser usados como funções nativas tais como ABS() ou SOUNDEX().

Sintaxe CREATE FUNCTION/DROP FUNCTION

CREATE [AGGREGATE] FUNCTION nome_função RETURNS {STRING|REAL|INTEGER}
 SONAME nome_bibliot_compartilhada DROP FUNCTION function_name

Uma função definida pelo usuário (user-definable function - UDF) é um modo de extender o MariaDB com uma nova função que funciona como funções nativas do MariaDB tais como ABS() e CONCAT().

AGGREGATE é uma nova opção do MariaDB Versão 3.23. Uma função AGGREGATE funciona exatamente como uma função GROUP nativa do MariaDB como SUM ou COUNT().

CREATE FUNCTION salva o nome e o tipo da função e o nome da biblioteca compartilhada na tabela do sistema mysql.func. Você deve ter privilégios INSERT e DELETE no banco de dados MariaDB para criar e deletar funções.

Todas as funções ativas são recarregadas a cada vez que o servidor é reiniciado, a menos que você reinicie o mysqld com a opção --skip-grant-tables. Neste caso, a inicialização de UDF é ignorada e as UDFs estão indisponíveis. (Uma função ativa é aquela que foi carregada com CREATE FUNCTION e não foi removida com DROP FUNCTION.)

Para instruções sobre como escrever funções denidas por usuários, veja Seção 14.2, 'Adicionando Novas Funções ao MySQL'. Para o mecanisnmo UDF funcionar, as funções dever ser escritas em C ou C++, seu sistema operacional deve suporta carregamento dinâmico e você deve compilar o mysqld dinamicamente (e não estaticamente).

Note que para fazer AGGREGATE funcioanr, você deve ter uma tabela mysql.func que contém a coluna type. Se você não tem esta tabela, você deve executar o script mysql_fix_privilege_tables para criá-la.

Adicionando Novas Funções Definidas Por Usuário

Sequência de Chamadas UDF para Funções Simples
Sequência de Chamadas UDF para Funções Agregadas
Processando Argumentos
Valor de Retorno e Tartamento de Erros
Compilando e Instalando Funções Definidas Por Usuário

Para o mecanismo UDF funcionar, as funções devem estar em C ou C++ e o seu sistema operacional deve suporta carregamento dinâmico. A distribuição fonte do MariaDB inclui um arquivo sql/udf_example.cc que definem 5 novas funções. Consulte este arquivo para ver como a convenção de chamadas UDF funciona.

Para o mysqld estar apto a usar funções UDF, você deve configurar o MariaDB com --with-mysqld-ldflags=-rdynamic. A razão é que para muitas plataformas (incluindo Linux) você pode carregar uma biblioteca (com dlopen()) de um programa ligado estaticamente, que você teria se estivesse usando --with-mysqld-ldflags=-all-static. Se você quiser usar uma UDF que precisa acessar símbolos do mysqld (como o exemplo metaphone em sql/udf_example.cc que usa default_charset_info), você deve ligar o programa com -rdynamic (veja man dlopen).

Se você estiver usando uma versão precompilada do servidor, use o MySQL-Max, que suporta carregamento dinâmico.

Para cada função que você deseja usar nas instruções SQL, você deve definir funções C (ou C++) correspondente. Na discussão abaixo, o nome xxx é usado um nome de função exemplo. Para distinguir entre o uso de SQL e C/C++, XXX() (maiúscula) indica a chamada da função SQL e xxx() (minúscula) indica da chamada da função C/C++.

Aa funções C/C++ que você escreve para implemmentar a interface para XXX() são:

Quando uma instrução SQL invoka XXX(), o MariaDB chama a função de inicialização xxx_init() para realizar qualquer configuração necessária, tais como verificação de argumentos e alocação de memória. Se xxx_init() retorna um erro, a instrução SQL é abortada com uma mensagem e as funções principais e de finalização não são chamadas. Senão, a função principal xxx() é chamada uma vez para cada linha. Depois de todas as linhas tiverem sido processadas, a função de finalização xxx_deinit() é chamada, podendo assim realizar qualquer 'limpeza'.

Para funções agregadas (como SUM()), você também deve fornecer as seguintes funções:

Quando se usa UDF's agregadas o MariaDB funciona da seguinte maneira:

  1. Chama xxx_init() para deixar funções agregadas alocarem a memória necessária para armazenar os resultados.
  2. Ordena a tabela de acordo com a expressão GROUP BY.
  3. Para a primeira linha em um novo grupo, chama a função xxx_reset().
  4. Para cada nova linha que pertence ao mesmo grupo, chame a função xxx_add().
  5. Quando o grupo muda ou depois da última linha ter sido processada, chame xxx() para obter o resultado para o conjunto.
  6. Repita 3-5 até que todas as linhas tenham sido processada.
  7. Chame xxx_deinit() para deixar a UDF liberar a memória alocada.

Todas as funções devem ser seguras com thread (não apenas a função principal, mas também as funções de inicialização e finalização). Isto significa que você não tem permissão para alocar qualquer variável global ou estática que alterou! Se você precisa de memória, você deve alocá-la em xxx_init() e liberá-la em xxx_deinit().

Sequência de Chamadas UDF para Funções Simples

A função principal deve ser declarada como mostrado aqui. Note que o tipo retornado e os parâmetros diferem, dependendo se você irá declarar a função SQL XXX() para retornar STRING, INTEGER, ou REAL na instrução CREATE FUNCTION:

Para funções STRING:

char *xxx(UDF_INIT *initid, UDF_ARGS *args,
 char *result, unsigned long *length,
 char *is_null, char *error);

Para funções INTEGER:

long long xxx(UDF_INIT *initid, UDF_ARGS *args,
 char *is_null, char *error);

Para funções REAL:

double xxx(UDF_INIT *initid, UDF_ARGS *args,
 char *is_null, char *error);

As funções de inicialização e finalização são declaradas desta forma:

my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
void xxx_deinit(UDF_INIT *initid);

O parâmetro initid é passado para todas as três funções. Ela aponta para uma estrutura UDF_INIT que é usada para passar informações entre as funções. Os membros da estrutura UDF_INIT são listados abaixo. A função de inicialização deve estar em todos os menbros que desejam ser alterados. (Para utilizar o padrão para um membro, deixe-o inalterado.):

Sequência de Chamadas UDF para Funções Agregadas

Aqui segue uma descrição das diferentes funções que você precisa definir quando você quer criar uma função UDF agregada.

Note que a seguinte função NÃO é necessária ou usada pelo MariaDB. Você ainda pode manter a definição de sua função se você quiser o seu código funcinonando com o MariaDB e MariaDB

char *xxx_reset(UDF_INIT *initid, UDF_ARGS *args,
 char *is_null, char *error);

Esta função é chamada quando o MariaDB encontra a primiera linha em um novo grupo. Na função você deve zerar quaisquer variáveis sumárias internas e então definir o argumento dados como o primeiro argumento no grupo.

Em muitos casos isto é implementado internamente zerando todas as variáveis (por exemplo, chamando xxx_clear() e então chamando xxx_add().

A seguinte função só é exigida pelo MariaDB e acima:

char *xxx_clear(UDF_INIT *initid, char *is_null, char *error);

Esta função é chamada quando o MariaDB precisa de zerar o resumo dos resultados. Ele será chamado no começo de cada grupo novo mas também pode ser chamado para zerar os valores para uma consulta que não tiver registros coincidentes. is_null será definido para apontar para CHAR(0) antes de chamar xxx_clear().

Você pode usar o ponteiro error para armazenar um byte se alguma coisa der errado.

char *xxx_add(UDF_INIT *initid, UDF_ARGS *args,
 char *is_null, char *error);

Esta função é chamada por todas as linhas que pertencem ao mesmo grupo, exceto na primeira linha. Nesta você deve adicionar o valor em UDF_ARGS a sua variavel sumária interna.

A função xxx() deve ser declarada da mesma forma que você define uam função UDF simples. Leia Seção 14.2.2.1, 'Sequência de Chamadas UDF para Funções Simples'.

A função é chamada quando todas as linhas no grupo tem sido processada. Normamente você nunca deve acessar a variável args aqui mas retornar o seu valor baseado em sua variável sumária interna.

Todos os argumentos processados em xxx_reset() e xxx_add() devem ser feito de forma idêntica as UDF's normais. Leia Seção 14.2.2.3, 'Processando Argumentos'.

O tratamento do valor de retorno em xxx() deve ser feito de forma idêntica a uma UDF normal. Leia Seção 14.2.2.4, 'Valor de Retorno e Tartamento de Erros'.

O argumento ponteiro para is_null e error é o mesmo para todas as chamadas xxx_reset(), xxx_clear(), xxx_add() e xxx(). Você pode utilizar isto para lembrar que você obteve um erro ou se a função xxx() deve retornar NULL. Note que você não deve armazenar uma string em *error! Ela é um parâmetro de apenas 1 byte!

is_null é zerado para cada grupo (antes de chamar xxx_clear()). error nunca é zerado.

Se isnull ou error são definidos depois de xxx() então o MariaDB retornará NULL como o rsultado para a função do grupo.

Processando Argumentos

O parâmetro args aponta para uma estrutura UDF_ARGS que tem os mambros listados abaixo:

Valor de Retorno e Tartamento de Erros

A função de inicialização deve retornar 0 se nenhum erro ocorrer e 1 em outro caso. Se ocorrer um erro, xxx_init() deve armazenar uma mensagem de erro terminada em null no parâmetro message. A mensagem será retornada ao cliente. O buffer de mensagens tem MYSQL_ERRMSG_SIZE caracteres, mas você deve tentar manter a mensagem com menos que 80 caracteres assim ela cabe na tela de terminal padrão.

O valor de retorno de uma função principal xxx() é o valor da função, para funções long long e double. Uma função string deve retornar um ponteiro ao resultado e armazenar o tamanho da string no argumento length.

Definá-os ao conteúdo e tamanho do valor de retorno. Por exemplo:

memcpy(result, 'result string', 13);
*length = 13;

O buffer result que é passado para o cálculo da função é de 255 bytes. Se o seu resultado couber nele, você não terá que se preocupar com alocação de memória para os resultados.

Se a sua função string precisar retornar uma string maior que 255 bytes, você deve alocar o espaço para ela com malloc() em sua função xxx_init() ou sua função xxx() e liberá-la em sua função xxx_deinit(). Você pode armazenar a memória alocada na posição ptr na estrutura UDF_INIT para ser reutilizado por chamadas xxx() futuras. Leia Seção 14.2.2.1, 'Sequência de Chamadas UDF para Funções Simples'.

Para indicar um valor de retorno de NULL na função principal, defina is_null com 1:

*is_null = 1;

Para indicar um erro retornado na função principal, atribua 1 ao parâmetro error:

*error = 1;

Se xxx() definir *error com 1 para qualquer linha, o valor da função é NULL para a linha atual e qualquer linha subsequente processada pela instrução na qual XXX() foi chamado. (xxx() nem mesmo será chamado para linhas subsequentes.) Nota: na versão do MariaDB anterior a 3.22.10, você deve configurar *error e *is_null:

*error = 1;
*is_null = 1;

Compilando e Instalando Funções Definidas Por Usuário

Arquivos implementando UDFs devem ser compilados e instalados na máquina onde o servidor está sendo executado. Este processo é descrito abaixo pelo arquivo UDF exemplo udf_example.cc que é incluído na distribuição fonte do MariaDB. Este arquivo contém as seguintes funções:

A arquivo carregável dinamicamente deve ser compilado como um arquivo objeto compartilhável usando um comando como este:

shell> gcc -shared -o udf_example.so myfunc.cc

Você pode encontrar facilmente as opções de compilador corretas para seu sistema executando este comando no diretório sql da sua árvore de fonte MySQL:

shell> make udf_example.o

Você deve executar comando de compilador similar àquele que o make mostra, exceto que você deve remover a opção -c próxima ao fim da linha e adicionar -o udf_example.so. (Em alguns sistemas você pode precisar deixar o comando -c.)

Uma vez que você tenha compilado um objeto compartilhado contendo UDFs, você deve instalá-lo e avisar o MariaDB sobre ele. Compilar um objeto compartilhado de udf_example.cc produz um arquivo com nome parecido com udf_example.so (o nome exato pode variar de plataforma para plataforma). Copie este arquivo para algum diretório procurado com o ligador dinâmico ld, tal como /usr/lib ou adicione o diretório no qual você colocou o objeto compartilhado ao arquivo de configuração do ligador (e.g. /etc/ld.so.conf).

Em muitos sistemas você pode as variáveis de ambiente LD_LIBRARY ou LD_LIBRARY_PATH para apontar para o diretório onde se encontra os seus arquivos de funções UDF. A página dlopen do manual diz a você quais variáveis você deve utilizar em seu sistema. Você deve configurar isto nos scripts de inicialização mysql.server ou mysqld_safe e reiniciar o mysqld.

Depois da biblioteca ser instalada, notifique mysqld sobre as novas funções com estes comandos:

mysql> CREATE FUNCTION metaphon RETURNS STRING SONAME 'udf_example.so';
mysql> CREATE FUNCTION myfunc_double RETURNS REAL SONAME 'udf_example.so';
mysql> CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME 'udf_example.so';
mysql> CREATE FUNCTION lookup RETURNS STRING SONAME 'udf_example.so';
mysql> CREATE FUNCTION reverse_lookup
 -> RETURNS STRING SONAME 'udf_example.so';
mysql> CREATE AGGREGATE FUNCTION avgcost
 -> RETURNS REAL SONAME 'udf_example.so';

Funções podem ser deletadas utilizando-se DROP FUNCTION:

mysql> DROP FUNCTION metaphon;
mysql> DROP FUNCTION myfunc_double;
mysql> DROP FUNCTION myfunc_int;
mysql> DROP FUNCTION lookup;
mysql> DROP FUNCTION reverse_lookup;
mysql> DROP FUNCTION avgcost;

As instruções CREATE FUNCTION e DROP FUNCTION atualizam a tabela de sistema func no banco de dados MariaDB. O nome da função, tipo e biblioteca compartilhada são salvas na tabela. Você deve ter os privilégios INSERT e DELETE para o banco de dados MariaDB para criar e deletar funções.

Você não deve usar CREATE FUNCTION para adicionar uma função que já tenha sido criada. Se você precisar reinstalar uma função, você deve removê-la com DROP FUNCTION e então reinstalá-la com CREATE FUNCTION. Você precisaria fazer isto, por exemplo, se você recompilar uma nova versão da sua função, assim o mysqld obtem a nova versão. Por outro lado, o servidor continuará a utilizar a versão antiga.

Funções ativas são recarregadas a cada vez que o servidor inicia, a menos que você inicie mysqld com a opção --skip-grant-tables. Neste caso, a a inicialização de UDF é ignorada e as UDFs ficam indisponíveis. Uma função ativa é aquela que carregada com CREATE FUNCTION e não removida com DROP FUNCTION.)

Adicionando uma Nova Função Nativa

O procedimento para adicionar uma nova função nativa é descrito aqui. Note que você não pode adicionar funções nativas a distribuição binária porque o procedimento envolve modificação no código fonte do MariaDB. Você deve compilar o MariaDB de uma distribuição fonte. Note também que se você migrar para outra versão do MariaDB (por exemplo, quando uma nova versão é liberada), você precisará repetir o procedimento com a nova versão.

Para adicionar uma função MariaDB nativa, siga estes passos:

  1. Adicionr uma linha a lex.h que defina o nome da função no vetor sql_functions[].
  2. Na função protótipo é simples (utilize apenas zero, um, dois ou três argumentos), você deve especificar SYM(FUNC_ARG#) em lex.h (onde # é o número de argumentos) como o segundo argumento no vetor sql_functions[] e adicionar uma função que cria um objeto de função em item_create.cc. De uma olhada em 'ABS' e create_funcs_abs() para um exemplo disto.

    Se o protótipo da função for complicado (por exemplo, tiver um número variável de argumentos), você deve adicionar duas linhas a sql_yacc.yy. Uma indica o símbolo pre-processador que o yacc deve difinir (isto deve ser adicionado no começo do arquivo). Então defina os parâmetros da função e adicione um item com estes parâmetros a regra simple_expr do analizador. Por exemplo, verifique todas as acorrências de ATAN em sql_yacc.yy para ver como ele é feito.

  3. Em item_func.h, declare uma classe herdada de Item_num_func ou Item_str_func, dependendo se sua função retorna um número ou uma string.
  4. Em item_func.cc, adicione uma das seguintes declarações, dependendo se você está definindo uma função numérica ou string:

    double Item_func_newname::val()
    longlong Item_func_newname::val_int()
    String *Item_func_newname::Str(String *str)
    

    Se você herdar seu objeto de qualquer um dos itens padrões (como Item_num_func), você provavelmente só deverá definir uma das funções acima e deixar os objetos pais cuidar das outras funções. Por exemplo, a classe Item_str_func define uma função val() que executa atof() no valor retornado por ::str().

  5. Você também deve, provavelmente, definir a seguinte função objeto:

    void Item_func_newname::fix_length_and_dec()
    

    Esta função deve pelo menos calcular max_length baseado nos argumentos dados. max_length é o número máximo de caracteres que a função pode retornar. Esta função também deve definir maybe_null = 0 se a função principal não puder retornar um valor NULL. A função pode verificar se algum dos argumentos da função pode retornar NULL verificando a variável de argumentos maybe_null. Você pode dar uma olhada em Item_func_mod::fix_length_and_dec para um exemplo típico de como fazer isto.

Todas as funções devem ser seguras com thread (em outras palavras, não utilize qualquer variável global ou estática nas funções sem protege-las com mutexes).

Se você retornar NULL, de ::val(), ::val_int() ou ::str() você deve definir null_value com 1 e retornar 0.

Para funções objetos ::str(), existem algumas considerações adicionais das quais você deve estar ciente:

Adicionado Novos Procedimentos ao MariaDB

Análise de Procedimento
Escrevendo um Procedimento

No MySQL, você pode definir um procedimento em C++ que pode acessar e modificar os dados em uma consulta antes que ela seja enviada ao cliente. A modificação pode ser feita linha a linha ou a nivel GROUP BY.

Nós criamos um procedimento exemplo no MariaDB Versão 3.23 para mostrar o que pode ser feito.

Adicionalmente recomendamos que você de uma olhada em mylua. Com isto você pode utilizar a linguagem LUA para carregar um procedimento em tempo de execução no mysqld.

Análise de Procedimento

analyse([max elements,[max memory]])

Este procedimento é definido em sql/sql_analyse.cc. Ele examina o resultado de sua consulta e retorna uma análise do resultado:

SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max elements,[max memory]])

Escrevendo um Procedimento

No momento, a única documentação sobre isto é o código fonte.

Você pode encontrar todas as informações sobre procedimentos examinando os seguintes arquivos:



Anterior Próximo
Tratamento de Erros no MySQL Início Problemas e Erros Comuns