I have just converted a SQL select statement into a stored procedure
The SQL Statement use select statement takes
0.4784s to run the first time and 0.0003s after that
The Stored procedure takes 0.4784s to run every time.
I assume the query cache is not been used
How can I rectify this?
A simplified version of the code
SELECT * FROM Venues WHERE VenueName = :TheVenue
=======
CREATE PROCEDURE GetVenues
(
TheVenue VarChar(22)
)
BEGIN
SELECT * FROM Venues WHERE VenueName = TheVenue
END;
Welcome to MySQL... it is really difficult to get anything within a stored procedure to take advantage of the query cache. The dev article A Practical Look at the MySQL Query Cache discusses this in some detail. The limitations are also mentioned in the reference documentation here and on the MySQL Performance Blog.
Basically, don't depend on caching of queries executed inside of stored procedures. It is near impossible to get it to work though the first reference does claim that it is possible. This usually isn't a problem if you are using stored procedures to encapsulate complicated logic. Most of the problems that I have seen were caused by using stored procedures for very simple queries where a VIEW would have sufficed.
You could try a dynamic SQL stored procedure, like:
CREATE PROCEDURE GetVenues (TheVenue varchar(22))
BEGIN
SET #s = 'SELECT * FROM Venues WHERE VenueName = ?';
SET #v = TheVenue;
PREPARE stmt1 FROM #s;
EXECUTE stmt1 USING #v;
DEALLOCATE PREPARE stmt1;
END;
No MySQL server by hand to test the syntax, so you might have to tweak it.
Related
I have a MySQL stored procedure that uses Dynamic SQL. We went with Dynamic SQL because we have several tables with similar columns and we're trying to make the code as reusable as possible.
I'm concerned about SQL Injection, but standard attack strings don't seem to work on this. Is this stored procedure susceptible to SQL Injection? If so, is there a better way to write it?
CREATE DEFINER=CURRENT_USER PROCEDURE `sp_lookup`(IN tableName VARCHAR(256))
BEGIN
SET #sql = CONCAT('SELECT id, name, FROM ', tableName, ' ORDER BY name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
END
Stored procedure won't add any additional security.
It does not prevent the SQL injection, you need to use prepared statement.
Another approach would be to use table white-listing, this means you first check that the table matches one of your existing tables if not do not run the query.
SQL Injection can only occur when a user-supplied text string is concatenated into a SQL statement without adequate escaping. Since your table name is not going to be supplied by users, but by programmers, you're not in trouble.
You should however not do this. It is better for the database engine to have explicit statements, so they can be optimized correctly, and not have to re-parse the SQL on every call.
The table name should be surrounded by "ticks", and have any ticks escaped, like so:
CREATE DEFINER=CURRENT_USER PROCEDURE `sp_lookup`(IN tableName VARCHAR(256))
BEGIN
SET #sql = CONCAT('SELECT id, name FROM `', REPLACE(tableName, '`', '\`'), '` ORDER BY name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
END
Despite currently lacking these, it seems your vulnerability would be fairly low; according to the official docs "SQL syntax for prepared statements does not support multi-statements"
In isolation I would say that yes, this stored proc may be vulnerable, but that the vulnerability may not matter. What does matter is whether or not the value of the tablename parameter is coming from a trustworthy source.
If you examine one of your "standard attack strings," you'll see the attacker is attempting to complete your SQL query and then follow it with another query. In order to do this, the attacker has to be able to directly modify the string which is being spliced into your query.
If you use this SP in such a way that the value of tablename cannot be directly modified by a hostile user, then you do not have a SQL injection vulnerability.
I hope that made sense.
Should stored procedure cached in Mysql? If yes, How long it is stay in cache?
In my case, when I call one stored procedure first time, it is giving me result in 1sec, after that it gives me result in 400ms. And when I am changing some parameters passed to the stored procedure and call first time, same behavior performed. So, I can not understand what happening? Can some one guide me?
Thanks.
This is the normal behavior of any system that uses a cache :
at first time execution the results are loaded in cache , thus a small overhead appears
the following execution will take much less because they are already in cache BUT if any input is changed (like in your case : changing some parameters of the stored procedure) then the results that are already in cache are not longer viable so the new results (using the changed inputs) must be put in cache that is why it takes longer
You can read more here
Use
Deallocate in the your procedure:
CREATE PROCEDURE GetVenues (TheVenue varchar(22))
BEGIN
SET #s = 'SELECT * FROM Venues WHERE VenueName = ?';
SET #v = TheVenue;
PREPARE stmt1 FROM #s;
EXECUTE stmt1 USING #v;
DEALLOCATE PREPARE stmt1;
END;
Other option: RESET QUERY CACHE;
If the user you're running as has reload rights. Alternatively, you can defragment the query cache via:
FLUSH QUERY CACHE;
I am trying to write a simple stored procedure which will clear the contents of some tables in the current database - I'm doing this by matching a prefix against the list of tables in the information_schema:
delimiter $$
create procedure local_flush_cache(db varchar(255))
begin
select #str_sql:=concat('delete from ',group_concat(table_name))
from information_schema.tables
where table_schema=db and table_name like 'cache_%';
prepare stmt from #str_sql;
execute stmt;
drop prepare stmt;
end$$
delimiter ;
I would like to be able to remove the db parameter and have the function work on the currently active database.
I guess I will also need the ability to recognise that a database has not yet been selected and output an error (to prevent accidentally flushing all cache tables in all databases).
Considering that the procedure is tied to a database, the current is the called unless you do CALL db.proc().
However, if you really want the selected one, you can the DATABASE() function:
where table_schema=database() and table_name like 'cache_%';
I have a stored procedure that populates a temp table. The temp table is populated using multiple dynamic SQL (it has "having" & "between" clause). I am executing this SQL inside my stored procedure using:
set #sql = concat("insert into my_temp select * from my_table where my_date between ", date1, " and ", date2)
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
/* more prepared dynamic statements */
The problem is my driver complaints as soon as it encounters the first EXECUTE statement, apparently it thinks MySQL is trying to return a resultset from a stored procedure. Is that how mysql behaves when it comes to dynamic sqls in stored procedures?
I get this error from ruby/rails/mysql2 driver -
Mysql2::Error: PROCEDURE my_db.sp_special_customers can't return a result set in the given context:
Basically the driver does not support returning result-sets from stored procedure, which is fine. And that's not the issue, the issue for me is why does my driver think that EXECUTE stmt1 means a result set is being returned?
Is there a way in Mysql to fix this?
Have a look at: http://dev.mysql.com/doc/refman/5.0/en/stored-program-restrictions.html
Particularly: "Stored routines cannot contain arbitrary SQL statements. The following statements are not permitted: "
They don't allow 'EXECUTE' in the stored procedure. You can however use prepared statements if you're using mysql 5.x. It might be a viable alternative, depending on what you're trying to accomplish
When using prepared statements inside stored procedures, should they be deallocated at the end of the procedure or not, or does it not matter, and why?
Some code to explain:
CREATE PROCEDURE getCompanyByName (IN name VARCHAR(100))
NOT DETERMINISTIC
BEGIN
PREPARE gcbnStatement FROM 'SELECT * FROM Companies WHERE name=? LIMIT 1';
SET #companyName = name;
EXECUTE gcbnStatement USING #companyName;
DEALLOCATE PREPARE gcbnStatement;
END $$
So - should the DEALLOCATE statement be there or not? Cheers!
/Victor
According to the MySQL docs:
A prepared statement is specific to
the session in which it was created.
If you terminate a session without
deallocating a previously prepared
statement, the server deallocates it
automatically.
So, no, I wouldn't bother doing it explicitly, unless you have very long-running sessions.
If you use connection pooling, it is definitely a good idea to deallocate them.
Deallocating Prepared statements in stored routines is best practice.
Prepared statements are global to a session and are not automatically deallocated when a stored routine ends.
If you have too many created but not deallocated prepared statement, you might encounter/exceed the max_prepared_stmt_count limit