perl mySQL procedure with insert and select fails when in transaction - mysql

This is my perl code:
my $dbc = DBI->connect('DBI:mysql:test', "entcfg", "entcfg") || die "Could not connect to database: $DBI::errstr";
$dbc->{TraceLevel} = "2"; #debug mode
$dbc->{AutoCommit} = 0; #enable transactions, if possible
$dbc->{RaiseError} = 1; #raise database errors
###sql commands
my $particle_value = $dbc->prepare('CALL particle_test_value(?,?,?,?)');
my $particle_name = $dbc->prepare('CALL particle_test_name(?,?,?,?)');
my $table_test = $dbc->prepare('CALL table_test(?,?,?)');
sub actionMessage {
my ($sh,$msgobj) = #_;
my #result;
my $return_ID;
eval {
$table_test->execute(undef,"value","value"); #new item
$return_ID = $table_test->fetchrow_array(); #get new row id
};
if ($#) {
warn $#; # print the error
}
}
The mySQL transaction is as follows:
CREATE DEFINER=`root`#`localhost` PROCEDURE `table_test`(
v_id INT,
v_name VARCHAR(255),
v_value VARCHAR(255)
)
BEGIN
INSERT INTO test (name,value) VALUES (v_name,v_value);
SELECT LAST_INSERT_ID();
END
If I put $dbc->commit; after the execute or the fetchrow_array,I get a Commands out of sync error.
If I remove the AutoCommit line, the code works but I can't use transactions.
If I try to change AutoCommit during the sub, I get this error:Turning off AutoCommit failed.
Any help would be much appreciated.

You can't extract values from stored procedures like that.
Make table_test a function:
CREATE DEFINER=`root`#`localhost` FUNCTION `table_test`(
v_name VARCHAR(255),
v_value VARCHAR(255)
) RETURNS integer
BEGIN
INSERT INTO test (name,value) VALUES (v_name,v_value);
RETURN LAST_INSERT_ID();
END //
and have $table_test use it like a function:
my $table_test = $dbc->prepare('SELECT table_test(?,?,?)');
edit: MySQL stored procedures can actually return results - the result of a SELECT statement inside the procedure is sent back to the SQL client. You have found a bug in DBD::mysql. The above works as a workaround.

Related

SQL Event - DELETE AND UPDATE rows on tables after UPDATE other table

I'd like to have a tricky SQL statement as an Event that runs every couple of minutes.
Currently, I'm doing so with Java, using 3 separate statements that executing sequentiality in a transaction connection.
Q: I don't know how to construct such an SQL statement without Java. If impossible to have a single SQL statement, I'd like to use transaction (as I'm using in Java) and rollback in case of failure in any of those separate statements.
My Case:
I have 3 tables: "Factory", "Plan", "Machine".
I want to do something as below:
1.
WHERE Machines.annualCheck == "TRUE"
SET Machine.status = "IN_ANNUAL_CHECK"
For machines that got updated I need to do the following:
2.1 Update the related factory
WHERE Factory.id == Machine.linkFactoryID
UPDATE Factory.totalActiveMachines = --1
2.2 Delete the upcoming plans that planned to be handled by the related machine
DELETE rows WHERE Plan.willHandleByMachineID = Machine.ID
p.s. I'm using MySQL
Thank you!
Update:
In following to Simonare suggestion, I tired to do the following:
DELIMITER $
CREATE PROCEDURE annualCheck(IN Machine_ID int, IN Factory_ID int)
BEGIN
UPDATE machine_table
SET machine_table.annualCheck = 'IN_ANNUAL_CHECK'
WHERE machine_table.machine_id = Machine_ID;
UPDATE factory_table
SET factory_table.totalActiveMachines = factory_table.totalActiveMachines - 1
WHERE factory_table.factory_id = Factory_ID;
DELETE FROM plan_table WHERE plan_table.assign_to_machine = Machine_ID
END$
DELIMITER $$
BEGIN
SELECT #m_id = machine_id, #f_id = link_factory_id
FROM machine_table
WHERE machine_table.annualCheck = 'TRUE';
END$$
CALL annualCheck(#m_id,#f_id)
I don't know why, but I'm running into syntax errors - one after the other.
It's my first time to use PROCEDURE and DELIMITER. Am I doing it right?
you can use stored procedure
delimiter //
CREATE PROCEDURE myProc (IN Machine_ID int)
BEGIN
UPDATE myTable
SET Machine.status = "IN_ANNUAL_CHECK"
WHERE Machines.annualCheck == "TRUE";
Update the related factory
WHERE Factory.id == Machine.linkFactoryID
UPDATE Factory.totalActiveMachines = totalActiveMachines -1;
DELETE FROM Plan WHERE Plan.willHandleByMachineID = Machine_ID;
END//
then you can execute it either from mysql
CALL simpleproc(#a);
or from Java
It is also possible to create trigger on the Machine table, something like this:
CREATE TRIGGER `TRG_Machines_AfterUpdate` AFTER UPDATE ON `Machine` FOR EACH ROW BEGIN
IF OLD.annualCheck = 'TRUE' AND NEW.annualCheck = 'IN_ANNUAL_CHECK' THEN
UPDATE
Factory
SET
totalActiveMachines = totalActiveMachines - 1
WHERE
id = NEW.linkFactoryID
;
DELETE FROM
Plan
WHERE
willHandleByMachineID = NEW.ID
;
END;
END
So you can just issue normal update:
UPDATE Machine SET annualCheck = 'IN_ANNUAL_CHECK' WHERE annualCheck = 'TRUE'

MySQL Stored procedure returning all results ignoring WHERE claus

I have two stored procs which i call from my laravel application.
My laravel application passes in a cID parameter which is then passed to the stored procedure as the "where clause". But it seems something is going astray and possibly my variables arent set up properly.
Also i know that laravel IS passing the correct cID to my stored proc because i enabled the logs for mysql to see if it was passing any params.
Also the stored procedure select statement runs fine as a query if i manually set the ClientID = '';
My stored proc sends ALL clients and cards to the view, totally ignoring the where clause.
Laravel code:
Route::get('/clients/{cID}', function ($cID) {
$details = DB::select('CALL sp_Details(' . DB::raw($cID) . ')');
$cards = DB::select('CALL sp_Cards(' . DB::raw($cID) . ')');
return view('client.show', compact('details','cards'));
});
Any my Stored Proc
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_Details`(IN cID int )
BEGIN
SET #ClientID = cID;
SELECT
ClientID,
Client_Name
FROM accounts
where #ClientID = cID;
END
Stored Proc #2
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_Cards`(cID int)
BEGIN
SET #ClientID = cID;
SELECT
ClientID,
Code
FROM cards
where cID = #ClientID;
END
You are using local variables like #ClientID and you are confusing it to column names, must change your code to avoid them and there is no necessary use the local variable:
CREATE PROCEDURE `sp_Details`(IN cID int )
BEGIN
SELECT
ClientID,
Client_Name
FROM accounts
where ClientID = cID;
END
The other proc:
CREATE PROCEDURE `sp_Cards`(cID int)
BEGIN
SELECT
ClientID,
Code
FROM cards
where ClientID = cID;
END

call simple database procedure using Slick 3.0

I have written a simple database procedure in mySQL as follows:
DROP PROCEDURE IF EXISTS sp_doSomething;
DELIMITER //
CREATE PROCEDURE sp_doSomething
(IN pVal1 varchar(100),
IN pVal2 int(15)
)
BEGIN
DECLARE vCnt int(5) DEFAULT 0;
DECLARE vID int(15) DEFAULT 0;
DECLARE vTempID int(15) DEFAULT 0;
-- get ID
SELECT id INTO vID FROM T1
WHERE name = pVal1;
-- get count
SELECT count(*) INTO vCnt FROM T1
WHERE owner = vID;
-- get the log
INSERT INTO log select CONCAT('-v1-:', pVal1, ':-v2-:', pVal2);
-- Create basic stuff if it doesn't exist
IF vFolderCnt = 0 THEN
INSERT INTO T1 (`id`, `col1`, `col2`, `col3`)
SELECT null, vID, 'some value', CONCAT(vID,'^1') FROM T1
WHERE owner = 0;
END IF;
commit;
END //
DELIMITER ;
Now, I want to call this procedure in my Play Framework 2.4 application which uses Slick 3.0. It is such a simple thing but I am really struggling with it as there is no proper documentation available. It's very frustrating.
As it's mentioned on Google Group here https://groups.google.com/forum/#!searchin/scalaquery/procedure/scalaquery/BUB2-ryR0bY/EFZGX663tRYJ
I tried calling the procedure by different way. The code compiles but the procedure does not get called at all.
This statement gives an action error.
db.run(sql"{call sp_doSomething('${st.val1}', 1)}")
The following statement compiles fine but does not invoke the procedure.
db.run(sql"{call sp_doSomething('${st.val1}', 1)}".as[Int])
The following statement compiles fine but does not invoke the procedure.
db.run(sqlu"{call sp_doSomething('${st.val1}', 1)}")
Or
db.run(sqlu"{?=call sp_doSomething('${st.val1}', 1)}")
I have granted the Execute permission on the procedure to my DB user and verified it.
Also, I am not sure, whether the COMMIT is required in the procedure or not?
Any help, will be highly appreciated.
I have managed to invoke the stored procedure using old prepareCall method. Here's how I have done it. Hope, it might help someone.
db.withSession {
implicit session => {
val cs = session.conn.prepareCall("{call sp_doSomething(?, ?)}")
cs.setString(1, st.val1)
cs.setLong(2, 1L)
val result = cs.executeUpdate()
}
}
But I would be still interested in invoking the procedure using sql"" or sqlu"".
For me the following works in Slick 3.2:
delimiter //
create procedure Try1()
begin
select userid from TBL_USER where id = "4";
end //
delimiter ;
Then
def runProcByRawSql() : Future[Vector[String]] = {
def runproc = sql"""call Try1()""".as[String]
db.run(runproc)
}

MySql: Can I Create array using sql

I am Using Mysql DB. my question is : can i create array using sql?
if Yes then how and how to populate this array with output of following query -- "Select column_name1 From tableName".
Help me, Thanks in Advance
As I mentioned in my comment, MySQL does not support arrays by itself. That kind of structures are supported by other programming languages (like PHP, Java, Python, etcetera) and you can write a program capable of connecting to a MySQL database, read data from it and populate arrays (I think PostgreSQL supports an array data type, but I'm not sure).
What you can do is use cursors in a stored procedure to retreive data from a query and store it into variables.
Example:
delimiter $$
create procedure my_procedure()
begin
declare value varchar(100);
declare done int default false;
declare cur cursor for
select column_name1 from your_table;
declare continue handler for not found set done = true;
open cur; -- This line will open the row set and place the cursor
-- on the first row.
loop_data: loop
fetch cur into value; -- This line will fetch the current row
-- into the variable and move the cursor
-- to the next row.
if done then -- If there are no more rows in the
leave loop_data; -- row set, the loop is terminated here
end if; -- and the execution moves to the next
-- instruction after "end loop;"
-- do whatever you need to do with the retrieved value
end loop;
close cur;
end $$
delimiter ;
If you want to use an array in a high level programming language, you can do it using the appropriate methods. Here's an example using Java (read The Java tutorials: JDBC Database access for more info):
public class SomeClass {
/*
Retrieve data from a database and return an array with it.
Parameters:
- conn: Connection to the database.
*/
public String[] getValues(Connection conn) {
String[] ans = new String[10];
int i;
try(
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(
"select column_name1 from your_table limit 10"
);
) {
rs.beforeFirst();
i = 0;
while(rs.next()) {
ans[i] = rs.getString("column_name1");
i++;
}
} catch(SQLException e) {
// Code to handle the SQL exception
}
return ans;
}
}
References:
MySQL reference manual: Cursors
You can use an variable to simply select your row values into a string. Not precisely an array, but it allows you to store all your values into a single variable:
-- load test data
create table tableName (column_name1 varchar(5));
insert into tableName values
('abcde');
insert into tableName values
('fghij');
insert into tableName values
('klmno');
insert into tableName values
('pqrst');
insert into tableName values
('uvwzy');
insert into tableName values
('z');
-- build "array"
set #array := '';
select #array := concat(#array,column_name1) as array
from tableName;
select #array;

MySql 5.1.32: call another procedure within a stored procedure and set variable

I'm new at creating and working with stored procedures.
After spending several hours on trying, reading tutorials (and yes reading all the related questions at stackoverflow :-) ) I'm stuck.
This works fine:
PROCEDURE GetAgent(IN AgentName VARCHAR(50), OUT AgentID SMALLINT(6))
BEGIN
IF EXISTS (SELECT id FROM tbl_lookup WHERE value = AgentName AND cat = 'agent') THEN
SELECT id FROM tbl_lookup WHERE value = AgentName AND cat = 'agent';
ELSE
INSERT INTO tbl_lookup(cat, value) VALUES ('agent', AgentName);
SELECT id FROM tbl_lookup WHERE value = AgentName AND cat = 'agent';
END IF;
END;
When called like:
Call GetAgent("Firefox 3.6.18", #AgentID);
It gives the proper response: "2"
So far so good. So let's get that into another procedure: (GetOS does the same thing, left out tot minimize reading :-)
PROCEDURE SetSessionInfo(IN OsName VARCHAR(50), IN AgentName VARCHAR(50), IN SessionID BIGINT(30), OUT SessionInfoID SMALLINT(6))
BEGIN
DECLARE nw_AgentID SMALLINT;
DECLARE nw_OSID SMALLINT;
CALL GetOs(OsName, #OsID);
SET NW_OSID = #OSID;
CALL GetAgent(AgentName, #AgentID);
SET NW_AgentID = #AgentID;
IF EXISTS (SELECT id FROM tbl_session_info WHERE session = SessionID) THEN
SELECT id AS SessionInfoID FROM tbl_session_info WHERE session = SessionID;
ELSE
INSERT INTO tbl_session_info(session, agent_id, os_id) VALUES (SessionID, GetAgent(AgentName, #AgentID), GetOs(OsName , #OsID));
SELECT id AS SessionInfoID FROM tbl_session_info WHERE session = SessionID;
END IF;
END;
When called with
Call SetSessionInfo("Windows XP", "Firefox 3.6.18", 857264713, #SessionInfoID)
I get the answer "3" (proper response from GetOS), then the procedure stops and does not insert anything.
After installing Toad I saw the reason: an error: "FUNCTION GetAgent does not exist"
Well, it is not a function, it's a procedure.
So basicly, my question:
How do I call another procedure within a stored procedure and set a variable with the result?
This is why you are getting "FUNCTION GetAgent does not exist" error:
INSERT INTO tbl_session_info(session, agent_id, os_id)
VALUES (SessionID, GetAgent(AgentName, #AgentID), GetOs(OsName , #OsID));
You are trying to call GetAgent as a function (while it is a procedure). But you have already got Agent and OS IDs into variables. Just use them:
INSERT INTO tbl_session_info(session, agent_id, os_id)
VALUES (SessionID, NW_AgentID, NW_OSID);