mysql trigger write file with dynamic file name - mysql

I'm interested in creating a trigger that will write a file after I import a csv into my table. The file name includes a time stamp and my code isn't working properly. Here's what I have so far.
DELIMITER $$
CREATE TRIGGER peachtree_trigger
AFTER INSERT ON peachtree
FOR EACH ROW
BEGIN
SET #sql_text = CONCAT("SELECT * FROM peachtree
INTO OUTFILE '/srv/samba/share/peachtree_",
DATE_FORMAT(NOW(), '%Y_%m_%D'), ".csv'");
PREPARE s1 FROM #sql_text;
EXECUTE s1;
DROP PREPARE s1;
END $$ DELIMITER ;
The set statement works fine outside of the trigger. However when I execute the above set of code and then try SHOW TRIGGERS IN test; it returns an empty set. If anyone could help I would be very grateful.

from http://dev.mysql.com/doc/refman/5.5/en/stored-program-restrictions.html
<snip>
SQL prepared statements (PREPARE, EXECUTE, DEALLOCATE PREPARE) can be used in stored procedures, but not stored functions or triggers. Thus, stored functions and triggers cannot use dynamic SQL (where you construct statements as strings and then execute them).
</snip>

Related

Use MySQL procedure to change the name of a table

I am trying to create a stored procedure to change the name of an existing table by appending the date to it.
I have only been using MySQL for a short while and cannot see why the code is not working. I'm using MySQL Workbench 8.0 Community to connect to the MySQL database and run the code. In Workbench no errors are returned when I run the code.
The code I have managed to find so far is:
DELIMITER \\
DROP PROCEDURE IF EXISTS `usp_test_dynamic_sql`\\
CREATE PROCEDURE `usp_test_dynamic_sql`()
BEGIN
SET #s = concat('ALTER TABLE MyDashboardTable RENAME TO MyDashboardTable_',replace(date(now()),'-',''));
PREPARE stmt1 FROM #s;
EXECUTE stmt1;
END\\
DELIMITER ;
I have run part of the code, to find what #s is being set to by running:
SET #s = concat('ALTER TABLE MyDashboardTable RENAME TO MyDashboardTable_',replace(date(now()),'-',''));
select #s;
I have then pasted the result for #s
ALTER TABLE MyDashboardTable RENAME TO MyDashboardTable_20200904
This does change the name of the table but I cannot figure out why the stored procedure does not. Any help will be greatly appreciated.
Execute your SP as
CALL usp_test_dynamic_sql();
You may also want to modify your
replace(date(now()),'-','')
with
DATE_FORMAT(NOW(),'%Y%m%d')
Even though it's not absolutely necessary. If you do, do not leave spaces or use '-' or '/' between the formats since MySQL with throw an error.

can procedures within mysql triggers work?

Can a stored procedure plus a sql query be executed from within a trigger?
procedure plus query
SET #tmp = (SELECT fvalue FROM ftable WHERE id=2);
SET #SQL = CONCAT('INSERT INTO ',#tmp,' VALUES (''buhambug'')');
PREPARE stmt FROM #SQL;
EXECUTE stmt;
If so what are the rules and links to examples? I have't been able to find any.
Yeah, you can call a stored procedure from a trigger like this:
Create trigger foo after insert on table for each row
BEGIN
call your_procedure(params);
END$$
Note the ending delimiter. If ; is the default delimiter inside the trigger as well, then it won't work, for MySQL will treat each command separately and throw error. You want the entire code inside the trigger to be executed together. hence declare a different delimiter, like $$ prior to defining the trigger, through Delimiter $$. Then, ; will work correctly inside the the trigger. After you terminate the trigger through $$, don't forget to restore the default delimiter.

MYSQL MyTAP issue

My question may be stupid but I have a 2 days experience in MYSQL. I'm trying to use MyTAP for unit testing and face a problem .
here's the SQL code exucted on a mysql console:
drop procedure IF EXISTS tap.tstTableCount;
delimiter //
CREATE PROCEDURE tap.tstTableCount (IN db CHAR(64), IN tbl CHAR(64))
BEGIN
DECLARE l_sql VARCHAR (4000);
SET l_sql=CONCAT('SELECT COUNT(*)>0 FROM ',db,'.',tbl,';');
SET #sql=l_sql;
PREPARE s1 FROM #sql;
EXECUTE s1;
DEALLOCATE PREPARE s1;
END //
delimiter ;
call tap.tstTableCount('storibo','relationCategory'); /* This call works fine and returns 1 (true)*/
SELECT tap.ok(
tap.tstTableCount('storibo','relationCategory'),
'relationCategory contains data'
); /* this one returns :
ERROR 1305 (42000): FUNCTION tap.tstTableCount does not exist */
is it an issue with the MyTAP fmk or do I make a mistake in my syntax ?
The problem is your tap.tstTableCount is a PROCEDURE and not a FUNCTION. All the MyTAP tests are are FUNCTIONS, as noted by being able to call it in a SELECT statement. You cannot call a PROCEDURE from a SELECT statement, instead needing to call it via:
CALL tap.tstTableCount();
Unfortunately, your example is one that cannot simply be converted to a FUNCTION to be used by the mytap functions. Dynamic SQL is not permitted in FUNCTION, but is allowed in PROCEDURE.

mysql stored procedure oddity

I learned today through this section of the MySQL documentation that prepared statements cannot be performed in stored functions, but, as of MySQL version 5.0.13, they can be performed in stored procedures.
Today I was putting together a stored procedure and thought initially it might be interesting to try doing an INSERT statement in it as a prepared statement. However, despite this supposedly being possible (I'm using MySQL 5.5.14), the ? parameter marker in the statement string caused MySQL to throw a syntax error.
I threw a couple of simplified examples together using the same exact sort of syntax I used for the prepared INSERT statement. I'm hoping I just have a syntax error somewhere I just haven't caught. The first block, below, is the procedure that works, i.e. it uses the standard CONCAT(your query string) syntax.
DROP PROCEDURE IF EXISTS TestConc;
DELIMITER $$
CREATE Procedure TestConc()
BEGIN
SET #sql := CONCAT('CREATE TABLE Foo (FooID INT) ENGINE = InnoDB');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #tn := 'Foo';
SET #sql := CONCAT('INSERT INTO ', #tn, ' VALUES (5)');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END;
$$
DELIMITER ;
Upon calling the procedure with this code, the expected happens; 5 is stored in the FooID field of the newly-generated Foo table. However, if we change the lines between the two DEALLOCATE PREPARE directives to this:
SET #tn := 'Foo';
SET #sql := 'INSERT INTO ? VALUES (5)';
PREPARE stmt FROM #sql;
EXECUTE stmt USING #tn;
We get an error that tells us to check the syntax of the statement near '? VALUES (5)'.
Is it just not possible to substitute a parameter marker for a table name? I haven't tried doing something along the lines of 'SELECT ? FROM Foo' to see if this will work yet. Also, and I don't know if it's important, I've been trying this using MySQL Workbench 5.2.35 CE, rather than a command line.
I don't have any specific need to run queries as prepared statements within procedures ATM, I just want to make sure I have the syntax correct for doing so if I ever should need to.
The parameter '?' cannot be used for identifiers. Use first variant. From the reference - Parameter markers can be used only where data values should appear, not for SQL keywords, identifiers, and so forth.
Is it just not possible to substitute a parameter marker for a table name?
No, it's not possible. If you ever think you need this feature, it could be a sign that you have a bad table design.
If you really need to specify the table at runtime, you can use dynamic SQL but be careful not to introduce SQL injection vulnerabilities.

Can MySQL triggers be created with dynamic SQL from within a stored procedure?

Is it possible to create a trigger in MySQL using dynamically generated SQL from within a stored procedure? I am executing other dynamically constructed queries in my procedure by preparing a statement, but when I try the same approach to create a trigger I get the following error:
ERROR Code: 1295This command is not supported in the prepared statement protocol yet
From Bug #31625, PREPARED STATEMENT syntax does not allow to create TRIGGERS I see other people have been complaining about the same thing since 2007.
And from the look of WL#2871: Prepare any SQL it has not yet been fixed.
Is there a workaround for this problem? Is there another way of creating triggers with dynamic SQL?
Basically what I am trying to do is dynamically create triggers for recording audit data for inserts on various different tables. I am listing the tables I want to audit in an *audit_tables* table. The stripped-down procedure below iterates over the entries in that table and tries to create the trigger.
drop procedure if exists curtest;
delimiter |
create procedure curtest()
BEGIN
DECLARE done INT DEFAULT 0;
declare tn varchar(16);
declare cur cursor for select table_name from audit_tables;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
read_loop: LOOP
fetch cur into tn;
if done then
leave read_loop;
end if;
/* Create the BEFORE INSERT trigger */
set #sql = concat('CREATE TRIGGER audit_', tn, '_bi BEFORE INSERT ON ', tn, '
FOR EACH ROW BEGIN
set new.foo="bar";
END;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
end LOOP;
close cur;
END;
|
delimiter ;
call curtest();
As the error you mention says, the CREATE TRIGGER command is not supported within prepared statements.
I think a more viable option is to use a scripting language that has MySQL bindings, like PHP, to automate the trigger creation. By the way, I just remembered that MySQL Workbench uses Lua as a scripting language for this sort of things.