using nulls in a mysqli prepared statement - php

In a mysqli prepared statement, a NULL gets turned into '' (in the case of a string) or 0 (in the case of an integer). I would like to store it as a true NULL. Is there any way of doing this?

It's possible to bind a true NULL value to the prepared statements (read this).
You can, in fact, use mysqli_bind_parameter to pass a NULL value to the database. simply create a variable and store the NULL value (see the manpage for it) to the variable and bind that. Works great for me anyway.
Thus it'll have to be something like:
<?php
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'world');
// person is some object you have defined earlier
$name = $person->name();
$age = $person->age();
$nickname = ($person->nickname() != '') ? $person->nickname() : NULL;
// prepare the statement
$stmt = $mysqli->prepare("INSERT INTO Name, Age, Nickname VALUES (?, ?, ?)");
$stmt->bind_param('sis', $name, $age, $nickname);
?>
This should insert a NULL value into the database.

For anyone coming looking at this because they are having problems binding NULL in their WHERE statement, the solution is this:
There is a mysql NULL safe operator that must be used:
<=>
Example:
<?php
$price = NULL; // NOTE: no quotes - using php NULL
$stmt = $mysqli->prepare("SELECT id FROM product WHERE price <=> ?"); // Will select products where the price is null
$stmt->bind_param($price);
?>

The comments to the PHP documentation on mysqli_stmt::bind_param indicate that passing in NULL was not easily possible.
Please see #creatio's answer: https://stackoverflow.com/a/6892491/18771
Solutions offered in the comments do some pre-preparation work on the prepared statement, replacing the "?" markers with "NULL" for every param that has the PHP null value. The modified query string is then used.
The following function is from user comment 80119:
function preparse_prepared($sQuery, &$saParams)
{
$nPos = 0;
$sRetval = $sQuery;
foreach ($saParams as $x_Key => $Param)
{
//if we find no more ?'s we're done then
if (($nPos = strpos($sQuery, '?', $nPos + 1)) === false)
{
break;
}
//this test must be done second, because we need to
//increment offsets of $nPos for each ?.
//we have no need to parse anything that isn't NULL.
if (!is_null($Param))
{
continue;
}
//null value, replace this ? with NULL.
$sRetval = substr_replace($sRetval, 'NULL', $nPos, 1);
//unset this element now
unset($saParams[$x_Key]);
}
return $sRetval;
}
(It's not really the coding style I would have done it in, but if it works...)

I store all parameters in an array and pass them in bind_param function using array_shift($myArray). NULL is accepted like that.

<?php
$mysqli=new mysqli('localhost','root','','test');
$mysqli->query("CREATE TABLE test_NULL (id int(11))");
if($query=$mysqli->prepare("insert into test_NULL VALUES(?)")){
$query->bind_param('i',$null); //note that $null is undefined
$query->execute();
}else{
echo __LINE__.' '.$mysqli->error;
}
?>

Related

General error: 1366 Incorrect integer value: 'NULL' for column of type INT [duplicate]

I'm using this code and I'm beyond frustration:
try {
$dbh = new PDO('mysql:dbname=' . DB . ';host=' . HOST, USER, PASS);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}
catch(PDOException $e)
{
...
}
$stmt = $dbh->prepare('INSERT INTO table(v1, v2, ...) VALUES(:v1, :v2, ...)');
$stmt->bindParam(':v1', PDO::PARAM_NULL); // --> Here's the problem
PDO::PARAM_NULL, null, '', all of them fail and throw this error:
Fatal error: Cannot pass parameter 2 by reference in /opt/...
You need to use bindValue, not bindParam
bindParam takes a variable by reference, and doesn't pull in a value at the time of calling bindParam. I found this in a comment on the PHP docs:
bindValue(':param', null, PDO::PARAM_INT);
P.S. You may be tempted to do this bindValue(':param', null, PDO::PARAM_NULL); but it did not work for everybody (thank you Will Shaver for reporting.)
When using bindParam() you must pass in a variable, not a constant. So before that line you need to create a variable and set it to null
$myNull = null;
$stmt->bindParam(':v1', $myNull, PDO::PARAM_NULL);
You would get the same error message if you tried:
$stmt->bindParam(':v1', 5, PDO::PARAM_NULL);
When using INTEGER columns (that can be NULL) in MySQL, PDO has some (to me) unexpected behaviour.
If you use $stmt->execute(Array), you have to specify the literal NULL and cannot give NULL by variable reference.
So this won't work:
// $val is sometimes null, but sometimes an integer
$stmt->execute(array(
':param' => $val
));
// will cause the error 'incorrect integer value' when $val == null
But this will work:
// $val again is sometimes null, but sometimes an integer
$stmt->execute(array(
':param' => isset($val) ? $val : null
));
// no errors, inserts NULL when $val == null, inserts the integer otherwise
Tried this on MySQL 5.5.15 with PHP 5.4.1
For those who still have problems (Cannot pass parameter 2 by reference), define a variable with null value, not just pass null to PDO:
bindValue(':param', $n = null, PDO::PARAM_INT);
Hope this helps.
I had the same problem and I found this solution working with bindParam :
bindParam(':param', $myvar = NULL, PDO::PARAM_INT);
If you want to insert NULL only when the value is empty or '', but insert the value when it is available.
A) Receives the form data using POST method, and calls function insert with those values.
insert( $_POST['productId'], // Will be set to NULL if empty
$_POST['productName'] ); // Will be to NULL if empty
B) Evaluates if a field was not filled up by the user, and inserts NULL if that's the case.
public function insert( $productId, $productName )
{
$sql = "INSERT INTO products ( productId, productName )
VALUES ( :productId, :productName )";
//IMPORTANT: Repace $db with your PDO instance
$query = $db->prepare($sql);
//Works with INT, FLOAT, ETC.
$query->bindValue(':productId', !empty($productId) ? $productId : NULL, PDO::PARAM_INT);
//Works with strings.
$query->bindValue(':productName',!empty($productName) ? $productName : NULL, PDO::PARAM_STR);
$query->execute();
}
For instance, if the user doesn't input anything on the productName field of the form, then $productName will be SET but EMPTY. So, you need check if it is empty(), and if it is, then insert NULL.
Tested on PHP 5.5.17
Good luck,
Several answers have given examples of what you should do. But they haven't really explained why you should do one of those things.
The bindParam method is meant to be used with something like a loop (or just repeated statements). It binds a variable reference. So something like
$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
$stmt->bindParam(':v1', $i, PDO::PARAM_INT);
for ($i = 0; $i < 10; $i++) {
$stmt->execute();
}
Would insert values 0 through 9 in a table.
That's obviously a very simple example that could be implemented in other, more efficient ways. You could have more complex logic here. But the basic idea is that you bind a reference to a variable and then you can change the value of the variable.
You can get around the need for a reference by creating a variable before calling bindParam. But in your case, you don't particularly want to bind to a variable reference. You just want to bind a value. So go ahead and do exactly that with bindValue.
You can mostly just use bindValue. But to show why both methods exist, let's rewrite the previous example to use bindValue instead of bindParam:
$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
for ($i = 0; $i < 10; $i++) {
$stmt->bindValue(':v1', $i, PDO::PARAM_INT);
$stmt->execute();
}
This will work, but you have to call bindValue on every iteration of the loop whereas you only needed to call bindParam once. But you aren't doing anything like that, so you can just
$stmt->bindValue(':v1', null, PDO::PARAM_INT);
And everything will work, as stated in the accepted answer. Because you want to bind a value, not a variable reference.
Based on the other answers but with a little more clarity on how to actually use this solution.
If for example you have an empty string for a time value but you want to save it as a null:
if($endtime == ""){
$db->bind(":endtime",$endtime=NULL,PDO::PARAM_STR);
}else{
$db->bind("endtime",$endtime);
}
Notice that for time values you would use PARAM_STR, as times are stored as strings.
So you just need to add an extra If statement that properly changes your variable to NULL before you call bindParam(). Here is an example that I figured out for my situation (I was stuck on this for days trying to INSERT a new DB record with a NULL value for one column):
if ($this->companyid == 'NULL' || $this->companyid == NULL) {
$this->companyid = NULL;
$this->companyname = NULL;
$stmt->bindParam(':companyid', $this->companyid);
$stmt->bindParam(':companyname', $this->companyname);
} else {
$stmt->bindParam(':companyid', $this->companyid);
$stmt->bindParam(':companyname', $this->companyname);
}
Try This.
$stmt->bindValue(':v1', null, PDO::PARAM_NULL); // --> insert null
In my case I am using:
SQLite,
prepared statements with placeholders to handle unknown number of fields,
AJAX request sent by user where everything is a string and there is no such thing like NULL value and
I desperately need to insert NULLs as that does not violates foreign key constrains (acceptable value).
Suppose, now user sends with post: $_POST[field1] with value value1 which can be the empty string "" or "null" or "NULL".
First I make the statement:
$stmt = $this->dbh->prepare("INSERT INTO $table ({$sColumns}) VALUES ({$sValues})");
where {$sColumns} is sth like field1, field2, ... and {$sValues} are my placeholders ?, ?, ....
Then, I collect my $_POST data related with the column names in an array $values and replace with NULLs:
for($i = 0; $i < \count($values); $i++)
if((\strtolower($values[$i]) == 'null') || ($values[$i] == ''))
$values[$i] = null;
Now, I can execute:
$stmt->execute($values);
and among other bypass foreign key constrains.
If on the other hand, an empty string does makes more sense then you have to check if that field is part of a foreign key or not (more complicated).

PHP/mySQL INSERT NULL if variable is empty

i want to check if a variable from $_POST is empty and INSERT a NULL to my Database.
But when I do it my way i always get a String called NULL and not a real NULL in my data set.
This is how I tried it:
if(isset($_POST['folge'])){
$comment = !empty($_POST['comment']) ? "'".$_POST['comment']."'" : null;
$sqlstring = "INSERT INTO eventstest (comment) VALUES (".$comment.")";
echo $sqlstring;
if ($mysqli->query($sqlstring) === TRUE) {
printf("Table myCity successfully created.\n");
}else{
printf("Errorcode: %d\n", $mysqli->errno);
printf("Error: %d\n", $mysqli->error);
}
if I send the form without making inputs to "comment" page output is:
INSERT INTO eventstest (comment) VALUES ()Errorcode: 1136 Error: 0
Whats wrong? Or whats the better way to check for empty inputs and add NULL to DB?
PS: The database cell has STANDARD: NULL
If you want to insert a NULL value into MySQL, you have to pass a null-value in the SQL query, not the string of null. It will still be a string from a PHP perspective, but not from the MySQL perspective.
if (!empty($_POST['comment'])) {
$comment = "'".$mysqli->real_escape_string($_POST['comment'])."'";
} else {
$comment = "NULL";
}
You can also shorten that into a one-liner, using a ternary operator
$comment = !empty($_POST['comment']) ? "'".$mysqli->real_escape_string($_POST['comment'])."'" : "NULL";
Then, because you assign the quotes around the comment-string itself, as you should do, since you alternatively want to pass a null-value, you need to remove the single quotes surrounding the variable from the query. This would otherwise break it too, as you'd get ''comment''.
$sql = "INSERT INTO table (comment) VALUES (".$comment.")";
Of course this assumes that the column comment allows for null-values. Otherwise it will fail, in which case you should either insert empty strings or change the column to accept null values.
It should also be noted that this query is exposed to SQL injection attacks, and you should use an API that supports prepared statements - such as PDO or MySQLi, and utilize those to secure your database against these kinds of attacks. Using a prepared statement with MySQLi would look something like this. See how we supply a PHP null to the value in bind_param() if $_POST['comment'] is empty.
// Set MySQLi to throw exceptions on errors, thereby removing the need to individually check every query
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
if (isset($_POST['folge'])) {
// $comment = !empty($_POST['comment']) ? $_POST['comment'] : null; // Ternary operator
$comment = $_POST['comment'] ?? null; // Null coalescing operator, since PHP 7
$sql = "INSERT INTO eventstest (comment) VALUES (?)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("s", $comment);
$stmt->execute();
$stmt->close();
}
PHP Ternary Operator
How can I prevent SQL injection in PHP?
Several things wrong here. First is that you are using string concatenation instead of prepared statements. This leaves you open to SQL injection. I suggest you stop this project right now and return after learning to use PDO and prepared statements.
Secondly, 'NULL' != null you need to specify it as null
Last but not least, generally there isn't a need to explicitly check for null in postvars and then pass a null again. If the column type allows null and you do not pass in a non null value. null will be stored in it anyway

PHP/MySQL/PDO binding null parameter doesn't work

I am having trouble binding a null parameter in the following code
$nullVariable = NULL;
$sql = new PDO('mysql:host=' . $Server, $User, $Password);
$sql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$statement = $sql->prepare("SELECT * FROM Table WHERE Binary16Column = :uuid");
$statement->bindParam(":uuid", $nullVariable, PDO::PARAM_NULL);
$statement->execute();
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
The results variable will be a empty array. If I dont use parameters and modify my query to "WHERE Binary16Column IS NULL" it returns the expected number of rows. So the problem must be with how I am handling the parameter, rather than my SQL query.
My code is more complex than listed above, and I need to be able to use a parameter variable which may be null, so checking to see the variable is null and running a different query is less than ideal. Technically I have my own function for setting parameters, this is where I am checking if the contents of the variable is null, and binding the parameter appropriately, so I dont have to write an unnecessary number of queries. The query works also works fine if the variable contains valid data, and the parameter type is PARAM_LOB.
Does anyone know what i'm doing wrong? Thanks a lot!
Read up on three-valued logic. NULL is not a value; it is a marker for the absence of a value, and so NULL can never be equal to anything, including itself.
However, there is a null-safe comparison operator also known as the "spaceship operator," which does consider two nulls to be equivalent.
WHERE Binary16Column <=> :uuid
... should do what you expected.
If you want to select the record with Binary16Column is null, you need to use IS NULL as the condition, but not = NULL.
SELECT * FROM Table WHERE Binary16Column IS NULL
You need to do:
$uuid = /**some value**/;
$sql = new PDO('mysql:host=' . $Server, $User, $Password);
$sql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
if ($uuid === null) {
$statement = $sql->prepare("SELECT * FROM Table WHERE Binary16Column IS NULL");
} else {
$statement = $sql->prepare("SELECT * FROM Table WHERE Binary16Column = :uuid");
$statement->bindParam(":uuid", $uuid);
}
$statement->execute();
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
I think the reason you are not getting a result because NULL is a keyword. Because of the way MySQL treats NULL values, I think you are going to have to do IS NULL, when you are performing a search for NULL values. I did a bunch of tests in my local database where I have NULL values. The only time that it worked is when I was using IS NULL or IS NOT NULL.
I am sorry I can't be more help (or if I'm just telling you what you already know), but it seems like you are going to have to write separate queries, or perhaps some simple logic to concatenate the appropriate WHERE logic, depending on whether a variable is null or not.

PHP PDO fetch null

How do you check if a columns value is null? Example code:
$db = DBCxn::getCxn();
$sql = "SELECT exercise_id, author_id, submission, result, submission_time, total_rating_votes, total_rating_values
FROM submissions
LEFT OUTER JOIN submission_ratings ON submissions.exercise_id=submission_ratings.exercise_id
WHERE id=:id";
$st = $db->prepare($sql);
$st->bindParam(":id", $this->id, PDO::PARAM_INT);
$st->execute();
$row = $st->fetch();
$this->total_rating_votes = $row['total_rating_votes'];
if($this->total_rating_votes == null) // this doesn't seem to work even though there is no record in submission_ratings????
{
...
}
When you connect to the database, you can set some attributes to control how PDO handles Nulls and Empty Strings when they are returned by the database query
PDO::setAttribute (PDO::ATTR_ORACLE_NULLS, $option )
Where $option is one of the following:
PDO::NULL_NATURAL: No conversion.
PDO::NULL_EMPTY_STRING: Empty stringis converted to NULL.
PDO::NULL_TO_STRING: NULL is converted to an empty string.
Isnt it something like that that you want to do?
foreach($row as $r){
if($r->total_rating_votes == null){
//do something
}
Actually you might want to try:
if($r->total_rating_votes == ""){/*do something*/}
Because php might have converted the null value into an empty string, and then it's not actually null, it's ""
Hope this helps!
Thanks for all of your answers. After a bit of experimentation this code solved my problem
$this->total_rating_votes = $row['total_rating_votes'];
if(!isset($this->total_rating_votes)) // this is now true if this record had a NULL value in the DB!!!
{
...
}

Why I am getting Cannot pass parameter 2 by reference error when I am using bindParam with a constant value?

I'm using this code and I'm beyond frustration:
try {
$dbh = new PDO('mysql:dbname=' . DB . ';host=' . HOST, USER, PASS);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}
catch(PDOException $e)
{
...
}
$stmt = $dbh->prepare('INSERT INTO table(v1, v2, ...) VALUES(:v1, :v2, ...)');
$stmt->bindParam(':v1', PDO::PARAM_NULL); // --> Here's the problem
PDO::PARAM_NULL, null, '', all of them fail and throw this error:
Fatal error: Cannot pass parameter 2 by reference in /opt/...
You need to use bindValue, not bindParam
bindParam takes a variable by reference, and doesn't pull in a value at the time of calling bindParam. I found this in a comment on the PHP docs:
bindValue(':param', null, PDO::PARAM_INT);
P.S. You may be tempted to do this bindValue(':param', null, PDO::PARAM_NULL); but it did not work for everybody (thank you Will Shaver for reporting.)
When using bindParam() you must pass in a variable, not a constant. So before that line you need to create a variable and set it to null
$myNull = null;
$stmt->bindParam(':v1', $myNull, PDO::PARAM_NULL);
You would get the same error message if you tried:
$stmt->bindParam(':v1', 5, PDO::PARAM_NULL);
When using INTEGER columns (that can be NULL) in MySQL, PDO has some (to me) unexpected behaviour.
If you use $stmt->execute(Array), you have to specify the literal NULL and cannot give NULL by variable reference.
So this won't work:
// $val is sometimes null, but sometimes an integer
$stmt->execute(array(
':param' => $val
));
// will cause the error 'incorrect integer value' when $val == null
But this will work:
// $val again is sometimes null, but sometimes an integer
$stmt->execute(array(
':param' => isset($val) ? $val : null
));
// no errors, inserts NULL when $val == null, inserts the integer otherwise
Tried this on MySQL 5.5.15 with PHP 5.4.1
For those who still have problems (Cannot pass parameter 2 by reference), define a variable with null value, not just pass null to PDO:
bindValue(':param', $n = null, PDO::PARAM_INT);
Hope this helps.
I had the same problem and I found this solution working with bindParam :
bindParam(':param', $myvar = NULL, PDO::PARAM_INT);
If you want to insert NULL only when the value is empty or '', but insert the value when it is available.
A) Receives the form data using POST method, and calls function insert with those values.
insert( $_POST['productId'], // Will be set to NULL if empty
$_POST['productName'] ); // Will be to NULL if empty
B) Evaluates if a field was not filled up by the user, and inserts NULL if that's the case.
public function insert( $productId, $productName )
{
$sql = "INSERT INTO products ( productId, productName )
VALUES ( :productId, :productName )";
//IMPORTANT: Repace $db with your PDO instance
$query = $db->prepare($sql);
//Works with INT, FLOAT, ETC.
$query->bindValue(':productId', !empty($productId) ? $productId : NULL, PDO::PARAM_INT);
//Works with strings.
$query->bindValue(':productName',!empty($productName) ? $productName : NULL, PDO::PARAM_STR);
$query->execute();
}
For instance, if the user doesn't input anything on the productName field of the form, then $productName will be SET but EMPTY. So, you need check if it is empty(), and if it is, then insert NULL.
Tested on PHP 5.5.17
Good luck,
Several answers have given examples of what you should do. But they haven't really explained why you should do one of those things.
The bindParam method is meant to be used with something like a loop (or just repeated statements). It binds a variable reference. So something like
$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
$stmt->bindParam(':v1', $i, PDO::PARAM_INT);
for ($i = 0; $i < 10; $i++) {
$stmt->execute();
}
Would insert values 0 through 9 in a table.
That's obviously a very simple example that could be implemented in other, more efficient ways. You could have more complex logic here. But the basic idea is that you bind a reference to a variable and then you can change the value of the variable.
You can get around the need for a reference by creating a variable before calling bindParam. But in your case, you don't particularly want to bind to a variable reference. You just want to bind a value. So go ahead and do exactly that with bindValue.
You can mostly just use bindValue. But to show why both methods exist, let's rewrite the previous example to use bindValue instead of bindParam:
$stmt = $dbh->prepare('INSERT INTO t1 (v1) VALUES(:v1)');
for ($i = 0; $i < 10; $i++) {
$stmt->bindValue(':v1', $i, PDO::PARAM_INT);
$stmt->execute();
}
This will work, but you have to call bindValue on every iteration of the loop whereas you only needed to call bindParam once. But you aren't doing anything like that, so you can just
$stmt->bindValue(':v1', null, PDO::PARAM_INT);
And everything will work, as stated in the accepted answer. Because you want to bind a value, not a variable reference.
Based on the other answers but with a little more clarity on how to actually use this solution.
If for example you have an empty string for a time value but you want to save it as a null:
if($endtime == ""){
$db->bind(":endtime",$endtime=NULL,PDO::PARAM_STR);
}else{
$db->bind("endtime",$endtime);
}
Notice that for time values you would use PARAM_STR, as times are stored as strings.
So you just need to add an extra If statement that properly changes your variable to NULL before you call bindParam(). Here is an example that I figured out for my situation (I was stuck on this for days trying to INSERT a new DB record with a NULL value for one column):
if ($this->companyid == 'NULL' || $this->companyid == NULL) {
$this->companyid = NULL;
$this->companyname = NULL;
$stmt->bindParam(':companyid', $this->companyid);
$stmt->bindParam(':companyname', $this->companyname);
} else {
$stmt->bindParam(':companyid', $this->companyid);
$stmt->bindParam(':companyname', $this->companyname);
}
Try This.
$stmt->bindValue(':v1', null, PDO::PARAM_NULL); // --> insert null
In my case I am using:
SQLite,
prepared statements with placeholders to handle unknown number of fields,
AJAX request sent by user where everything is a string and there is no such thing like NULL value and
I desperately need to insert NULLs as that does not violates foreign key constrains (acceptable value).
Suppose, now user sends with post: $_POST[field1] with value value1 which can be the empty string "" or "null" or "NULL".
First I make the statement:
$stmt = $this->dbh->prepare("INSERT INTO $table ({$sColumns}) VALUES ({$sValues})");
where {$sColumns} is sth like field1, field2, ... and {$sValues} are my placeholders ?, ?, ....
Then, I collect my $_POST data related with the column names in an array $values and replace with NULLs:
for($i = 0; $i < \count($values); $i++)
if((\strtolower($values[$i]) == 'null') || ($values[$i] == ''))
$values[$i] = null;
Now, I can execute:
$stmt->execute($values);
and among other bypass foreign key constrains.
If on the other hand, an empty string does makes more sense then you have to check if that field is part of a foreign key or not (more complicated).

Categories