diff --git a/docs/content/connection-options.md b/docs/content/connection-options.md
index 3f822ed57..2faae739f 100644
--- a/docs/content/connection-options.md
+++ b/docs/content/connection-options.md
@@ -451,6 +451,11 @@ These are the other options that MySqlConnector supports. They are set to sensib
distributed transactions, but may not be compatible with server replication; there are other limitations.
When set to false
, regular MySQL transactions are used, just like Connector/NET.
+
+ Use Procedure Cache, UseProcedureCache |
+ true |
+ When false disables the procedure cache |
+
## Unsupported Options
diff --git a/src/MySqlConnector/Core/CommandExecutor.cs b/src/MySqlConnector/Core/CommandExecutor.cs
index a2581334a..f21d0b3fb 100644
--- a/src/MySqlConnector/Core/CommandExecutor.cs
+++ b/src/MySqlConnector/Core/CommandExecutor.cs
@@ -20,17 +20,21 @@ public static async ValueTask ExecuteReaderAsync(CommandListPos
Log.CommandExecutorExecuteReader(command.Logger, connection.Session.Id, ioBehavior, commandListPosition.CommandCount);
- Dictionary? cachedProcedures = null;
+ var cachedProcedures = new Dictionary();
for (var commandIndex = 0; commandIndex < commandListPosition.CommandCount; commandIndex++)
{
var command2 = commandListPosition.CommandAt(commandIndex);
- if (command2.CommandType == CommandType.StoredProcedure)
+ if (command2.CommandType == CommandType.StoredProcedure && connection.Session.UseProcedureCache)
{
- cachedProcedures ??= [];
var commandText = command2.CommandText!;
if (!cachedProcedures.ContainsKey(commandText))
{
- cachedProcedures.Add(commandText, await connection.GetCachedProcedure(commandText, revalidateMissing: false, ioBehavior, cancellationToken).ConfigureAwait(false));
+ var cachedProcedure = await connection.GetCachedProcedure(commandText, revalidateMissing: false, ioBehavior, cancellationToken).ConfigureAwait(false);
+
+ if (cachedProcedure != null)
+ {
+ cachedProcedures.Add(commandText, cachedProcedure);
+ }
// because the connection was used to execute a MySqlDataReader with the connection's DefaultCommandTimeout,
// we need to reapply the command's CommandTimeout (even if some of the time has elapsed)
@@ -41,7 +45,7 @@ public static async ValueTask ExecuteReaderAsync(CommandListPos
var writer = new ByteBufferWriter();
//// cachedProcedures will be non-null if there is a stored procedure, which is also the only time it will be read
- if (!payloadCreator.WriteQueryCommand(ref commandListPosition, cachedProcedures!, writer, false))
+ if (!payloadCreator.WriteQueryCommand(ref commandListPosition, cachedProcedures, writer, false))
throw new InvalidOperationException("ICommandPayloadCreator failed to write query payload");
cancellationToken.ThrowIfCancellationRequested();
diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index f43162fbe..1f962affd 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -147,6 +147,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
UseAffectedRows = csb.UseAffectedRows;
UseCompression = csb.UseCompression;
UseXaTransactions = csb.UseXaTransactions;
+ UseProcedureCache = csb.UseProcedureCache;
static int ToSigned(uint value) => value >= int.MaxValue ? int.MaxValue : (int) value;
}
@@ -245,6 +246,7 @@ private static MySqlGuidFormat GetEffectiveGuidFormat(MySqlGuidFormat guidFormat
public bool UseAffectedRows { get; }
public bool UseCompression { get; }
public bool UseXaTransactions { get; }
+ public bool UseProcedureCache { get; }
public byte[]? ConnectionAttributes { get; set; }
@@ -335,6 +337,7 @@ private ConnectionSettings(ConnectionSettings other, string host, int port, stri
UseAffectedRows = other.UseAffectedRows;
UseCompression = other.UseCompression;
UseXaTransactions = other.UseXaTransactions;
+ UseProcedureCache = other.UseProcedureCache;
}
private static readonly string[] s_localhostPipeServer = ["."];
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index fdead8006..e30272439 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -63,6 +63,7 @@ public ServerSession(ILogger logger, IConnectionPoolMetadata pool)
public bool SupportsQueryAttributes { get; private set; }
public bool SupportsSessionTrack { get; private set; }
public bool ProcAccessDenied { get; set; }
+ public bool UseProcedureCache { get; private set; }
public ICollection> ActivityTags => m_activityTags;
public MySqlDataReader DataReader { get; set; }
public MySqlConnectionOpenedConditions Conditions { get; private set; }
@@ -148,14 +149,20 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
string commandToPrepare;
if (command.CommandType == CommandType.StoredProcedure)
{
- var cachedProcedure = await command.Connection!.GetCachedProcedure(commandText, revalidateMissing: false, ioBehavior, cancellationToken).ConfigureAwait(false);
- if (cachedProcedure is null)
+ var parameterCount = command.RawParameters?.Count ?? 0;
+
+ if (UseProcedureCache)
{
- var name = NormalizedSchema.MustNormalize(command.CommandText!, command.Connection.Database);
- throw new MySqlException($"Procedure or function '{name.Component}' cannot be found in database '{name.Schema}'.");
+ var cachedProcedure = await command.Connection!.GetCachedProcedure(commandText, revalidateMissing: false, ioBehavior, cancellationToken).ConfigureAwait(false);
+ if (cachedProcedure is null)
+ {
+ var name = NormalizedSchema.MustNormalize(command.CommandText!, command.Connection.Database);
+ throw new MySqlException($"Procedure or function '{name.Component}' cannot be found in database '{name.Schema}, or procedure caching is disabled");
+ }
+
+ parameterCount = cachedProcedure.Parameters.Count;
}
- var parameterCount = cachedProcedure.Parameters.Count;
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
commandToPrepare = string.Create(commandText.Length + 7 + (parameterCount * 2) + (parameterCount == 0 ? 1 : 0), (commandText, parameterCount), static (buffer, state) =>
{
@@ -607,6 +614,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
}
m_payloadHandler.ByteHandler.RemainingTimeout = Constants.InfiniteTimeout;
+ UseProcedureCache = cs.UseProcedureCache;
return redirectionUrl;
}
catch (ArgumentException ex)
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index e6c2c641d..a1dcc5c15 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -184,8 +184,8 @@ private static void WriteBinaryParameters(ByteBufferWriter writer, MySqlParamete
private static bool WriteStoredProcedure(IMySqlCommand command, IDictionary cachedProcedures, ByteBufferWriter writer)
{
var parameterCollection = command.RawParameters;
- var cachedProcedure = cachedProcedures[command.CommandText!];
- if (cachedProcedure is not null)
+ var cachedProcedureExist = cachedProcedures.TryGetValue(command.CommandText!, out var cachedProcedure);
+ if (cachedProcedureExist && cachedProcedure is not null)
parameterCollection = cachedProcedure.AlignParamsWithDb(parameterCollection);
MySqlParameter? returnParameter = null;
diff --git a/src/MySqlConnector/MySqlCommandBuilder.cs b/src/MySqlConnector/MySqlCommandBuilder.cs
index 7b63dd5ef..64e9c4edf 100644
--- a/src/MySqlConnector/MySqlCommandBuilder.cs
+++ b/src/MySqlConnector/MySqlCommandBuilder.cs
@@ -31,7 +31,7 @@ private static async Task DeriveParametersAsync(IOBehavior ioBehavior, MySqlComm
if (cachedProcedure is null)
{
var name = NormalizedSchema.MustNormalize(command.CommandText!, command.Connection.Database);
- throw new MySqlException($"Procedure or function '{name.Component}' cannot be found in database '{name.Schema}'.");
+ throw new MySqlException($"Procedure or function '{name.Component}' cannot be found in database '{name.Schema}', or procedure caching is disabled");
}
command.Parameters.Clear();
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 27219bb47..4055b8d42 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -962,6 +962,9 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
internal async Task GetCachedProcedure(string name, bool revalidateMissing, IOBehavior ioBehavior, CancellationToken cancellationToken)
{
+ if (!m_session!.UseProcedureCache)
+ return null;
+
Log.GettingCachedProcedure(m_logger, m_session!.Id, name);
if (State != ConnectionState.Open)
throw new InvalidOperationException("Connection is not open.");
diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index 762bcc639..22ac81c06 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -800,6 +800,19 @@ public bool UseXaTransactions
set => MySqlConnectionStringOption.UseXaTransactions.SetValue(this, value);
}
+ ///
+ /// Enables procedure cache.
+ ///
+ [Category("Other")]
+ [DefaultValue(true)]
+ [Description("Enables procedure cache.")]
+ [DisplayName("Use Procedure Cache")]
+ public bool UseProcedureCache
+ {
+ get => MySqlConnectionStringOption.UseProcedureCache.GetValue(this);
+ set => MySqlConnectionStringOption.UseProcedureCache.SetValue(this, value);
+ }
+
// Other Methods
///
@@ -958,6 +971,7 @@ internal abstract partial class MySqlConnectionStringOption
public static readonly MySqlConnectionStringValueOption UseAffectedRows;
public static readonly MySqlConnectionStringValueOption UseCompression;
public static readonly MySqlConnectionStringValueOption UseXaTransactions;
+ public static readonly MySqlConnectionStringValueOption UseProcedureCache;
public static MySqlConnectionStringOption? TryGetOptionForKey(string key) =>
s_options.TryGetValue(key, out var option) ? option : null;
@@ -1262,6 +1276,10 @@ static MySqlConnectionStringOption()
AddOption(options, UseXaTransactions = new(
keys: ["Use XA Transactions", "UseXaTransactions"],
defaultValue: true));
+
+ AddOption(options, UseProcedureCache = new(
+ keys: ["Use Procedure Cache", "UseProcedureCache"],
+ defaultValue: true));
#pragma warning restore SA1118 // Parameter should not span multiple lines
#if NET8_0_OR_GREATER