From:             tim at moocowproductions dot org
Operating system: Gentoo Linux & Mac OS X Leopard
PHP version:      5.2.5
PHP Bug Type:     MySQLi related
Bug description:  prepared statements not sending correct data to MySQL when 
using transactions

Description:
------------
When I am trying to run a transaction, with inserts that depend on 
each other, I get the following:

Cannot add or update a child row: a foreign key constraint fails 
(`q2test/UserPermissions`, CONSTRAINT `up_userid_fk` FOREIGN KEY 
(`userID`) REFERENCES `Users` (`userID`) ON DELETE CASCADE)

I thought it might be MySQL at first, but when I tried to prepare the 
statements by hand in the command-line interface, I did not have this 
problem. The issue seems to be that it is trying to set the userID 
variable to 0, when in fact it should be LAST_INSERT_ID.

Now if I grab LAST_INSERT_ID and put it into a variable, I get the 
correct number. But if I try to execute the second query, it appears 
to set it back to 0 for some reason. I turned on the MySQL general log 
which indicates that a quit is being issued after the prepare (so PHP 
isn't getting as far as to execute the statement):

071122 19:53:39      31 Connect     [EMAIL PROTECTED] on q2test
                     31 Query       set autocommit=0
                     31 Prepare     [1] INSERT INTO Users (username, 
firstName, lastName, password, phone, email, role) VALUES (?, ?, ?, ?, 
?, ?,?)
                     31 Execute     [1] INSERT INTO Users (username, 
firstName, lastName, password, phone, email, role) VALUES ('test', 
'test', 'test', 'NI!GpasswordNI!G', '555', 'email','estimator')
                     31 Prepare     [2] INSERT INTO UserPermissions 
        VALUES( userID=?,
                canAddBuilders=?,
                canAddAddresses=?,
                canAddPlans=?,
                canAddUsers=?,
                canModifyBuilders=?,
                canModifyAddresses=?,
                canModifyPlans=?,
                canModifyUsers=?,
                canDeleteBuilders=?,
                canDeleteAddresses=?,
                canDeletePlans=?,
                canDeleteUsers=?,
                canAssignPlans=?
                )
                     31 Quit       

Now I tried to set specific values in the code to insert another 
userID and I also tried to remove the foreign key restriction. When 
this happens, both INSERTS complete, but the insert in the 
UserPermissions table has a userID of instead of the one from the 
previous insert (or one that I hard-code in for testing).

Reproduce code:
---------------
The code that generates this is:

<?php
include '../htdocs/global.inc.php';

$dbHost = '127.0.0.1';
$dbDatabase = 'test';
$dbUsername = 'test';
$dbPassword = '?????';

$mysqli = new mysqli($dbHost, $dbUsername, $dbPassword, $dbDatabase)
        or die("Cannot connect to database");

function generateSalt($length = 4)
{
        $chars =
"[EMAIL PROTECTED]&*-+";
    $code = "";
    while (strlen($code) < $length) {
        $code .= $chars[mt_rand(0,strlen($chars))];
    }
    return $code;
}

function addUser($username, $firstName, $lastName, $role, $phone, $email,
$password)
{
        global $mysqli;
        $salt =generateSalt();
        $password = $salt.$password.$salt;
        $stmt =  $mysqli->stmt_init();
        $stmt->prepare("INSERT INTO Users (username, firstName, lastName,
password, phone, email, role) VALUES (?, ?, ?, ?, ?, ?,?)") or
die($mysqli->error);
        $stmt->bind_param('sssssss', $username, $firstName, $lastName, 
$password,
$phone, $email, $role);
        if(!$stmt->execute())
        {
                $stmt->close();
                return 0;
        }
        $stmt->close();
        echo $mysqli->thread_id."\n";
        return $mysqli->insert_id;
}       

function addUserPermissions($userID, $canAddBuilders, $canAddAddresses,
$canAddPlans, $canAddUsers, $canModifyBuilders, $canModifyAddresses,
$canModifyPlans, $canModifyUsers, $canDeleteBuilders, $canDeleteAddresses,
$canDeletePlans, $canDeleteUsers, $canAssignPlans)
{       
        global $mysqli;
        $stmt =  $mysqli->stmt_init();
        $stmt->prepare("INSERT INTO UserPermissions 
        VALUES( userID=?,
                canAddBuilders=?,
                canAddAddresses=?,
                canAddPlans=?,
                canAddUsers=?,
                canModifyBuilders=?,
                canModifyAddresses=?,
                canModifyPlans=?,
                canModifyUsers=?,
                canDeleteBuilders=?,
                canDeleteAddresses=?,
                canDeletePlans=?,
                canDeleteUsers=?,
                canAssignPlans=?
                )");
        $stmt->bind_param('isssssssssssss', $userID, $canAddBuilders,
$canAddAddresses, $canAddPlans, $canAddUsers, $canModifyBuilders,
$canModifyAddresses, $canModifyPlans, $canModifyUsers, $canDeleteBuilders,
$canDeleteAddresses, $canDeletePlans, $canDeleteUsers, $canAssignPlans);
        if(!$stmt->execute())
        {
                echo $mysqli->thread_id."\n";
                echo $stmt->error;
                $stmt->close();
                return false;
        }
        $stmt->close();
        echo $mysqli->thread_id."\n";
        return true;
}


echo $mysqli->thread_id."\n";
$mysqli->autocommit(FALSE);

$userID = addUser('test', 'test', 'test', 'estimator', '555', 'email',
'password');
if($userID == 0)
        $mysqli->rollback();
addUserPermissions($userID, 'n', 'n', 'n', 'n', 'n', 'n', 'n', 'n', 'n',
'n', 'n', 'n', 'n');
$mysqli->close();


And the schema is:


mysql> show create table Users\G
*************************** 1. row ***************************
       Table: Users
Create Table: CREATE TABLE `Users` (
  `userID` int(10) unsigned NOT NULL auto_increment,
  `username` varchar(32) NOT NULL,
  `password` varchar(32) NOT NULL,
  `salt` varchar(4) NOT NULL,
  `firstName` varchar(32) NOT NULL,
  `lastName` varchar(48) NOT NULL,
  `role` enum('Admin','Webmaster','Estimator') NOT NULL default
'Estimator',
  `phone` varchar(40) default NULL,
  `email` varchar(255) NOT NULL,
  PRIMARY KEY  (`userID`),
  UNIQUE KEY `username_idx` USING BTREE (`username`),
  UNIQUE KEY `firstlastname_idx` (`firstName`,`lastName`)
) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8
1 row in set (0.08 sec)

mysql> show create table UserPermissions\G
*************************** 1. row ***************************
       Table: UserPermissions
Create Table: CREATE TABLE `UserPermissions` (
  `userID` int(10) unsigned NOT NULL,
  `canAddBuilders` enum('Y','N') NOT NULL default 'N',
  `canAddAddresses` enum('Y','N') NOT NULL default 'N',
  `canAddPlans` enum('Y','N') NOT NULL default 'N',
  `canAddUsers` enum('Y','N') NOT NULL default 'N',
  `canModifyBuilders` enum('Y','N') NOT NULL default 'N',
  `canModifyAddresses` enum('Y','N') NOT NULL default 'N',
  `canModifyPlans` enum('Y','N') NOT NULL default 'N',
  `canModifyUsers` enum('Y','N') NOT NULL default 'N',
  `canDeleteBuilders` enum('Y','N') NOT NULL default 'N',
  `canDeleteAddresses` enum('Y','N') NOT NULL default 'N',
  `canDeletePlans` enum('Y','N') NOT NULL default 'N',
  `canDeleteUsers` enum('Y','N') NOT NULL default 'N',
  `canAssignPlans` enum('Y','N') NOT NULL default 'N',
  PRIMARY KEY  (`userID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.01 sec)

Expected result:
----------------
Both inserts to complete and the transaction is committed.

Actual result:
--------------
First INSERT completes but the second fails with this:

Cannot add or update a child row: a foreign key constraint fails 
(`q2test/UserPermissions`, CONSTRAINT `up_userid_fk` FOREIGN KEY 
(`userID`) REFERENCES `Users` (`userID`) ON DELETE CASCADE)







-- 
Edit bug report at http://bugs.php.net/?id=43381&edit=1
-- 
Try a CVS snapshot (PHP 4.4): 
http://bugs.php.net/fix.php?id=43381&r=trysnapshot44
Try a CVS snapshot (PHP 5.2): 
http://bugs.php.net/fix.php?id=43381&r=trysnapshot52
Try a CVS snapshot (PHP 5.3): 
http://bugs.php.net/fix.php?id=43381&r=trysnapshot53
Try a CVS snapshot (PHP 6.0): 
http://bugs.php.net/fix.php?id=43381&r=trysnapshot60
Fixed in CVS:                 http://bugs.php.net/fix.php?id=43381&r=fixedcvs
Fixed in release:             
http://bugs.php.net/fix.php?id=43381&r=alreadyfixed
Need backtrace:               http://bugs.php.net/fix.php?id=43381&r=needtrace
Need Reproduce Script:        http://bugs.php.net/fix.php?id=43381&r=needscript
Try newer version:            http://bugs.php.net/fix.php?id=43381&r=oldversion
Not developer issue:          http://bugs.php.net/fix.php?id=43381&r=support
Expected behavior:            http://bugs.php.net/fix.php?id=43381&r=notwrong
Not enough info:              
http://bugs.php.net/fix.php?id=43381&r=notenoughinfo
Submitted twice:              
http://bugs.php.net/fix.php?id=43381&r=submittedtwice
register_globals:             http://bugs.php.net/fix.php?id=43381&r=globals
PHP 3 support discontinued:   http://bugs.php.net/fix.php?id=43381&r=php3
Daylight Savings:             http://bugs.php.net/fix.php?id=43381&r=dst
IIS Stability:                http://bugs.php.net/fix.php?id=43381&r=isapi
Install GNU Sed:              http://bugs.php.net/fix.php?id=43381&r=gnused
Floating point limitations:   http://bugs.php.net/fix.php?id=43381&r=float
No Zend Extensions:           http://bugs.php.net/fix.php?id=43381&r=nozend
MySQL Configuration Error:    http://bugs.php.net/fix.php?id=43381&r=mysqlcfg

Reply via email to