Can't use parameters in subquery when selecting from view - mysql

System: MariaDB 10.3.15, python 3.7.2, mysql.connector python package
I'm having trouble to determine to the exact cause of a problem, possibly a bug in MariaDB/mySQL, when executing the query with the table structure as described below. The confusing part is the error message
1356 (HY000): View 'test_project.denormalized' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
which seems to relate to the problem at first, but the further I dig into why this is happening, the more I get the feeling this error message is a red herring.
Steps to reproduce:
CREATE DATABASE `test_project`;
USE `test_project`;
CREATE TABLE `normalized` (
`id` INT NOT NULL AUTO_INCREMENT,
`foreign_key` INT NOT NULL,
`name` VARCHAR(45) NOT NULL,
`value` VARCHAR(45) NULL,
PRIMARY KEY (`id`));
INSERT INTO `normalized` (`foreign_key`, `name`, `value`) VALUES
(1, 'attr_1', '1'),
(1, 'attr_2', '2'),
(2, 'attr_1', '3'),
(2, 'attr_2', '4');
CREATE OR REPLACE ALGORITHM=UNDEFINED DEFINER=`root`#`localhost` SQL SECURITY DEFINER VIEW `denormalized` AS
select
max(`iq`.`foreign_key`) AS `foreign_key`,
max(`iq`.`attr_1`) AS `attribute_1`,
max(`iq`.`attr_2`) AS `attribute_2`
from (
select
`foreign_key` AS `foreign_key`,
if(`name` = 'attr_1',`value`,NULL) AS `attr_1`,
if(`name` = 'attr_2',`value`,NULL) AS `attr_2`
from `normalized`
) as `iq`
group by `iq`.`foreign_key`;
Using python connect to the database and execute the following query:
conn = mysql.connector.connect(host="somehost", user="someuser", password="somepassword")
cursor = conn.cursor()
query = """select * from denormalized as d
where d.`foreign_key` in
(
SELECT distinct(foreign_key)
FROM normalized
where value = %s
);"""
cursor.execute(query, ["2"])
results = cursors.fetchall()
Further information: At first I thought that obviously it's a privilege issue, but even using root for everything and double checking hosts and specific privileges didn't change anything.
Then I dug deeper into what the queries and views involved do (the test case above is a reduced version of what's actually in our database) and tested each part. Selecting from the view works. Running the query of the view works. Selecting from the view with a static subquery works. In fact, replacing the view in the problematic query with it's definition works too.
I've boiled it down to selecting from the view using a subquery in the where clause using parameters in that subquery. This causes the error to appear. Using a static subquery or replacing the view with it's definition works just fine, it's only this specific circumstance where it fails.
And I have no idea why.

The group by does not make sense; did you really mean one of these?
This returns one row:
select max(`foreign_key`) AS `foreign_key`,
max(if(`name` = 'attr_1', `value`,NULL)) AS `attribute_1`,
max(if(`name` = 'attr_2', `value`,NULL)) AS `attribute_2`
from `normalized`;
This uses the GROUP BY and returns one row per foreign_key:
select `foreign_key`,
max(if(`name` = 'attr_1', `value`,NULL)) AS `attribute_1`,
max(if(`name` = 'attr_2', `value`,NULL)) AS `attribute_2`
from `normalized`
group by `foreign_key`;
Your python query is probably better in either of these formulations:
select d.*
FROM ( SELECT distinct(foreign_key)
FROM normalized
where
value = %s )
JOIN denormalized as d;
select d.*
FROM denormalized as d
WHERE EXISTS ( SELECT 1
FROM normalized
where foreign_key = d.foreign_key
AND value = %s )
They would benefit from INDEX(value, foreign_key).

Related

MySQL WHERE Condition on integer field returning incorrect values

I'm having a problem with MySQL returning the incorrect result when applying a WHERE condition to an integer field with a string value.
CREATE TABLE `people` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
INSERT INTO `people` (`id`, `name`)
VALUES
(1, 'Bob'),
(2, 'Sally'),
(3, 'Jim');
Now when I run the query:
SELECT *
FROM people
WHERE id = '1-abcd';
My result set is:
id name
1 Bob
MySQL appears to be truncating the string value '1-abcd' to '1' behind the scenes as soon as it hits a non-integral character (in the conversion from a string to INT).
You're probably wondering why this matters. I'm trying to fix a site for a PCI compliance scan. The scan thinks the URI '/some/page?id=102-1' is allowing some form of sequel injection, but in reality it's showing the same content at '/some/page?id=102'.
This is not an issue in one place. It is an issue all over the place, and it's a fairly large system. Is there some way to rectify this on the MySQL end of things, so it no longer mistakenly judges the two values to be equivalent? I looked at the documentation for SQL modes, but didn't see anything regarding this circumstance.
UPDATE: I filed a dispute with the company that produced the scan, which they accepted, so I'm no longer in the woods. But it is disappointing that there's apparently no way to configure the casting behavior of MySQL from a string to INT in this case. (You can, but only for INSERTs and UPDATEs.)
What happens that MySQL type-casts the string literal value to an integer, and when it does that it starts from the left of the string and as soon as it reaches a character that cannot be considered part of a number, it strips out everything from that point on. So 1-0 gives output matching to 1. To do this you can use cast. I am not 100% sure about the syntax but it is like this:
select * from people
where id =
(
case when ISNUMERIC( '1-0' )
then cast ('1-0' as int)
else null
end )
What this will do is that if it is an numeric value then it will return the correct matching row or else not.
Edit:
The above query seems to be of MSSQL/Oracle and would not work with MySQL. For MySQL you can use RegExp. I have never use one but you can find more details here:
http://mysqlhints.blogspot.in/2012/01/how-to-find-out-if-entire-string-is.html
http://www.ash.burton.fm/blogs/2010/12/quick-tip-mysql-equivalent-of-isnumeric
http://www.justskins.com/forums/how-to-use-isnumeric-137604.html

MySQL fails to return items with AES_ENCRYPT function

I added an item with the following SQL sentence:
INSERT INTO `db`.`users` (`name`, `pass`) VALUES ('Terry', AES_ENCRYPT('32145', '32145'))
Then the new line was added into the table, for sure.
When I tried to find this item with 'name' and 'pass',an empty set was returned. The query sentence is below:
SELECT * FROM `users` WHERE `name` ='Terry' AND `pass`= AES_ENCRYPT('32145', '32145')
Is there anything wrong with the function AES_ENCRYPT? :-(
Updates in Apr 27th, 2014:
The type of "pass" was defined as varchar(255), which turned out to be a disaster. :-(
After I modified its type as varbinary(255), everything worked.
The original problem has been solve. But, why did type varchar(255) fail in this situation?
See this fiddle and the aes_encrypt documentation.
What type is your pass column? on the fiddle I defined it like:
pass varbinary(200)
Did this insert successfully:
INSERT INTO users (name, pass) VALUES ('Terry', aes_encrypt('32145', '32145'))
Then queried it like this:
select *
from users
where name = 'Terry' and pass = aes_encrypt('32145', '32145');
select *, aes_decrypt(pass, '32145')
from users
where pass = aes_encrypt('32145', '32145');
So your query should have worked.
SELECT * FROM users WHERE name ='Terry' AND pass= AES_DECRYPT('32145', '32145')
MySQL Reference

MySql Basic table creation/handing

I'm trying to create a simple table where I insert field and I do some checks in MySql. I've used Microsoft SQL relatively easy. Instead, MySql give evrrytime query errors without even specifying what's going on. Poor MySql software design apart, here's what I'm trying to do:
1 table with 4 fields with an autoincremental autogenerated number to det an ID as primary key
CREATE TABLE `my_db`.`Patients_table` (
`ID_Patient` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`Patient_name` VARCHAR( 200 ) NOT NULL ,
`Recovery_Date` DATETIME NOT NULL ,
`Recovery_count` INT NOT NULL
) ENGINE = MYISAM
a simple stored procedure to insert such fields and check if something exist before inserting:
CREATE PROCEDURE nameInsert(IN nome, IN data)
INSERT INTO Patients_table (Patient_name,Recovery_Date) values (nome,data)
IF (EXISTS (SELECT Recovery_count FROM Tabella_nomi) = 0) THEN
INSERT INTO (Patients_table (Recovery_count)
ELSE
SET Recovery_count = select Recovery_count+1 from Patients_table
END
this seems wrong on many levels and MySQL useless syntax checker does not help.
How can I do this? Thanks.
There seems to be a lot wrong with this block of code. (No offense intended!)
First, Procedures need to be wrapped with BEGIN and END:
CREATE PROCEDURE nameInsert(IN nome, IN data)
BEGIN
...[actually do stuff here]
END
Second, since your table is declared with all fields as NOT NULL, you must insert all fields with an INSERT statement (this includes the Recovery_Date column, and excludes the AUTO_INCREMENT column). You can add DEFAULT CURRENT_TIMESTAMP to the date column if you want it to be set automatically.
INSERT INTO Patients_table (Patient_name,Recovery_Date) values (nome,data)
Third, what exactly is your IF predicate doing?
EXISTS (SELECT Recovery_count FROM Tabella_nomi) = 0
If you want to check if a row exists, don't put the = 0 at the end. Also, Tabella_nomi isn't declared anywhere in that procedure. Also, your SELECT statement should have a WHERE clause, since I'm assuming you want to select a specific row (this is going to select a result set of all recovery_counts).
Fourth, the second INSERT statement seems a little messy. It should look more like the first INSERT, and keep the point I made above in mind.
INSERT INTO (Patients_table (Recovery_count)
Fifth, the ELSE statement
SET Recovery_count = select Recovery_count+1 from Patients_table
Has some problems too. SET is meant for setting variables, not values in rows. I'm not 100% sure what your intent is from this statement, but it looks like you meant to increment the Recovery_count column of a certain row if it already exists. In which case, you meant to do something like this:
UPDATE Patients_table SET Recovery_count = Recovery_count+1 WHERE <conditional predicate>
Where the conditional predicate is something like this:
Patients_name = nome
Try these things, and look at the errors it gives you when you try to execute the CREATE STATEMENT. I bet they're more useful then you think!

Query input must contain atleast one table or query

I have a query in access that is suppose to check whether the item already exists in the database before inserting it:
INSERT INTO FinalizedPrintedStickers
Values('0000846043-481-9-0',
'48IG - 1F Straight Panel ',
'481 ',
'0',
'0',
'',
'210',
'Printed')
WHERE NOT EXISTS(SELECT [SN] FROM FinalizedPrintedStickers Where SN = '0000846043-481-9-0')
Now, I've gotten this error before but usually it's when there's no table for example if you "select * from test table" and you type "Select *" and leave out the from clause you get the same error. But I have a table ? Perhaps my where not exists syntax is wrong?
Edit:
Ok, I've added a table "Dual" as suggested with code copy pasted from this question : Table-less UNION query in MS Access (Jet/ACE)
Attempting to add a constraint as shown gave me this error :
after i press ok it highlights the word "Check"
I've never really dealt with constraints (in access atleast..) my syntax is probably wrong
Edit 2:
Adding constraints using ctrl G command
And when I press enter...
Adding constraints using ADO:
And when i press run...
This is one of those cases where a Dual table can be helpful. A Dual table is a single-row table that can be used in the FROM clause of a query when you don't really need a source table but the SQL parser insists on there being one.
Some database systems (e.g., Oracle) provide a Dual virtual table as "standard equipment", but in Access we need to create our own. For an excellent description of the process check out HansUp's answer here.
So, once you have your [Dual] table in place, i.e.,
id
--
1
...then you can use this query do perform your INSERT (or not...):
INSERT INTO FinalizedPrintedStickers
(
SN,
Field2
)
SELECT
"0000846043-481-9-0" AS SN,
"48IG - 1F Straight Panel" AS Field2
FROM Dual
WHERE DCount("SN","FinalizedPrintedStickers","SN=""0000846043-481-9-0""")=0
try this you can use simple where Clause
INSERT INTO FinalizedPrintedStickers
Values('0000846043-481-9-0',
'48IG - 1F Straight Panel ',
'481 ',
'0',
'0',
'',
'210',
'Printed')
WHERE SN Not In(SELECT [SN] FROM FinalizedPrintedStickers Where SN = '0000846043-481-9-0');

Strange behavior when query for varchar filed

I came across this strange behavior when I was hunting for a bug in a system. Consider following.
We have a mysql table which have varchar(100) column. See the following sql script.
create table user(`id` bigint(20) NOT NULL AUTO_INCREMENT,`user_id` varchar(100) NOT NULL,`username` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `user_id` (`user_id`)) ENGINE=InnoDB AUTO_INCREMENT=129 DEFAULT CHARSET=latin1;
insert into user(user_id, username) values('20120723145614834', 'user1');
insert into user(user_id, username) values('20120723151128642', 'user1');
When I execute following query I received 0 results.
select * from user where user_id=20120723145614834;
But When I execute following I get the result(note the single quote).
select * from user where user_id='20120723145614834';
This is expected since user_id field is varchar. Strange thing is that both following queries yield result.
select * from user where user_id=20120723151128642;
select * from user where user_id='20120723151128642';
Can anybody explain me the reason for this strange behavior. My MySql version is 5.1.63-0ubuntu0.11.10.1
Check mysql document 12.2. Type Conversion in Expression Evaluation
Comparisons that use floating-point numbers (or values that are
converted to floating-point numbers) are approximate because such
numbers are inexact. This might lead to results that appear
inconsistent:
mysql> SELECT '18015376320243458' = 18015376320243458;
-> 1
mysql> SELECT '18015376320243459' = 18015376320243459;
-> 0
So we better use always right data type for SQL.