Como posso contornar o código de erro 13 do MySQL com SELECT INTO OUTFILE?

114

Estou tentando despejar o conteúdo de uma tabela em um arquivo csv usando uma instrução SELECT INTO OUTFILE do MySQL. Se eu fizer:

SELECT column1, column2
INTO OUTFILE 'outfile.csv'
FIELDS TERMINATED BY ','
FROM table_name;

outfile.csv será criado no servidor no mesmo diretório onde os arquivos deste banco de dados estão armazenados.

No entanto, quando eu mudo minha consulta para:

SELECT column1, column2
INTO OUTFILE '/data/outfile.csv'
FIELDS TERMINATED BY ','
FROM table_name;

Eu recebo:

ERROR 1 (HY000): Can't create/write to file '/data/outfile.csv' (Errcode: 13)

Errcode 13 é um erro de permissão, mas eu o recebo mesmo se eu mudar a propriedade de / data para mysql: mysql e conceder a ele 777 permissões. O MySQL está sendo executado como usuário "mysql".

Estranhamente, posso criar o arquivo em / tmp, mas não em qualquer outro diretório que tentei, mesmo com permissões definidas de forma que o usuário mysql possa gravar no diretório.

Este é o MySQL 5.0.75 em execução no Ubuntu.

Ryan Olson
fonte
3
Visto que 13 é um erro do sistema, provavelmente não é isso, mas há uma configuração de mySQL limitando INTO OUTFILE a um diretório: dev.mysql.com/doc/refman/5.0/en/… talvez valha a pena dar uma olhada rápida para saber se é definido como /tmp.
Pekka
Essa variável está em branco na minha instalação, o que de acordo com esse documento significa que meus diretórios de saída não devem ser limitados.
Ryan Olson

Respostas:

189

Qual versão particular do Ubuntu é esta e esta é o Ubuntu Server Edition?

As edições recentes do Ubuntu Server (como a 10.04) vêm com o AppArmor e o perfil do MySQL pode estar no modo obrigatório por padrão. Você pode verificar isso executando da seguinte sudo aa-statusforma:

# sudo aa-status
5 profiles are loaded.
5 profiles are in enforce mode.
   /usr/lib/connman/scripts/dhclient-script
   /sbin/dhclient3
   /usr/sbin/tcpdump
   /usr/lib/NetworkManager/nm-dhcp-client.action
   /usr/sbin/mysqld
0 profiles are in complain mode.
1 processes have profiles defined.
1 processes are in enforce mode :
   /usr/sbin/mysqld (1089)
0 processes are in complain mode.

Se o mysqld estiver incluído no modo forçado, provavelmente é ele que nega a gravação. As entradas também seriam gravadas /var/log/messagesquando o AppArmor bloquear as gravações / acessos. O que você pode fazer é editar /etc/apparmor.d/usr.sbin.mysqlde adicionar /data/e /data/*próximo à parte inferior, assim:

...  
/usr/sbin/mysqld  {  
    ...  
    /var/log/mysql/ r,  
    /var/log/mysql/* rw,  
    /var/run/mysqld/mysqld.pid w,  
    /var/run/mysqld/mysqld.sock w,  
    **/data/ r,  
    /data/* rw,**  
}

E então faça o AppArmor recarregar os perfis.

# sudo /etc/init.d/apparmor reload

AVISO: a mudança acima permitirá que o MySQL leia e grave no diretório / data. Esperamos que você já tenha considerado as implicações de segurança disso.

Vin-G
fonte
2
Eu odeio apontar isso, mas há um motivo pelo qual o App Armor não permite isso. O MySQL agora tem a capacidade de modificar e ler qualquer coisa na pasta / data. Só não seja hackeado agora.
Ryan Ward
2
@Serdar, O conjunto de regras AppArmor MySQL distribuído com a distro não permite isso por padrão . Isso faz sentido, pois é uma boa base de regras para uma nova instalação. Acredito que devemos e temos permissão para modificar os conjuntos de regras para atender às nossas necessidades pós-instalação. A intenção do solicitante original era permitir que o MySQL gravasse em diretórios específicos. Mas se não estiver prontamente explícito acima, uma nota para informar as pessoas que estão tropeçando nesta solução: AVISO: a mudança acima permitirá que o MySQL leia e grave no diretório / data. Esperamos que você já tenha considerado as implicações de segurança disso.
Vin-G
1
ÓTIMA RESPOSTA!!! Isso resolveu meu problema, eu também estava tentando escrever em qualquer outro diretório. Agora, eu tenho que pesquisar sobre o que tudo isso se trata! :) Aprendendo sobre isso, recomendo que outras pessoas leiam sobre o apparmor (e, portanto, o comando aa-status): en.wikipedia.org/wiki/AppArmor
David L
1
No meu caso ajudou: /your/abs/folder/ r, /your/abs/folder/** rwk, }não se esqueça de colocar a vírgula no final!
ACV
1
funciona para escrever em / tmp. Em vez disso, use o Windows. Linux é uma merda
Victor Ionescu
17

Ubuntu usa AppArmor e isso é o que está impedindo você de acessar / data /. O Fedora usa selinux e isso impediria isso em uma máquina RHEL / Fedora / CentOS.

Para modificar o AppArmor para permitir que o MySQL acesse / data / faça o seguinte:

sudo gedit /etc/apparmor.d/usr.sbin.mysqld

adicione esta linha em qualquer lugar na lista de diretórios:

/data/ rw,

então faça um:

sudo /etc/init.d/apparmor restart

Outra opção é desabilitar o AppArmor para mysql completamente, isso NÃO É RECOMENDADO :

sudo mv /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/

Não se esqueça de reiniciar o aparelho:

sudo /etc/init.d/apparmor restart

torre
fonte
Para realmente desabilitar o apparmor para o mysql eu precisei fazer: cyberciti.biz/faq/ubuntu-linux-howto-disable-apparmor-commands
silver_mx
14

Eu sei que você disse que já tentou definir as permissões para 777, mas como tenho uma evidência de que para mim era um problema de permissão, estou postando o que exatamente executei esperando que possa ajudar. Aqui está minha experiência:

tmp $ pwd
/Users/username/tmp
tmp $ mkdir bkptest
tmp $ mysqldump -u root -T bkptest bkptest
mysqldump: Got error: 1: Can't create/write to file '/Users/username/tmp/bkptest/people.txt' (Errcode: 13) when executing 'SELECT INTO OUTFILE'
tmp $ chmod a+rwx bkptest/
tmp $ mysqldump -u root -T bkptest bkptest
tmp $ ls bkptest/
people.sql  people.txt
tmp $ 
basilikode
fonte
me @ server: / data $ pwd / data me @ server: / data $ ls -al total 60 ... drwxrwxrwx 2 mysql mysql 4096 2010-05-06 16:27 dumptest me @ server: / data $ mysqldump -u dbuser -p -T dumptest -B db_name --tables test Digite a senha: mysqldump: Erro obtido: 1: Não é possível criar / gravar no arquivo '/data/dumptest/test.txt' (Código de erro: 13) ao executar 'SELECT INTO OUTFILE 'me @ server: / data $ sudo chmod a + rwx dumptest / me @ server: / data $ mysqldump -u dbuser -p -T dumptest -B db_name --tables test Digite a senha: mysqldump: Erro obtido: 1: ( mesmo erro)
Ryan Olson
Oy, bem, não percebi que os comentários não formatariam, mas verifiquei algumas maneiras diferentes. Primeiro com o diretório de destino de propriedade de mysql: mysql, então com o diretório de destino de propriedade do usuário que eu estava executando o comando dump, ambas as formas ainda me dão o mesmo erro de permissão.
Ryan Olson
Para registro, isso funcionou para mim, apesar de ter alterado as permissões do aparelho
Alex
Tentei fazer modificações no aparelho, mas não funcionou. Alterar a permissão 'chmod 777' funcionou para mim!
Sudarshan_SMD
7

O MySQL está ficando estúpido aqui. Ele tenta criar arquivos em / tmp / data / .... Então, o que você pode fazer é o seguinte:

mkdir /tmp/data
mount --bind /data /tmp/data

Em seguida, tente sua consulta. Isso funcionou para mim depois de horas depurando o problema.

vimdude
fonte
Eu gosto mais dessa resposta. É fácil, funciona e não exige que você mexa no aparelho. A outra maneira de fazer isso usando canais não funciona bem para grandes exportações por causa de todo o armazenamento em buffer que é feito.
Chris Seline
6

Esse problema já me incomoda há muito tempo. Percebi que essa discussão não aponta a solução no RHEL / Fecora. Estou usando o RHEL e não encontro os arquivos de configuração correspondentes ao AppArmer no Ubuntu, mas resolvi meu problema tornando CADA diretório no diretório PATH legível e acessível por mysql. Por exemplo, se você criar um diretório / tmp, os dois comandos a seguir tornam SELECT INTO OUTFILE capaz de gerar o arquivo .sql E .sql

chown mysql:mysql /tmp
chmod a+rx /tmp

Se você criar um diretório em seu diretório pessoal / home / tom, você deve fazer isso para / home e / home / tom.

Fanchyna
fonte
3
Usar / tmp como exemplo não é uma boa ideia e você realmente não deseja alterar a propriedade do diretório / tmp (na maioria dos casos).
sastorsl
Alterar a propriedade de / tmp é ruim, mas criar uma pasta temporária dentro de / tmp e chown mysql:mysqlresolveu meu problema
Samuel Prevost
6

Você consegue fazer isso :

mysql -u USERNAME --password=PASSWORD --database=DATABASE --execute='SELECT `FIELD`, `FIELD` FROM `TABLE` LIMIT 0, 10000 ' -X > file.xml
Navrattan Yadav
fonte
Obrigado! É possível controlar a saída como CSV?
Hamman Samuel
4

Algumas coisas para tentar:

  • é o secure_file_priv variável sistema está definida? Se for, todos os arquivos devem ser gravados nesse diretório.
  • assegure-se de que o arquivo não exista - o MySQL apenas criará novos arquivos, não sobrescreverá os existentes.
mdma
fonte
1
Eu também escolheria secure_file_priv. Se o arquivo já existe, a mensagem de erro é diferente (não errcode 13).
Xavier Maillard
secure_file_priv não está definido no momento, então, pelo que entendi, isso significa que não devo ficar limitado quanto a onde posso escrever arquivos. Estou entendendo mal isso e preciso definir explicitamente algo como '/' se quiser escrever em qualquer lugar do sistema de arquivos?
Ryan Olson
Além disso, estou verificando se o arquivo não existe antes de executar a consulta.
Ryan Olson
Obrigado pelo feedback. Com base em suas descobertas, acho que nenhuma dessas sugestões causa seu problema.
mdma
3

Eu tenho o mesmo problema e resolvi-o seguindo as etapas:

  • Sistema operacional: ubuntu 12.04
  • lâmpada instalada
  • suponha que seu diretório para salvar o arquivo de saída seja: / var / www / csv /

Execute o seguinte comando no terminal e edite este arquivo usando o editor gedit para adicionar seu diretório ao arquivo de saída.

sudo gedit /etc/apparmor.d/usr.sbin.mysqld

  • agora o arquivo será aberto no editor, adicione seu diretório lá

    / var / www / csv / * rw,

  • da mesma forma, adicionei em meu arquivo, como a seguinte imagem dada:

insira a descrição da imagem aqui

Execute o próximo comando para reiniciar os serviços:

sudo /etc/init.d/apparmor restart

Por exemplo, eu executo a seguinte consulta no construtor de consultas phpmyadmin para gerar dados em arquivo csv

SELECT colName1, colName2,colName3
INTO OUTFILE '/var/www/csv/OUTFILE.csv'
FIELDS TERMINATED BY ','
FROM tableName;

Feito com sucesso e gravar todas as linhas com colunas selecionadas no arquivo OUTPUT.csv ...

Sunny SM
fonte
2

No meu caso, a solução foi tornar cada diretório no caminho do diretório legível e acessível por mysql( chmod a+rx). O diretório ainda foi especificado por seu caminho relativo na linha de comando.

chmod a+rx /tmp
chmod a+rx /tmp/migration
etc.
Alsciende
fonte
2

Acabei de encontrar o mesmo problema. Meu problema era que o diretório no qual eu estava tentando despejar não tinha permissão de gravação para o processo mysqld. O dump inicial do sql seria gravado, mas a gravação do arquivo csv / txt falharia. Parece que o dump sql é executado como o usuário atual e a conversão para csv / txt é executada como o usuário que está executando o mysqld. Portanto, o diretório precisa de permissões de gravação para ambos os usuários.

Matthew McMillan
fonte
1

Você precisa fornecer um caminho absoluto, não um caminho relativo.

Forneça o caminho completo para o diretório / data no qual você está tentando gravar.

Ike Walker
fonte
Isso parece um caminho absoluto para mim. Não é?
Pekka
2
Tente isso como o usuário mysql para verificar se você pode criar o arquivo fora do mysql:touch /data/outfile.csv
Ike Walker
1
Primeiro eu não pude fazer isso porque o shell do usuário mysql estava definido como / bin / false, então eu não pude logar como mysql. Apenas para ter certeza de que isso não estava contribuindo para o problema, eu configurei o shell do mysql para / bin / bash, su'd para aquele usuário e toquei um arquivo em / data. O arquivo foi criado com sucesso, pertencente a mysql.
Ryan Olson
3
Você pode usar uma conta mesmo se ela estiver usando um dos shells "desabilitar": su --shell=/bin/sh nameofaccount
Marc B
Obrigado, eu não sabia disso.
Ryan Olson
1

Ubuntu usa SELinux? Verifique se está habilitado e obrigatório. /var/log/audit/audit.log pode ser útil (se é onde o Ubuntu o coloca - é a localização do RHEL / Fedora).

Charles
fonte
0

Eu tive o mesmo problema em um CentOs 6.7 No meu caso todas as permissões foram definidas e ainda assim ocorreu o erro. O problema era que o SE Linux estava no modo "enforcing".

Mudei para "permissivo" usando o comando sudo setenforce 0

Então tudo deu certo para mim.

Stefan Bicher
fonte