views:

338

answers:

3

I have created a table with NOT NULL constraints on some columns in MySQL. Then in PHP I wrote a script to insert data, with an insert query. When I omit one of the NOT NULL columns in this insert statement I would expect an error message from MySQL, and I would expect my script to fail. Instead, MySQL inserts empty strings in the NOT NULL fields. In other omitted fields the data is NULL, which is fine. Could someone tell me what I did wrong here?

I'm using this table:

CREATE TABLE IF NOT EXISTS tblCustomers (
  cust_id int(11) NOT NULL AUTO_INCREMENT,
  custname varchar(50) NOT NULL,
  company varchar(50),
  phone varchar(50),
  email varchar(50) NOT NULL,
  country varchar(50) NOT NULL,
  ...
  date_added timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (cust_id)
) ;

And this insert statement:

$sql = "INSERT INTO tblCustomers (custname,company) 
        VALUES ('".$customerName."','".$_POST["CustomerCompany"]."')";
$res = mysqli_query($mysqli, $sql);

Or using bind variables:

$stmt = mysqli_prepare($mysqli, "INSERT INTO tblCustomers (custname,company, email, country) VALUES (?, ?, ?, ?)");

mysqli_stmt_bind_param($stmt, 'ssss', $customerName, $_POST["CustomerCompany"], $_POST["CustomerEmail"], $_POST["AddressCountry"]);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
+4  A: 

What you are doing wrong is building your query using strings instead of using bind parameters.

Aside from the SQL injection vulnerability, the null values are being converted to empty strings even before the database sees them.

$x = null;
print_r("VALUES ('$x', 42)");

Outputs:

VALUES ('', 42)

In other words, you are inserting an empty string, not a NULL. To insert a NULL you would have needed to write this:

VALUES (NULL, 42)

If you use bind parameters then you won't get this problem and as a bonus your site won't have so many security holes. I suggest you read the answer to this question and follow the advice there. This will solve your immediate problem as well as improving the security of your site.

Mark Byers
I must admit that using bind parameters is better, but it doesn't solve my problem yet. I'll look into the strict mode setting now...
Marga Keuvelaar
+1  A: 
AJ
jmucchiello
+2  A: 

If you're sure you're not using explicit default values, then check your strict mode:

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

MySQL Data Type Default Values

As of MySQL 5.0.2, if a column definition includes no explicit DEFAULT value, MySQL determines the default value as follows:

If the column can take NULL as a value, the column is defined with an explicit DEFAULT NULL clause. This is the same as before 5.0.2.

If the column cannot take NULL as the value, MySQL defines the column with no explicit DEFAULT clause. For data entry, if an INSERT or REPLACE statement includes no value for the column, or an UPDATE statement sets the column to NULL, MySQL handles the column according to the SQL mode in effect at the time:

  • If strict SQL mode is not enabled, MySQL sets the column to the implicit default value for the column data type.

  • If strict mode is enabled, an error occurs for transactional tables and the statement is rolled back. For nontransactional tables, an error occurs, but if this happens for the second or subsequent row of a multiple-row statement, the preceding rows will have been inserted.

Server SQL Modes

Marcus Adams
The strict mode did it! Thanks! At least when I omit a value the record isn't inserted. When $_POST["CustomerEmail"] is empty, the record is still inserted, with an empty string, but that wasn't the scope of this question. The search to prevent the insert of an empty string goes on.
Marga Keuvelaar
To answer your second question then, you should use parameter binding, as explained in other answers, but think about this. You don't need '.' concat operator to build query when using double quotes. You can do this "VALUES ($customerEmail)". Therefore: if (! empty($_POST['CustomerEmail']) { $customerEmail = "'$_POST['CustomerEmail']'"; } else { $customerEmail = "NULL";}
Marcus Adams
Thanks for the advice, I already used parameter binding, when Mark Byers posted it I changed my code, and... I already do check on the php side whether it's empty or not. I'm searching for a database check, like a check constraint I used with Oracle databases. To be honest: I wouldn't need a check constraint in Oracle, since an empty string is treated as NULL in Oracle. An empty string is just as bad to me as NULL in this case, so when I still can insert an empty string, the NOT NULL constraint is rather useless to me. Maybe I should post a new question about this.
Marga Keuvelaar