3

Background: I moved some websites from a shared hosting account to a VPS. Everything works extremely smooth with the only exception of lines of php code which execute queries with LOAD DATA LOCAL INFILE, using data sources from external servers (i.e. http://server1.net/getfile.php?f=101). This code works (and worked for long time) at the shared hosting account, however, this account is reaching resource limits, especially in the area of database size/speed.

Problem: Code with LOAD DATA INFILE which downloads from a http:// address does not work anymore after been moved to a (new) VPS.

System and software: The VPS is used as a web-server, running Ubuntu (Ubuntu 14.04) with Vesta (0.9.8 (amd64)), apache, nginx, mysql (5.5.44), app-armor among others. All recently installed and very little or not changed.

Selection from phpinfo() output:

System  Linux MyServer 3.13.0-62-generic #102-Ubuntu SMP Tue Aug 11 14:29:36 UTC 2015 x86_64 
Apache Version  Apache/2.4.7 (Ubuntu) mod_fcgid/2.3.9 PHP/5.5.9-1ubuntu4.11 OpenSSL/1.0.1f 
Server Root     /etc/apache2
PATH    /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SERVER_SOFTWARE     Apache/2.4.7 (Ubuntu) mod_fcgid/2.3.9 PHP/5.5.9-1ubuntu4.11 OpenSSL/1.0.1f
allow_url_fopen On  On
allow_url_include   Off
doc_root    no value    
docref_ext  no value
docref_root no value
file_uploads    On
open_basedir    no value
sys_temp_dir    /tmp
upload_tmp_dir  /tmp
user_dir    no value
cURL support    enabled 
mysql
Directive                   Local Value  Master Value
mysql.allow_local_infile    On           On
mysqli
Directive                   Local Value  Master Value
mysqli.allow_local_infile   On           On

A few lines from /etc/mysql/my.cnf

[client]
loose-local-infile=1
local-infile=1

[mysqld]
basedir=/usr
datadir=/var/lib/mysql
tmpdir=/tmp
local-infile=1

Below code runs in the same directory as the file with phpinfo()

Test code:

$url = "http://myserver.net/x.csv";
$file = "/var/lib/mysql/test2.csv";

$handle = curl_init($url);
curl_setopt($handle,  CURLOPT_RETURNTRANSFER, TRUE);
/* Get the HTML or whatever is linked in $url. */
$response = curl_exec($handle);
/* Check for 404 (file not found). */
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
echo("<br>Http code: ". $httpCode);
curl_close($handle);

echo "<br>Temp_name: ". $_FILES["file"]["tmp_name"];
echo "<br>Host info: " . $conn->host_info . "\n";
echo "<br>Client info: ".mysqli_get_client_info($conn) ."\n";

$conn = mysqli_init();
if (!$conn) {
    die('mysqli_init failed');
}

if (!$conn->options(MYSQLI_OPT_LOCAL_INFILE, true)) {
    die('Setting MYSQLI_OPT_LOCAL_INFILE failed');
}

if (!$conn->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5)) {
    die('Setting MYSQLI_OPT_CONNECT_TIMEOUT failed');
}

if (!$conn->real_connect('localhost','root','pass','database')) {
    die('Connect Error (' . mysqli_connect_errno() . ') '
            . mysqli_connect_error());
}

$conn->query("LOAD DATA INFILE '". $file ."' INTO TABLE x_import (@col1) SET ID = @col1;");
echo("<br>Error file: " . mysqli_error($conn));

$conn->query("LOAD DATA INFILE '". $url ."' INTO TABLE x_import (@col1) SET ID = @col1;");
echo("<br>Error url: " . mysqli_error($conn));

$conn->query("LOAD DATA LOCAL INFILE '". $file ."' INTO TABLE x_import (@col1) SET ID = @col1;");
echo("<br>Error file (LOCAL): " . mysqli_error($conn));

$conn->query("LOAD DATA LOCAL INFILE '". $url ."' INTO TABLE x_import (@col1) SET ID = @col1;");
echo("<br>Error url (LOCAL): " . mysqli_error($conn));

$conn->query("SELECT * INTO OUTFILE 'andreas.txt' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n' FROM x_import;");
echo("<br>Error OutFile: " . mysqli_error($conn));

Code result:

Http code: 200
Temp_name:
Host info:
Client info: 5.5.44
Error file:
Error url: Can't get stat of '/var/lib/mysql/http:/myserver.net/x.csv' (Errcode: 2)
Error file (LOCAL): File '/var/lib/mysql/test2.csv' not found (Errcode: 13)
Error url (LOCAL): File 'http:/myserver.net/x.csv' not found (Errcode: 2)
Error OutFile: File 'andreas.txt' already exists

The php code using the root account with mysql, so full access/privileges there.

The first line with sql code (LOAD DATA INFILE without LOCAL using the local file) adds the data from the file in the database correctly.

The last line with the INTO OUTFILE code creates correctly a file in /var/lib/mysql/database/ (and 2nd time gives the already exists error)

From all research so far I understand:

  • Error code 13 is a permission issues.
  • Error code 02 is when it is unable to find the file.
  • "LOAD DATA" is for a file on the database server.
  • "LOAD DATA LOCAL" is for a file on the client (apache/php-code).

What I do not understand is why there is a / missing in the URL when using LOCAL (see code output). I also do not understand why there is a error code 13 when LOCAL is added to the URL (php receives code 200 in first part of code). It seems to me that both php and mysql are not able to look at the right place for several reasons.

Below I will explain a few attempts of many things I have tried (mostly from loads of other answers on this website):

I have changed the file permissions for /var/ and /var/lib/ to 755 (and once to 777) but this did not change my test code output. /tmp/ was already set with 777.

In app-armor I have changed mysql into complain mode, as well tried some change in the mysql config file for app-armor (usr.sbin.mysqld), but both did not change the test code output.

Useful documentation (but maybe not fully understood by me): http://dev.mysql.com/doc/refman/5.5/en/load-data-local.html

I prefer using the LOAD DATA LOCAL but if it works in any other way I am happy too.

Please help. Any questions and suggestions are welcome.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Andreas
  • 31
  • 4

1 Answers1

0

I meet the same problem when i trying to use mysql load infile (datafile and project on localhost and remote database). I use direct mysql command from php (via system function). Here is example of my code:

system('mysql --local-infile -h '.$host. ' -D '.$dbname.' -u'.$dbuser.' -p'.$password." -e \"LOAD DATA LOCAL INFILE '". $pathToFile . "' INTO TABLE table FIELDS TERMINATED BY ';' (field1, field2, field3)\"");

I hope this can be usefull for you.

Nitromoon
  • 378
  • 1
  • 2
  • 11
  • Thank you! I think this can be a very good alternative. I tried your code in my test setup, but unfortunately nothing happened. When i add the $return_val to the function i get back a 1 (see http://php.net/system), so it should have done something, isn't? I also run the command in putty, using remote access, and this one gives basically the same message: ERROR 2 (HY000) at line 1: File 'http:/myserver2.net/x.csv' not found (Errcode: 2). Please note that again the http:// becomes http:/ (one slash forward less). Any clues why the path/address is changed in the error msg? – Andreas Sep 24 '15 at 11:47
  • My reasoning is that php can access remote paths, but apparently mysql not (and why is that?). It looks to me the local-infile variable can block the load data, i can replicate that, but I cannot resolve the remote path access error. – Andreas Sep 24 '15 at 11:55
  • So, on your host, where your database located on you can trying to get datafile from remote host and apply it into db, currect? So you need to download file to your db server and run LOAD DATA INFILE or run LOAD DATA LOCAL INFILE on http://myserver.net/x.csv. There are two available directions 1) local file -> local db 2) local file -> remote db. local db -> remote file is not provided :D – Nitromoon Sep 25 '15 at 13:12
  • Thank you for the suggestion. Writing additional PHP code to download is an option. Not preferred but maybe easier than move to MariaDB or something like that. Extra code will result in: remote file -> local file -> local db. The myserver.net is part of the test environment. The real system works with 3rd parties (only access to data), so no option to run remote code. Before moving to the VPS the situation was: remote server -> local db (php using localhost to connect to mysql and using LOAD DATA LOCAL INFILE). This is what I would like on the VPS too. – Andreas Sep 26 '15 at 14:21