Minimum access levels to run MySql stored procedure - c#

I am trying to setup my .NET 4.7.1 program that is connecting to a MySQL database 8.0 to use the minimum privileges to run.
The .NET program is using MySql.Data to make connection. The minimum right for a user to execute a stored procedure is typically only EXECUTE privilege. This works fine from MySQL workbench or command line.
Upon running the .NET program this does return the following exception:
System.Data.SqlTypes.SqlNullValueException: 'Data is Null. This method or property cannot be called on Null values.'
To make it easy, I have create a very small demo program to demonstrate the issue.
Setup of the database:
CREATE DATABASE Spike;
CREATE PROCEDURE TestAccess()
BEGIN
END;
CREATE USER Spike#localhost IDENTIFIED WITH mysql_native_password BY 'sample';
GRANT EXECUTE ON PROCEDURE `TestAccess` TO Spike#localhost;
Setup program code:
static void Main(string[] args)
{
using (MySqlConnection conn = new MySqlConnection("Server=localhost;Database=Spike;uid=Spike;pwd=sample"))
{
conn.Open();
Console.WriteLine("Connection open");
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "TestAccess";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
Console.WriteLine("Query executed");
}
Console.ReadKey();
}
The crash happens at the line cmd.ExecuteNonQuery();
The stack from the crash is interesting, since it seems to indicate that the information_schema is queried. When logging all statements I can see that the last statement before the exception is:
SELECT * FROM information_schema.routines WHERE 1=1 AND routine_schema LIKE 'Spike' AND routine_name LIKE 'TestAccess'
I cannot grant different rights on information_schema, but I could give more rights on the stored procedure to make more information visible in the routines table, this feels wrong however. Simple tests with granting CREATE and ALTER access also did not work.
Is there something else I can do, without granting too much privileges?

This appears to be a bug in Connector/NET, similar to bug 75301 but a little different. When it's trying to determine parameter metadata for the procedure, it first creates a MySqlSchemaCollection named Procedures with all metadata about the procedure. (This is the SELECT * FROM information_schema.routines WHERE 1=1 AND routine_schema LIKE 'Spike' AND routine_name LIKE 'TestAccess' query you see in your log.)
The Spike user account doesn't have permission to read the ROUTINE_DEFINITION column, so it is NULL. Connector/NET expects this field to be non-NULL and throws a SqlNullValueException exception trying to read it.
There are two workarounds:
1) The first, which you've discovered, is to set CheckParameters=False in your connection string. This will disable retrieval of stored procedure metadata (avoiding the crash), but may lead to harder-to-debug problems calling other stored procedures if you don't get the order and type of parameters exactly right. (Connector/NET can no longer map them for you using the metadata.)
2) Switch to a different ADO.NET MySQL library that doesn't have this bug: MySqlConnector on NuGet. It's highly compatible with Connector/NET, performs faster, and fixes a lot of known issues.

I found an answer with which I am quite pleased. It is changing the connection string by adding CheckParameters=false:
using (MySqlConnection conn = new MySqlConnection("Server=localhost;Database=Spike;uid=Spike;pwd=sample;CheckParameters=false"))
This disables parameter checking, and thereby information_schema queries.

Related

How to Select an out parameter from a MySql Procedure in .net

Assume we have a stored procedure like so
CREATE PROCEDURE CopyValue(IN src INT, OUT dest INT)
BEGIN
SET dest = src;
END
I want to call this from a .net app (assume connection etc created successfully)
var sql = "call CopyValue(100, #destValue); select #destValue as Results;";
The string in the above statement works perfectly well when called in MySql Workbench.
However this - obviously - fails with "MySqlException: Parameter '#destValue' must be defined" when executed on a MySqlCommand object in .net
How do I arrange this statement so I can capture an output parameter from an existing procedure?
NB: I'm running against MySql 5.6, which I can't upgrade at this time.
NB Calling the procedure directly with CommandType.StoredProcedure goes against company guidelines.
By default, user-defined variables aren't allowed in SQL statements by MySQL Connector/NET. You can relax this restriction by adding AllowUserVariables=true; to your connection string. No modifications to your SQL or how you're executing the MySqlCommand should be necessary.
For information about why this is the default, you can read the research on this MySqlConnector issue (which also has the same default behaviour, but a much better error message that will tell you how to solve the problem): https://github.com/mysql-net/MySqlConnector/issues/194
A colleague (who wishes to remain anonymous) has answered this perfectly. Essentially put backticks ` after the # and at the end of the variable name e.g.
#`MyParam`
A fully working example.
static void Main(string[] args)
{
using var con = new MySql.Data.MySqlClient.MySqlConnection("Data Source=localhost; User Id=...;Password=...;Initial Catalog=...");
con.Open();
using var cmd = con.CreateCommand();
cmd.CommandText = "call CopyValue2(100, #`v2`); select #`v2` as Results;";
using var reader = cmd.ExecuteReader();
if (reader.Read())
Console.WriteLine($"Copied Value {reader.GetInt64(0)}");
}
Thanks OG :)

MySql.Data.MySqlClient Error when calling stored procedure

I am running into an issue, that I believe lies within my privileges granted. However, I can't figure out why this is happening. I have a stored procedure in MySQL defined by:
DELIMITER //
CREATE PROCEDURE my_stored_procedure (var_one VARCHAR(20), var_two INT4)
BEGIN
UPDATE table_name SET ACTIVATION_DATE = UTC_TIMESTAMP(),
DEACTIVATION_DATE = TIMESTAMPADD(MONTH, var_two, UTC_TIMESTAMP()),
USER_ACTIVATED = 1 WHERE ID = var_one;
END //
DELIMITER ;
I am calling this from my c# application. The code runs fine when I use userA's credentials. However, when I use userB it doesn't work. userA was granted privileges with the command:
GRANT ALL PRIVILEGES ON * . * TO 'userA'#'%';
userB was granted privileges with these commands:
GRANT UPDATE, SELECT ON current_db.table_im_updating TO 'userB'#'%';
GRANT EXECUTE ON PROCEDURE my_stored_procedure TO 'userB'#'%';
My code to call the mysql stored procedure is as follows:
connection = new MySql.Data.MySqlClient.MySqlConnection("server=ip_address; port=3306; database=data_base; UID=userA; password=password; pooling=false");
MySql.Data.MySqlClient.MySqlCommand command = new MySql.Data.MySqlClient.MySqlCommand();
command.Connection = connection;
command.CommandText = "my_stored_procedure";
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.AddWithValue("#_id","associated_id");
command.Parameters["#_id"].Direction = System.Data.ParameterDirection.Input;
command.Parameters.AddWithValue("#subscription_length", "6");
command.Parameters["#subscription_length"].Direction = System.Data.ParameterDirection.Input;
command.ExecuteNonQuery();
Like I mentioned. This code works fine when userA and userA's password are entered; but when I switch to userB the error says,
System.Data.SqlTypes.SqlNullValueException: 'Data is Null. This method or property cannot be called on Null values.'
It is also worth noting that if I change my method of calling the procedure to this:
MySql.Data.MySqlClient.MySqlCommand command = new MySql.Data.MySqlClient.MySqlCommand("CALL my_stored_procedure(var_one, var_two)", connection);
I get no errors. I am not trying to just make my code work, I want it to work properly. Please help me understand what is causing this Error. Thanks.
TL;DR
userB should be granted with SELECT access to the mysql.proc table.
Explaination
First, MySQL Connnector/NET is executed SHOW CREATE PROCEDURE my_stored_procedure to get all parameters definition. (Order, Direction, DbType etc.)
Then, it will combine procedure name and parameters to create a sql like CALL my_stored_procedure(var_one, var_two) to execute.
To use SHOW CREATE PROCEDURE, you must be the user named in the routine DEFINER clause or have SELECT access to the mysql.proc table. If you do not have privileges for the routine itself, the value displayed for the Create Procedure field will be NULL.
-- Doc: SHOW CREATE PROCEDURE Syntax
-- Issue: Connector 8.0 - Stored Procedure Error in MySqlDataReader
Give the userB the privileges to Execute the SP and add CheckParameters=false to Connection String

Inserting a duplicate record using Npgsql

I'm trying to insert data into the following table using the Npgsql ADO.NET provider:
CREATE SCHEMA core;
CREATE TABLE core.config(
name TEXT NOT NULL,
value TEXT NOT NULL,
CONSTRAINT pk_config PRIMARY KEY (name)
);
The first insert works fine but if I try to insert an item with the same name again, Npgsql just hangs. I would expect an exception to be thrown stating that the primary key constraint has been violated, but Npgsql just freezes instead. Sure enough, if I check the server logs I see this:
duplicate key value violates unique constraint "pk_config"
but this message doesn't seem to be getting back to Npgsql. Am I doing something wrong? My code is here:
using (NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;Database=temp;User Id=postgres;Password=test1"))
{
conn.Open();
string cmdText = "INSERT INTO core.config(name, value) values ('item', 'value')";
using (NpgsqlCommand cmd = new NpgsqlCommand(cmdText, conn))
{
cmd.ExecuteNonQuery(); // This works fine
cmd.ExecuteNonQuery(); // Insert again: this hangs, but would expect an exception
}
}
This is a simplified version of a larger application (hence the use of a schema). I then tried creating the table in public (no schema specified). Interestingly this DOES generate an exception on the second call to ExecuteNonQuery():
Backend sent unrecognized response type
I'm a little lost as to whether this is a bug in Npgsql or whether it is something to do with Schema privileges (even though for this test I'm using the "postgres" superuser).
I'm using Npgsql 2.0.12.0
We have the same problem. According to this thread:
http://pgfoundry.org/forum/forum.php?set=custom&forum_id=518&style=nested&max_rows=50&submit=Change+View
We just noticed that Npgsql has a problem when handling error messages from postgresql 9.3.x.
Glen Parker already fixed in our latest code: https://github.com/npgsql/Npgsql/pull/99. We are working to create a new 2.0.12 stable release with this fix.
If you want to give it a try you can get the latest git code and check if it works ok for you. It is in beta state though.
Please, let me know if you have any problems with it.
So if you are using PostgreSQL 9.3, this may be your issue. You'll have to download the fix and experiment to see if this is actually your issue in this case.

Paramertized SQL, Oracle 11g, and C# Failing to create index

I am trying to use a parametrized query to create an index on our Oracle server. I can create the index fine if I use string concatenation, so I believe its not an account or permissions issue. I get the error:
ORA-01036: illegal variable name/number
I can not find any error in the code, but I am sure I am missing something. I am using Oracle.DataAccess.dll version 4.112.3.0. The version the server reports is "Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production"
I have tried removing all the params, except one and get the same error. Any combination of 1 or more params causes the error. The error happens every time without fail. I have used watches to verify that the params are correctly getting set, and I have tried clearing the params before setting. No matter what I do so far, I still get the error. Here is the code:
using (OracleCommand mycom = new OracleCommand())
{
using (OracleConnection Connection = new OracleConnection(connectionString))
{
mycom.BindByName = true;
mycom.CommandText = "CREATE INDEX :indexname on :tablename (:colname)";
mycom.Parameters.Add("indexname", indexName);
mycom.Parameters.Add("tablename", tableName);
mycom.Parameters.Add("colname", colName);
mycom.Connection = Connection;
mycom.CommandType = CommandType.Text;
mycom.Connection.Open();
mycom.ExecuteNonQuery();
}
}
You cannot use bind variables in a DDL statement (like CREATE INDEX). Bind variables are only used in DML statements. You would need to build up the DDL statement in your C# code.
Is this a part of the installation process for your application? Otherwise, it would be exceedingly unusual to want to create an index from an application. Your application shouldn't be creating any database objects outside of the initial installation process.

C#: DB2 test available connection first

I have a C# .NET program running an ETL which connects to a DB2 database. Sometimes this database is down, so I'd like to do a health check at the beginning of the application to see if the database is available, without actually calling any stored procedures or pushing any data. Here's an example of the code I'm using now:
OdbcConnection myODBCConnection = new OdbcConnection("DSN=DB2AA;UID=ABCD;PWD=1234;");
OdbcCommand myODBCCommand = new OdbcCommand();
myODBCCommand.CommandType = CommandType.StoredProcedure;
myODBCCommand.CommandText = "{CALL SYSPROC.ABC001(?, ?)}";
myODBCCommand.Parameters.Add("INPUT", OdbcType.VarChar, 500);
myODBCCommand.Parameters["INPUT"] = myString
myODBCCommand.Connection = myODBCConnection
myODBCConnection.Open();
OdbcTransaction myTrans;
myTrans = myODBCConnection.BeginTransaction();
myODBCCommand.Transaction = myTrans;
myTrans.Commit();
myODBCCommand.ExecuteNonQuery();
myODBCConnection.Close();
What's the best way to test this connection without actually pushing any data?
You can simply run some innoccuous select query to check to see if the db is available.
You can try to do something as simple as:
Select 1
Or
Select getdate()
Those simple queries don't even touch any tables but will return only if the rdbms is running.
Note: those examples are for sql server but might work for db2. I haven't had to do a live check on a db2 yet though the similar concept should be doable.
Note 2: after a closer look at your code, all you should really have/need to do is check for success of your odbc connection's .Open() call.

Categories