mysql stored function syntax for dummies - mysql

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.

Related

Does --init-file parameter support the creation of procedures through a .sql file?

So I have this SQL file whose contents are like below
DELIMITER $$
CREATE PROCEDURE FOO(....)
BEGIN
Insert into BAR(...) Values(....);
Insert into BARTOO(...) Values(...);
END$$
DELIMITER ;
Now this seems to work just fine when we I use the mysql client to execute this script. However If i pass it to initialize as a --init-file=./myscript.sql this fails with the follow error
2020-06-04T04:22:37.307204Z 5 [ERROR] [MY-000061] [Server] 1064 You
have an error in your SQL syntax; check the manual that corresponds to
your MySQL server version for the right syntax to use near 'DELIMITER
$$ CREATE PROCEDURE FOO(..... at line 1.
Initially this made sense that the keyword DELIMITER is not supported by the SQL syntax and that this is purely a client related command.Does that mean you cannot create a multi-line stored procedure using an --init-file? is there another way to create this procedure on initialization?
I also came across this bug report (https://bugs.mysql.com/bug.php?id=17843) that seems to indicate that DELMITER is supported in --init-file??
very confused, please help.
I don't known about mysql but there's an open bug on mariadb : https://jira.mariadb.org/browse/MDEV-18394
The dev team seems to say that this is the same behavior as mysql. They are probably not going to fix this soon as this is marked as a feature request.
I don't know about the context in which you are using this init script, but in my case this is for a container initialization, which makes it hard to use the mysql client. I ended up using a small shell script as my entry point, starting the mysqld in the background with a minimal init file, waiting a little bit, then passing the commands with the mysql client, then putting the mysqld back to foreground.
Single-line procedures do appear to work in init files:
init.sql
USE mysql;
CREATE OR REPLACE DEFINER='root'#'localhost' PROCEDURE abc() READS SQL DATA BEGIN SELECT 1; SELECT 2; SELECT 3; END;

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

Can I neatly skip all or part of a mySQL script (esp that contains creation of stored procedures)?

I have a mySQL script, that I have to execute, but for a bunch of useless reasons (and more importantly boring), I might want it to skip most of that script.
Let's say before each part of the script I want to check select okToRun from options returns true before running the script.
So my script looks something like this
DELIMITER $$
DROP PROCEDURE IF EXISTS procedure1$$
CREATE PROCEDURE procedure1()
BEGIN
IF (select okToRun from options) THEN
... -- do some stuff
END IF;
END$$
CALL procedure1()$$
DROP PROCEDURE IF EXISTS procedure2$$
CREATE PROCEDURE procedure2()
BEGIN
IF (select okToRun from options) THEN
... -- do some other stuff
END IF;
END$$
DROP PROCEDURE IF EXISTS procedure3$$
CREATE PROCEDURE procedure3()
BEGIN
IF (select okToRun from options) THEN
... -- do even more stuff!
END IF;
END$$
CALL procedure2()$$
CALL procedure3()$$
I hate repeated code, so how can I put the check right at the start of the script, rather than having to keep repeating myself?
I thought I'd be clever and wrap the whole thing in one stored procedure and do my if statement there... but you can't really create a stored procedure within another. And I need to keep the other stuff within their own stored procedures.
I also realise it'd be better to do the check before executing the script at all, but, well, I can't ;)
If the answer simply is that I can't do this, fair enough. But I want to be sure before repeating code like a big fat heathen.
The MySQL client script syntax does not support conditionals or loops or other programming logic.
The best idea I could come up with is to use prepared statements, but unfortunately CREATE PROCEDURE is not supported in this manner.
SET #do_create := true;
SET #create_proc1 := IF(#do_create, 'CREATE PROCEDURE ...', 'DO 1');
PREPARE stmt FROM #create_proc1
I got this error:
ERROR 1295 (HY000): This command is not supported in the prepared statement protocol yet
The list of commands supported in this way is documented here: http://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html
In MySQL 5.7, they introduced a new client shell called mysqlsh that is supposed to support a more featureful scripting environment, allowing you to write Python code and so on. I haven't tried this tool yet.
But you could do the same thing by writing your own script in Python or any other language you want. Then you can code the conditional logic to create procedures or whatever. That's your best bet.

Unable to Run Query in MySQL syntax error unexpected

I'm running Workbench 5.2.47.
I have a long procedure I wrote with basic data checking. If a record did not exist in the database, the record would be inserted.
The procedure saved with no problems, but MySQL 5.5 throws an error when I try running it.
It is long, and has a lot of company sensitive data in it, or I would post it here.
I am trying to debug the procedure by executing small chunks of the code, but I can't seem to get Workbench to allow anything I try.
MySQL shows how to create a stored procedure in 5.1.5 Working with Stored Procedures.
Let me show you something very basic I am trying to write:
DELIMITER $$
DROP PROCEDURE IF EXISTS my_test;
CREATE PROCEDURE my_test()
BEGIN
SELECT * FROM Employees;
END $$
DELIMITER ;
With that, Workbench gives me the error, "syntax error, unexpected CREATE, expecting $end".
I don't understand that, but I need to get something done, so I am moving on.
I make a simpler query:
SET #Count=(SELECT Count(*) FROM tbl_object_users WHERE username='jp2code');
IF (#Count < 1) THEN
INSERT INTO tbl_object_users (username, date_time) VALUES ('jp2code', NOW());
END IF;
Again, I get an error, this time on my IF statement.
Next, I go into PhpMyAdmin to try running something from there using its database:
SET #Count=Count(id) FROM `tbl_object_users` WHERE `username`='jp2code';
It, too, tells me I have an error in my SQL syntax.
I did download and install the newest Workbench 6, but it did not solve the problem - and I did not like the interface, so I uninstalled it and went back to Workbench 5.2.
What is going on? SQL isn't that hard, so what is with these hurdles?
Problem with this:
DELIMITER $$
DROP PROCEDURE IF EXISTS my_test;
CREATE PROCEDURE my_test() ...
is that MySQL isn't seeing the semicolon at the end of the DROP PROCEDURE statement line as the end of the statement. This is because the preceding line told MySQL that the statement terminator was something other than a semicolon. You told MySQL that statements were going to be terminated with two dollar signs. So MySQL is reading the DROP PROCEDURE line, looking for the statement terminator. And the whole blob it reads is NOT a valid MySQL statement, it generates a syntax error.
The fix: either move the DROP PROCEDURE line before the DELIMITER $$ line; or terminate the DROP PROCEDURE statement with the specified delimiter rather than a semicolon.
The second problem you report is a syntax error. That's occurring because MySQL doesn't recognize IF as the beginning of a valid SQL statement.
The IF statement is valid only within the context of a MySQL stored program (for example, within a CREATE PROCEDURE statement.)
The fix: Use an IF statement only within the context of a MySQL stored program.
The third problem you report is also a syntax error. That's occurring because you don't have a valid syntax for a SET statement; MySQL syntax for SET statement to assign a value to user variable is:
SET #uservar = expr
MySQL is expecting an expression after the equals sign. MySQL is not expecting a SQL statement.
To assign a value to a user variable as the result from a SELECT statement, do the assignment within the SELECT statement, for example:
SELECT #Count := Count(id) FROM `tbl_object_users` WHERE `username`='jp2code'
Note that the assignment operator inside the SELECT statement is := (colon equals), not just =.
try this
DELIMITER $$
DROP PROCEDURE IF EXISTS my_test$$
CREATE PROCEDURE my_test()
BEGIN
SELECT * FROM `customer_to_pay`;
END $$
DELIMITER ;

create procedure fails?

when trying to create a simple procedure in mysql 5.1.47-community it fails everytime i've tried everything!
even simple things like this!
DELIMITER //
CREATE PROCEDURE two ()
begin
SELECT 1+1;
end;
//
The error is
ERROR: Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'mydb' at line 1
The error message you've given doesn't correspond to the code you've pasted. You're referring to "mydb" somewhere in the SQL you're running yet it's not anywhere in the code you've put in the question.
The code you've given should work fine as I see no syntax errors, you may just need to give it a database to work on ("test" in my case here, perhaps it should be "mydb" for you?).
DELIMITER //
CREATE PROCEDURE test.two ()
begin
SELECT 1+1;
end;
//
DELIMITER ;
CALL test.two;
However, I suspect the error you're getting is become of a line in your SQL that you're not showing us.
EDIT
It could perhaps be the delimiter command. You're changing the delimiter to // rather than the default ;. So perhaps you've run that command (and changed the delimiter for your session to //), and then tried to run USE mydb; where the ; is no longer recognised as a valid delimiter for your session, and that could be giving you the error. Try putting delimiter ; before your use line and see if that helps (and then use it again after you've defined your stored procedure so you can call it). This is just a theory though, as I'm not sure of the intricacies of the delimiter command.
Remove the final delimiter "end" instead "end;"
I had the same problem using heidisql as the fronted to enter the SQL. My first attempt was:
CREATE PROCEDURE Add_Two (IN someNumber int, OUT result INT)
BEGIN
SELECT someNumber +2 INTO result;
END
and this resulted in SQL ERROR (1064) because i was not aware that when using a client program a delimiter is needed to define the stored procedures.
After changing the above to this:
DELIMITER //
CREATE PROCEDURE Add_Two(IN someNumber int, OUT result INT)
BEGIN
SELECT someNumber +2 INTO result;
END
//
It worked out.
Example to call it
SET #someNumber :=8;
CALL Add_Two(#someNumber, #result);
SELECT #result;