Skip to content

Commit 9c02738

Browse files
committed
Add better exception for unsupported ParameterDirection. Fixes #234
Add better tracking of whether the client has explicitly set MySqlParameter.ParameterDirection, and add parameter validation. Verify that MySqlParameter default values match the baseline.
1 parent 456b253 commit 9c02738

File tree

7 files changed

+81
-7
lines changed

7 files changed

+81
-7
lines changed

src/MySqlConnector/MySqlClient/Caches/CachedProcedure.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ internal MySqlParameterCollection AlignParamsWithDb(MySqlParameterCollection par
7575
alignParam = index >= 0 ? parameterCollection[index] : throw new ArgumentException($"Parameter '{cachedParam.Name}' not found in the collection.");
7676
}
7777

78-
if (alignParam.Direction == default(ParameterDirection))
78+
if (!alignParam.HasSetDirection)
7979
alignParam.Direction = cachedParam.Direction;
8080
if (alignParam.DbType == default(DbType))
8181
alignParam.DbType = cachedParam.DbType;
@@ -93,4 +93,4 @@ internal MySqlParameterCollection AlignParamsWithDb(MySqlParameterCollection par
9393
readonly string m_component;
9494
readonly ReadOnlyCollection<CachedParameter> m_parameters;
9595
}
96-
}
96+
}

src/MySqlConnector/MySqlClient/CommandExecutors/StoredProcedureCommandExecutor.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ public override async Task<DbDataReader> ExecuteReaderAsync(string commandText,
3737
var outName = "@outParam" + i;
3838
switch (param.Direction)
3939
{
40-
case 0:
4140
case ParameterDirection.Input:
4241
case ParameterDirection.InputOutput:
4342
var inParam = param.WithParameterName(inName);
@@ -79,7 +78,7 @@ public override async Task<DbDataReader> ExecuteReaderAsync(string commandText,
7978
var reader = (MySqlDataReader) await base.ExecuteReaderAsync(commandText, inParams, behavior, ioBehavior, cancellationToken).ConfigureAwait(false);
8079
if (returnParam != null && await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false))
8180
returnParam.Value = reader.GetValue(0);
82-
81+
8382
return reader;
8483
}
8584

@@ -115,4 +114,4 @@ internal void SetParams()
115114
List<string> m_outParamNames;
116115
private CancellationToken m_cancellationToken;
117116
}
118-
}
117+
}

src/MySqlConnector/MySqlClient/CommandExecutors/TextCommandExecutor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ public virtual async Task<DbDataReader> ExecuteReaderAsync(string commandText, M
5656
statementPreparerOptions |= StatementPreparerOptions.AllowUserVariables;
5757
if (m_command.Connection.OldGuids)
5858
statementPreparerOptions |= StatementPreparerOptions.OldGuids;
59+
if (m_command.CommandType == CommandType.StoredProcedure)
60+
statementPreparerOptions |= StatementPreparerOptions.AllowOutputParameters;
5961
var preparer = new MySqlStatementPreparer(commandText, parameterCollection, statementPreparerOptions);
6062
var payload = new PayloadData(preparer.ParseAndBindParameters());
6163
try

src/MySqlConnector/MySqlClient/MySqlParameter.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,19 @@ public MySqlParameter()
1313

1414
public override DbType DbType { get; set; }
1515

16-
public override ParameterDirection Direction { get; set; }
16+
public override ParameterDirection Direction
17+
{
18+
get => m_direction.GetValueOrDefault(ParameterDirection.Input);
19+
set
20+
{
21+
if (value != ParameterDirection.Input && value != ParameterDirection.Output &&
22+
value != ParameterDirection.InputOutput && value != ParameterDirection.ReturnValue)
23+
{
24+
throw new ArgumentOutOfRangeException(nameof(value), "{0} is not a supported value for ParameterDirection".FormatInvariant(value));
25+
}
26+
m_direction = value;
27+
}
28+
}
1729

1830
public override bool IsNullable { get; set; }
1931

@@ -64,7 +76,7 @@ public override void ResetDbType()
6476
private MySqlParameter(MySqlParameter other, string parameterName)
6577
{
6678
DbType = other.DbType;
67-
Direction = other.Direction;
79+
m_direction = other.m_direction;
6880
IsNullable = other.IsNullable;
6981
Size = other.Size;
7082
ParameterName = parameterName ?? other.ParameterName;
@@ -75,6 +87,8 @@ private MySqlParameter(MySqlParameter other, string parameterName)
7587
#endif
7688
}
7789

90+
internal bool HasSetDirection => m_direction.HasValue;
91+
7892
internal string NormalizedParameterName { get; private set; }
7993

8094
internal void AppendSqlString(BinaryWriter writer, StatementPreparerOptions options)
@@ -200,5 +214,6 @@ internal static string NormalizeParameterName(string name)
200214
}
201215

202216
string m_name;
217+
ParameterDirection? m_direction;
203218
}
204219
}

src/MySqlConnector/MySqlClient/MySqlStatementPreparer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Data;
23
using System.IO;
34
using System.Text;
45
using MySql.Data.Serialization;
@@ -67,6 +68,9 @@ protected override void OnPositionalParameter(int index)
6768
private void DoAppendParameter(int parameterIndex, int textIndex, int textLength)
6869
{
6970
AppendString(m_preparer.m_commandText, m_lastIndex, textIndex - m_lastIndex);
71+
var parameter = m_preparer.m_parameters[parameterIndex];
72+
if (parameter.Direction != ParameterDirection.Input && (m_preparer.m_options & StatementPreparerOptions.AllowOutputParameters) == 0)
73+
throw new MySqlException("Only ParameterDirection.Input is supported when CommandType is Text (parameter name: {0})".FormatInvariant(parameter.ParameterName));
7074
m_preparer.m_parameters[parameterIndex].AppendSqlString(m_writer, m_preparer.m_options);
7175
m_lastIndex = textIndex + textLength;
7276
}

src/MySqlConnector/MySqlClient/StatementPreparerOptions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ internal enum StatementPreparerOptions
88
None = 0,
99
AllowUserVariables = 1,
1010
OldGuids = 2,
11+
AllowOutputParameters = 4,
1112
}
1213
}

tests/SideBySide/QueryTests.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,59 @@ private void UseReaderWithoutDisposingThread(object obj)
581581
}
582582
}
583583

584+
[Fact]
585+
public void ParameterDefaults()
586+
{
587+
var parameter = new MySqlParameter();
588+
Assert.Equal(DbType.AnsiString, parameter.DbType);
589+
Assert.Equal(ParameterDirection.Input, parameter.Direction);
590+
Assert.False(parameter.IsNullable);
591+
Assert.Null(parameter.ParameterName);
592+
Assert.Equal(0, parameter.Precision);
593+
Assert.Equal(0, parameter.Scale);
594+
Assert.Equal(0, parameter.Size);
595+
Assert.Null(parameter.Value);
596+
}
597+
598+
[Fact]
599+
public void InputOutputParameter()
600+
{
601+
using (var cmd = m_database.Connection.CreateCommand())
602+
{
603+
cmd.CommandText = "set @param = 1234";
604+
605+
cmd.Parameters.Add(new MySqlParameter
606+
{
607+
ParameterName = "@param",
608+
Direction = ParameterDirection.InputOutput,
609+
Value = 123,
610+
});
611+
612+
Assert.Throws<MySqlException>(() => cmd.ExecuteNonQuery());
613+
614+
// Issue #231: Assert.Equal(1234, cmd.Parameters["@param"].Value);
615+
}
616+
}
617+
618+
[Fact]
619+
public void OutputParameter()
620+
{
621+
using (var cmd = m_database.Connection.CreateCommand())
622+
{
623+
cmd.CommandText = "set @param = 1234";
624+
625+
cmd.Parameters.Add(new MySqlParameter
626+
{
627+
ParameterName = "@param",
628+
Direction = ParameterDirection.Output,
629+
});
630+
631+
Assert.Throws<MySqlException>(() => cmd.ExecuteNonQuery());
632+
633+
// Issue #231: Assert.Equal(1234, cmd.Parameters["@param"].Value);
634+
}
635+
}
636+
584637
class BoolTest
585638
{
586639
public int Id { get; set; }

0 commit comments

Comments
 (0)