-
Notifications
You must be signed in to change notification settings - Fork 934
/
Copy pathSqlCommandImpl.cs
160 lines (146 loc) · 7.73 KB
/
SqlCommandImpl.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using NHibernate.Engine;
using NHibernate.Param;
using NHibernate.SqlTypes;
namespace NHibernate.SqlCommand
{
public partial interface ISqlCommand
{
SqlType[] ParameterTypes { get; }
SqlString Query { get; }
QueryParameters QueryParameters { get; }
/// <summary>
/// re-set the index of each parameter in the final <see cref="DbCommand">command</see>.
/// </summary>
/// <param name="singleSqlParametersOffset">The offset from where start the list of <see cref="DbParameter"/>, in the given command, for the this <see cref="SqlCommandImpl"/>. </param>
/// <remarks>
/// Suppose the final <see cref="DbCommand">command</see> is composed by two queries. The <paramref name="singleSqlParametersOffset"/> for the first query is zero.
/// If the first query command has 12 parameters (size of its SqlType array) the offset to bind all <see cref="IParameterSpecification"/>s, of the second query in the
/// command, is 12 (for the first query we are using from 0 to 11).
/// <para>
/// This method should be called before call <see cref="IBatcher.PrepareCommand"/>.
/// </para>
/// </remarks>
void ResetParametersIndexesForTheCommand(int singleSqlParametersOffset); // Note: should be included ParameterTypes getter
/// <summary>
/// Bind the appropriate value into the given command.
/// </summary>
/// <param name="command">The command into which the value should be bound.</param>
/// <param name="commandQueryParametersList">The parameter-list of the whole query of the command.</param>
/// <param name="singleSqlParametersOffset">The offset from where start the list of <see cref="DbParameter"/>, in the given <paramref name="command"/>, for the this <see cref="SqlCommandImpl"/>. </param>
/// <param name="session">The session against which the current execution is occurring.</param>
/// <remarks>
/// Suppose the <paramref name="command"/> is composed by two queries. The <paramref name="singleSqlParametersOffset"/> for the first query is zero.
/// If the first query in <paramref name="command"/> has 12 parameters (size of its SqlType array) the offset to bind all <see cref="IParameterSpecification"/>s, of the second query in the
/// <paramref name="command"/>, is 12 (for the first query we are using from 0 to 11).
/// </remarks>
void Bind(DbCommand command, IList<Parameter> commandQueryParametersList, int singleSqlParametersOffset, ISessionImplementor session);
/// <summary>
/// Bind the appropriate value into the given command.
/// </summary>
/// <param name="command">The command into which the value should be bound.</param>
/// <param name="session">The session against which the current execution is occurring.</param>
/// <remarks>
/// Use this method when the <paramref name="command"/> contains just 'this' instance of <see cref="ISqlCommand"/>.
/// Use the overload <see cref="Bind(DbCommand, IList{Parameter}, int, ISessionImplementor)"/> when the <paramref name="command"/> contains more instances of <see cref="ISqlCommand"/>.
/// </remarks>
void Bind(DbCommand command, ISessionImplementor session);
}
public partial class SqlCommandImpl : ISqlCommand
{
private readonly SqlString query;
private readonly ICollection<IParameterSpecification> specifications;
private readonly QueryParameters queryParameters;
private readonly ISessionFactoryImplementor factory;
private SqlType[] parameterTypes;
IList<Parameter> sqlQueryParametersList;
public SqlCommandImpl(SqlString query, ICollection<IParameterSpecification> specifications, QueryParameters queryParameters, ISessionFactoryImplementor factory)
{
this.query = query;
this.specifications = specifications;
this.queryParameters = queryParameters;
this.factory = factory;
}
public IList<Parameter> SqlQueryParametersList
{
get { return sqlQueryParametersList ?? (sqlQueryParametersList = query.GetParameters().ToBackTrackCacheParameterList()); }
}
public SqlType[] ParameterTypes
{
get { return parameterTypes ?? (parameterTypes = specifications.GetQueryParameterTypes(SqlQueryParametersList, factory)); }
}
public SqlString Query
{
get { return query; }
}
public IEnumerable<IParameterSpecification> Specifications
{
get { return specifications; }
}
public QueryParameters QueryParameters
{
get { return queryParameters; }
}
public void ResetParametersIndexesForTheCommand(int singleSqlParametersOffset)
{
// a better place could be the Bind of each IParameterSpecification but we have to do it before bind values
// in this way the same parameter of a dynamic-filter will be set with two different parameter-names in the same command (when it is a command-set).
if (singleSqlParametersOffset < 0)
{
throw new AssertionFailure("singleSqlParametersOffset < 0 - this indicate a bug in NHibernate ");
}
// due to IType.NullSafeSet(DbCommand , object, int, ISessionImplementor) the SqlType[] is supposed to be in a certain sequence.
// this mean that found the first location of a parameter for the IType span, the others are in sequence
foreach (IParameterSpecification specification in Specifications)
{
string firstParameterId = specification.GetIdsForBackTrack(factory).First();
int[] effectiveParameterLocations = SqlQueryParametersList.GetEffectiveParameterLocations(firstParameterId).ToArray();
if (effectiveParameterLocations.Length > 0) // Parameters previously present might have been removed from the SQL at a later point.
{
int firstParamNameIndex = effectiveParameterLocations[0] + singleSqlParametersOffset;
foreach (int location in effectiveParameterLocations)
{
int parameterSpan = Math.Min(specification.ExpectedType.GetColumnSpan(factory), SqlQueryParametersList.Count);
for (int j = 0; j < parameterSpan; j++)
{
sqlQueryParametersList[location + j].ParameterPosition = firstParamNameIndex + j;
}
}
}
}
}
/// <summary>
/// Bind the appropriate value into the given command.
/// </summary>
/// <param name="command">The command into which the value should be bound.</param>
/// <param name="commandQueryParametersList">The parameter-list of the whole query of the command.</param>
/// <param name="singleSqlParametersOffset">The offset from where start the list of <see cref="DbParameter"/>, in the given <paramref name="command"/>, for the this <see cref="SqlCommandImpl"/>. </param>
/// <param name="session">The session against which the current execution is occuring.</param>
public void Bind(DbCommand command, IList<Parameter> commandQueryParametersList, int singleSqlParametersOffset, ISessionImplementor session)
{
foreach (IParameterSpecification parameterSpecification in Specifications)
{
parameterSpecification.Bind(command, commandQueryParametersList, singleSqlParametersOffset, SqlQueryParametersList, QueryParameters, session);
}
}
/// <summary>
/// Bind the appropriate value into the given command.
/// </summary>
/// <param name="command">The command into which the value should be bound.</param>
/// <param name="session">The session against which the current execution is occuring.</param>
/// <remarks>
/// Use this method when the <paramref name="command"/> contains just 'this' instance of <see cref="ISqlCommand"/>.
/// Use the overload <see cref="Bind(DbCommand, IList{Parameter}, int, ISessionImplementor)"/> when the <paramref name="command"/> contains more instances of <see cref="ISqlCommand"/>.
/// </remarks>
public void Bind(DbCommand command, ISessionImplementor session)
{
foreach (IParameterSpecification parameterSpecification in Specifications)
{
parameterSpecification.Bind(command, SqlQueryParametersList, QueryParameters, session);
}
}
}
}