// Copyright (c) 2004-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as published by
// the Free Software Foundation
//
// There are special exceptions to the terms and conditions of the GPL
// as it is applied to this software. View the full text of the
// exception in file EXCEPTIONS in the directory of this software
// distribution.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Data;
using MySql.Data.MySqlClient;
using NUnit.Framework;
using System.Globalization;
using System.Threading;
using MySql.Data.Types;
using System.Data.Common;
namespace MySql.Data.MySqlClient.Tests
{
[TestFixture]
public class StoredProcedureAccess : StoredProcedure
{
public override void Setup()
{
accessToMySqlDb = true;
base.Setup();
}
///
/// Bug #40139 ExecuteNonQuery hangs
///
[Test]
public void CallingStoredProcWithOnlyExecPrivs()
{
if (Version < new Version(5, 0)) return;
execSQL("CREATE PROCEDURE spTest() BEGIN SELECT 1; END");
execSQL("CREATE PROCEDURE spTest2() BEGIN SELECT 1; END");
suExecSQL("CREATE USER abc IDENTIFIED BY 'abc'");
try
{
suExecSQL(String.Format("GRANT SELECT ON `{0}`.* TO 'abc'@'%'", database0));
suExecSQL(String.Format("GRANT EXECUTE ON PROCEDURE `{0}`.spTest TO abc", database0));
string connStr = GetConnectionString("abc", "abc", true);
using (MySqlConnection c = new MySqlConnection(connStr))
{
c.Open();
MySqlCommand cmd = new MySqlCommand("spTest", c);
cmd.CommandType = CommandType.StoredProcedure;
object o = cmd.ExecuteScalar();
try
{
cmd.CommandText = "spTest2";
cmd.ExecuteScalar();
}
catch (MySqlException ex)
{
string s = ex.Message;
}
}
}
finally
{
suExecSQL("DROP USER abc");
}
}
[Test]
public void ProcedureParameters()
{
if (Version < new Version(5, 0)) return;
execSQL("DROP PROCEDURE IF EXISTS spTest");
execSQL("CREATE PROCEDURE spTest (id int, name varchar(50)) BEGIN SELECT 1; END");
string[] restrictions = new string[5];
restrictions[1] = database0;
restrictions[2] = "spTest";
DataTable dt = conn.GetSchema("Procedure Parameters", restrictions);
Assert.IsTrue(dt.Rows.Count == 2);
Assert.AreEqual("Procedure Parameters", dt.TableName);
Assert.AreEqual(database0.ToLower(), dt.Rows[0]["SPECIFIC_SCHEMA"].ToString().ToLower());
Assert.AreEqual("sptest", dt.Rows[0]["SPECIFIC_NAME"].ToString().ToLower());
Assert.AreEqual("id", dt.Rows[0]["PARAMETER_NAME"].ToString().ToLower());
Assert.AreEqual(1, dt.Rows[0]["ORDINAL_POSITION"]);
Assert.AreEqual("IN", dt.Rows[0]["PARAMETER_MODE"]);
restrictions[4] = "name";
dt.Clear();
dt = conn.GetSchema("Procedure Parameters", restrictions);
Assert.AreEqual(1, dt.Rows.Count);
Assert.AreEqual(database0.ToLower(), dt.Rows[0]["SPECIFIC_SCHEMA"].ToString().ToLower());
Assert.AreEqual("sptest", dt.Rows[0]["SPECIFIC_NAME"].ToString().ToLower());
Assert.AreEqual("name", dt.Rows[0]["PARAMETER_NAME"].ToString().ToLower());
Assert.AreEqual(2, dt.Rows[0]["ORDINAL_POSITION"]);
Assert.AreEqual("IN", dt.Rows[0]["PARAMETER_MODE"]);
execSQL("DROP FUNCTION IF EXISTS spFunc");
execSQL("CREATE FUNCTION spFunc (id int) RETURNS INT BEGIN RETURN 1; END");
restrictions[4] = null;
restrictions[1] = database0;
restrictions[2] = "spFunc";
dt = conn.GetSchema("Procedure Parameters", restrictions);
Assert.IsTrue(dt.Rows.Count == 2);
Assert.AreEqual("Procedure Parameters", dt.TableName);
Assert.AreEqual(database0.ToLower(), dt.Rows[0]["SPECIFIC_SCHEMA"].ToString().ToLower());
Assert.AreEqual("spfunc", dt.Rows[0]["SPECIFIC_NAME"].ToString().ToLower());
Assert.AreEqual(0, dt.Rows[0]["ORDINAL_POSITION"]);
Assert.AreEqual(database0.ToLower(), dt.Rows[1]["SPECIFIC_SCHEMA"].ToString().ToLower());
Assert.AreEqual("spfunc", dt.Rows[1]["SPECIFIC_NAME"].ToString().ToLower());
Assert.AreEqual("id", dt.Rows[1]["PARAMETER_NAME"].ToString().ToLower());
Assert.AreEqual(1, dt.Rows[1]["ORDINAL_POSITION"]);
Assert.AreEqual("IN", dt.Rows[1]["PARAMETER_MODE"]);
}
[Test]
public void SingleProcedureParameters()
{
if (Version < new Version(5, 0)) return;
execSQL("DROP PROCEDURE IF EXISTS spTest");
execSQL("CREATE PROCEDURE spTest(id int, IN id2 INT(11), " +
"INOUT io1 VARCHAR(20), OUT out1 FLOAT) BEGIN END");
string[] restrictions = new string[4];
restrictions[1] = database0;
restrictions[2] = "spTest";
DataTable procs = conn.GetSchema("PROCEDURES", restrictions);
Assert.AreEqual(1, procs.Rows.Count);
Assert.AreEqual("spTest", procs.Rows[0][0]);
Assert.AreEqual(database0.ToLower(), procs.Rows[0][2].ToString().ToLower(CultureInfo.InvariantCulture));
Assert.AreEqual("spTest", procs.Rows[0][3]);
DataTable parameters = conn.GetSchema("PROCEDURE PARAMETERS", restrictions);
Assert.AreEqual(4, parameters.Rows.Count);
DataRow row = parameters.Rows[0];
Assert.AreEqual(database0.ToLower(CultureInfo.InvariantCulture),
row["SPECIFIC_SCHEMA"].ToString().ToLower(CultureInfo.InvariantCulture));
Assert.AreEqual("spTest", row["SPECIFIC_NAME"]);
Assert.AreEqual(1, row["ORDINAL_POSITION"]);
Assert.AreEqual("IN", row["PARAMETER_MODE"]);
Assert.AreEqual("id", row["PARAMETER_NAME"]);
Assert.AreEqual("INT", row["DATA_TYPE"].ToString().ToUpper(CultureInfo.InvariantCulture));
row = parameters.Rows[1];
Assert.AreEqual(database0.ToLower(CultureInfo.InvariantCulture), row["SPECIFIC_SCHEMA"].ToString().ToLower(CultureInfo.InvariantCulture));
Assert.AreEqual("spTest", row["SPECIFIC_NAME"]);
Assert.AreEqual(2, row["ORDINAL_POSITION"]);
Assert.AreEqual("IN", row["PARAMETER_MODE"]);
Assert.AreEqual("id2", row["PARAMETER_NAME"]);
Assert.AreEqual("INT", row["DATA_TYPE"].ToString().ToUpper(CultureInfo.InvariantCulture));
row = parameters.Rows[2];
Assert.AreEqual(database0.ToLower(CultureInfo.InvariantCulture), row["SPECIFIC_SCHEMA"].ToString().ToLower(CultureInfo.InvariantCulture));
Assert.AreEqual("spTest", row["SPECIFIC_NAME"]);
Assert.AreEqual(3, row["ORDINAL_POSITION"]);
Assert.AreEqual("INOUT", row["PARAMETER_MODE"]);
Assert.AreEqual("io1", row["PARAMETER_NAME"]);
Assert.AreEqual("VARCHAR", row["DATA_TYPE"].ToString().ToUpper(CultureInfo.InvariantCulture));
row = parameters.Rows[3];
Assert.AreEqual(database0.ToLower(CultureInfo.InvariantCulture), row["SPECIFIC_SCHEMA"].ToString().ToLower(CultureInfo.InvariantCulture));
Assert.AreEqual("spTest", row["SPECIFIC_NAME"]);
Assert.AreEqual(4, row["ORDINAL_POSITION"]);
Assert.AreEqual("OUT", row["PARAMETER_MODE"]);
Assert.AreEqual("out1", row["PARAMETER_NAME"]);
Assert.AreEqual("FLOAT", row["DATA_TYPE"].ToString().ToUpper(CultureInfo.InvariantCulture));
}
///
/// Bug #27679 MySqlCommandBuilder.DeriveParameters ignores UNSIGNED flag
///
[Test]
public void UnsignedParametersInSP()
{
if (Version < new Version(5, 0)) return;
execSQL("CREATE PROCEDURE spTest(testid TINYINT UNSIGNED) BEGIN SELECT testid; END");
MySqlCommand cmd = new MySqlCommand("spTest", conn);
MySqlCommandBuilder.DeriveParameters(cmd);
Assert.AreEqual(MySqlDbType.UByte, cmd.Parameters[0].MySqlDbType);
Assert.AreEqual(DbType.Byte, cmd.Parameters[0].DbType);
}
[Test]
public void CheckNameOfReturnParameter()
{
if (Version < new Version(5, 0)) return;
execSQL("CREATE FUNCTION fnTest() RETURNS CHAR(50)" +
" LANGUAGE SQL DETERMINISTIC BEGIN RETURN \"Test\"; END");
MySqlCommand cmd = new MySqlCommand("fnTest", conn);
cmd.CommandType = CommandType.StoredProcedure;
MySqlCommandBuilder.DeriveParameters(cmd);
Assert.AreEqual(1, cmd.Parameters.Count);
Assert.AreEqual("@RETURN_VALUE", cmd.Parameters[0].ParameterName);
}
///
/// Bug #13632 the MySQLCommandBuilder.deriveparameters has not been updated for MySQL 5
/// Bug #15077 Error MySqlCommandBuilder.DeriveParameters for sp without parameters.
/// Bug #19515 DiscoverParameters fails on numeric datatype
///
[Test]
public void DeriveParameters()
{
if (Version < new Version(5, 0)) return;
execSQL("CREATE TABLE test2 (c CHAR(20))");
execSQL("INSERT INTO test2 values ( 'xxxx')");
MySqlCommand cmd2 = new MySqlCommand("SELECT * FROM test2", conn);
using (MySqlDataReader reader = cmd2.ExecuteReader())
{
}
execSQL("CREATE PROCEDURE spTest(IN \r\nvalin DECIMAL(10,2), " +
"\nIN val2 INT, INOUT val3 FLOAT, OUT val4 DOUBLE, INOUT val5 BIT, " +
"val6 VARCHAR(155), val7 SET('a','b'), val8 CHAR, val9 NUMERIC(10,2)) " +
"BEGIN SELECT 1; END");
MySqlCommand cmd = new MySqlCommand("spTest", conn);
cmd.CommandType = CommandType.StoredProcedure;
MySqlCommandBuilder.DeriveParameters(cmd);
Assert.AreEqual(9, cmd.Parameters.Count);
Assert.AreEqual("@valin", cmd.Parameters[0].ParameterName);
Assert.AreEqual(ParameterDirection.Input, cmd.Parameters[0].Direction);
Assert.AreEqual(MySqlDbType.NewDecimal, cmd.Parameters[0].MySqlDbType);
Assert.AreEqual("@val2", cmd.Parameters[1].ParameterName);
Assert.AreEqual(ParameterDirection.Input, cmd.Parameters[1].Direction);
Assert.AreEqual(MySqlDbType.Int32, cmd.Parameters[1].MySqlDbType);
Assert.AreEqual("@val3", cmd.Parameters[2].ParameterName);
Assert.AreEqual(ParameterDirection.InputOutput, cmd.Parameters[2].Direction);
Assert.AreEqual(MySqlDbType.Float, cmd.Parameters[2].MySqlDbType);
Assert.AreEqual("@val4", cmd.Parameters[3].ParameterName);
Assert.AreEqual(ParameterDirection.Output, cmd.Parameters[3].Direction);
Assert.AreEqual(MySqlDbType.Double, cmd.Parameters[3].MySqlDbType);
Assert.AreEqual("@val5", cmd.Parameters[4].ParameterName);
Assert.AreEqual(ParameterDirection.InputOutput, cmd.Parameters[4].Direction);
Assert.AreEqual(MySqlDbType.Bit, cmd.Parameters[4].MySqlDbType);
Assert.AreEqual("@val6", cmd.Parameters[5].ParameterName);
Assert.AreEqual(ParameterDirection.Input, cmd.Parameters[5].Direction);
Assert.AreEqual(MySqlDbType.VarChar, cmd.Parameters[5].MySqlDbType);
Assert.AreEqual("@val7", cmd.Parameters[6].ParameterName);
Assert.AreEqual(ParameterDirection.Input, cmd.Parameters[6].Direction);
Assert.AreEqual(MySqlDbType.Set, cmd.Parameters[6].MySqlDbType);
Assert.AreEqual("@val8", cmd.Parameters[7].ParameterName);
Assert.AreEqual(ParameterDirection.Input, cmd.Parameters[7].Direction);
Assert.AreEqual(MySqlDbType.String, cmd.Parameters[7].MySqlDbType);
Assert.AreEqual("@val9", cmd.Parameters[8].ParameterName);
Assert.AreEqual(ParameterDirection.Input, cmd.Parameters[8].Direction);
Assert.AreEqual(MySqlDbType.NewDecimal, cmd.Parameters[8].MySqlDbType);
execSQL("DROP PROCEDURE spTest");
execSQL("CREATE PROCEDURE spTest() BEGIN END");
cmd.CommandText = "spTest";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
MySqlCommandBuilder.DeriveParameters(cmd);
Assert.AreEqual(0, cmd.Parameters.Count);
}
///
/// Bug #13632 the MySQLCommandBuilder.deriveparameters has not been updated for MySQL 5
///
[Test]
public void DeriveParametersForFunction()
{
if (Version < new Version(5, 0)) return;
execSQL("CREATE FUNCTION fnTest(v1 DATETIME) RETURNS INT " +
" LANGUAGE SQL DETERMINISTIC BEGIN RETURN 1; END");
MySqlCommand cmd = new MySqlCommand("fnTest", conn);
cmd.CommandType = CommandType.StoredProcedure;
MySqlCommandBuilder.DeriveParameters(cmd);
Assert.AreEqual(2, cmd.Parameters.Count);
Assert.AreEqual("@v1", cmd.Parameters[1].ParameterName);
Assert.AreEqual(ParameterDirection.Input, cmd.Parameters[1].Direction);
Assert.AreEqual(MySqlDbType.DateTime, cmd.Parameters[1].MySqlDbType);
Assert.AreEqual(ParameterDirection.ReturnValue, cmd.Parameters[0].Direction);
Assert.AreEqual(MySqlDbType.Int32, cmd.Parameters[0].MySqlDbType);
}
///
/// Bug #49642 FormatException when returning empty string from a stored function
///
[Test]
public void NotSpecifyingDataTypeOfReturnValue()
{
if (Version < new Version(5, 0)) return;
execSQL(@"CREATE FUNCTION `TestFunction`()
RETURNS varchar(20)
RETURN ''");
MySqlCommand cmd = new MySqlCommand("TestFunction", conn);
cmd.CommandType = CommandType.StoredProcedure;
MySqlParameter returnParam = new MySqlParameter();
returnParam.ParameterName = "?RetVal_";
returnParam.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add(returnParam);
cmd.ExecuteNonQuery();
}
///
/// Bug #50123 Batch updates bug when UpdateBatchSize > 1
/// Bug #50444 Parameters.Clear() not working
///
[Test]
public void UpdateBatchSizeMoreThanOne()
{
execSQL("DROP TABLE IF EXISTS test");
execSQL(@"CREATE TABLE test(fldID INT NOT NULL,
fldValue VARCHAR(50) NOT NULL, PRIMARY KEY(fldID))");
MySqlDataAdapter adapter = new MySqlDataAdapter("SELECT * FROM test", conn);
DataTable data = new DataTable();
adapter.Fill(data);
MySqlCommand ins = new MySqlCommand(
"INSERT INTO test(fldID, fldValue) VALUES (?p1, ?p2)", conn);
ins.Parameters.Add("p1", MySqlDbType.Int32).SourceColumn = "fldID";
ins.Parameters.Add("p2", MySqlDbType.String).SourceColumn = "fldValue";
ins.UpdatedRowSource = UpdateRowSource.None;
adapter.InsertCommand = ins;
adapter.UpdateBatchSize = 10;
int numToInsert = 20;
for (int i = 0; i < numToInsert; i++)
{
DataRow row = data.NewRow();
row["fldID"] = i + 1;
row["fldValue"] = "ID = " + (i + 1);
data.Rows.Add(row);
}
Assert.AreEqual(numToInsert, adapter.Update(data));
//UPDATE VIA SP
MySqlCommand comm = new MySqlCommand("DROP PROCEDURE IF EXISTS pbug50123", conn);
comm.ExecuteNonQuery();
comm.CommandText = "CREATE PROCEDURE pbug50123(" +
"IN pfldID INT, IN pfldValue VARCHAR(50)) " +
"BEGIN INSERT INTO test(fldID, fldValue) " +
"VALUES(pfldID, pfldValue); END";
comm.ExecuteNonQuery();
// Set the Insert Command
ins.Parameters.Clear();
ins.CommandText = "pbug50123";
ins.CommandType = CommandType.StoredProcedure;
ins.Parameters.Add("pfldID", MySqlDbType.Int32).SourceColumn = "fldID";
ins.Parameters.Add("pfldValue", MySqlDbType.String).SourceColumn = "fldValue";
ins.UpdatedRowSource = UpdateRowSource.None;
for (int i = 21; i < 41; i++)
{
DataRow row = data.NewRow();
row["fldID"] = i + 1;
row["fldValue"] = "ID = " + (i + 1);
data.Rows.Add(row);
}
// Do the update
Assert.AreEqual(numToInsert, adapter.Update(data));
}
}
}