Skip to content

Commit c786e0b

Browse files
committed
Implement Prepare for stored procedures. Fixes #742
1 parent 43d532b commit c786e0b

File tree

5 files changed

+210
-92
lines changed

5 files changed

+210
-92
lines changed

docs/content/tutorials/migrating-from-connector-net.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,4 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
216216
* [#97872](https://bugs.mysql.com/bug.php?id=97872): `KeepAlive` in connection string throws exception on .NET Core
217217
* [#98322](https://bugs.mysql.com/bug.php?id=98322): `new MySqlConnection(null)` throws `NullReferenceException`
218218
* [#99091](https://bugs.mysql.com/bug.php?id=99091): Unexpected return value getting integer for `TINYINT(1)` column
219+
* [#99793](https://bugs.mysql.com/bug.php?id=99793): Prepared stored procedure command doesn't verify parameter types

src/MySqlConnector/Core/ServerSession.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,36 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
140140
// caller has validated this already
141141
var commandText = command.CommandText!;
142142

143-
var statementPreparer = new StatementPreparer(commandText, command.RawParameters, command.CreateStatementPreparerOptions());
143+
// for a stored procedure, the statement to be prepared is "CALL commandText(?,?,?,...);"
144+
string commandToPrepare;
145+
if (command.CommandType == CommandType.StoredProcedure)
146+
{
147+
var cachedProcedure = await command.Connection!.GetCachedProcedure(commandText, revalidateMissing: false, ioBehavior, cancellationToken).ConfigureAwait(false);
148+
if (cachedProcedure is null)
149+
{
150+
var name = NormalizedSchema.MustNormalize(command.CommandText!, command.Connection.Database);
151+
throw new MySqlException("Procedure or function '{0}' cannot be found in database '{1}'.".FormatInvariant(name.Component, name.Schema));
152+
}
153+
154+
var parameterCount = cachedProcedure.Parameters.Count;
155+
var callStatement = new StringBuilder("CALL ", commandText.Length + 8 + parameterCount * 2);
156+
callStatement.Append(commandText);
157+
callStatement.Append('(');
158+
for (int i = 0; i < parameterCount; i++)
159+
callStatement.Append("?,");
160+
if (parameterCount == 0)
161+
callStatement.Append(')');
162+
else
163+
callStatement[callStatement.Length - 1] = ')';
164+
callStatement.Append(';');
165+
commandToPrepare = callStatement.ToString();
166+
}
167+
else
168+
{
169+
commandToPrepare = commandText;
170+
}
171+
172+
var statementPreparer = new StatementPreparer(commandToPrepare, command.RawParameters, command.CreateStatementPreparerOptions());
144173
var parsedStatements = statementPreparer.SplitStatements();
145174

146175
var columnsAndParameters = new ResizableArray<byte>();

src/MySqlConnector/MySql.Data.MySqlClient/MySqlCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ private bool NeedsPrepare(out Exception? exception)
128128
if (exception is object || Connection!.IgnorePrepare)
129129
return false;
130130

131-
if (CommandType != CommandType.Text)
131+
if (CommandType != CommandType.StoredProcedure && CommandType != CommandType.Text)
132132
{
133-
exception = new NotSupportedException("Only CommandType.Text is currently supported by MySqlCommand.Prepare");
133+
exception = new NotSupportedException("Only CommandType.Text and CommandType.StoredProcedure is currently supported by MySqlCommand.Prepare");
134134
return false;
135135
}
136136

0 commit comments

Comments
 (0)