Return of mysql procedure using golang - sql

I'm having a problem getting the return of a procedure using golang. If I remove the parameters from the procedure the procedure is executed normally, however, when I need to get the return (OUT parameter) I can not get it and the procedure is not executed. To make the tests simpler and to get help, I created a simple procedure as below and I want to get the return of it.
Follow procedure below:
drop procedure if exists PESSOA_TESTE;
delimiter $$
create procedure PESSOA_TESTE(out psaida int)
begin
set psaida = 2;
end
$$
Now it follows the section of golang code that I'm using to try to get the value 2 specified in the procedure.
var GerenciaBD GERENCIABD
var PontoExecucao int
GerenciaBD.F_GERENCIABD_ABRIR_CONEXAO_MYSQL()
GerenciaBD.DataBase.ExecContext(context.TODO(),"call PESSOA_TESTE", sql.Named("psaida", sql.Out{Dest:&PontoExecucao}))
println(PontoExecucao)
When executing I have as answer 0 and not 2, which is the one specified in the procedure.
My golang version is at 1.10.

You can see here that usage of out parameter is not yet implemented for https://github.com/go-sql-driver/mysql library.
I'm not sure what you want to do with your code, but if you want to return only one value, you can do something like this:
Stored procedure:
drop procedure if exists PESSOA_TESTE;
delimiter $$
create procedure PESSOA_TESTE(IN psaida int)
begin
SET psaida = 2;
select psaida;
end
$$
Code:
var GerenciaBD GERENCIABD
var PontoExecucao int
GerenciaBD.F_GERENCIABD_ABRIR_CONEXAO_MYSQL()
GerenciaBD.DataBase.QueryRowContext(context.TODO(),"CALL PESSOA_TESTE(?)", PontoExecucao).Scan(&PontoExecucao)
println(PontoExecucao)

Related

How to pass arguments from a function to the creation of a TRIGGER?

I am trying to make a TRIGGER that responds on an update at a table (appointments) which then calls a procedure (proc1()). The procedure needs to get arguments in order to insert a new row -based on those arguments- on a different table (medical_folder). Procedures can't have arguments, but after a bit of search I found that you can use a method like the following to kinda force your way though:
Passing arguments to a trigger function
With the above as my base I made the following UDF:
CREATE OR REPLACE FUNCTION AppointmentUpdate(docAMKA bigint, patAMKA bigint, dateNtime timestamp, conclusion varchar(500),cure2 varchar(500), drug_id integer)
RETURNS void AS $$
DECLARE
patAMKAv2 text;
drug_idv3 text;
BEGIN
patAMKAv2 := cast(AppointmentUpdate.patAMKA as text);
drug_idv3 := cast(AppointmentUpdate.drug_id as text);
DROP TRIGGER IF EXISTS tr1 on appointments;
CREATE TRIGGER tr1 BEFORE UPDATE ON appointments
EXECUTE PROCEDURE proc1(patAMKAv2,cure2,drug_idv3);
UPDATE appointments
SET diagnosis = conclusion
WHERE patientamka = patAMKA
AND doctoramka = docAMKA
AND t = dateNtime;
END;
$$ LANGUAGE plpgsql;
My procedure is as follows:
CREATE OR REPLACE FUNCTION proc1()
RETURNS trigger AS $$
declare
newid integer;
BEGIN
newid =((select max(medical_folder.id) from medical_folder)+1);
INSERT INTO medical_folder AS Medf(id,patient,cure,drug_id)
VALUES(newid,cast(TG_ARGV[0] as bigint),TG_ARGV[1],cast(TG_ARGV[2] as integer));
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
If I run it as is I am getting an error in proc1() here cast(TG_ARGV[0] as bigint) and it seems that in the UDF instead of sending the value of the arguments it sends the arguments themselves(if for example i do this EXECUTE PROCEDURE proc1(324,cure2,234); INSTEAD OF THIS EXECUTE PROCEDURE proc1(patAMKAv2,cure2,drug_idv3); ). Is there any way that you can force it to get the values instead?
P.S.:I now that this can be done a lot easier without the use of a TRIGGER and just make a UDF that does everything itself but unfortunately I have to do it using a TRIGGER.
P.S.2: I tried using function_name.variable_name instead of just var_name also tried using $1, $2, $3, ... , $n.
From the CREATE TRIGGER docs:
arguments
An optional comma-separated list of arguments to be provided to the function when the trigger is executed. The arguments are literal string constants. Simple names and numeric constants can be written here, too, but they will all be converted to strings.
http://rextester.com/OCA59277
You might be able to achieve what you are trying with dynamic SQL though (see EXECUTE). But I believe that you overcomplicate things. What you want to do is simply to get rows or IDs which participated in an UPDATE statement. PostgreSQL's DML statements (INSERT, UPDATE & DELETE) has a RETURNING clause just for that. Also, you can actually write more DML (sub-)statements within a single statement with writeable CTEs. Something like this should suffice:
WITH upd AS (
UPDATE appointments
SET diagnosis = conclusion
WHERE patientamka = patAMKA
AND doctoramka = docAMKA
AND t = dateNtime
RETURNING *
)
INSERT INTO medical_folder(patient, cure, drug_id)
SELECT patAMKAv2, cure2, drug_idv3
FROM upd;
Note: while writing this I realized that you actually don't use any of the fields from the UPDATE, but using FROM upd will ensure that as much rows will be inserted into medical_folder just as much appointments got updated. Which is what your original trigger-based logic did.

how to apply an aggregation function over a measure dynamically passed to a stored procedure?

I am trying to do spit by dimension on an analytic view within a stored procedure, and I want to pass the measure on which I will apply the aggregation function dynamically. So I did the following:
create procedure procHO (in currentMeasure varchar(60))
language sqlscript as
begin
data_tab = select MONTH_NAME as ID, sum(:currentMeasure) from
_SYS_BIC."schema/analyticView" GROUP BY MONTH_NAME;
end;
then I call the procedure this way:
call procHO("MARGIN");
but I am getting an error saying :
inconsistent datatype: only numeric type is available for aggregation function: line 5 col 38 (at pos 124) Could not execute 'call procHO("MARGIN")'
I also tried to do this using CE_ functions, here is what I did:
create procedure procHO1(in currentMeasure varchar(60))
language sqlscript as
begin
out1 = CE_OLAP_VIEW("schema/analyticView", ["MONTH_NAME",
SUM(:currentMeasure)]);
end;
and I call the procedure this way:
call procHO1("MARGIN");
but still, I am getting an error saying:
feature not supported: line 5 col 70 (at pos 157)
Could not execute 'call procHO1("MARGIN")'
by the way, as a workaround, it is possible to create a dynamic SQL query that would resolve the issue, here is an example:
create procedure procHO2(in currentMeasure varchar(60))
language sqlscript as
begin
exec 'select MONTH_NAME AS ID, sum('||:currentMeasure||') as SUM_MEASURE from
_SYS_BIC."schema/analyticView" GROUP BY MONTH_NAME';
end;
I call it this way
call procHO2('MARGIN');
but I don't want to create the SQL query dynamically since it's not recommended by SAP.
So what to do to pass an aggregated measure dynamically?
this is what the sample code from the documentation:
CREATE PROCEDURE my_proc_caller (IN in_client INT, IN in_currency INT, OUT outtab mytab_t) LANGUAGE SQLSCRIPT
BEGIN
outtab = SELECT * FROM CALC_VIEW (PLACEHOLDER."$$client$$" => :in_client , PLACEHOLDER."$$currency$$" => :in_currency );
END;
of course this works only on the latest release. Which revision are you running on?

Returning a cursor from a procedure

I am trying to return a cursor from a procedure which will display the contents of a table invoice. The package and procedure was successfully created but when I run the line - Exec CursorPckg.CursorTest_Proc(); I get the following error component 'CURSORTEST_PROC' must be declared any ideas? I'm running SQL*PLUS
CREATE or REPLACE PACKAGE CursorPckg
IS
TYPE salary_type IS REF CURSOR RETURN Invoice%ROWTYPE;
END CursorPckg;
CREATE OR REPLACE PACKAGE BODY CursorPckg AS
PROCEDURE CursorTest_Proc (c1 OUT CursorPckg.salary_type)
IS
BEGIN
OPEN c1 FOR
SELECT * FROM Invoice;
END CursorTest_Proc;
END CursorPckg;
You need to add procedure declaration in package declaration for it to be accessible from outside your package.
Like this:
CREATE or REPLACE PACKAGE CursorPckg
IS
TYPE salary_type IS REF CURSOR RETURN Invoice%ROWTYPE;
PROCEDURE CursorTest_Proc (c1 OUT CursorPckg.salary_type);
END CursorPckg;
Edit:
To answer Your comment. You cannot execute this procedure without providing parameter of type CursorPckg.salary_type.

Postgresql trigger function with parameters

I want to create a trigger on a table called takes in postgresql to update a value in another table called student
I'm trying to do it in the following way. But I'm getting an error that there is syntax error near "OLD". I don't understand whats wrong with this. This is my code:
CREATE OR REPLACE FUNCTION upd8_cred_func
(id1 VARCHAR, gr1 VARCHAR,id2 VARCHAR, gr2 VARCHAR)
RETURNS void AS $$
BEGIN
IF (id1=id2 and gr1 is null and gr2 is not null) THEN
update student set tot_cred = tot_cred + 6 where id = id1;
END IF;
RETURN;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER upd8_cred
AFTER UPDATE ON takes
FOR EACH ROW
EXECUTE PROCEDURE upd8_cred_func(OLD.id,OLD.grade,NEW.id,NEW.grade);
You do not need to pass the NEW and OLD as parameters to the trigger function. They are automagically available there:
http://www.postgresql.org/docs/9.1/interactive/trigger-definition.html :
The trigger function must be declared as a function taking no arguments and returning type trigger. (The trigger function receives its input through a specially-passed TriggerData structure, not in the form of ordinary function arguments.)
About the records passed to the trigger procedure, please see http://www.postgresql.org/docs/9.1/interactive/plpgsql-trigger.html :
When a PL/pgSQL function is called as a trigger, several special variables are created automatically in the top-level block. They are: [...] NEW, [...] OLD [...]
As SeldomNeedy pointed in the comment below, you can still pass and use parameters to the trigger function. You declare the function as taking no parameters, but when defining the trigger (by CREATE TRIGGER), you may add some.
They will be available for the trigger as TG_NARG (the number of such parameters), and TG_ARGV[] (an array of text values).
As Greg stated, trigger functions can take arguments, but the functions themselves cannot have declared parameters. Here's a simple example in plpgsql:
CREATE TABLE my_table ( ID SERIAL PRIMARY KEY ); -- onelined for compactness
CREATE OR REPLACE FUNCTION raise_a_notice() RETURNS TRIGGER AS
$$
DECLARE
arg TEXT;
BEGIN
FOREACH arg IN ARRAY TG_ARGV LOOP
RAISE NOTICE 'Why would you pass in ''%''?',arg;
END LOOP;
RETURN NEW; -- in plpgsql you must return OLD, NEW, or another record of table's type
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER no_inserts_without_notices BEFORE INSERT ON my_table
FOR EACH ROW EXECUTE PROCEDURE raise_a_notice('spoiled fish','stunned parrots');
INSERT INTO my_table DEFAULT VALUES;
-- the above kicks out the following:
--
-- NOTICE: Why would you pass in 'spoiled fish'?
-- NOTICE: Why would you pass in 'stunned parrots'?
--
There are a few other goodies such as TG_NARGS (to know how many args you got without looping through them) discussed in the docs. There's also information there about how to get the name of the triggering table in case you have mostly-but-not-quite-shared logic for one trigger-function that spans a number of tables.
The trigger function can have parameters, but, you can't have those parameters passed like a normal function (e.g. arguments in the function definition). You can get the same result... In python you get access to the OLD and NEW data as the answer above describes. For example, I can use TD['new']['column_name'] in python to reference the new data for column_name. You also have access to the special variable TD['args']. So, if you like:
create function te() returns trigger language plpython2u as $function$
plpy.log("argument passed 1:%s 2:%s" %(TD['args'][0], TD['args'][1], ))
$function$
create constraint trigger ta after update of ttable
for each for execute procedure te('myarg1','myarg2');
Granted, these arguments are static, but, they are useful when calling a common trigger function from multiple trigger declarations. I am pretty sure that the same variables are available for other stored procedure languages. (sorry if the code doesn't work verbatim, but, I do practice this technique, so I know you can pass arguments!).

Dynamic SQL not working as expected

create or replace procedure createtables
Authid current_user as
begin
execute immediate 'create table newcustomer as select * from customer';
end;
create or replace procedure e
is
begin
createtables;
select * from newcustomer;
end;
I got two procedures above. first one will create a new tables called newcustomer, second procedure will call the first procedure and query to the newcustomer table. when I try to compile this code, it says the table is not yet created, I don't really get it as I have called createtables procedure so I assume I have created the table.
Any help will be appreciated. Thanks
Compiling the second procedure without executing the first procedure first will fail, since the table has not been created.
You cannot compile a procedure that relies on objects that do not exist.
Use EXEC createtables before creating procedure e, and do not call createtables in there.
Procedure e will also not compile because you are not using the results of select * from newcustomer as cursor or store the results into variables.
EDIT:
Instead of procedures, you could use an anonymous block. Put the following into a file and execute it (via SQL*Plus for example):
Create Table newcustomer As Select * From customer;
Begin
Null; --# Do something with your new table in here.
End;
/