Create procedure ERROR 1046 (3D000): No database selected - mysql

I am trying to create a simple procedure which would create a new database.
complete code which i am trying to run over mysql is :
SET #DB_NAME := "mydb";
SET #DB_CREATE:= "CREATE DATABASE ";
DELIMITER //
drop procedure if exists create_db //
create procedure create_db(name TEXT)
BEGIN
DECLARE temp TEXT;
DECLARE user TEXT;
SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = name INTO temp;
if temp = name then
SIGNAL SQLSTATE '45002' SET MESSAGE_TEXT = 'This database already exist';
else
SELECT USER() INTO user;
SET #s = CONCAT('CREATE DATABASE ', name);
PREPARE stmt_create FROM #s;
EXECUTE stmt_create;
DEALLOCATE PREPARE stmt_create;
SET #s = CONCAT('GRANT ALL PRIVILEGES ON ', name, '.* TO ', user, ' WITH GRANT OPTION');
PREPARE stmt_grant FROM #s;
EXECUTE stmt_grant;
DEALLOCATE PREPARE stmt_grant;
END IF;
END //
DELIMITER ;
call create_db(#DB_NAME);
I took the help of how do I use a variable in create database statement for creating this procedure.
While running this procedure over mysql i am getting error :
ERROR 1046 (3D000): No database selected
I have googled it a lot but is unable to fix it.
While trying different things i tried to execute the above procedure by first executing:
mysql> USE mysql;
Using that, error is gone. And
mysql> show databases;
is displaying all the databases along with the newly created database. Could somebody tell me whether using mysql as default database is correct or not.
If somebody has another method please tell it to me.

Use the :: use database to select the database to which you want the proc to be created or
try using databasename.
create procedure databasename.create_db(name TEXT)

I don't believe you can have functions that are "GLOBAL" i.e. outside the scope of a database, so not selecting a database before calling your function is not possible

Related

MySQL create database if not exist

I have a T-SQL query which create database if it does not exist yet:
IF (NOT EXISTS (SELECT name
FROM master.dbo.sysdatabases
WHERE ('[' + 'DBName' + ']' = 'DBName'
OR name = 'DBName')))
BEGIN
CREATE DATABASE DBName
PRINT 'DATABASE_CREATED'
END
ELSE
PRINT 'DATABASE_EXIST'
When I want use this in MySQL I get an error:
'IF' is not valid input at this postion
I change this script as
IF(SELECT COUNT(*) FROM SCHEMA_NAME
FROM INFORMATION_SCHEMA.SCHEMATA
WHERE SCHEMA_NAME = 'DBName') > 0)
THEN BEGIN
CREATE DATABASE DBName
PRINT 'DATABASE_CREATED'
ELSE
PRINT 'DATABASE_EXIST'`
but it still doesn't work
How can I create this query in MySQL?
I'm not sure exactly how you'd check, but if you just want to create it if it doesn't exist, then you can do
CREATE DATABASE IF NOT EXISTS DBname
Here is the example in a helper (permanent) database. That db's name is permanent
One time db create:
create schema permanent;
Now make sure you
USE permanent;
then
Stored Proc:
DROP PROCEDURE IF EXISTS createDB;
DELIMITER $$
CREATE PROCEDURE createDB(IN pDbName VARCHAR(100))
BEGIN
DECLARE preExisted INT;
DECLARE ret VARCHAR(50);
SET ret='DATABASE_EXIST';
SELECT COUNT(*) INTO preExisted
FROM INFORMATION_SCHEMA.SCHEMATA
WHERE SCHEMA_NAME=pDbName;
IF preExisted=0 THEN
SET #sql=CONCAT('CREATE SCHEMA ',pDbName); -- add on any other parts of string like charset etc
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
-- right here you could assume it worked or take additional
-- step to confirm it
SET ret='DATABASE_CREATED';
END IF;
SELECT ret as 'col1';
END$$
DELIMITER ;
Test:
use permanent;
call createDB('xyz');
-- returns col1 DATABASE_CREATED
call createDB('xyz');
-- returns col1 DATABASE_EXIST

How to write stored procedure to check Grant in MySql?

I want to create a stored procedure to check grant. I have tried by following way but I don't what I am missing here.
//simple Query "SHOW GRANTS FOR testuser #'192.168.1.180'" - It is working
CREATE DEFINER=`abc`#`localhost` PROCEDURE `SP_GetGrantAllPriviledge`(
IN Username TEXT,
IN Hostname TEXT
)
BEGIN
SHOW GRANTS FOR Username #Hostname; //doesn't work
END
Can anybody suggest what I am missing?
You can use the TABLE_PRIVILEGES in the INFORMATION_SCHEMA.
SELECT *
FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES
WHERE `GRANTEE` = CONCAT('''', Username, '''#''', Hostname, '''')
#Hostname; is the source of issue
Update your SP with prepare statement:
CREATE PROCEDURE `SP_GetGrantAllPriviledge`(
IN Username TEXT,
IN Hostname TEXT
)
BEGIN
SET #sql = CONCAT('SHOW GRANTS FOR ',Username,' #',Hostname,'; ');
PREPARE stmt FROM #sql;
EXECUTE stmt;
END

How to restrict other user drop root user in MySQL?

I created a user and granted "CREATE USER" privileges. I found that user can drop any other user include root. How can I prevent that happen?
Actually we are implementing Mysql as a Service just like AWS's RDS. We will create a super user which will be used by system management. Our customer will have another user which has most of privileges including "CREATE USER", so that they can manage account themselves. I want to find out a approach other than Mysql normal privileges to get that
If you have AWS RDS, you will find RDS has a 'rdsadmin' user account. If you run DROP USER 'rdsadmin'#'localhost' you will get a error: ERROR 1396 (HY000): Operation DROP USER failed for 'rdsadmin'#'localhost'. I'm curious how to implement that.
I tried add a trigger on mysq.user table to throw a error when user delete 'rdsadmin' from table. but the trigger only work for DELETE FROM USER ... sql not for DROP USER. Do you know the reason, or is there any way to fix it?
It is not possible to grant privileges to specified users, CREATE USER is a global privilege.
I'd suggest you to separate MySQL users, for example - there are can be some of them: for administrating (with root privileges), for developers (to access and change database objects and tables data), and so on...
You can use this work around by creating an database API.
The SQL code should help you.
CREATE TABLE mysql.`created_users` (
`user_name` varchar(255) DEFAULT NULL,
`owner` varchar(255) DEFAULT NULL
)
The table hold the usernames and what user created them.
Note create the Procedures with your root account
Procedure to create an mysql user.
DROP PROCEDURE IF EXISTS mysql.createUser;
DELIMITER //
CREATE PROCEDURE mysql.createUser(IN userName VARCHAR(255), IN userPassword VARCHAR(255))
BEGIN
SET #createUserQuery = CONCAT('
CREATE USER "',userName,'"#"localhost" IDENTIFIED BY "',userPassword,'" '
);
PREPARE stmt FROM #createUserQuery;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #createcreatedUserQuery = CONCAT('
INSERT INTO mysql.created_users (user_name, owner) VALUE("',userName,'", "',USER(),'")'
);
PREPARE stmt FROM #createcreatedUserQuery;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;
Procedure to drop and with check on created_users table to make sure the user exists and delete right check.
DROP PROCEDURE IF EXISTS mysql.dropUser;
DELIMITER //
CREATE PROCEDURE mysql.dropUser(IN userName VARCHAR(255))
BEGIN
SET #canDeleteUser = 0;
SET #createCountUserQuery = CONCAT('
SELECT COUNT(*) FROM mysql.created_users WHERE user_name = "',userName,'" AND owner = "',USER(),'" INTO #canDeleteUser'
);
PREPARE stmt FROM #createCountUserQuery;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
IF #canDeleteUser = 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'The user is not known on the server or you dont have rights to delete this user';
END IF;
IF #canDeleteUser = 1 THEN
SET #createDropUserQuery = CONCAT('
DROP USER "',userName,'"#"localhost"'
);
PREPARE stmt FROM #createDropUserQuery;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #createDeleteUserQuery = CONCAT('
DELETE FROM created_users WHERE user_name = "',userName,'" AND owner = "',USER(),'"'
);
PREPARE stmt FROM #createDeleteUserQuery;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
END //
DELIMITER ;
And to give rights to execute these
GRANT EXECUTE ON PROCEDURE mysql.createUser TO '[user]'#'localhost';
GRANT EXECUTE ON PROCEDURE mysql.dropUser TO '[user]'#'localhost';
And you may want to give the user select priv on mysql.proc so they can see the source code behind the procedures and know the parameters
You can use the database API like this.
CALL mysql.createUser('user', 'password');
CALL mysql.dropUser('user');
Note that root account can only remove users with mysql.dropUser that have the owner root#localhost
As Devart said, You cannot change the privileges of CREATE USER.
However, what it appears like you are doing is provisioning mysql instances out to users to control for themselves. To that end, how about a server management tool. There's many out there, to include froxlor Server Management Studio (which is free and open source): https://www.froxlor.org/
It's just a thought.

Iterate MySQL schemas

Problem description
I have a single-tenant MySQL database setup. That is, I have one identical schema for each client.
Now I need to run a specific query for each client. That would be easy in a multi-tenant setting (where all clients share a single schema). With my setup however, I need to iterate the schemas. More generally, I want to access a schema whose name is given by a variable. How can that be done?
What I've tried
If I try USE varSchemaName (where varSchemaName is a varchar
variable), I get the error message ERROR 1314: USE is not allowed
in stored procedures.
If I try SELECT * FROM varSchemaName.MyTable I get
Error Code: 1146. Table 'varSchemaName.MyTable' doesn't exist. Apparently MySQL considers varSchemaName to be a literal, not a
variable.
Building on the answer from fancyPants, you can call that procedure within a loop from another procedure which queries information_schema.tables to identify the databases containing MyTable and then call fancyPants' procedure with the db names as a parameter. This method is easy if the databases have a consistent naming scheme or contain identically named objects, which sounds like the case here. The structure would be something like:
DELIMITER //
DROP PROCEDURE IF EXISTS mydriver //
CREATE PROCEDURE mydriver()
BEGIN
DECLARE varSchemaName VARCHAR(64);
DECLARE done BOOLEAN;
DECLARE cur CURSOR FOR
SELECT table_schema
FROM information_schema.tables
WHERE table_name = 'MyTable';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO varSchemaName;
IF done THEN
LEAVE read_loop;
CLOSE cur;
END IF;
CALL fancypants_proc(varSchemaName);
END LOOP;
END //
DROP PROCEDURE IF EXISTS fancypants_proc //
CREATE PROCEDURE fancypants_proc(IN varSchemaName VARCHAR(64))
BEGIN
SET #sql = CONCAT('SELECT * FROM ', varSchemaName, '.MyTable');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;
CALL mydriver();
You have to build the statement first.
SET #sql = CONCAT('SELECT * FROM ', varSchemaName, '.MyTable');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
You can read more about prepared statements here.
You might be tempted to use variables for tablenames, but that doesn't work. Those parameters are for values in where clauses and so on. The above way is the way to go.

How do I create a mysql view with a user variable in the name

I'm using mysql 5.5, trying to write a script to create users, views, and grant select privileges on those views. Here's what I have so far.
set #type_id := 1;
set #username := 'somecompany';
set #password := 'company1234';
set #prefix := 'somecompany';
CREATE OR REPLACE VIEW CONCAT(#prefix, '_report') AS
SELECT * FROM my_table
WHERE type_id = #type_id;
Which won't work because it isn't looking for a string for the view name. I got around this for creating users with the statement:
INSERT INTO mysql.user (Host, User, Password) VALUES ('%', #username, PASSWORD(#password));
Is there a similar trick I can use to create views and grant select on those views to the user I created?
You can use Prepared Statements to execute this queries. Just construct the query as a string and run it with prepared statements.
Edit
MySQL 5.5.12-log
SET #s = 'CREATE VIEW view_actor AS SELECT * FROM sakila.actor;';
PREPARE stmt2 FROM #s;
EXECUTE stmt2;
DEALLOCATE PREPARE stmt2;
-- Check CREATE VIEW
SHOW CREATE VIEW view_actor;
CREATE ALGORITHM=UNDEFINED DEFINER=`root`#`%` SQL SECURITY DEFINER VIEW `view_actor` AS select `sakila`.`actor`.`actor_id` AS `actor_id`,`sakila`.`actor`.`first_name` AS `first_name`,`sakila`.`actor`.`last_name` AS `last_name`,`sakila`.`actor`.`last_update` AS `last_update` from `sakila`.`actor`