Skip to content

Commit a37b151

Browse files
authored
auto-detect stored procedures as anything without whitespace (#1975)
* 1. auto-detect stored procedures as anything without whitespace 2. we have public fields? (hangs head in shame) * release notes * no need to use regex
1 parent 47ef1e7 commit a37b151

File tree

8 files changed

+140
-37
lines changed

8 files changed

+140
-37
lines changed

Dapper/CommandDefinition.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,15 @@ internal void OnCompleted()
4848
/// </summary>
4949
public int? CommandTimeout { get; }
5050

51+
internal readonly CommandType CommandTypeDirect;
52+
5153
/// <summary>
5254
/// The type of command that the command-text represents
5355
/// </summary>
54-
public CommandType? CommandType { get; }
56+
#if DEBUG // prevent use in our own code
57+
[Obsolete("Prefer " + nameof(CommandTypeDirect), true)]
58+
#endif
59+
public CommandType? CommandType => CommandTypeDirect;
5560

5661
/// <summary>
5762
/// Should data be buffered before returning?
@@ -92,11 +97,21 @@ public CommandDefinition(string commandText, object? parameters = null, IDbTrans
9297
Parameters = parameters;
9398
Transaction = transaction;
9499
CommandTimeout = commandTimeout;
95-
CommandType = commandType;
100+
CommandTypeDirect = commandType ?? InferCommandType(commandText);
96101
Flags = flags;
97102
CancellationToken = cancellationToken;
103+
104+
static CommandType InferCommandType(string sql)
105+
{
106+
if (sql is null || sql.IndexOfAny(WhitespaceChars) >= 0) return System.Data.CommandType.Text;
107+
return System.Data.CommandType.StoredProcedure;
108+
}
98109
}
99110

111+
// if the sql contains any whitespace character (space/tab/cr/lf): interpret as ad-hoc; but "SomeName" should be treated as a stored-proc
112+
// (note TableDirect would need to be specified explicitly, but in reality providers don't usually support TableDirect anyway)
113+
private static readonly char[] WhitespaceChars = new char[] { ' ', '\t', '\r', '\n' };
114+
100115
private CommandDefinition(object? parameters) : this()
101116
{
102117
Parameters = parameters;
@@ -124,8 +139,7 @@ internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object?>?
124139
{
125140
cmd.CommandTimeout = SqlMapper.Settings.CommandTimeout.Value;
126141
}
127-
if (CommandType.HasValue)
128-
cmd.CommandType = CommandType.Value;
142+
cmd.CommandType = CommandTypeDirect;
129143
paramReader?.Invoke(cmd, Parameters);
130144
return cmd;
131145
}

Dapper/DynamicParameters.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ internal static bool ShouldSetDbType(DbType dbType)
168168
/// <param name="identity">Information about the query</param>
169169
protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
170170
{
171-
var literals = SqlMapper.GetLiteralTokens(identity.sql);
171+
var literals = SqlMapper.GetLiteralTokens(identity.Sql);
172172

173173
if (templates is not null)
174174
{

Dapper/PublicAPI.Shipped.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,17 @@ override Dapper.SqlMapper.Identity.ToString() -> string!
152152
override Dapper.SqlMapper.StringTypeHandler<T>.Parse(object! value) -> T
153153
override Dapper.SqlMapper.StringTypeHandler<T>.SetValue(System.Data.IDbDataParameter! parameter, T? value) -> void
154154
readonly Dapper.SqlMapper.Identity.commandType -> System.Data.CommandType?
155+
Dapper.SqlMapper.Identity.CommandType.get -> System.Data.CommandType?
155156
readonly Dapper.SqlMapper.Identity.connectionString -> string!
156157
readonly Dapper.SqlMapper.Identity.gridIndex -> int
158+
Dapper.SqlMapper.Identity.GridIndex.get -> int
157159
readonly Dapper.SqlMapper.Identity.hashCode -> int
158160
readonly Dapper.SqlMapper.Identity.parametersType -> System.Type?
161+
Dapper.SqlMapper.Identity.ParametersType.get -> System.Type?
159162
readonly Dapper.SqlMapper.Identity.sql -> string!
163+
Dapper.SqlMapper.Identity.Sql.get -> string!
160164
readonly Dapper.SqlMapper.Identity.type -> System.Type?
165+
Dapper.SqlMapper.Identity.Type.get -> System.Type?
161166
static Dapper.DbString.IsAnsiDefault.get -> bool
162167
static Dapper.DbString.IsAnsiDefault.set -> void
163168
static Dapper.DefaultTypeMap.MatchNamesWithUnderscores.get -> bool

Dapper/SqlMapper.Async.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ private static DbCommand TrySetupAsyncCommand(this CommandDefinition command, ID
422422
private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, Type effectiveType, CommandDefinition command)
423423
{
424424
object? param = command.Parameters;
425-
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType());
425+
var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, effectiveType, param?.GetType());
426426
var info = GetCacheInfo(identity, param, command.AddToCache);
427427
bool wasClosed = cnn.State == ConnectionState.Closed;
428428
var cancel = command.CancellationToken;
@@ -477,7 +477,7 @@ private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn,
477477
private static async Task<T> QueryRowAsync<T>(this IDbConnection cnn, Row row, Type effectiveType, CommandDefinition command)
478478
{
479479
object? param = command.Parameters;
480-
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType());
480+
var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, effectiveType, param?.GetType());
481481
var info = GetCacheInfo(identity, param, command.AddToCache);
482482
bool wasClosed = cnn.State == ConnectionState.Closed;
483483
var cancel = command.CancellationToken;
@@ -652,7 +652,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD
652652

653653
private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, object? param)
654654
{
655-
var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param?.GetType());
655+
var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, null, param?.GetType());
656656
var info = GetCacheInfo(identity, param, command.AddToCache);
657657
bool wasClosed = cnn.State == ConnectionState.Closed;
658658
using var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader);
@@ -930,7 +930,7 @@ public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFo
930930
private static async Task<IEnumerable<TReturn>> MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, CommandDefinition command, Delegate map, string splitOn)
931931
{
932932
object? param = command.Parameters;
933-
var identity = new Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(command.CommandText, command.CommandType, cnn, typeof(TFirst), param?.GetType());
933+
var identity = new Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(command.CommandText, command.CommandTypeDirect, cnn, typeof(TFirst), param?.GetType());
934934
var info = GetCacheInfo(identity, param, command.AddToCache);
935935
bool wasClosed = cnn.State == ConnectionState.Closed;
936936
try
@@ -979,7 +979,7 @@ private static async Task<IEnumerable<TReturn>> MultiMapAsync<TReturn>(this IDbC
979979
}
980980

981981
object? param = command.Parameters;
982-
var identity = new IdentityWithTypes(command.CommandText, command.CommandType, cnn, types[0], param?.GetType(), types);
982+
var identity = new IdentityWithTypes(command.CommandText, command.CommandTypeDirect, cnn, types[0], param?.GetType(), types);
983983
var info = GetCacheInfo(identity, param, command.AddToCache);
984984
bool wasClosed = cnn.State == ConnectionState.Closed;
985985
try
@@ -1029,7 +1029,7 @@ public static Task<GridReader> QueryMultipleAsync(this IDbConnection cnn, string
10291029
public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn, CommandDefinition command)
10301030
{
10311031
object? param = command.Parameters;
1032-
var identity = new Identity(command.CommandText, command.CommandType, cnn, typeof(GridReader), param?.GetType());
1032+
var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, typeof(GridReader), param?.GetType());
10331033
CacheInfo info = GetCacheInfo(identity, param, command.AddToCache);
10341034

10351035
DbCommand? cmd = null;
@@ -1227,7 +1227,7 @@ private static async Task<DbDataReader> ExecuteWrappedReaderImplAsync(IDbConnect
12271227
object? param = command.Parameters;
12281228
if (param is not null)
12291229
{
1230-
var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType());
1230+
var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, null, param.GetType());
12311231
paramReader = GetCacheInfo(identity, command.Parameters, command.AddToCache).ParamReader;
12321232
}
12331233

@@ -1296,7 +1296,7 @@ static async IAsyncEnumerable<T> Impl(IDbConnection cnn, Type effectiveType, Com
12961296
[EnumeratorCancellation] CancellationToken cancel)
12971297
{
12981298
object? param = command.Parameters;
1299-
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType());
1299+
var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, effectiveType, param?.GetType());
13001300
var info = GetCacheInfo(identity, param, command.AddToCache);
13011301
bool wasClosed = cnn.State == ConnectionState.Closed;
13021302
using var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader);

Dapper/SqlMapper.Identity.cs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.ComponentModel;
23
using System.Data;
34
using System.Runtime.CompilerServices;
45

@@ -92,6 +93,7 @@ public class Identity : IEquatable<Identity>
9293

9394
internal virtual Type GetType(int index) => throw new IndexOutOfRangeException(nameof(index));
9495

96+
#pragma warning disable CS0618 // Type or member is obsolete
9597
internal Identity ForGrid<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(Type primaryType, int gridIndex) =>
9698
new Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(sql, commandType, connectionString, primaryType, parametersType, gridIndex);
9799

@@ -110,12 +112,14 @@ internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex) =>
110112
/// <returns></returns>
111113
public Identity ForDynamicParameters(Type type) =>
112114
new Identity(sql, commandType, connectionString, this.type, type, 0, -1);
115+
#pragma warning restore CS0618 // Type or member is obsolete
113116

114117
internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type? type, Type? parametersType)
115118
: this(sql, commandType, connection.ConnectionString, type, parametersType, 0, 0) { /* base call */ }
116119

117120
private protected Identity(string sql, CommandType? commandType, string connectionString, Type? type, Type? parametersType, int otherTypesHash, int gridIndex)
118121
{
122+
#pragma warning disable CS0618 // Type or member is obsolete
119123
this.sql = sql;
120124
this.commandType = commandType;
121125
this.connectionString = connectionString;
@@ -133,6 +137,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti
133137
hashCode = (hashCode * 23) + (connectionString is null ? 0 : connectionStringComparer.GetHashCode(connectionString));
134138
hashCode = (hashCode * 23) + (parametersType?.GetHashCode() ?? 0);
135139
}
140+
#pragma warning restore CS0618 // Type or member is obsolete
136141
}
137142

138143
/// <summary>
@@ -144,48 +149,101 @@ private protected Identity(string sql, CommandType? commandType, string connecti
144149
/// <summary>
145150
/// The raw SQL command.
146151
/// </summary>
152+
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
153+
[Obsolete("Please use " + nameof(Sql) + ". This API may be removed at a later date.")]
147154
public readonly string sql;
148155

156+
/// <summary>
157+
/// The raw SQL command.
158+
/// </summary>
159+
#pragma warning disable CS0618 // Type or member is obsolete
160+
public string Sql => sql;
161+
#pragma warning restore CS0618 // Type or member is obsolete
162+
149163
/// <summary>
150164
/// The SQL command type.
151165
/// </summary>
166+
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
167+
[Obsolete("Please use " + nameof(CommandType) + ". This API may be removed at a later date.")]
152168
public readonly CommandType? commandType;
153169

170+
/// <summary>
171+
/// The SQL command type.
172+
/// </summary>
173+
#pragma warning disable CS0618 // Type or member is obsolete
174+
public CommandType? CommandType => commandType;
175+
#pragma warning restore CS0618 // Type or member is obsolete
176+
154177
/// <summary>
155178
/// The hash code of this Identity.
156179
/// </summary>
180+
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
181+
[Obsolete("Please use " + nameof(GetHashCode) + ". This API may be removed at a later date.")]
157182
public readonly int hashCode;
158183

159184
/// <summary>
160185
/// The grid index (position in the reader) of this Identity.
161186
/// </summary>
187+
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
188+
[Obsolete("Please use " + nameof(GridIndex) + ". This API may be removed at a later date.")]
162189
public readonly int gridIndex;
163190

164191
/// <summary>
165-
/// This <see cref="Type"/> of this Identity.
192+
/// The grid index (position in the reader) of this Identity.
166193
/// </summary>
194+
#pragma warning disable CS0618 // Type or member is obsolete
195+
public int GridIndex => gridIndex;
196+
#pragma warning restore CS0618 // Type or member is obsolete
197+
198+
/// <summary>
199+
/// The <see cref="Type"/> of this Identity.
200+
/// </summary>
201+
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
202+
[Obsolete("Please use " + nameof(Type) + ". This API may be removed at a later date.")]
167203
public readonly Type? type;
168204

205+
/// <summary>
206+
/// The <see cref="Type"/> of this Identity.
207+
/// </summary>
208+
#pragma warning disable CS0618 // Type or member is obsolete
209+
public Type? Type => type;
210+
#pragma warning restore CS0618 // Type or member is obsolete
211+
169212
/// <summary>
170213
/// The connection string for this Identity.
171214
/// </summary>
215+
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
216+
[Obsolete("This API may be removed at a later date.")]
172217
public readonly string connectionString;
173218

174219
/// <summary>
175220
/// The type of the parameters object for this Identity.
176221
/// </summary>
222+
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
223+
[Obsolete("Please use " + nameof(ParametersType) + ". This API may be removed at a later date.")]
177224
public readonly Type? parametersType;
178225

226+
/// <summary>
227+
/// The type of the parameters object for this Identity.
228+
/// </summary>
229+
#pragma warning disable CS0618 // Type or member is obsolete
230+
public Type? ParametersType => parametersType;
231+
#pragma warning restore CS0618 // Type or member is obsolete
232+
179233
/// <summary>
180234
/// Gets the hash code for this identity.
181235
/// </summary>
182236
/// <returns></returns>
237+
#pragma warning disable CS0618 // Type or member is obsolete
183238
public override int GetHashCode() => hashCode;
239+
#pragma warning restore CS0618 // Type or member is obsolete
184240

185241
/// <summary>
186242
/// See object.ToString()
187243
/// </summary>
244+
#pragma warning disable CS0618 // Type or member is obsolete
188245
public override string ToString() => sql;
246+
#pragma warning restore CS0618 // Type or member is obsolete
189247

190248
/// <summary>
191249
/// Compare 2 Identity objects
@@ -198,6 +256,7 @@ public bool Equals(Identity? other)
198256
if (other is null) return false;
199257

200258
int typeCount;
259+
#pragma warning disable CS0618 // Type or member is obsolete
201260
return gridIndex == other.gridIndex
202261
&& type == other.type
203262
&& sql == other.sql
@@ -206,6 +265,7 @@ public bool Equals(Identity? other)
206265
&& parametersType == other.parametersType
207266
&& (typeCount = TypeCount) == other.TypeCount
208267
&& (typeCount == 0 || TypesEqual(this, other, typeCount));
268+
#pragma warning restore CS0618 // Type or member is obsolete
209269
}
210270

211271
[MethodImpl(MethodImplOptions.NoInlining)]

0 commit comments

Comments
 (0)