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
- Sintaxe
- 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:
- A thread da conexão TCP/IP trata todas as requisições de conexão e cria uma nova thread dedicada para tratar a autenticação e consulta SQL processada por cada conexão.
- No Windows NT existe um thread que trata named pipe que fazem o mesmo trabalho que as threads da conexão TCP/IP em pedidos de conexão de named pipe.
- A thread de sinal trata todos os sinais. Esta thread também trata normalmente de alarmes e chamadas
process_alarm()
para forçar um tempo limite em conexões que têm estado parados por um tempo grande. - Se o
mysqld
é compilado com-DUSE_ALARM_THREAD
, uma thread dedicada que trata dos alarmes é criada. Ela só é utilizadas em alguns sistemas onde há problemas comsigwait()
ou se deseja utilizar o códigothr_alarm()
em aplicações sem uma thread dedicada para tratar sianis. - Se é utilizada a opção
--flush_time=#
, uma thread dedicada é criada para descarregar todas as tabelas em um dado intervalo. - Cada conexão tem a sua própria thread.
- Cada tabela diferente na qual é utilizada
INSERT DELAYED
tem sua própria thread. - Se você quiser utilizar
--master-host
, uma thread de replicação slave será iniciada para ler e aplicar atualizações do master.
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:
- Os teste estão localizados em
mysql-test/t/*.test
- Um caso de teste consiste de instruções terminadas em
;
e é similar a entrada do cliente de linha de comandoMariaDB
. Uma instrução por padrão é uma consulta a ser enviada ao servidor MySQL, a menos que ele seja reconhecido como um comando insterno (ex.sleep
). - Todas as consultas que produzem resultados¯ex.,
SELECT
,SHOW
,EXPLAIN
, etc., devem ser precedidas com@/path/to/result/file
. O arquivo deve conter os resultados esperados. Um modo fácil de gerar o arquivo resultante é executarmysqltest -r < t/test-case-name.test
do diretóriomysql-test
, e então editar o arquivo resultante gerado e, se necessário, ajustá-los a saída esperada. Neste caso, tenha cuidado de não adicionar ou deletar quaisquer caracteres invisíveis - tenha certeza de apenas alterar o texto e/ou adicionar linhas deletadas. Se você tiver que inserir uma linha, esteja certo que os campos são separados com tabulação e que há uma tabulação no final. Você pode querer utilizarod -c
para ter certeza que seu editor de texto não bagunçõu nada durante a edição. Nós, é claro, esperamos que você nunca tenha que editar a saída demysqltest -r
já que você só deverá fazê-lo quando encontra um bug. - Para estar consistente com a nossa configuração, você deve colocar seus arquivos de resultados no diretório
mysql-test/r
e o nomeie comotest_name.result
. Se o teste produzir mais de um resultado, você deve usartest_name.a.result
,test_name.b.result
, etc. - Se uma instrução retornar um erro, você eve espacificar na linha anterior a instrução com
--error error-number
. O número do erro pode ser uma lista de números de erros possíveis separados com','
. - Se você estiver escrevendo em teste de replicação, você deve coloca
source include/master-slave.inc;
na primeira linha do arquivo. Para trocar entre master e slave, utilizeconnection master;
econnection slave;
. se você precisar fazer alguma coisa em uma conexão alternativa, você pode fazerconnection master1;
para o master econnection slave1;
para o slave. - Se você precisar fazer alguma coisa em um loop, você pode usar algo assim:
let $1=1000; while ($1) { # do your queries here dec $1; }
- Para 'dormir' entre consultas, use o comando
sleep
. Ele suporta frações de um segundo, assim você pode fazersleep 1.3;
, por exemplo, para dormir 1.3 segundos. - Para executar o slave com opções adicionais para o seu caso de teste, coloque-os na formato de linha de comando
mysql-test/t/test_name-slave.opt
. Para o master, coloque-os emmysql-test/t/test_name-master.opt
. - Se você tiver uma questão sobre o pacote de testes, ou tiver um caso de teste para contribuir, envie um e-mail para lista de email
internals
do MariaDB. Leia 'As Listas de Discussão do MariaDB'. Como a lista não aceita anexos, você deve utilizar o ftp para enviar os arquivos relevantes: ftp://support.mysql.com/pub/mysql/Incoming/
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:
- Não envie um relatório de bug antes de ter feito tudo possível para encontrar o que esta errado! Quando o fizer, por favor, utilize o script
mysqlbug
assim podemoster informações sobre o seu sistema e a versão doMariaDB
. Leia 'Como relatar erros ou problemas'. - Esteja certo de inluir a saída de
mysql-test-run
, assim como o conteúdoi de todos os arquivos.reject
no diretóriomysql-test/r
. - Se um pacote de teste falhar, verifique se o teste também falha quando executado sozinho:
cd mysql-test mysql-test-run --local test-name
Se falhar, você deve configurar o MariaDB com
--with-debug
e executarmysql-test-run
com a opção--debug
. Se into também falhar envie o arquivo de rastreamentovar/tmp/master.trace
para ftp://support.mysql.com/pub/mysql/secret assim nós podemos examiná-los. Por favor, se lembre de também incluir uma descrição completa do seu sistema, a versão do binário do mysqld e como você o compilou. - Tente também executar
mysql-test-run
com a opção--force
para ver se há qualquer outro teste que tenha falhado. - Se você próprio compilou o MySQL, verifique nosso manual sobre como compilar o MariaDB na sua platforma ou, de preferência, use um dos binários que nós compilamos para você no http://www.mysql.com/downloads/. Todos os seus binários padrões devem passar no pacote de teste!
- Se você obter um erro, como
Result length mismatch
ouResult content mismatch
, significa que a saída do teste não é igual a saída esperada. Este pode ser um bug no MariaDB ou que o seu versão do mysqld produz resultados um pouco diferentes sobre certas circuntâncias.
Resultado de testes que falharam são colocados em um arquivo com o mesmo nome base que o arquivo de resultado com a extensão
.reject
. Se o seu caso de teste está falhando, você deve fazer um diff nos dois arquivos. Se você não puder ver como els são diferentes, examine ambos comod -c
e també verifique os seus tamanhos. - Se um teste falhar totalmente, você deve verificar os arquivos de log no diretório
mysql-test/var/log
para avisos sobre o que deu errado. - Se você tiver compilado o MariaDB com depuração você pode tentar depurá-lo executando
mysql-test-run
com a opções--gdb
e/ou--debug
. Leia Seção E.1.2, 'Criando Arquivos Trace (Rastreamento)'.
Se você não tiver compilado o MariaDB com depuração você deve, provavelmente, fazê-lo. Apenas especifique a opção
--with-debug
noconfigure
! Leia 'Instalando uma distribuição com fontes do MariaDB'.
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:
- Você pode adicionar novas funções através da interface de funções definidas por usuários - user-definable function (UDF). Funções definidas por usuários são adicionadas e removidas dinamicamente usando as instruções
CREATE FUNCTION
eDROP FUNCTION
. Leia Seção 14.2.1, 'SintaxeCREATE FUNCTION/DROP FUNCTION
'. - Você pode adicionar as funções como uma função nativa do MariaDB. Funções nativas são compiladas no servidor
mysqld
e ficam disponíveis em uma base permanente.
Cada método tem suas vantagens e desvantagens:
- Se você escreve uma função definida pelo usuário, você deve instalar o arquivo objeto no seu servidor. Se você compilou a sua função dentro do servidor você não precisará fazer isto.
- Você pode adicionar UDFs para um distribuição binária MySQL. Funções nativas exigem que você modifique a sua distribuição fonte.
- Se você atualizar a sua ditribuição MySQL, você pode continuar a usar a sua UDF previamente instalada. Para funções nativas, você deve repetir as suas modificações a cada vez que você atualizar.
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:
xxx()
(exigido)
A função principal. É onde o resultado da função é computado. A correspondência entre o tipo SQL e o tipo retornado da sua função C/C++ é mostrada aqui:
Tipo SQL Tipo C/C++ STRING
char *
INTEGER
long long
REAL
double
xxx_init()
(opcional)
A função de inicialização para
xxx()
. Ela pode ser usada para:- Verifica o número de argumentos para
XXX()
. - Verifica se os argumentos são de um tipo exigido ou, alternativamente, diga ao MariaDB para converter os argumentos para o tipo desejado quando a função principal é chamada.
- Aloca a memória exigida pela função principal.
- Especifica o tamanho máximo do resultado.
- Especifica (para funções
REAL
) o número máximo de decimais. - Especifica se o resultado pode ser
NULL
.
- Verifica o número de argumentos para
xxx_deinit()
(opicional)
A função de finalização para
xxx()
. Ela deve liberar qualquer memória alocada pela função de inicializaçã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:
xxx_reset()
(exigida)
Zera a soma e insere um argumento como o valor inicial para um novo grupo.
xxx_add()
(exigida)
Adiciona o argumento a soma antiga.
Quando se usa UDF's agregadas o MariaDB funciona da seguinte maneira:
- Chama
xxx_init()
para deixar funções agregadas alocarem a memória necessária para armazenar os resultados. - Ordena a tabela de acordo com a expressão
GROUP BY
. - Para a primeira linha em um novo grupo, chama a função
xxx_reset()
. - Para cada nova linha que pertence ao mesmo grupo, chame a função
xxx_add()
. - Quando o grupo muda ou depois da última linha ter sido processada, chame
xxx()
para obter o resultado para o conjunto. - Repita 3-5 até que todas as linhas tenham sido processada.
- 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.):
my_bool maybe_null
xxx_init()
deve definirmaybe_null
com1
sexxx()
pode retornarNULL
. O valor padrão é1
se qualquer um dos argumentos são declarados comomaybe_null
.unsigned int decimals
Número de decimais. O valor padrão é o número máximo de deciamis no argumento passado na função principal. (Por exemplo, se a função é passada function is passed
1.34
,1.345
e1.3
, o padrão seria 3, pois1.345
tem 3 decimais.unsigned int max_length
O tamanho máximo de um resultado string. O valor padrão difere dependendo do tipo de resultado da função. Para funções strings, o padrão é o temanho do maior argumento. Para funções do tipo inteiro, o padrão é 21 digitos. Para funções do tipo real, o padrão é 13 mais o número de decimais indicados por
initid->decimals
. (Para funções numéricas, o tamanho inclui qualquer caracter de sinal ou ponto decimal.)Se você quiser retornar um blon, você pode definí-lo com 65K ou 16M; esta memória não é alocada, mas usada para decidir qual tipo de coluna utilizar se houver necessidade dese armazenar dados temporários.
char *ptr
Um ponteiro que a função pode usar para o seus propósitos. Por exemplo, funções pode usar
initid->ptr
para comunicar memórias alocadas entre funções. Naxxx_init()
, aloca a memória e a atribui a este ponteiro:initid->ptr = allocated_memory;
Em
xxx()
exxx_deinit()
, se refira ainitid->ptr
para usar ou liberar a memória.
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:
unsigned int arg_count
O número de argumentos. Verifique o valor na função de inicialização se você quiser que ssua função seja chamada com um número específico de argumentos. For exemplo:
if (args->arg_count != 2) { strcpy(message,'XXX() requires two arguments'); return 1; }
enum Item_result *arg_type
Os tipos para cada argumento. Os valores de tipos possíveis são
STRING_RESULT
,INT_RESULT
, eREAL_RESULT
.Para ter certeza que os argumentos são de um tipo dado e retornar um erro se não forem, verifique o vetor
arg_type
na função de inicialização. Por exemplo:if (args->arg_type[0] != STRING_RESULT || args->arg_type[1] != INT_RESULT) { strcpy(message,'XXX() requires a string and an integer'); return 1; }
Como uma alternativa para exigir que os argumentos de sua função sejam de um tipo específico, você pode usar a função de inicialização para definir o elemento
arg_type
com o tipo que você quiser. Isto faz com que o MariaDB converta argumentos para aqueles tipo a cada chamada dexxx()
. Por exemplo, para fazer conversão dos dois primeiros argumentos para string e integer, faça isto comxxx_init()
:args->arg_type[0] = STRING_RESULT; args->arg_type[1] = INT_RESULT;
char **args
args->args
informa a função de inicialização sobre a natureza geral dos argumentos chamados com sua função. Para um argumento constantei
,args->args[i]
aponta para o valor do argumento. (Veja abaixo sobre instruções de como acessar o valor de forma apropriada). Para um argumento não constante,args->args[i]
é0
. Um argumento constante é uma expressão é uma expressão que utiliza apenas constante, tais como3
ou4*7-2
ouSIN(3.14)
. Um argumento não constante é uma expressão que refere a valores que podem alterar a cada linha, tais como nomes de coluna ou funções que são chamadas com argumentos não contantes.Para cada chamada da função principal,
args->args
contém os argumentos atuais que são passados pela linhas sendo processadas atualmente.As funções podem se referir a um argumento
i
como a seguir:- Um argumento do tipo
STRING_RESULT
é dado como um apontador string mais um tamanho, para permitir o tratamento de dados binários de tamanho arbitrário. Os conteúdo da string estão disponíveis comoargs->args[i]
e o tamanho da string éargs->lengths[i]
. Você não deve assumir aue as strings são terminadas em null. - Para um argumnto do tipo
INT_RESULT
, você deve converterargs->args[i]
para um valorlong long
:
long long int_val; int_val = *((long long*) args->args[i]);
- Para um argumento do tipo
REAL_RESULT
, você deve converterargs->args[i]
para um valordouble
:
double real_val; real_val = *((double*) args->args[i]);
- Um argumento do tipo
unsigned long *lengths
Para a função de inicialização, o vetor
lengths
indica o tamanho máximo da string para cada argumento. Você não deve alterá-los. Para cada chamada da função principal,lengths
contém o tamanho atual de quaisquer argumentos string que são passados para a linha sendo processada atualmente. Para argumentos do tipoINT_RESULT
ouREAL_RESULT
,lengths
ainda contém o tamanho máximo do argumento (como para a função de inicialização).
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:
metaphon()
retorna uma string metafonica do argumento string. Ela é algo como uma string soundex, mas é mais voltada para o inglês.myfunc_double()
retorna a soma de valores ASCII de caracteres e seus argumentos, dividido pela soma de tamanho de seus argumentos.myfunc_int()
retorna a soma do tamanho de seus argumentos.sequence([const int])
retorna uma sequência iniciando a partir de um número dado ou 1 se nenhum número for fornecido.lookup()
retorna o IP de um nome de máquina.reverse_lookup()
retorna o nome de mauina para um número IP. A função pode ser chamada com uma string'xxx.xxx.xxx.xxx'
ou quatro números.
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:
- Adicionr uma linha a
lex.h
que defina o nome da função no vetorsql_functions[]
. - 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 emitem_create.cc
. De uma olhada em'ABS'
ecreate_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 oyacc
deve difinir (isto deve ser adicionado no começo do arquivo). Então defina os parâmetros da função e adicione umitem
com estes parâmetros a regrasimple_expr
do analizador. Por exemplo, verifique todas as acorrências deATAN
emsql_yacc.yy
para ver como ele é feito. - Em
item_func.h
, declare uma classe herdada deItem_num_func
ouItem_str_func
, dependendo se sua função retorna um número ou uma string. - 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 classeItem_str_func
define uma funçãoval()
que executaatof()
no valor retornado por::str()
. - 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 definirmaybe_null = 0
se a função principal não puder retornar um valorNULL
. A função pode verificar se algum dos argumentos da função pode retornarNULL
verificando a variável de argumentosmaybe_null
. Você pode dar uma olhada emItem_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:
- O arguemto
String *str
fornece um buffer string que pode ser utilizado para guardar o resultado. (Para mais informações sobre o tipoString
, dê uma olhada no arquivosql_string.h
.) - A função
::str()
deve retornar a string que guarda o resultado ou(char*) 0
se o resultado éNULL
. - Todas as funções string atuais tentam evitar a alocação de memória a menos que seja absolutamente necessário!
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:
max elements
(padrão 256) é o número máximo de valores distintos queanalyse
notificará por coluna. Isto é utilizado poranalyse
para verificar se o tipo ótimo da coluna deve ser do tipoENUM
.max memory
(padrão 8192) é a memória máxima queanalyse
deve alocar por coluna enquanto tenta encontrar todos os valores distintos.
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:
sql/sql_analyse.cc
sql/procedure.h
sql/procedure.cc
sql/sql_select.cc
Anterior | Próximo | |
Tratamento de Erros no MySQL | Início | Problemas e Erros Comuns |