SQL Server: +(unary) operator on non-numeric Strings - sql

I am surprised! This statement below is valid in SQL SERVER:
SELECT +'ABCDEF'
Has SQL Server defined + as a Unary operator for string types?

Here is my own answer to this question (Please also see the update at the end):
No, there isn't such unary operator defined on the String expressions. It is possible that this is a bug.
Explanation:
The given statement is valid and it generates the below result:
(No column name)
----------------
ABCDEF
(1 row(s) affected)
which is equivalent to doing the SELECT statement without using the + sign:
SELECT 'ABCDEF'
Being compiled without giving any errors, in fact being executed successfully, gives the impression that + is operating as a Unary operation on the given string. However, in the official T-SQL documentation, there is no mentioning of such an operator. In fact, in the section entitled "String Operators", + appears in two String operations which are + (String Concatenation) and += (String Concatenation); but neither is a Unary operation. Also, in the section entitled "Unary Operators", three operators have been introduced, only one of them being the + (Positive) operator. However, for this only one that seems to be relevant, it soon becomes clear that this operator, too, has nothing to do with non-numeric string values as the explanation for + (Positive) operator explicitly states that this operator is applicable only for numeric values: "Returns the value of a numeric expression (a unary operator)".
Perhaps, this operator is there to successfully accept those string values that are successfully evaluated as numbers such as the one that has been used here:
SELECT +'12345'+1
When the above statement is executed, it generates a number in the output which is the sum of both the given string evaluated as a number and the numberic value added to it, which is 1 here but it could obviously be any other amount:
(No column name)
----------------
12346
(1 row(s) affected)
However, I doubt this explanation is the correct as it raises to below questions:
Firstly, if we accept that this explanation is true, then we can conclude that expressions such +'12345' are evaluated to numbers. If so, then why is it that these numbers can appear in the string related functions such as DATALENGTH, LEN, etc. You could see a statement such as this:
SELECT DATALENGTH(+'12345')
is quite valid and it results the below:
(No column name)
----------------
5
(1 row(s) affected)
which means +'12345' is being evaluated as a string not a number. How this can be explained?
Secondly, while similar statements with - operator, such as this:
`SELECT -'ABCDE'`
or even this:
`SELECT -'12345'`
generate the below error:
Invalid operator for data type. Operator equals minus, type equals varchar.
Why, shouldn't it generate an error for similar cases when + operator has been wrongly used with a non-numeric string value?
So, these two questions prevent me from accepting the explanation that this is the same + (unary) operator that has been introduced in the documentation for numeric values. As there is no other mentioning of it anywhere else, it could be that it is deliberately added to the language. May be a bug.
The problem looks to be more severe when we see no error is generated for statements such as this one either:
SELECT ++++++++'ABCDE'
I do not know if there are any other programming languages out there which accept these sort of statements. But if there are, it would be nice to know for what purpose(s) they use a + (unary) operator applied to a string. I cannot imagine any usage!
UPDATE
Here it says this has been a bug in earlier versions but it won't be fixed because of backward compatibility:
After some investigation, this behavior is by design since + is an unary operator. So the parser accepts "+ , and the '+' is simply ignored in this case.
Changing this behavior has lot of backward compatibility implications so we don't intend to change it & the fix will introduce unnecessary changes for application code.

Related

ORA-01722 invalid number on different numbers?

I have a condition in a query:
ABS ( FIB.QUANT1 ) = ( OI.KLINE * :intv1000 ) / 1000.000000
when I run the query with intv1000 = 1000000 - query runs ok.
when I run the query with intv1000 = 1000 I get ORA-01722 (not immediately, after about 5-6 seconds).
Any Idea why ?
QUANT1 - NUMBER(16,2)
KLINE - NUMBER(38)
The condition is self generated from the application, So I can't really change it.
Thank you
The ORA-01722 is an invalid number error.
You would get this error when a non-number -- typically a string -- has to be converted to a number. For example here.
This conversion can occur in many different ways although the three most common are:
to_number()
cast()
implicit conversion
The expression you have highlighted may or may not have anything to do with where the error actually occurs. What it is saying is that:
When :intv1000 = 1000000 then the row(s) with the problem data are filtered out.
When intv1000 = 1000 then the row(s) with the problem data are being processed.
Arrggh. This is very hard to determine. I would suggest starting by looking at the query and finding all explicit conversions to see if they are a problem.
If you find no back data, then you need to resort to looking at all comparisons (including joins), arithmetic expressions, and function calls to find the problem.
In general, I strongly recommend avoiding implicit conversion. Use explicit conversion to avoid such problems! Note: I do make an exception for conversion to strings with string functions and operators. These are usually pretty safe.

sql concatenation blank between plus signs + [duplicate]

I am surprised! This statement below is valid in SQL SERVER:
SELECT +'ABCDEF'
Has SQL Server defined + as a Unary operator for string types?
Here is my own answer to this question (Please also see the update at the end):
No, there isn't such unary operator defined on the String expressions. It is possible that this is a bug.
Explanation:
The given statement is valid and it generates the below result:
(No column name)
----------------
ABCDEF
(1 row(s) affected)
which is equivalent to doing the SELECT statement without using the + sign:
SELECT 'ABCDEF'
Being compiled without giving any errors, in fact being executed successfully, gives the impression that + is operating as a Unary operation on the given string. However, in the official T-SQL documentation, there is no mentioning of such an operator. In fact, in the section entitled "String Operators", + appears in two String operations which are + (String Concatenation) and += (String Concatenation); but neither is a Unary operation. Also, in the section entitled "Unary Operators", three operators have been introduced, only one of them being the + (Positive) operator. However, for this only one that seems to be relevant, it soon becomes clear that this operator, too, has nothing to do with non-numeric string values as the explanation for + (Positive) operator explicitly states that this operator is applicable only for numeric values: "Returns the value of a numeric expression (a unary operator)".
Perhaps, this operator is there to successfully accept those string values that are successfully evaluated as numbers such as the one that has been used here:
SELECT +'12345'+1
When the above statement is executed, it generates a number in the output which is the sum of both the given string evaluated as a number and the numberic value added to it, which is 1 here but it could obviously be any other amount:
(No column name)
----------------
12346
(1 row(s) affected)
However, I doubt this explanation is the correct as it raises to below questions:
Firstly, if we accept that this explanation is true, then we can conclude that expressions such +'12345' are evaluated to numbers. If so, then why is it that these numbers can appear in the string related functions such as DATALENGTH, LEN, etc. You could see a statement such as this:
SELECT DATALENGTH(+'12345')
is quite valid and it results the below:
(No column name)
----------------
5
(1 row(s) affected)
which means +'12345' is being evaluated as a string not a number. How this can be explained?
Secondly, while similar statements with - operator, such as this:
`SELECT -'ABCDE'`
or even this:
`SELECT -'12345'`
generate the below error:
Invalid operator for data type. Operator equals minus, type equals varchar.
Why, shouldn't it generate an error for similar cases when + operator has been wrongly used with a non-numeric string value?
So, these two questions prevent me from accepting the explanation that this is the same + (unary) operator that has been introduced in the documentation for numeric values. As there is no other mentioning of it anywhere else, it could be that it is deliberately added to the language. May be a bug.
The problem looks to be more severe when we see no error is generated for statements such as this one either:
SELECT ++++++++'ABCDE'
I do not know if there are any other programming languages out there which accept these sort of statements. But if there are, it would be nice to know for what purpose(s) they use a + (unary) operator applied to a string. I cannot imagine any usage!
UPDATE
Here it says this has been a bug in earlier versions but it won't be fixed because of backward compatibility:
After some investigation, this behavior is by design since + is an unary operator. So the parser accepts "+ , and the '+' is simply ignored in this case.
Changing this behavior has lot of backward compatibility implications so we don't intend to change it & the fix will introduce unnecessary changes for application code.

Regular expression metacharacter in SQL yields different results in Oracle vs. Postgres

I'm trying to convert some queries from an Oracle environment to Postgres. This is a simplified version of one of the queries:
SELECT * FROM TABLE
WHERE REGEXP_LIKE(TO_CHAR(LINK_ID),'\D')
I believe the equivalent postgreSQL should be this:
SELECT * FROM TABLE
WHERE CAST(LINK_ID AS TEXT) ~ '\D'
But when I run these queries in their respective environments on the exact same dataset, the first query outputs no records (which is correct) and the second query outputs all records in the table. I didn't write the original code, but as I understand it, it's looking for any values in the numeric field LINK_ID that are non-digit characters. Is the \D metacharacter supposed to behave differently in Oracle vs. postgres? I'm not seeing anything in documentation to say they should.
The documentation for Oracle's TO_CHAR(number) states
If you omit fmt, then n is converted to a VARCHAR2 value exactly long enough to hold its significant digits.
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions181.htm
This means that the only non-numeric character which might be produced is a negative sign or a decimal point. If the number is positive and has no fractional part, it will not match the regular expression \D.
On the other hand, on PostgreSQL CAST(numeric(38,8)as TEXT) returns a value with the number of decimal places specified by the type specification, in this case 8.
E.g.:
cast( cast(12341234 as numeric(38,8)) as TEXT)
Generates 12341234.00000000 The result of such a cast will always contain a decimal point and therefore will always match the regular expression \D.
You may find that replacing it with this solves your problem:
(LINK_ID % 1) <> 0.0
Alternatively, If you need to use the regex (e.g. to simplify migration work), consider changing it to '\.0*[1-9]' i.e. to find a decimal point with any nonzero digit after it.

Is this theoretically valid method to protect against SQL injections?

I know there are methods (escaping/ prepared statements) to protect against injections. However, for "fun", do you think the following method works?
Assume you are given a string. You get the string and add "space" between every character.
This way, even if there is a security breach somehow, commands will not make sense because adding the "space" invalidates them.
I understand there are performance issues and stuff... But in theory, will it work?
No.
SQL injection is defined as the modification of the intended SQL command. If even a single character is able to modify it (e. g., a single quote prematurely ends the string literal, resulting in a syntax error), it is considered an SQL injection.
Even though this is sort of an "opinion" based question I'm still going to answer. "No." :) This will not safeguard against all possible cases. Most likely it will simply make some forms of injection more difficult, but certainly not all. Here's why.
Introducing spaces will not properly solve the problem, in addition to rendering the data into a format that is unlikely to reflect what you really want to store. For example, some applications will introduce user input as integer values in database lookups.
Given:
SELECT * FROM table WHERE id=$user_input_value
User input:
1OR1=1
Rendered statement:
SELECT * FROM table WHERE id= 1 OR 1 = 1
This remains completely valid SQL and will return all rows in the table. Whitespace around the equals sign will be discarded.
This will break up any operator or keywords of more than one character. An attacker would need to cause damage using single characters separated by spaces. Let's see what we can do with that.
Assume a one-character column named c:
SELECT 1
FROM (VALUES (1234)) x(c)
WHERE c = '" + injectedSql + "'
Inject
' + c + '
which gives:
SELECT 1
FROM (VALUES (1234)) x(c)
WHERE c = '' + c + ''
Pwned.

Preg_replace solution for prepared statements

I have a command class that abstracts almost all specific database functions (We have the exactly same application running on Mssql 2005 (using ODBC and the native mssql library), MySQL and Oracle. But sometimes we had some problems with our prepare method that, when executed, replaces all placeholders with their respective values. But the problem is that I am using the following:
if(is_array($Parameter['Value']))
{
$Statement = str_ireplace(':'.$Name, implode(', ', $this->Adapter->QuoteValue($Parameter['Value'])), $Statement);
}
else
{
$Statement = str_ireplace(':'.$Name, $this->Adapter->QuoteValue($Parameter['Value']), $Statement);
}
The problem arises when we have two or mer similar parameters names, for example, session_browser and session_browse_version... The first one will partially replace the last one.
Course we learned to go around specifying the parameters within a specific order, but now that I have some "free" time I want to make it better, so I am thinking on switching to preg_replace... and I am not good in regular expression, can anyone give any help with a regex to replace a string like ':parameter_name`?
Best Regards,
Bruno B B Magalhaes
You should use the \b metacharacter to match the word boundary, so you don't accidentally match a short parameter name within a longer parameter name.
Also, you don't need to special-case arrays if you coerce a scalar Value to an array of one entry:
preg_replace("/:$Name\b/",
implode(",", $this->Adapter->QuoteValue( (array) $Parameter['Value'] )),
$Statement);
Note, however, that this can make false positive matches when an identifier or a string literal contains a pattern that looks like a parameter placeholder:
SELECT * FROM ":Name";
SELECT * FROM Table WHERE column = ':Name';
This gets even more complicated when quoted identifiers and string literals can contain escaped quotes.
SELECT * FROM Table WHERE column = 'word\':Name';
You might want to reconsider interpolating variables into SQL strings during prepare, because you're defeating any benefits of prepared statements with respect to security or performance.
I understand why you're doing what you're doing, because not all RDBMS back-end supports named parameters, and also SQL parameters can't be used for lists of values in an IN() predicate. But you're creating an awfully leaky abstraction.