I am trying to understand which user is called when I execute a MySQL procedure because my procedure tries to create a file but the file can not be created because of a permission problem.
Here is my procedure:
CREATE PROCEDURE `EXPORT_TEST`()
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY INVOKER
COMMENT ''
BEGIN
DECLARE VAR_INS INT(10);
DECLARE VAR_DT_DEBUT DATETIME;
SET VAR_DT_DEBUT = CURRENT_TIMESTAMP;
INSERT INTO BC_ALIM (NOM_TABLE, DATE_DEBUT)
VALUES('EXPORT_TEST', VAR_DT_DEBUT);
BEGIN
SELECT * INTO OUTFILE '/home/bureau/IMPORTS/fichierSortie3.csv'
FIELDS TERMINATED BY ';'
ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
FROM db_baby_concept.BC_TYPE_LIVRAISON;
END;
END
Thank you for help.
MySQL stored procedures have a SQL SECURITY characteristic, which specifies security contact in which the procedure code runs. This can be either DEFINER or INVOKER, and the default is DEFINER. So when you run your stored procedure, it executes with the permissions of the user who defined the procedure; if they don't have the FILE privilege, it won't be able to create the file.
You can set the procedure's security to INVOKER instead. Then it will use your permissions rather than the creator's.
CREATE PROCEDURE procName()
SQL SECURITY INVOKER
BEGIN
...
END;
Also, the file is written by the MySQL server process, so the Linux userid used for the server needs to have write permission to the directory containing the file. So you probably need to make your IMPORTS directory world-writable. And if the secure_file_priv system variable is set, you can only write to the directory named there.
Related
I run the following script:
USE MODERN_FAMILY;
DROP FUNCTION IF EXISTS compare_news;
DELIMITER $$
CREATE FUNCTION compare_news(n INT, m INT)
RETURNS VARCHAR(20)
BEGIN
DECLARE s VARCHAR(20);
IF n>m THEN SET s='>';
ELSEIF n=m THEN SET s='=';
ELSE SET s='<';
END IF;
SET s = CONCAT(n, ' ', s, ' ',m);
RETURN s; END;$$
First script returns this error :
Error Code: 1418. This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you might want to use the less safe log_bin_trust_function_creators variable).
Then, I run this:
USE MODERN_FAMILY;
SELECT compare_news(2,5);
It returns this error:
Error Code: 2014. Commands out of sync; you can't run this command now.
Does someone know if I have an error with the script? Or is related to my SQL configuration?
log_bin_trust_function_creators variable controls whether binary logging should trust the stored function creators for not to create unsafe stored functions.
Reference: Stored Program Logging
When you create a stored function, you must declare either that it is
deterministic or that it does not modify data. Otherwise, it may be
unsafe for data recovery or replication.
By default, for a CREATE FUNCTION statement to be accepted, at least
one of DETERMINISTIC, NO SQL, or READS SQL DATA must be specified
explicitly. Otherwise an error occurs:
ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL,
or READS SQL DATA in its declaration and binary logging is enabled
(you *might* want to use the less safe log_bin_trust_function_creators
variable)
To relax the preceding conditions on function creation (that you must
have the SUPER privilege and that a function must be declared
deterministic or to not modify data), set the global
log_bin_trust_function_creators system variable to 1.
Solution 1: Make your function which doesn't manipulate data or deterministic in nature
CREATE FUNCTION `compare_news`(
`n` INT,
`m` INT
) RETURNS VARCHAR(20) CHARSET latin1 LANGUAGE SQL DETERMINISTIC NO SQL SQL SECURITY DEFINER COMMENT ''
BEGIN
DECLARE s VARCHAR(20);
IF n>m THEN
SET s='>';
ELSEIF n=m THEN
SET s='=';
ELSE
SET s='<';
END IF;
SET s = CONCAT(n, ' ', s, ' ',m);
RETURN s;
END
Solution 2: Enable MySQL to trust such functions by setting mysql log_bin_trust_function_creators variable to ON.
Reference: log_bin_trust_function_creators
SET GLOBAL log_bin_trust_function_creators = 1;
The variable will change upon restart if you do not update the config to reflect the change.
I have an issues in that when i try to execute a stored procedure through my web application, i am shown the following error
SQLSTATE[42000]: Syntax error or access violation: 1142 INSERT command denied to user 'elitecareers_admin'#'%' for table 'user'
Here is my stored procedure just for clarity
DELIMITER $$
CREATE PROCEDURE `sp_create_account`( username_param VARCHAR(40), email_param VARCHAR(60), pass_w VARCHAR(30), category_id TINYINT )
BEGIN
DECLARE salt VARCHAR(60);
DECLARE password_var VARCHAR(128);
SET salt = 'ELiCrs#4$^7EC%?';
SET salt = CONCAT( username_param, salt );
SET password_var = SHA2( CONCAT( pass_w, salt ), 0 );
INSERT INTO elite.user
( user_id, username, email, pass, active, date_joined, user_category_id )
VALUES
( DEFAULT, username_param, email_param, password_var, DEFAULT, DEFAULT, category_id );
END $$
DELIMITER ;
and here is how the stored procedure looks when i run the show command
Here is my server information too
Here are the privileges for the user 'elitecareers_admin'#'%' under which the web application is executing
What i don't understand if the user has all privileges on the database, then why does mysql/maria server keeping those errors?
"The SQL SECURITY characteristic can be DEFINER or INVOKER to specify the security context; that is, whether the routine executes using the privileges of the account named in the routine DEFINER clause or the user who invokes it. This account must have permission to access the database with which the routine is associated. The default value is DEFINER. The user who invokes the routine must have the EXECUTE privilege for it, as must the DEFINER account if the routine executes in definer security context. " -- https://dev.mysql.com/doc/refman/5.7/en/create-procedure.html
So, regardless of who runs the proc, the permissions for 'elitecareers_admin'#'%' will be used for things such as accessing the table elite.user. But that user seems to have privileges only to the database elitecareers\_elite.
Plan A: Recreate the proc with SQL SECURITY INVOKER if that is appropriate.
Plan B: Figure out the difference between databases elite and elitecareers\_elite.
Plan C: Add another GRANT to let that admin get to elite.
Plan D: (There are probably other solutions.)
I am currently building a MySQL database. The database is storing data which is being supplied by another organisation and the format of the files we recieve is, shall we say, somewhat inconsistent.
I have writted a function which assess the file based on some simple rules and returns a message saying 'file ok' or else one of a list of hard coded error messages. The way I would like to use this function would be to say something like:
if check_10m_file() = 'file ok' then
load data infile '\\\\server\\filepath\file.csv'
fields terminated by ','
...(etc)
My problem is that the if..then..else control structure does not seem to be allowed outside of stored procedures and the load data infile command is not allowed inside stored procedures.
I have attempted to trick my way around this by building the load data infile statement as a prepared statement but then I get an error
"This command is not supported in the prepared statement protocol yet"
So my question is, does anyone know of a way in which I can run a load data statement only when conditions are met? I would ideally like to put this into a stored procedure but if I could just save the code as a script to run later, that would be acceptable.
One option is to use UDF, for example: lib_mysqludf_sys.
After installing the UDF, you can do something like:
Shell script (/server/loadpath/load.sh):
mysql -u [user] -p[pass] -e "LOAD DATA INFILE '$1' INTO TABLE $2;"
Stored Procedure:
DELIMITER $$
DROP PROCEDURE IF EXISTS load_data$$
CREATE PROCEDURE load_data(pfile VARCHAR(100), pdbtable VARCHAR(100))
BEGIN
DECLARE exec_str VARCHAR(500);
DECLARE ret_val INT;
IF (check_10m_file() = 'file ok') THEN
SET exec_str := CONCAT('sh /server/loadpath/load.sh ', pfile, ' ', pdbtable);
SET ret_val := sys_exec(exec_str);
IF ret_val = 0 THEN
SELECT 'OK' Result;
ELSE
SELECT 'ERROR' Result;
END IF;
END IF;
END$$
DELIMITER ;
CALL load_data('/server/filepath/file.csv', 'mydb.mytable');
IMPORTANT: Validate the input data to prevent any code injection.
Calling a stored procedure results in this exception:
SQLException1 java.sql.SQLException: User does not have access to
metadata required to determine stored procedure parameter types. If
rights can not be granted, configure connection with
"noAccessToProcedureBodies=true" to have driver generate parameters
that represent INOUT strings irregardless of actual parameter types.
To resolve this, I tried:
Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306
/database?useInformationSchema=true&noAccessToProcedureBodies=true",
"user_name", "pasword");
But it still does not work.
I am using shared hosting.
I am using
Software version: 5.0.91-community-log - MySQL Community Edition (GPL)
Protocol version: 10
Java 1.6
mysql-connector-java-5.1.14-bin.jar
One of my stored procedures is:
DROP PROCEDURE IF EXISTS `share_message`
DELIMITER //
CREATE PROCEDURE share_message(in messageid1 int(200),in received_by1 int(20),
in sent_by1 int(20),in shared_of1 int(20),author1 int(20), OUT query_status1 TINYINT)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- ERROR
SET query_status1 = -1;
rollback;
END;
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
-- WARNING
SET query_status1 = -1;
rollback;
END;
START TRANSACTION;
SET query_status1 = 0;
INSERT INTO post_message_users(messageid,received_by,sent_by,shared_of,author)
VALUES(messageid1,received_by1,sent_by1,shared_of1,author1);
UPDATE post_messages SET total_share=total_share+1 WHERE messageid=messageid1;
SET query_status1 =1;
COMMIT;
END//
DELIMITER ;
This is working properly with my local database.
It seems that the stored procedure you are attempting to use needs access to MySQL's INFORMATION_SCHEMA. That's a (fake) database built in every MySQL server; it's used to fetch descriptions of tables, columns, indexes, and the like.
It looks like the user id you're using doesn't have access to the INFORMATION_SCHEMA. That's understandable on a hosting service.
Go on MyPhpAdmin and try a query like this to be sure about that.
SELECT table_schema, table_name
FROM information_schema.columns
WHERE column_name = 'something'
AND table_schema = 'your database name'
If you get some kind of error saying you don't have permission, this is definitely your problem.
You could try rewriting your stored proc, or you could ask your hosting service to grant you the appropriate priv.
TLDR; Change your Java code, make the CallableStatement reference parameters by index instead of name.
After having a similar problem I updated my JDBC driver mysql-connector-java-5.1.26-bin.jar.
The error then changed from
User does not have access to metadata required to determine stored
procedure parameter types. If rights can not be granted, configure
connection with "noAccessToProcedureBodies=true" to have driver
generate parameters that represent INOUT strings irregardless of
actual parameter types.
to
No access to parameters by name when connection has been configured not to access procedure bodies
I changed my Callable Statement to reference parameters by index instead of name, and hey presto it works.
Updating the driver may not be necessary, just knowing to use indexes instead of names when you don't have metadata access or routine body access.
Good Luck
Is there a way to programmatically create stored procedures using MySQL? I can write a stored procedure to create databases and tables using prepared statements, but I get an error message saying it is not supported to create stored procedures with prepared statements.
I realize I can do this in PHP or Java, but so far I have been keeping all my schema management tools as SQL scripts, and I would like to avoid a second language dependency if I can.
One method you can try is to build the create procedure statement dynamically in SQL, then use select into outfile to dump the statement to local disk, and then source the file to load the procedure into the DB.
Here's a quick example:
set #proc_name = 'my_proc';
set #body1 = 'select ''hello''; ';
set #body2 = 'select ''world''; ';
set #delimiter = '$$';
set #create_proc_stmt = concat(
'create procedure ',
#proc_name,
'() begin ',
#body1,
#body2,
' end ',
#delimiter
);
select #create_proc_stmt into outfile '/tmp/create_proc_stmt.sql';
delimiter $$
\. /tmp/create_proc_stmt.sql
delimiter ;
call my_proc();
I think you can do it by inserting a record into INFORMATION_SCHEMA.ROUTINES table. I haven't tried it, but it should work (one day in the past I forgot to add --routines switch to mysqldump, and later I restored all procedures by dumping this one table).