I am making a registration page. I’m worrying about concurrency issues where two users register with the same username at the same time (know it’s real rare, but hate having small flaws in code).
So my approach is, check if the username exists, and if not, insert a new row. I’m using PDO
What I’ve tried
I’m using transactions, but from my question here How exactly do transactions with PHP PDO work with concurrency? it appears that two transactions can read at the same time
- I don’t think I can use
select...for updatebecause I am not updating; in fact, I need to lock an “imaginary” row where a new entry will be added, if it does not exist already - I’ve tried googling some examples, but they don’t seem to handle the concurrency issue mentioned above http://php.about.com/od/finishedphp1/ss/php_login_code_2.htm
Solution?
I’ve googled and added a UNIQUE constraint on the username field, but do not want to rely on a MySQL error to rollback the transaction. I’m no expert, but it just doesn’t feel elegant to me. So is there a way to insert if not exists with pure MySQL?
Now, if this really is the way to go, I’ve got a couple questions. If a warning is thrown, does that stop the queries? Like will it throw the exception shown in the example here PHP + MySQL transactions examples ? Or will only a downright-error throw the exception?
Also, it seems to imply here Can I detect and handle MySQL Warnings with PHP? that warnings will show up somehow in the PHP output, but I’ve never had “visible MySQL errors.” The question was not using PDO like in my code, but I was just wondering. Do MySQL errors and warnings make html output? Or does it only say something if I callerrorInfo()?
Thanks
UPDATE
I think youre over thinking this. You can show a different error to the user for each one but you can really only detect one at a time because mysql is only going to give you one error message with the first problem it encounters. Assuming the previous query (to insert all values at once):
Now the hitch with that is the error message is going to look something like:
Duplicate entry 'THE_VALUE_YOU_TRIED_TO_INSERT' for key THE_KEY_NAMEThe way to get more meaningful reporting on which column it was is to name the indexes with something meanigful when you create the table for example:
so now your error message will look like:
Duplicate entry 'THE_VALUE_YOU_TRIED_TO_INSERT' for key uk_usernameOR
Duplicate entry 'THE_VALUE_YOU_TRIED_TO_INSERT' for key uk_emailWhich is easily parsable.
The only real alternative to doing it this way is to do a select on table before you insert to ensure the values dont already exist.
If youre using PDO you shouldnt have PHP warnings… Just exceptions, which if uncaught will generate a standard php error. IF you have
display_errorsturned off then that wont be output to the screen, only to the error log.I dont think there is a way to
insert if not existsso you are back to using a unique key and then catching the exception: