MySQLi prepared statements and REPLACE INTO - php

I have to following code:
http://www.nomorepasting.com/getpaste.php?pasteid=22987
If PHPSESSID is not already in the table the REPLACE INTO query works just fine, however if PHPSESSID exists the call to execute succeeds but sqlstate is set to 'HY000' which isn't very helpful and $_mysqli_session_write->errno and
$_mysqli_session_write->error are both empty and the data column doesn't update.
I am fairly certain that the problem is in my script somewhere, as manually executing the REPLACE INTO from mysql works fine regardless of whether of not the PHPSESSID is in the table.

Why are you trying to doing your prepare in the session open function? I don't believe the write function is called more then once during a session, so preparing it in the open doesn't do much for you, you might as well do that in your session write.
Anyway I believe you need some whitespace after the table name, and before the column list. Without the whitespace I believe mysql would act as if you where trying to call the non-existent function named session().
REPLACE INTO session (phpsessid, data) VALUES(?, ?)
MySQL sees no difference between
'COUNT ()' and 'COUNT()'
Interesting, when I run the below in the mysql CLI I seem to get a different result.
mysql> select count (*);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '*)' at line 1
mysql> select count(*);
+----------+
| count(*) |
+----------+
| 1 |
+----------+
1 row in set (0.00 sec)

REPLACE INTO executes 2 queries: first a DELETE then an INSERT INTO.
(So a new auto_increment is "By Design")
I'm also using the REPLACE INTO for my database sessions, but I'm using the MySQLi->query() in combination with MySQLI->real_escape_string() in stead of a MySQLi->prepare()

So as it turns out there are other issues with using REPLACE that I was not aware of:
Bug #10795: REPLACE reallocates new AUTO_INCREMENT (Which according to the comments is not actually a bug but the 'expected' behaviour)
As a result my id field keeps getting incremented so the better solution is to use something along the lines of:
INSERT INTO session(phpsessid, data) VALUES('{$id}', '{$data}')
ON DUPLICATE KEY UPDATE data='{$data}'
This also prevents any foreign key constraints from breaking and potential causing data integrity problems.

Related

PHP / MySQL query stopped working for no apparent reason

I've got a page that alters a large quantity of variables in my MySQL database, and so far they've all worked great, but now all of the queries inside of a single logic-gate have stopped working for no apparent reason.
I've confirmed the following:
- The variable posted and used in the "if" statement of the gate is as it was intended
- The logic gate is triggered as intended (I can echo stuff and etc inside of it).
- The database connection is established, I am successfully running queries of various types before and after this logic gate on the same connection variable.
- The connection user has ALL PRIVILEDGES enabled, the aforementioned queries surrounding this logic gate are using similar functions successfully.
Here's the logic gate:
if (!empty($_POST["addqual"])){
$coladqual = $_POST["addqual"];
$sqlf = "ALTER TABLE users ADD UNIQUE ('$coladqual') INT( 2 ) NOT NULL";
$conn->query($sqlf);
$sqlc = "INSERT INTO competencebonus (competence,bonus)
VALUES ($coladqual,0)";
$conn->query($sqlc);
}
I've tried multiple alterations, but they don't seem to execute no matter what I do. I've got at least 20 other queries in other logic gates before and after these two and there seems to be virtually no difference between them, apart from these two just not working at all.
EDIT - Here's the error (Thanks to all of you who provided me with the error report syntax)
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''TestAA') INT( 2 ) NOT NULL UNIQUE' at line 1
What strikes me as odd is that only the closing parenthesis is around the post input (TestAA). Is it supposed to have both or neither?
I tried changing the syntax and got the following error:
Duplicate entry '0' for key 'TestAB'
The syntax was:
$sqlf = "ALTER TABLE users ADD `$coladqual` INT( 2 ) NOT NULL UNIQUE";
FINAL EDIT:
Made it work. Deleted the "NOT NULL" statement as recommended by Jeff Pucket II. Somehow this combined with the deletion of the parenthesis and use of backticks instead of apostrophe made the thing work.
Thanks for those of you who had the patience to help me with this.
It looks like you're trying to alter an existing table with a unique not null column. I would expect this to fail if any rows already exist in the table, unless your engine imputes zero. Even then this would fail if there were more than one record because of the unique constraint. Also make sure that the column name being added doesn't already exist.
To get the error using MySQLi, try:
$result = $conn->query($sqlf) or die($conn->error);
The 'unique' constraint should go at the end of your query
$sqlf = "ALTER TABLE users ADD ('$coladqual') INT( 2 ) NOT NULL UNIQUE";
Error checking depends on what flavor of MySQL you're running

Trouble with creating simple MySQL Trigger on Update

(Edit: Apologies for the funky formatting. SO was not having my code formatted.)
I feel dumb, but i've been racking my brain for this for longer then I like to admit.
I need a trigger that, when any update to menu_button is made, it updates a field in soa_config to the datetime the update was made (parameterValue is a Varchar). I've tried doing it as simple as setting parameterValue='1', no dice either. Running the update by itself, and the trigger, sans everything, and both work. It's the combination of the two that makes it hard. I get the error
"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'END' at line 1".
delimiter |
CREATE TRIGGER imatrigger AFTER UPDATE on `menu_button`
FOR EACH ROW
BEGIN
UPDATE soa_config
SET parameter_value = 'CURRENT_TIMESTAMP'
WHERE parameter_name = last_menu_update_itme
END
|
delimiter ;
I've looked at:
Quick MySQL Trigger Update
Trouble in creating Trigger in MySQL
MYSQL trigger trouble
sql creating a trigger on update
and more googling.
You need a statement delimiter after the WHERE clause and before the END statement
i.e. you should have a semi-colon here: WHERE parameter_name = last_menu_update_itme; <----

Why does this not error out

I'm working with mysql and php and I'm attempting to test the error handling of a call, but I can't figure out why this doesn't give an error. I'm executing the following line:
if (! mysql_query("UPDATE Accounts SET disabled='0' WHERE id='15'")) { ... }
Here's the scenario... There is a table called 'Accounts', but there isn't a record with an id of 15 (which is the primary key). I have tried this from the command line and via a web browser, but this line executes without problems. I checked the php manual for this and here's a quote from their pages:
For other type of SQL statements, INSERT, UPDATE, DELETE, DROP, etc, mysql_query() returns TRUE on success or FALSE on error.
Why is this not generating an error? Any help would greatly be appreciated!
The query is not failing.
Just because the ID doesn't exist, doesn't mean that the query fails. Mysql successfully looked for the record, found none, and didn't apply any action. This is much different that what the !mysql_query statement suggests. That implies that mysql was unable to run your command.
Here your command ran successfully, just didn't affect your table due to the nonexistent row.
Your query will update no record.
This is not an error, it happens every time the conditions in the WHERE clause are not met.
There are many ways to cause your query to fail. One of them would be to use a non-existing field:
UPDATE Accounts SET blablabla='0' WHERE id='15'
There's a difference between an empty result set, and an error. A query which results in no changes is NOT an error, it's simply a valid result that happens to be empty, e.g.
this can never return anything:
mysql> select now() from dual where 1=0;
Empty set (0.01 sec)
but is still not an error. It's just an empty set. By comparison, this will always return one row:
mysql> select now() from dual where 1=1;
+---------------------+
| now() |
+---------------------+
| 2013-05-03 09:51:19 |
+---------------------+
1 row in set (0.00 sec)
and then there's errors. This will not return an empty set, because the query itself failed at the parser level:
mysql> select now() from dual where abc=def;
ERROR 1054 (42S22): Unknown column 'abc' in 'where clause'

Inserting data in MySQL table only if it doesn't exist already: trouble with INSERT IGNORE

so I've been looking around StackOverflow and the MySQL manual for a while, and I can't seem to solve my problem. What I'm trying to do is simply make my data INSERT function such that it doesn't add anything to my table if it already exists. I saw a few methods: the INSERT IGNORE function, together with a unique index, was the one that seemed the best for me, but I have no idea why it is not working... Here is a portion of my code (I have two columns: 'username' and 'email', and my table is called 'info4'):
$unique_index = mysqli_query ($con, "CREATE UNIQUE INDEX index ON info4 ( username, email);");
$insert = mysqli_query($con, "INSERT IGNORE INTO info4 (`username`, `email`) VALUES ('$array_values[0]', '$array_values[1]')");
Where am I going wrong? Am I missing anything?
Insert Ignore simply tells MySQL to not issue a duplicate key error when trying to insert duplicate data into fields with unique constraints.
The unique index should be all you need to stop duplicate entries from being inserted.
Generally you wouldn't want to do things like add indexes using PHP, you should do that via whatever tool you are using to access your database.
Are you seeing duplicate username/email combos being inserted into your table? What are the values of $array_values[0] and $array_values[1] ?
If you do SHOW INDEX FROM info4 do you see your unique index?
You can't create an index named index.
Mysql is not case sensitive. Your INDEX wrongly named "index" won't work.
Example:
mysql> create index index on tbladresse (strasse_zusatz);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use near 'index
on tbladresse (strasse_zusatz)' at line 1
mysql> create index iindex on tbladresse (strasse_zusatz);
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0

PDO + PHP lastInsertId() issue

Wouldn't there be a problem with it if for example when a user clicks on a link, a new row is automatically inserted and then the php code requests the last inserted id, and at the same time another row is inserted by another user, so the returned id is actually not the one I'm expecting..?
Am I wrong? Is there a way to do the same without that 'security' hole?
(like maybe from within the prepared statement or something...)
P.S the id is automatically generated.
Thank you.
As mentioned in the manual:
LAST_INSERT_ID() (with no argument) returns a BIGINT (64-bit) value representing the first automatically generated value that was set for an AUTO_INCREMENT column by the most recently executed INSERT statement to affect such a column. For example, after inserting a row that generates an AUTO_INCREMENT value, you can get the value like this:
mysql>SELECT LAST_INSERT_ID();
->195
The currently executing statement does not affect the value of
LAST_INSERT_ID(). Suppose that you generate an AUTO_INCREMENT value
with one statement, and then refer to LAST_INSERT_ID() in a
multiple-row INSERT statement that inserts rows into a table with its
own AUTO_INCREMENT column. The value of LAST_INSERT_ID() will remain
stable in the second statement; its value for the second and later
rows is not affected by the earlier row insertions. (However, if you
mix references to LAST_INSERT_ID() and LAST_INSERT_ID(expr), the
effect is undefined.)
If the previous statement returned an error, the value of
LAST_INSERT_ID() is undefined. For transactional tables, if the
statement is rolled back due to an error, the value of
LAST_INSERT_ID() is left undefined. For manual ROLLBACK, the value of
LAST_INSERT_ID() is not restored to that before the transaction; it
remains as it was at the point of the ROLLBACK.
So, LAST_INSERT_ID() is always transaction-safe (even though you don't use transaction).
The MySQL Server transfers the insert ID as part of the OK message after a successful INSERT. This ID is stored in PDO, therefore without a round-trip to the server PDO can return you the correct ID for your connection in a safe way.
Reference: http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#OK_Packet
To counteract this you would use a transaction.
This would essentially isolate your insert from others, so as long as your Insert/lastInsertId() call is within the same transaction, it will work just fine.

Categories