简体   繁体   中英

PHP- inserting binary data in mysql using prepared statements

I have to insert a row using php's mysql improved library to a table in mysql that has its primary key of type VARBINARY. The contents of this field are a computed sha1 hash.

If I run the query the old way it works perfectly:

$mysqli->$query("INSERT INTO table (id, field1) VALUES (0x" . $id . ",'" . $field1 . "')");

But when I try to execute it as a prepared statement, I can't figure out how to do it. If I perform the equivalent action:

if($stmt = $mysqli->prepare("INSERT INTO table (id, field1) VALUES (?, ?)")) {
    $stmt->bind_param('ss', "0x".$id, $field1);
    //execute statement
}

It throws an exception saying that the contents were too large for this field. And If I try to insert it as a BLOB field:

if($stmt = $mysqli->prepare("INSERT INTO table (id, field1) VALUES (?, ?)")) {
    $stmt->bind_param('bs', $id, $field1);
    //execute statement
}

It gives no error, the row is inserted, but the identifier field now is empty (not null, empty).

I know I can mix the query and input the id concatenated in the string and the other fields as bind parameters of the prepared statement, but i'm asking just to know what is the correct way to insert this and perhaps it will help somebody in the future.

PHP's sha1 function returns a string representation of a hex number.

What that means is that if you print it to screen, it'll display a hex number. But in memory, it is an bunch of ASCII characters.

So, take the hex number 1A2F . As ASCII in memory that would be 0x31413246 , instead of 0x1A2F

MySQL's normal interface sends all arguments as strings. When using the normal interface, MySQL will convert the ASCII string to a binary value.

The new prepared statement method sends everything as binary. So your nice value of "1A2F" will now be sent as 0x31413246 and inserted into the column. - source: dev.mysql.com - Prepared statements

Instead, convert your Hex string by packing it into a binary string using:

$binId = pack("H*", $id); // this string is not ASCII, don't print it to the screen! That will be ugly.

and then pass $binId to the MySQLi prepared statement instead of $id.

try this instead:

if($stmt = $mysqli->prepare("INSERT INTO table (id, field1) VALUES (unhex(?), ?)") {
    $stmt->bind_param('ss', $id, $field1);
    //execute statement
}

tl;dr: check out send_long_data() .

I know this is a very old question, but it was exactly what I was trying to do and failing at. After trying the above answers and spending much time experimenting, I finally found something that works the way the question and I were trying.

It's confusing because the only reference for how to proceed with "b" types in the PHP bind_param documentation is indirectly when referring to data exceeding the allowed packet size (which I initially skipped over):

If data size of a variable exceeds max. allowed packet size (max_allowed_packet), you have to specify b in types and use mysqli_stmt_send_long_data() to send the data in packets.

It turns out that binary types must be sent by themselves before executing your insert. I found this out from an article from Oracle's website .

Since they explain it so succinctly, I'll just paraphrase the most relevant part:

Storing the blob

Here's the code to store a blob using MySQLi:

$stmt = $mysqli->prepare("INSERT INTO images (image) VALUES(?)")
$null = NULL; //bolded
$stmt->bind_param("b", $null);

$stmt->send_long_data(0, file_get_contents("osaka.jpg")); //bolded

$stmt->execute();

I bolded two pieces of code, which I think are worth looking at:

The $null variable is needed, because bind_param() always wants a variable reference for a given parameters. In this case the "b" (as in blob) parameter. So $null is just a dummy, to make the syntax work.

In the next step I need to "fill" my blob parameter with the actual data. This is done by send_long_data() . The first parameter of this method indicates which parameter to associate the data with. Parameters are numbered beginning with 0. The second parameter of send_long_data() contains the actual data to be stored.

While using send_long_data() , please make sure that the blob isn't bigger than MySQL's max_allowed_packet

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM