I don't understand the reason why this happens.
Could somebody explain it?
SELECT * FROM users WHERE name = ''
returns 0 records as it should
SELECT * FROM users WHERE name = 'janet'
returns 1 record as it should
SELECT * FROM users WHERE name = ''-SLEEP(3)
returns EVERY record in the table, why??
SLEEP(3) returns 0
using name = ''-0 produces the same result (returning every record)
This is not for practical use it's because I'm testing time-based SQL injections
The documentation for the sleep function states:
Sleeps (pauses) for the number of seconds given by the duration argument, then returns 0. If SLEEP() is interrupted, it returns 1. The duration may have a fractional part.
So it returns an integer, so what you actually have is '' - 0, here '' is implicitly converted to an integer so the two types are comparable, so you then have 0 - 0 which equals 0, so your where clause is actually:
WHERE Name = 0;
Since you are comparing a string with an integer, type conversion must take place. Below is from the MySQL Docs
The following rules describe how conversion occurs for comparison operations:
If one or both arguments are NULL, the result of the comparison is NULL, except for the NULL-safe <=> equality comparison operator. For NULL <=> NULL, the result is true. No conversion is needed.
If both arguments in a comparison operation are strings, they are compared as strings.
If both arguments are integers, they are compared as integers.
Hexadecimal values are treated as binary strings if not compared to a number.
If one of the arguments is a TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed. This is done to be more ODBC-friendly. Note that this is not done for the arguments to IN()! To be safe, always use complete datetime, date, or time strings when doing comparisons. For example, to achieve best results when using BETWEEN with date or time values, use CAST() to explicitly convert the values to the desired data type.
If one of the arguments is a decimal value, comparison depends on the other argument. The arguments are compared as decimal values if the other argument is a decimal or integer value, or as floating-point values if the other argument is a floating-point value.
In all other cases, the arguments are compared as floating-point (real) numbers.
Since you have one argument that is a string, and one that is an integer it is the final clause that takes place. Converting a string (that is not a number) to a floating point number yields causes an err 0, which you can test using something as simple as:
SELECT CAST('A String' AS DECIMAL(10,5));
Which will give you 0.00000. So your final where clause after conversions have taken place is:
WHERE 0 = 0;
So all rows are returned.
MySQL is truncating the varchar value and is evaluating 0=0 in the Where clause. Execute the query and then execute show warnings; to see that the varchar values are truncated.
MySQL implicit conversions: http://dev.mysql.com/doc/refman/5.5/en/type-conversion.html
In MySQL, SLEEP() is a function that returns 0.
The documentation says it returns 1 when it is interrupted but it does not explain how this behaviour could be achieved. I thinks it's safe to assume it always returns 0 for this discussion.
The WHERE expression name = '' - SLEEP(3) is evaluated as follows:
MySQL calls the SLEEP(3) function; three seconds later it returns the integer value 0;
'' - 0 (the empty string minus zero) is evaluated as a numeric expression (the subtraction operator is not defined for strings); the empty string is converted to number 0, the value of the expression is the number 0 (zero);
the WHERE clause becomes name = 0 where 0 is a number.
The documentation page about Type Conversion in Expression Evaluation contains a list of rules used to convert the values of different types when they are compared:
The following rules describe how conversion occurs for comparison operations:
If one or both arguments are NULL, the result of the comparison is NULL, except for the NULL-safe <=> equality comparison operator. For NULL <=> NULL, the result is true. No conversion is needed.
If both arguments in a comparison operation are strings, they are compared as strings.
If both arguments are integers, they are compared as integers.
Hexadecimal values are treated as binary strings if not compared to a number.
If one of the arguments is a TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed. This is done to be more ODBC-friendly. Note that this is not done for the arguments to IN()! To be safe, always use complete datetime, date, or time strings when doing comparisons. For example, to achieve best results when using BETWEEN with date or time values, use CAST() to explicitly convert the values to the desired data type.
A single-row subquery from a table or tables is not considered a constant. For example, if a subquery returns an integer to be compared to a DATETIME value, the comparison is done as two integers. The integer is not converted to a temporal value. To compare the operands as DATETIME values, use CAST() to explicitly convert the subquery value to DATETIME.
If one of the arguments is a decimal value, comparison depends on the other argument. The arguments are compared as decimal values if the other argument is a decimal or integer value, or as floating-point values if the other argument is a floating-point value.
In all other cases, the arguments are compared as floating-point (real) numbers.
As you can see from this list, the last rule is the one that applies to the WHERE clause name = 0. Both operands are converted to floating point numbers.
Converting a string to an integer or floating point number will produce 0 the string is not the representation of a number.
This is the explanation of the apparently weird behaviour of your query.
Related
[The SQL Coalesce function evaluates the arguments in order and always returns first non-null value from the defined argument list.]https://www.sqlshack.com/using-the-sql-coalesce-function-in-sql-server/
Why bother adding second/third non-null paramaters or just use IFNULL() instead, if it will always return the first non-null parameter?
First, COALESCE() is Standard SQL. My recommendation is to use the standard SQL function, unless you have a reason to use a bespoke function such as IFNULL().
Second, IFNULL() is limited to two arguments. COALESCE() is more powerful because it can take any number of arguments.
Coalesce returns first non-null value in the list of arguments, if the first argument is null. These list of arguments could be constant value or derivation of other column value.
Primarily used for scenarios where you have to derive value from some other column or replace null with value for column where null is not an accepted for a given column.
Example : If a column is representing discount% then the value could be either a value or 0.0 but cannot be null.
coalesce(special_discount,base_discount_column,0.05)
Here special discount if null consider base discount value , if base discount is also null, then take 5% discount by default.
Why if I run a query with gibberish at the end of the where condition it incorrectly returns a result.
Example:
SELECT * FROM contractor_table WHERE contractorID = '97sd'
I am getting the row with the ID 97, when I should get no result.
What is happening here is that you are comparing the contractorID column, which is integer or some other numeric type, against a string literal 97sd. This doesn't make any sense, so MySQL, based on its casting rules, attempts to first cast 97sd to an integer. Consider the result of the following query:
SELECT CAST('97sd' AS unsigned);
In fact, this outputs just 97, the integer. So, in practice this means that the "gibberish" at the end of your string literal, which begins with an integer, will be ignored by MySQL.
But best practice here is to always compare columns against the correct literal types. So use the following version always, for best results:
SELECT * FROM contractor_table WHERE contractorID = 97;
This happends when you have column type int or other numeric if you convert it into varchar than it will retun no output
When I executed following query to find the country name by the ID, I accidentally passed a string that contained comma-separated values.
SELECT * FROM country WHERE id='6,AU,+61'
This query fetched that respective row.
When I tried casting this string into UNSIGNED using
SELECT CAST('6,AU,+61' AS UNSIGNED)
It returned 6, the first value.
When I tried integer values separated by comma (for eg: '7,8'), it also returned 7. So, it wasn't taking any values after the first comma.
In case of CAST('AU,+61' AS UNSIGNED), it returned zero.
Isn't '7,8' a string, so why is it not converting this into zero and taking first value instead?
MySql casts string to number by looking at the string from its left most char going right.
If the first char is a digit, it will iterate right until it reaches a non-digit char and will cast it to a number. if the string starts with a non-digit char it will cast to 0.
Thats why CAST('AU,+61' AS UNSIGNED) is 0
While CAST('7,8' AS UNSIGNED) is 7
However, The above is not documented specifically in the MySql Cast reference.
Although there are few examples over there and a specific line that implies such a behavior:
there are many different strings that may convert to the value 1, such as '1', ' 1', or '1a'.
However this can be validated with few simple tests:
SELECT CAST('a7' as UNSIGNED) as 'col_a7'; -- 0
SELECT CAST('7q6' as UNSIGNED) as 'col_7q6'; -- 7
SELECT CAST(' 7q6' as UNSIGNED) as 'col__7q6'; -- 7
SELECT CAST('1.4' as UNSIGNED) as 'col1.4'; -- 1
I might not be so clear in my description above, but these tests should clarify things.
According to MySQL Reference Manual - The SET Type
Normally, you search for SET values using the FIND_IN_SET() function or the LIKE operator:
mysql> SELECT * FROM tbl_name WHERE FIND_IN_SET('value',set_col)>0;
According to MySQL Reference Manual
FIND_IN_SET(str,strlist)
Returns a value in the range of 1 to N if the string str is in the string list strlist consisting of N substrings. A string list is a string composed of substrings separated by “,” characters. If the first argument is a constant string and the second is a column of type SET, the FIND_IN_SET() function is optimized to use bit arithmetic. Returns 0 if str is not in strlist or if strlist is the empty string. Returns NULL if either argument is NULL.
Therefore, I think that
SELECT * FROM tbl_name WHERE FIND_IN_SET('value',set_col)
works properly when you want to find value from a set.
So why does example from MySQL Reference Manual explicitly comparing with >0 ?
Is there any difference ?
Consider the following two WHERE clauses:
WHERE FIND_IN_SET('value', set_col) > 0
WHERE FIND_IN_SET('value', set_col)
In the first one, if any of the entries in sel_col contains value, then the return value for FIND_IN_SET() will be some number greater than 0, otherwise it will be 0. In other words, it will return TRUE if the value is found, and FALSE otherwise.
The second WHERE clause will evaluate to either WHERE X, where X is either 1 or greater, if value be found, or 0, if value be not found. In MySQL, the value 0 is synonymous with FALSE, and a positive number of 1 or greater is synonymous with TRUE (see documentation).
So these two WHERE clauses will behave identically.
I have a table _users with a field id as bigint(24).
The result of this query:
SELECT * FROM `_users` WHERE `id`='5text'
is the row with id = 5. I would expect no result since '5text' is not a number. How is this possible?
MySQL implicitly converts strings to numbers when evaluates expressions to make the operands compatible.
From the documentation:
If one of the arguments is a decimal value, comparison depends on the other argument. The arguments are compared as decimal values if the other argument is a decimal or integer value, or as floating-point values if the other argument is a floating-point value.
See the Type Conversion in Expression Evaluation
article for the detailed explanation and examples.