Can't execute MySQL stored procedure - mysql

I'm dipping my toe into using stored functions with MySQL and am having trouble.
Having created a function and tested it, I don't seem to be able to allow other users to execute it. From the documentation, it seems that I need to grant EXECUTE access to the other users, but that doesn't appear to be sufficient.
I've put together a couple of scripts that I believe demonstrate the problem:
# This script creates two databases with a stored function in each.
#
# On one database, tester in granted all privileges.
# On the other, tester only gets a few.
#
# We want to find the minimum privileges required to allow tester to execute the
# stored function.
#
# This script must be run by an administrative user, i.e. root
CREATE DATABASE test1;
DELIMITER $$
CREATE FUNCTION test1.foo () RETURNS VARCHAR(255) DETERMINISTIC
BEGIN
RETURN ('garp');
END$$
DELIMITER ;
GRANT ALL PRIVILEGES ON test1.* TO 'tester'#'localhost';
#
CREATE DATABASE test2;
DELIMITER $$
CREATE FUNCTION test2.foo () RETURNS VARCHAR(255) DETERMINISTIC
BEGIN
RETURN ('garp');
END$$
DELIMITER ;
GRANT EXECUTE ON PROCEDURE test2.foo TO 'tester'#'localhost';
and
# This script tests whether tester can access the stored functions
#
# It should be executed by tester
SELECT 'test1.foo(): ', test1.foo ();
SELECT 'test2.foo(): ', test2.foo ();
When I run execute the second script, I get an error:
$ mysql --user=tester --password=tester --skip-column-names < testScript2.sql
test1.foo(): garp
ERROR 1370 (42000) at line 6: execute command denied to user 'tester'#'localhost' for routine 'test2.foo'
I have no doubt that I'm missing something obvious, but I can't see what that is. I imagine that I've got something wrong in the GRANT EXECUTE... statement in the first script, and am deeply suspicious of my use of single quotes, but I recall trying most of the combinations of placement and inclusion of single quotes without success.
I'd be really grateful to anyone who can point out my error.
For reference, I'm running Server version: 5.1.67-0ubuntu0.10.04.1 (Ubuntu) (on Ubuntu!).
Thanks

test2.foo is a function not a procedure.
Try:
GRANT EXECUTE ON FUNCTION test2.foo TO 'tester'#'localhost';
(I was able to reproduce the problem locally and confirm that this change works.)

Related

Add MySql stored procedure via Propel [duplicate]

In my model I defined some procedures. The code (generated by MySQL Workbench) contains DELIMITER definitions, so the procedures look like:
-- schema
CREATE DATABASE ...
CREATE TABLE foo ...
-- procedures
DELIMITER $$
...
BEGIN
DECLARE ... ;
OPEN ... ;
SET ... ;
... ;
END$$
DELIMITER ;
Now I need to "import" the SQL to the database via PDO. I tried to pass it as input for the PDO#exec(...), but noticed, that the execution stops on the line of the first DELIMITER definition.
I don't want remove the DELIMITER statements. So the the SQL code should remain the same.
How to use PDO to execute SQL code containing DELIMITER statements?
From comments:
I don't want remove the DELIMITER statements. And actually I want to get it working without to execute every statement manually
That's not how it works.
To understand why, you need to understand how the mysql CLI -- and any other program that can read and execute a dump file like this -- actually handles it.
DELIMITER is not something the server understands.
DELIMITER is used to tell the client-side parser what the current statement delimiter should be, so that the client-side parser can correctly split the statements and deliver one at a time to the server for execution.
From the docs. Note carefully that mysql, every time it is used here, refers to the mysql client utility -- not the server.
If you use the mysql client program to define a stored program containing semicolon characters, a problem arises. By default, mysql itself recognizes the semicolon as a statement delimiter, so you must redefine the delimiter temporarily to cause mysql to pass the entire stored program definition to the server.
To redefine the mysql delimiter, use the delimiter command. [...] The delimiter is changed to // to enable the entire definition to be passed to the server as a single statement, and then restored to ; before invoking the procedure. This enables the ; delimiter used in the procedure body to be passed through to the server rather than being interpreted by mysql itself.
https://dev.mysql.com/doc/refman/5.7/en/stored-programs-defining.html
So, to handle such a file, you need a client-side parser that does the same thing mysql does... and here, the code you are writing is (needs to be) the client-side statement parser. So you are the one that needs to write the logic to handle the delimiter.
To do what you want, you have to interpret the DELIMITER statements, use them to keep track of the current statement delimiter, but do not send them to the server.
Then, you have to read through the input one line at a time, buffering what you've read, until you find the specified delimiter at the end of the line, and send the resulting statement to the server -- excluding the actual statement delimiter from what you send... so, for example, you would not send the ending $$ after the procedure body (unless the current statement delimiter is ;, which you can either send or not send -- the server doesn't care.) Then empty the buffer and start reading again until you see another instance of a delimiter (and send the statement to the server) or match a DELIMITER statement and set your code's current delimiter variable to match it so that you correctly identify the end of the next statement.
Delimiters is a thing that you don't need with PDO. You can just run your queries as is
$pdo->query("CREATE DATABASE ...");
$pdo->query("CREATE TABLE foo ...");
$pdo->query("BEGIN
DECLARE ... ;
OPEN ... ;
SET ... ;
... ;
END");
as simple as that
I met same problem with you when I tried with PostgreSQL. The problem seems PDO just allow you execute 1 query 1 time. As mentioned: PDO::exec() executes an SQL statement in a single function call, returning the number of rows affected by the statement. In php manual
Could you try this:
$stmt = $db->prepare($sql);
$stmt->execute();
Or with mysqli: multi_query. php manual
Here my whole class: http://sandbox.onlinephpfunctions.com/code/f0528fda6d7bd097c3199f1f3c019805a163ae3a

mysql stored function syntax for dummies

I am appear to be having a bad case of brain flatulence at the moment. Can someone see the bleedingly obvious error in the following mysql stored function?
delimiter $$
drop function if exists test$$
create function test()
returns boolean
begin
return true;
end$$
I know it is dancing around the place, laughing its head off and waving its bare buttocks in my direction - but I just cant see it.
The original error was in a much larger hunk of code that I have cut down to the above - but I can't seem to remove much more without running out of code to remove.
mysql version 5.7.14
Drop the function before setting the new delimiter
DROP function IF EXISTS `test`;
DELIMITER $$
USE `A_DB`$$
CREATE FUNCTION `test` ()
RETURNS BOOLEAN
BEGIN
RETURN TRUE;
END$$
DELIMITER ;
For anyone who comes this way again the problem turned out not to be with the SQL. The issue was with the driver and my understanding of how mysql hangs together.
The delimiter statement is part of the MySQL client (command line) and is not part of any driver (or at least any on node i can find)
So if you are loading your sql by sucking in a string and squirting it at the database via a driver (nodejs mariasql in my case) then delimiter is not understood.
You either have to parse the delimiters out yourself - or be more elaborate and logon to the mysql client shell somehow and load the file. I will probably simply put a line such as %start sql stuff here %end and parse the sql into chunks - and squirt these at the driver.

Google CloudSQL 2nd Generation create Function not working

For a Google CloudSQL 2nd generation instance, with Failover replication was enabled. After that when tried to import the database it is not allowing to create the procedure. Receiving below error.
Error Code: 1419. You do not have the SUPER privilege and binary
logging is enabled (you might want to use the less safe
log_bin_trust_function_creators variable)
Is it true that CloudSQL with failover will not support function ?
Sample execute query
DELIMITER ;;
CREATE FUNCTION `stutzen`(amount INT) RETURNS int(11)
DETERMINISTIC
BEGIN
DECLARE charges FLOAT DEFAULT 1.0;
SELECT valuesettings INTO charges FROM dreamer_tbl WHERE namesettings='stutzen.co';
RETURN FLOOR((amount / 100) * charges) ;
END ;;
DELIMITER ;
You just need to set 'log_bin_trust_function_creators' to ON
To do it, open https://console.cloud.google.com
Select SQL
Select your instance
Select EDIT
DB signals
Add:
log_bin_trust_function_creators on
general_log on
SAVE
You should run:
gcloud sql instances patch [INSTANCE_NAME] --database-flags
log_bin_trust_function_creators=ON
as mentioned here
Google Cloud SQL support both, stored procedures and functions.
In your case the problem seems to be that you're trying to import a sql file that has some kind of routine that needs the SUPER privilege, and this is not permitted.
That is not a Stored Procedure, that is a User Defined Function.
You would need to rewrite this UDF as a Stored Procedure, which would work.
I tried using the SET GLOBAL log_bin_trust_function_creators, which should allow creating functions without the SUPER privilege but setting that variable is also not allowed in Cloud SQL. It needs SUPER privilege for setting it.

Debugging MySQL Triggers

I love triggers for one reason - they just work. I hate triggers for one reason - when they don't work, forget about trying to debug. O the sweet frustration.
Basically, I want to see THE update, delete, insert, etc query that was ran. I want to see that query ... somewhere, in my terminal or a log, exactly how and when MySQL executes it, and possibly any corresponding output/errors. Thoughts/hacks?
I'm trying to debug an update query with a few joins and what not. My queries are much more complex but for brevity here's an example.
DELIMITER |
CREATE TRIGGER ireallyhateyourightnow AFTER UPDATE ON watch_this_table
FOR EACH ROW BEGIN
IF (OLD.my_value != NEW.my_value) THEN
update
my_table
set
my_column = NEW.my_value;
END IF;
END|
DELIMITER ;
Here is some additional context that may help influence a suggestion or answer. Again, I'm less interested in semantics/syntax and more interested in seeing MySQL run the query but by all means, I'm open to anything at this point.
Strace does not work/show query.
Non-replicated environment BUT if the bin logs show trigger statements I will certainly set this up.
Does "show full processlist" show trigger execution and/or statements executed within (I never see them after running show full processlist as fast as perl can run it but I might just be missing it)?
General query log does not show these queries (certainly not the error log).
I'm not using aliases (anymore).
No syntax errors when creating the trigger.
The IF statement works.
When I insert the NEW values into a "test/temp" table and manually run the update query it works (I've even went so far as to actually inserting the whole update query)
I can't show you the query but as I just mentioned, it works when I run manually if that helps.
I've removed all erroneous characters, tabs, carriage returns, newlines, etc.
The MySQL socket would only show local connection/data but not MySQL internal workings, I think.
MyISAM so INNODB logs aren't an option
lsof didn't seem to show anything else to be of use.
I'm using MySQL 5.0.77 on CentOS 5.5.
There's an alternate way of testing it by having a temporary debug table. In the example here, they create it in an own debug database.
Step 1: Create a table
DROP TABLE IF EXISTS debug;
CREATE TABLE debug (
proc_id varchar(100) default NULL,
debug_output text,
line_id int(11) NOT NULL auto_increment,
PRIMARY KEY (line_id)
)
Step 2: Create debug SPs to fill the debug table
DELIMITER $$
DROP PROCEDURE IF EXISTS `debug_insert` $$
CREATE PROCEDURE `debug_insert`(in p_proc_id varchar(100),in p_debug_info text)
begin
insert into debug (proc_id,debug_output)
values (p_proc_id,p_debug_info);
end $$
DROP PROCEDURE IF EXISTS `debug_on` $$
CREATE PROCEDURE `debug_on`(in p_proc_id varchar(100))
begin
call debug_insert(p_proc_id,concat('Debug Started :',now()));
end $$
DROP PROCEDURE IF EXISTS `debug_off` $$
CREATE PROCEDURE `debug_off`(in p_proc_id varchar(100))
begin
call debug_insert(p_proc_id,concat('Debug Ended :',now()));
select debug_output from debug where proc_id = p_proc_id order by line_id;
delete from debug where proc_id = p_proc_id;
end $$
Step 3: Invoke the debug SPs in your trigger
Like this,
CREATE PROCEDURE test_debug()
begin
declare l_proc_id varchar(100) default 'test_debug';
call debug_on(l_proc_id);
call debug_insert(l_proc_id,'Testing Debug');
call debug_off(l_proc_id);
end $$
As a result the debug table would be filled as follows,
+------------------------------------+
| debug_output |
+------------------------------------+
| Debug Started :2006-03-24 16:10:33 |
| Testing Debug |
| Debug Ended :2006-03-24 16:10:33 |
+------------------------------------+
You can debug triggers using dbForge Studio for MySQL. Try trial version.
There is a detailed description of the trigger debugging process in the documentation: Debugging \ Debugging Stored Routines \ How To: Start Trigger Debugging.
MYSQL PROCEDURE => incron => tail -f 'mysql_dynamic.log'
A Stored Procedure can be invoked inside a trigger but must return nothing
CREATE PROCEDURE `DYN_LOG` (IN s VARCHAR(500))
BEGIN
SELECT s into outfile '/var/spool/incron/mysql_dynamic_spool/foo_file';
DO SLEEP(.1); // create a gap beetween multiple shuts
END
Now anywhere in a trigger you can invoke
CREATE TRIGGER `trig_name` BEFORE UPDATE ON `tb_name`
FOR EACH ROW
BEGIN
CALL DYN_LOG(concat_ws('\t',NEW.col1,NEW.col2));
...
// rest of the code
END
for Linux machines apt-get install incron (debian incron tutorial)
Create the folder in which mysql will inject foo_file
mkdir -m 777 /var/spool/incron/mysql_dynamic_spool
incrontab -e
and add following incron job
/var/spool/incron/mysql_dynamic_spool IN_CREATE /path/foo_file_procesor $#/$#
Create executable script "/path/foo_file_procesor"
#!/bin/sh
# // $1 is the foo_file absolute addres
body="$(cat $1)" #// read file content
rm $1
log=/var/log/mysql_dynamic.log #// message collector
echo "`date "+%Y-%m-%d %H:%M:%S"`\t== dyn_log ==\t$body">>$log
exit 0
Now watch the collector file
tail -f /var/log/mysql_dynamic.log

How do you debug MySQL stored procedures?

My current process for debugging stored procedures is very simple. I create a table called "debug" where I insert variable values from the stored procedure as it runs. This allows me to see the value of any variable at a given point in the script, but is there a better way to debug MySQL stored procedures?
The following debug_msg procedure can be called to simply output a debug message to the console:
DELIMITER $$
DROP PROCEDURE IF EXISTS `debug_msg`$$
DROP PROCEDURE IF EXISTS `test_procedure`$$
CREATE PROCEDURE debug_msg(enabled INTEGER, msg VARCHAR(255))
BEGIN
IF enabled THEN
select concat('** ', msg) AS '** DEBUG:';
END IF;
END $$
CREATE PROCEDURE test_procedure(arg1 INTEGER, arg2 INTEGER)
BEGIN
SET #enabled = TRUE;
call debug_msg(#enabled, 'my first debug message');
call debug_msg(#enabled, (select concat_ws('','arg1:', arg1)));
call debug_msg(TRUE, 'This message always shows up');
call debug_msg(FALSE, 'This message will never show up');
END $$
DELIMITER ;
Then run the test like this:
CALL test_procedure(1,2)
It will result in the following output:
** DEBUG:
** my first debug message
** DEBUG:
** arg1:1
** DEBUG:
** This message always shows up
I do something very similar to you.
I'll usually include a DEBUG param that defaults to false and I can set to true at run time. Then wrap the debug statements into an "If DEBUG" block.
I also use a logging table with many of my jobs so that I can review processes and timing. My Debug code gets output there as well. I include the calling param name, a brief description, row counts affected (if appropriate), a comments field and a time stamp.
Good debugging tools is one of the sad failings of all SQL platforms.
How to debug a MySQL stored procedure.
Poor mans debugger:
Create a table called logtable with two columns, id INT and log VARCHAR(255).
Make the id column autoincrement.
Use this procedure:
delimiter //
DROP PROCEDURE `log_msg`//
CREATE PROCEDURE `log_msg`(msg VARCHAR(255))
BEGIN
insert into logtable select 0, msg;
END
Put this code anywhere you want to log a message to the table.
call log_msg(concat('myvar is: ', myvar, ' and myvar2 is: ', myvar2));
It's a nice quick and dirty little logger to figure out what is going on.
Yes, there is a specialized tools for this kind of thing - MySQL Debugger.
There are GUI tools for debugging stored procedures / functions and scripts in MySQL. A decent tool that dbForge Studio for MySQL, has rich functionality and stability.
Debugger for mysql was good but its not free. This is what i use now:
DELIMITER GO$
DROP PROCEDURE IF EXISTS resetLog
GO$
Create Procedure resetLog()
BEGIN
create table if not exists log (ts timestamp default current_timestamp, msg varchar(2048)) engine = myisam;
truncate table log;
END;
GO$
DROP PROCEDURE IF EXISTS doLog
GO$
Create Procedure doLog(in logMsg nvarchar(2048))
BEGIN
insert into log (msg) values(logMsg);
END;
GO$
Usage in stored procedure:
call dolog(concat_ws(': ','#simple_term_taxonomy_id', #simple_term_taxonomy_id));
usage of stored procedure:
call resetLog ();
call stored_proc();
select * from log;
Another way is presented here
http://gilfster.blogspot.co.at/2006/03/debugging-stored-procedures-in-mysql.html
with custom debug mySql procedures and logging tables.
You can also just place a simple select in your code and see if it is executed.
SELECT 'Message Text' AS `Title`;
I got this idea from
http://forums.mysql.com/read.php?99,78155,78225#msg-78225
Also somebody created a template for custom debug procedures on GitHub.
See here
http://www.bluegecko.net/mysql/debugging-stored-procedures/
https://github.com/CaptTofu/Stored-procedure-debugging-routines
Was mentioned here
How to catch any exception in triggers and store procedures for mysql?
I'm late to the party, but brought more beer:
http://ocelot.ca/blog/blog/2015/03/02/the-ocelotgui-debugger/
and
https://github.com/ocelot-inc/ocelotgui
I tried, and it seems pretty stable, supporting Breakpoints and Variable inspection.
It's not a complete suite (just 4,1 Mb) but helped me a lot!
How it works:
It integrates with your mysql client (I'm using Ubuntu 14.04), and after you execute:
$install
$setup yourFunctionName
It installs a new database at your server, that control the debugging process. So:
$debug yourFunctionName('yourParameter')
will give you a chance to step by step walk your code, and "refreshing" your variables you can better view what is going on inside your code.
Important Tip: while debugging, maybe you will change (re-create the procedure). After a re-creation, execute: $exit and $setup before a new $debug
This is an alternative to "insert" and "log" methods.
Your code remains free of additional "debug" instructions.
Screenshot:
I just simply place select statements in key areas of the stored procedure to check on current status of data sets, and then comment them out (--select...) or remove them before production.
MySQL Connector/Net 6.6 has a feature to Debug Stored Procedures and Functions
Installing the Debugger
To enable the stored procedure debugger:
For Connector/Net 6.6: Install Connector/Net 6.6 and choose the Complete option.
For Connector/Net 6.7 and later: Install the product MySQL for Visual Studio, to which the stored procedure debugger belongs.
Starting the Debugger
To start the debugger, follow these steps:
Choose a connection in the Visual Studio Server Explorer.
Expand the Stored Procedures folder. Only stored procedures can be debugged directly. To debug a user-defined function, create a stored
procedure that calls the function.
Click on a stored procedure node, then right-click and from the context menu choose Debug Routine.
MySql Connector/NET also includes a stored procedure debugger integrated in visual studio as of version 6.6,
You can get the installer and the source here:
http://dev.mysql.com/downloads/connector/net/
Some documentation / screenshots:
https://dev.mysql.com/doc/visual-studio/en/visual-studio-debugger.html
You can follow the annoucements here:
http://forums.mysql.com/read.php?38,561817,561817#msg-561817
UPDATE: The MySql for Visual Studio was split from Connector/NET into a separate product, you can pick it (including the debugger) from here https://dev.mysql.com/downloads/windows/visualstudio/1.2.html (still free & open source).
DISCLAIMER: I was the developer who authored the Stored procedures debugger engine for MySQL for Visual Studio product.
The first and stable debugger for MySQL is in dbForge Studio for MySQL
I had use two different tools to debug procedures and functions:
dbForge - many functional mysql GUI.
MyDebugger - specialized tool for debugging ... handy tool for debugging.vote http://tinyurl.com/voteimg
MySQL user defined variable (shared in session) could be used as logging output:
DELIMITER ;;
CREATE PROCEDURE Foo(tableName VARCHAR(128))
BEGIN
SET #stmt = CONCAT('SELECT * FROM ', tableName);
PREPARE pStmt FROM #stmt;
EXECUTE pStmt;
DEALLOCATE PREPARE pStmt;
-- uncomment after debugging to cleanup
-- SET #stmt = null;
END;;
DELIMITER ;
call Foo('foo');
select #stmt;
will output:
SELECT * FROM foo
Toad mysql. There is a freeware version
http://www.quest.com/toad-for-mysql/
Answer corresponding to this by #Brad Parks
Not sure about the MySQL version, but mine was 5.6, hence a little bit tweaking works:
I created a function debug_msg which is function (not procedure) and returns text(no character limit) and then call the function as SELECT debug_msg(params) AS my_res_set, code as below:
CREATE DEFINER=`root`#`localhost` FUNCTION `debug_msg`(`enabled` INT(11), `msg` TEXT) RETURNS text CHARSET latin1
READS SQL DATA
BEGIN
IF enabled=1 THEN
return concat('** DEBUG:', "** ", msg);
END IF;
END
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `proc_func_call`(
IN RegionID VARCHAR(20),
IN RepCurrency INT(11),
IN MGID INT(11),
IN VNC VARCHAR(255)
)
BEGIN
SET #enabled = TRUE;
SET #mainQuery = "SELECT * FROM Users u";
SELECT `debug_msg`(#enabled, #mainQuery) AS `debug_msg1`;
SET #lastQuery = CONCAT(#mainQuery, " WHERE u.age>30);
SELECT `debug_msg`(#enabled, #lastQuery) AS `debug_msg2`;
END $$
DELIMITER