Índice
Este capítulo descreve várias coisas que você precisa saber ao
trabalhar no código do MySQL. Se você planeja contribuir com o
desenvolvimento do MySQL, quiser ter acesso ao código entre
versões, ou apenas deseja acompanhar o desenvolvimento, siga as
instruções em Secção 2.3.4, “Instalando pela árvore de fontes do desenvolvimento”. Se você
está interessada nos MySQL 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 Secção 1.7.1.1, “As Listas de Discussão do MySQL”.
Todos os desenvolvedores na MySQL AB estão na lista
internals
e nós ajudamos outras pessoal que
estão trabalhando no código MySQL. 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!
O servidor MySQL 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 com
sigwait()
ou se deseja utilizar o código
thr_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.
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 MySQL. 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 MySQL irão funcionar bem com suas aplicações.
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. See
Secção 14.1.2.3, “Relatando Bugs no Pacote de Teste do MySQL”.
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.
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 comando mysql
. 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 é executar mysqltest -r <
t/test-case-name.test
do diretório
mysql-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 utilizar
od -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
de mysqltest -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 como
test_name.result
. Se o teste produzir
mais de um resultado, você deve usar
test_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, utilize
connection master;
e
connection slave;
. se você precisar
fazer alguma coisa em uma conexão alternativa, você pode
fazer connection master1;
para o master
e connection 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 fazer sleep
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 em
mysql-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 MySQL. See Secção 1.7.1.1, “As Listas de Discussão do MySQL”. Como a lista não aceita anexos, você deve utilizar o ftp para enviar os arquivos relevantes: ftp://support.mysql.com/pub/mysql/Incoming/
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 do
MySQL
. See
Secção 1.7.1.3, “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ório mysql-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 MySQL com
--with-debug
e executar
mysql-test-run
com a opção
--debug
. Se into também falhar envie o
arquivo de rastreamento
var/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 MySQL 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
ou Result content
mismatch
, significa que a saída do teste não
é igual a saída esperada. Este pode ser um bug no MySQL
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 com od -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 MySQL com depuração você
pode tentar depurá-lo executando
mysql-test-run
com a opções
--gdb
e/ou --debug
. See
Secção E.1.2, “Criando Arquivos Trace (Rastreamento)”.
Se você não tiver compilado o MySQL com depuração
você deve, provavelmente, fazê-lo. Apenas especifique a
opção --with-debug
no
configure
! See
Secção 2.3, “Instalando uma distribuição com fontes do MySQL”.
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
e DROP FUNCTION
. See
Secção 14.2.1, “Sintaxe CREATE FUNCTION/DROP FUNCTION
”.
Você pode adicionar as funções como uma função nativa do
MySQL. 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()
.
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 MySQL com uma nova função que
funciona como funções nativas do MySQL tais como
ABS()
e CONCAT()
.
AGGREGATE
é uma nova opção do MySQL
Versão 3.23. Uma função AGGREGATE
funciona
exatamente como uma função GROUP
nativa do
MySQL 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
mysql
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 Secçã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.
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 MySQL 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 MySQL 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 MySQL 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
.
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
MySQL 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 MySQL 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()
.
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 definir
maybe_null
com 1
se
xxx()
pode retornar
NULL
. O valor padrão é
1
se qualquer um dos argumentos são
declarados como maybe_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
e
1.3
, o padrão seria 3, pois
1.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. Na
xxx_init()
, aloca a memória e a
atribui a este ponteiro:
initid->ptr = allocated_memory;
Em xxx()
e
xxx_deinit()
, se refira a
initid->ptr
para usar ou liberar a
memória.
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 MySQL 4.1.1. Você ainda pode manter a definição de sua função se você quiser o seu código funcinonando com o MySQL 4.0 e MySQL 4.1.1
char *xxx_reset(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
Esta função é chamada quando o MySQL 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 MySQL 4.1.1 e acima:
char *xxx_clear(UDF_INIT *initid, char *is_null, char *error);
Esta função é chamada quando o MySQL 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. See
Secçã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. See
Secçã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. See
Secçã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 MySQL
retornará NULL
como o rsultado para a
função do grupo.
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
, e
REAL_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 MySQL converta argumentos para aqueles
tipo a cada chamada de xxx()
. Por
exemplo, para fazer conversão dos dois primeiros
argumentos para string e integer, faça isto com
xxx_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 constante
i
, 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 como 3
ou 4*7-2
ou SIN(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 como
args->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 converter
args->args[i]
para um valor
long long
:
long long int_val; int_val = *((long long*) args->args[i]);
Para um argumento do tipo
REAL_RESULT
, você deve converter
args->args[i]
para um valor
double
:
double real_val; real_val = *((double*) args->args[i]);
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 tipo
INT_RESULT
ou
REAL_RESULT
, lengths
ainda contém o tamanho máximo do argumento (como para a
função de inicialização).
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. See
Secçã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 MySQL anterior a 3.22.10, você deve configurar
*error
e *is_null
:
*error = 1; *is_null = 1;
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 MySQL. 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 MySQL 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
mysql
. 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
mysql
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
.)
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 MySQL. Você deve compilar o MySQL de uma distribuição fonte. Note também que se você migrar para outra versão do MySQL (por exemplo, quando uma nova versão é liberada), você precisará repetir o procedimento com a nova versão.
Para adicionar uma função MySQL nativa, siga estes passos:
Adicionr uma linha a lex.h
que defina o
nome da função no vetor
sql_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 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.
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.
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()
.
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:
O arguemto String *str
fornece um buffer
string que pode ser utilizado para guardar o resultado.
(Para mais informações sobre o tipo
String
, dê uma olhada no arquivo
sql_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!
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 MySQL 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
.
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 que analyse
notificará por coluna. Isto é utilizado por
analyse
para verificar se o tipo ótimo
da coluna deve ser do tipo ENUM
.
max memory
(padrão 8192) é a memória
máxima que analyse
deve alocar por
coluna enquanto tenta encontrar todos os valores distintos.
SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max elements,[max memory]])
This is a translation of the MySQL Reference Manual that can be found at dev.mysql.com. The original Reference Manual is in English, and this translation is not necessarily as up to date as the English version.