Function definition not properly passed to MySQL database using MySQL Workbench - mysql

I'm using MySQL 5.5 (x64) and MySQL Workbench 5.2 deployed locally on a Windows 7 workstation for development purposes. I used MySQL Workbench to build a schema with the following function definition:
CREATE FUNCTION `db`.`get_public_name` (GPN_entID INT) RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
DECLARE GPN_pubName VARCHAR(64);
SELECT public_name INTO GPN_pubName
FROM entity WHERE id_entity=GPN_entID LIMIT 1;
RETURN GPN_pubName;
END
I then attempt to "Forward Engineer" the schema to the database with the following options specified:
DROP Objects Before Each Create Object
Generate DROP SCHEMA
Add SHOW WARNINGS After Every DDL Statement
GENERATE INSERT Statements for Tables
After this, MySQL Workbench attempts to publish to the server:
CREATE FUNCTION `db`.`get_public_name` (GPN_entID INT) RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
DECLARE GPN_pubName VARCHAR(64);
SELECT public_name FROM entity WHERE id_entity = GPN_entID;
RETURN GPN_pubName;
END
This results in the following error:
Executing SQL script in server
ERROR: Error 1415: Not allowed to return a result set from a function
Upon closer examination, I noticed the "INTO" and "LIMIT" clauses of the SELECT statement have been removed from the original function definition. This looks like it might be a cached version of the function, but I have tried everything I can think of (short of uninstalling and reinstalling MySQL Workbench) to flush any such cache to reload the correct version, but to no avail.
So, why is this change happening and how do I prevent it from happening?

Try changing to this:
SELECT public_name FROM entity WHERE id_entity = GPN_entID LIMIT 1 INTO GPN_pubName;

I'm embarrassed; if it wasn't for the fact this may be useful to others, I'd just go ahead and delete this question to hide my shame.
It turns out I created two functions with the same name and MySQL Workbench happily let me do so. I didn't notice that was the case until I started going through the stored routines with a more careful eye. I was editing one, but the other one (which had the error) was never changed. Since publishing each function involved dropping any earlier version from the database, I probably wouldn't have noticed this until things weren't working properly.

Related

MYSQL Error #2014: in stored procedure when resultset is empty

I am trying to make a stored procedure in MYSQL(v5.7.21) with phpmyadmin(v4.7.9) that will sometimes return an empty result set.
CREATE DEFINER=`my_database`#`%` PROCEDURE `emptytest`(IN `_id` INT(11))
NOT DETERMINISTIC NO SQL SQL SECURITY DEFINER
select * from my_table where id=_id
and next call it with:
call emptytest(1);
this works fine when the id exists in the table:
id name
--------
1 bob
but throws an error when there are no rows to return:
call emptytest(11);
#2014 - Commands out of sync; you can't run this command now
However i would expect it to return the same as running the SQL statement:
select * from my_table where id=11
Which is an empty list:
id name
--------
I've been looking on StackOverflow for similar questions but most of them address issues with multiple queries which is not my case.
As far as I know MySQL documentation states that procedures should be able to return tables.
For the example I am showing the default options used by phpmyadmin, but i tried to tweak them to no avail.
What am I missing?
Nothing wrong with the procedure.
It is phpMyAdmin which does not handle stored procedures when executed using CALL in SQL tab.
You can run the procedure by clicking the icon in front of the procedure name in the left navigation tree.
Stored procedures tend to be 'fussier' about errors than simple statements (see below), so you need to put a handler in for that error.
DECLARE EXIT HANDLER FOR 2014 BEGIN select 'empty set' as `Error`; END;
The principle here is that you can't really handle these sort of errors in a simple query, so, from necessity, they are ignored; you can handle them in an SP, so, well, handle them...

Invalid descriptor index on LAST_INSERT_ID after insert

Strange situation with my ODBC code ( called from a C library ). Basically, I have the following sequence of events:
Create insert statement ( just a string )
Call SQLPrepare with that insert statement string
Bind the various parameters ( column values ), using
SQLBindParameter
Call SQLExecute to insert the row ( this works, by the way, as I can
see the row in the MySQL DB )
Create "select last_insert_id()" statement string
NOTE: if in SQL Server mode, we would create a "select ##identity"
statement
Bind column using SQLBindCol - this is where I get the "Invalid
descriptor index" error
NOTE: if in SQL Server mode, this works fine, with no error
Call SQLExecDirect to get the last insert id - this never happens
because of SQLBindCol error
Does the standard MySQL ODBC connector require something special in this situation? Does anyone have an ODBC example of this type of "insert" then "get last insert id" behavior? Maybe I need to call "SQLPrepare" before step 6 ( where I bind the column )? Another way to ask this: Should there be an SQLPrepare call for each SQLExecute or SQLExecDirect call?
I know it works directly in SQL, so the problem is my C ODBC code.
Thanks.
For those who are interested, I ended up changing the above steps by adding an SQLPrepare call between creating the "select last_insert_id()" ( step 5 ) and calling SQLBindCol ( step 6 ). Not sure if that would work for others, but it seems to be working rather well for me.
As for research, I looked all over the place online and never found a really good or clear answer. Most comments were about the SQL involved, not ODBC. And the references to ODBC were vague and did not seem to apply to my situation, from what I could see.
My assumption is that the SqlServer ODBC driver I am using handles the missing prepare statement differently ( maybe even better, but that is debatable ) than my MySql ODBC driver.
SQL Server ODBC driver was the one provided by Easysoft
MySql ODBC driver was the one provided with the standard CentOS install of MySql
Hopefully this will help people. Obviously, if people have a better idea, please tell us.

How to create a function using MySQL Workbench?

I have an MSSQL background.
I am trying to create a function in MySQL Workbench by rightclicking on 'Functions' > 'Create Function'.
I insert this text to create the function into the window but it says there are errors in sql at the last line missing 'if'.(SQL below). What am I missing?
2nd Qn. (Related)
If I create the function using the function SQL (not using the menu in MySQL Workbench), the function gets created but it doesn't appear in the 'Functions' being shown in the schema am working on.
What is the recommended way to create functions in MySQL Workbench?
Thanks.
CREATE FUNCTION fnIsExcluded(ConcattedString NVARCHAR(15), InValue DECIMAL)
RETURNS BIT
BEGIN
DECLARE individual VARCHAR(20) DEFAULT NULL;
DECLARE ReturnValue BIT;
IF (LENGTH(ConcattedString)) < 1
THEN
SET ReturnValue = 0;
ELSE IF ConcattedString IS NULL
THEN
SET ReturnValue = 0;
ELSE IF InValue IS NULL
THEN
SET ReturnValue = 0;
ELSE
SET ReturnValue = 1;
END IF;
RETURN ReturnValue;
END;
You didn't say what version of MySQL Workbench you are using.
1) The syntax error in the routine editor you is because in older versions the editor did not handle delimiters automatically. Hence you'd need a command to change the default delimiter, like:
delimiter $$
(at the top of the text) to change the default (semicolon) to a custom one (double dollar). The reason you have to do this is that the default delimiter is needed in the function definition and if not changed the client (here MySQL Workbench) would wrongly split your function command into individual commands. However, you can update to the latest MySQL Workbench version (6.2.3 at the time of this writing) to have this handled automatically.
2) Usually changes in the db structure cannot be picked up automatically because it would mean to load all db content at a frequent intervals, which is not feasible for any non-trivial db. Hence you have to trigger this manually, by clicking the refresh button.
However, some changes (especially additions or deletions you did yourself in MySQL Workbench) either are already tracked or will be there in future versions).

How to retrieve OUT parameter from MYSQL stored procedure to stream in Pentaho Data Integration (Kettle)?

I am unable to get the OUT parameter of a MySQL procedure call in the output stream with the procedure call step of Pentaho Kettle.
I'm having big trouble retrieving OUT parameter from MYSQL stored procedure to stream. I think it's maybe a kind of bug becouse it only occurs with Integer out parameter, it works with String out parameter. The exception I get is:
Invalid value for getLong() - '
I think the parameters are correctly set as you can see in the ktr.
You can replicate the bug in this way:
Schema
create schema if not exists test;
use test;
DROP PROCEDURE IF EXISTS procedure_test;
delimiter $$
CREATE PROCEDURE procedure_test(IN in_param INT UNSIGNED, OUT out_param INT UNSIGNED)
BEGIN
SET out_param := in_param;
END
$$
KTR
You can download here .ktr file with the steps. You just have to setup the connection to MySQL test schema to try it.
Other data
MySQL connector: 5.1.30
MySQL version: 5.5
Kettle version: 5.0.1-stable
OS: Ubuntu 12.04
Any help from the community will be highly appreciated.
Thanks in advance!
The bug does not seem to occur if number (and decimal at DB level) is used as the output parameter type. So this may be used as a work around.
With a DB procedure using this statement (as returned by phpMyAdmin):
CREATE DEFINER = `root`#`localhost`
PROCEDURE `procedure_test` ( IN `in_param` INT ZEROFILL,
OUT `out_param` DECIMAL( 10 ) )
NOT DETERMINISTIC NO SQL SQL SECURITY DEFINER
BEGIN
SET out_param =2 * in_param;
END
I was able to run this transformation
reading this input
IN_VALUE
1
2
3
99
yielding this output
<?xml version="1.0" encoding="UTF-8"?>
<Rows>
<Row><OUT_VALUE> 2,0</OUT_VALUE> </Row>
<Row><OUT_VALUE> 4,0</OUT_VALUE> </Row>
<Row><OUT_VALUE> 6,0</OUT_VALUE> </Row>
<Row><OUT_VALUE> 198,0</OUT_VALUE> </Row>
</Rows>
I have solved the problem using SQL script step with an SQL like this:
CALL procedure_test(?, #output_value);
INSERT INTO output_value_for(in, out) VALUES (?, #output_value);
SQL script step don't allow output value to stream, however it was a better solution for my problem. After procedure execution, I inserted in MySQL database output value of the procedure becouse I need to persist that relation.
However, I think Marcus answer is better approach for the posed problem. If you have this problem, you should take it into account as work around.

MySQL query browser procedure error code -1

I'm having a rather strange problem with MySQL. Trying to create a procedure to update some fields in the database (the code is below).
The problem is with the line that is currently commented. It seems that if no SELECT statements get executed during the procedure MySQL query browser will return an error code of "-1, error executing SQL query".
I tried the same thing in HeidiSQL and the error was "cannot return result set". So I suppose the question is do I always have to select something in the procedure, or is there some other thing I missed.
The query works fine when the comment is removed.
DELIMITER /
DROP PROCEDURE IF EXISTS updateFavourites /
CREATE PROCEDURE updateFavourites(quota INT)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE artist_id,releases INT;
DECLARE c_artist Cursor FOR
SELECT Artist.id_number,COUNT(Artist.id_number) FROM Artist
JOIN CD ON CD.is_fronted_by = Artist.id_number
GROUP BY Artist.id_number;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SET done=1;
IF quota > 0 THEN
OPEN c_artist;
REPEAT
FETCH c_artist INTO artist_id,releases;
IF NOT done THEN
IF releases >= quota THEN
UPDATE CD SET CD.rating='favourite' WHERE CD.is_fronted_by = artist_id;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE c_artist;
-- SELECT 'Great success';
ELSE
SELECT CONCAT('\'quota\' must be greater than 0.',' Got (',quota,')');
END IF;
END /
DELIMITER ;
Here's the sql to create the tables and some data:
DROP TABLE IF EXISTS CD;
DROP TABLE IF EXISTS Artist;
CREATE TABLE Artist (
id_number INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
);
CREATE TABLE CD (
catalog_no INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
is_fronted_by INT UNSIGNED,
rating ENUM ('favourite','top draw','good','so-so','poor','rubbish'),
CONSTRAINT fk_CD_Artist FOREIGN KEY (is_fronted_by) REFERENCES Artist(id_number) ON UPDATE CASCADE
);
INSERT INTO Artist VALUES(11,'Artist 1');
INSERT INTO Artist VALUES(10,'Artist 2');
INSERT INTO CD VALUES (7,11, 'top draw');
INSERT INTO CD VALUES (650,11,'good');
INSERT INTO CD VALUES (651,11,'good');
INSERT INTO CD VALUES (11,10,'favourite');
Query Browser is not for running scripts, just single query.
I tried your code by moving cursor into each query (except DELIMITER) and pressing Ctrl+Enter.
It created that stored procedure without problem. (just refresh schema on the left).
If you wish creating procedure, use menu "Script"->"Create stored procedure/function".
But better forget about QueryBrowser it is not supported at all (and actunally not useful).
If you have decent hardware and plenty resources, try Workbench 5.2 otherwise use SQLyog
Googling around, there are several reports of the same error, but little information to solve the problem. There's even a bug logged at mysql.com but it appears to have been abandoned without being resolved.
There's another StackOverflow question on the same error, but it's also unresolved.
All it means is that there is no result set from the query. Looking at the source code, it appears that sometimes an error status of MYX_SQL_ERROR is set when the query has no result set. Perhaps this is not an appropriate consequence?
I notice that when I use the mysql command-line client, it yields no error for calling a proc that returns no result set.
update: I tried to revive that MySQL bug report, and provide a good test case for them. They changed the bug from "no feedback" to "verified" -- so at least they acknowledge it's a bug in Query Browser:
[11 Dec 9:18] Sveta Smirnova
Bill,
thank you for the feedback. Verified
as described.
Although most likely this only be
fixed when MySQL Query Browser
functionality is part of MySQL
workbench.
I guess the workaround is to ignore the -1 error, or to test your stored procedures in the command-line mysql client, where the error does not occur.
The comment supposes the issue will disappear as the Query Browser functionality becomes part of MySQL Workbench. This is supposed to happen in MySQL Workbench 5.2. I'll download this beta and give it a try.
MySQL Workbench 5.2 is in Beta, but I would assume MySQL engineering can't predict when the Beta will become GA. Those kinds of predictions are hard enough under standard conditions, but there's a lot of extra uncertainty of MySQL's fate due to the unresolved Oracle acquisition.
update: Okay, I have tried MySQL Workbench 5.2.10 beta. I executed a stored procedure like this:
CREATE PROCEDURE FooProc(doquery SMALLINT)
BEGIN
IF doquery THEN
SELECT * FROM Foo;
END IF;
END
When I CALL FooProc(0) the response is no result set, and the status is simply "OK".
When I CALL FooProc(1) the response is the result of SELECT * FROM Foo as expected.
However, there's another bug related to calling procedures. Procedures may have multiple result sets, so it's hard to know when to close the statement when you execute a CALL query. The consequence is that MySQL Workbench 5.2 doesn't close the statement, and if you try to do another query (either CALL or SELECT) it gives you an error:
Commands out of sync; you can't run this command now.
MySQL doesn't support multiple concurrent open queries. So the last one must be closed before you can start a new one. But it isn't closing the CALL query. This bug is also logged at the MySQL site.
The bug about commands out of sync has been resolved. They say it's fixed in MySQL Workbench 5.2.11.
Try putting BEGIN and END blocks around the multiple statements in the IF block as such:
IF quota > 0 THEN
BEGIN
OPEN c_artist;
REPEAT
FETCH c_artist INTO artist_id,releases;
IF NOT done THEN
IF releases >= quota THEN
UPDATE CD SET CD.rating='favourite' WHERE CD.is_fronted_by = artist_id;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE c_artist;
END;
ELSE
SELECT CONCAT('\'quota\' must be greater than 0.',' Got (',quota,')');
END IF;