MySQL - Cloning then changing a field - Is this a race condition? - mysql

While debugging an issue in our software system, I came across a prepared statement that looks something like this:
"UPDATE Command SET expiredWhen=status, status='expired' WHERE id=?;"
The expiredWhen field is supposed to be set to whatever value is in the status field, then the status field is supposed to be set to "expired". It worked as expected in our MySQL 5.1 environment, but in MySQL 5.5 expiredWhen is being set to "expired".
What is the expected result of this statement? Is this a race condition, and we were lucky that it ever worked? Or is there defined behavior for this statement that changed from version 5.1 to 5.5?

The documentation is consistent between versions 5.1 and 5.5:
Single-table UPDATE assignments are generally evaluated from left to
right. For multiple-table updates, there is no guarantee that
assignments are carried out in any particular order
This suggests that it shouldn't be a race condition. That said, the word "generally" is a little concerning - it would probably be safest to assume that it could be a race condition.

Related

User-Defined Variable Assignment difference between MySQL and MariaDB

Of course, we do our job before and searched this community or the web for similar posts but we found nothing.
Issue:
We are not happy with our current hosting provider and have to make change. In the middle of our database switch from MySQL 5.7 to MariaDB 10.3, we have recognized a SET handling difference, which works perfect in MySQL.
SQL code:
SET #sb = 0;
SELECT art,sb
FROM ARTICLE
WHERE(#sb:=sb) > 700 AND #sb <= 1000 >AND art = 'AM';
MySQL result:
art
sb
AM
900.00
AM
960.00
AM
1000.00
AM
770.00
AM
800.00
the list is much longer...
MariaDB result:
art
sb
AM
770.00
AM
960.00
AM
1200.00
AM
3000.00
only 10 rows...
In fact there is a difference between them.
But we want to know:
Can we configure this in MariaDB, that we get the same result in MySQL?
Should we replace the Set handling in MariaDB with another one?
No, this is not possible.
In fact, the MySQL documentation warns about the use of variable assignments inside SQL statements:
The order of evaluation for expressions involving user variables is undefined. For example, there is no guarantee that SELECT #a, #a:=#a+1 evaluates #a first and then performs the assignment.
What's more, this is a "feature" that is subject to removal:
Previous releases of MySQL made it possible to assign a value to a user variable in statements other than SET. This functionality is supported in MySQL 8.0 for backward compatibility but is subject to removal in a future release of MySQL.
So you should not rely on that, and rewrite your queries.
The MySQL documentation is quite clear on this point:
As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement.
That your code ever worked was something of a coincidence. The reason for such warnings is that behavior may change between releases; or even that the results may be inconsistent between runs on the same database.
You should replace the variables with window functions. The exact function is a bit unclear. Sample data, desired results, and a clear explanation of the results always helps.

Division by zero handled differently

I am not sure if this has been answered before in S.O, but I couldn't find any answers googling to explain this behaviour in various databases. so I thought I would clarify this here from experts.
When I run this query in Oracle, Postgresql, mysql and sql-server, I get varying results.
Oracle 11g
select count(1/0) from dual;
ORA-01476: divisor is equal to zero
http://sqlfiddle.com/#!4/e0ee9e/751
SQL-Server 2017
select count(1/0) ;
1
but select 1/0 gives error.
Divide by zero error encountered
http://sqlfiddle.com/#!18/2be41/2
http://sqlfiddle.com/#!18/185a6/7
PostgreSQL 9.6
ERROR: division by zero
http://sqlfiddle.com/#!17/340e0/80
Mysql 5.6
select count(1/0) ;
0
http://sqlfiddle.com/#!9/2be41/10
Why these differences in implementations?
count() counts the number of rows or the non-NULL values of the expression.
When the expression is constant, then it returns the number of rows.
What you are seeing is the question of when and whether the constant is evaluated, and the different way that the databases handle this situation.
Oracle and Postgres are clearly trying to evaluate the constant. To be honest, I'm not sure if these are run-time or compile-time errors.
SQL Server postpones the evaluation until needed -- and it is never needed. So, it counts the number of rows.
MySQL is the strange one. It returns NULL for divide-by-zero. This is not standard behavior, but also not entirely unreasonable. COUNT(NULL) returns 0.
In mysql there is a specific mode (enabled or disable)
ERROR_FOR_DIVISION_BY_ZERO
that manage this situation and in your case seems
disable (you should check for proper mode value) so you apparently see a wrong (or unexpected result) but is simply a configuration for error handling
https://dev.mysql.com/doc/refman/5.7/en/precision-math-expressions.html

MySQL history ignore

So I recently updated to mysql 5.7 from 5.1, and now I notice on this version the up/down arrows do not cycle through the whole history. According to the mysql docs, certain commands related to passwords are no longer logged, but in my case there seems to be a lot more than that. Most of my queries are not logged at all and they are just simple queries like:
select table1.email, table1.password, table2.id, table2.ip from table1 join table2 on table1.property1 = table2.property2 order by table2.id;
I eventually figured out this is because I have a column called 'password' in one of my tables and mysql will not log this query at all just because it contains the word password. I've gone through a lot of googling, bug reports, mysql docs but there is not a single piece of information about disabling this history ignore behavior, or at least removing some rules from it, there's only instructions on how to add even more rules.
Is there any know solution for this, or do I have to rename my columns and change my stored procedures?
This is not a bug, this is a feature.
mysql ignores for logging purposes statements that match any pattern in the “ignore” list. By default, the pattern list is "*IDENTIFIED*:*PASSWORD*", to ignore statements that refer to passwords.
https://dev.mysql.com/doc/refman/5.7/en/mysql-logging.html
You can add patterns to the list, but not remove them.
https://bugs.mysql.com/bug.php?id=71987

MySQL allow to select column that is neither aggregate nor specified in group by. Can this behavior be disabled?

It seems to me like MySQL is setting up a subtle trap for me. It allows to select column that is neither aggregate function like sum nor specified in group by clause. Therefor value in that column from my point of view is totally random.
I think in Oracle database that wasn't possible (but I used it like 5 years ago so something might change in this regard).
Is there any way to prevent from such nasty/trap-a-like behavior or at least tell it to warn me? Tools should work with developer not set up traps.
I'm using MySQL 5.6.30 and MySQL Workbench 6.3.7
The behaviour you describe is known. MySQL gives the correct result set for any valid and sane query. It can return unexpected data on queries that make no sense.

UPSERT compatible with mySql and postgreSQL

I've been googling and trying different queries but I haven't managed to get the UPSERT query to work with both mySql and postgreSql. Does anyone here know how to do it?
There is not one, and cannot be one. PostgreSQL doesn't have an UPSERT statement. The new 9.5 statement INSERT ... ON CONFLICT UPDATE ... is syntactically different from MySQL's INSERT ... ON DUPLICATE KEY UPDATE ... because it's also semantically different, i.e. it works differently.
The newer PostgreSQL statement, added in 9.5, was designed with an awareness of MySQL's syntax. The decision not to use the same syntax was intentional because MySQL's statement has big ambiguities around handling of multiple unique indexes etc, where it basically shrugs and says "dunno".
You can possibly hide it behind stored functions, but really, this is why your application should be able to run different SQL on a different DBMS. Trying to always write one statement that works for all target DBMSes is a lost cause even if you're just targeting MySQL and PostgreSQL.