-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathExplicitConvertionFunction.cs
More file actions
141 lines (120 loc) · 6.28 KB
/
Copy pathExplicitConvertionFunction.cs
File metadata and controls
141 lines (120 loc) · 6.28 KB
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
using System.Diagnostics.CodeAnalysis;
using TeamTools.TSQL.ExpressionEvaluator.BuiltInFunctions.ArgumentDto;
using TeamTools.TSQL.ExpressionEvaluator.BuiltInFunctions.ArgumentValidators;
using TeamTools.TSQL.ExpressionEvaluator.Evaluation;
using TeamTools.TSQL.ExpressionEvaluator.Properties;
using TeamTools.TSQL.ExpressionEvaluator.TypeHandling;
using TeamTools.TSQL.ExpressionEvaluator.Values;
using TeamTools.TSQL.ExpressionEvaluator.Violations;
namespace TeamTools.TSQL.ExpressionEvaluator.BuiltInFunctions.Abstractions
{
// TODO : support culture, format for CONVERT and so on - the third arg
public abstract class ExplicitConvertionFunction : SqlGenericFunctionHandler<ExplicitConvertionFunction.ConvertArgs>
{
protected ExplicitConvertionFunction(string funcName, int requiredArgs = 2) : base(funcName, requiredArgs)
{
}
protected ExplicitConvertionFunction(string funcName, int minArgs, int maxArgs) : base(funcName, minArgs, maxArgs)
{
}
private static string MsgSourceIsAlreadyOfThatType => Strings.ViolationDetails_RedundantTypeConversionViolation_ValueIsAlreadyOfThisType;
public override bool ValidateArgumentValues(CallSignature<ConvertArgs> call)
{
// even if source value is unpredictable we can assume
// that result will anyways be of given type definition
// because this is an explicit conversion
ValidationScenario
.For("SRC", call.RawArgs[0], call.Context)
.When(ArgumentIsValue.Validate)
.Then(v => call.ValidatedArgs.SrcValue = v);
if (MaxArgs > 2 && call.RawArgs.Count == 3)
{
ValidationScenario
.For("STYLE", call.RawArgs[2], call.Context)
.When(ArgumentIsValue.Validate)
.And(ArgumentIsValidInt.Validate)
.Then(v => call.ValidatedArgs.ConvertionStyle = v);
}
return ValidationScenario
.For("TYPE", call.RawArgs[1], call.Context)
.When(ArgumentIsType.Validate)
.Then(t => call.ValidatedArgs.TargetType = t);
}
protected override string DoEvaluateResultType(CallSignature<ConvertArgs> call) => call.ValidatedArgs.TargetType.TypeName;
protected override SqlValue DoEvaluateResultValue(CallSignature<ConvertArgs> call)
{
if (call.ValidatedArgs.SrcValue is null)
{
return call.ValidatedArgs.TargetType.MakeUnknownValue();
}
if (call.ValidatedArgs.TargetType is SqlStrTypeReference str && str.IsUnicode
&& (call.ValidatedArgs.SrcValue is SqlIntTypeValue
|| call.ValidatedArgs.SrcValue is SqlBinaryTypeValue
|| call.ValidatedArgs.SrcValue is SqlDecimalTypeValue
|| call.ValidatedArgs.SrcValue is SqlDateTimeValue
|| call.ValidatedArgs.SrcValue is SqlDateOnlyValue
|| call.ValidatedArgs.SrcValue is SqlTimeOnlyValue))
{
// TODO : If this conversion result is used in concatenation with other NVARCHAR strings
// then no violation should be issued.
string msg;
if (call.ValidatedArgs.SrcValue.Source is SqlValueSourceVariable v)
{
msg = string.Format(Strings.ViolationDetails_NumbersHaveNoUnicode_VarIsNumber, FunctionName, v.VarName);
}
else if (call.ValidatedArgs.SrcValue.SourceKind == SqlValueSourceKind.Literal && call.ValidatedArgs.SrcValue.IsPreciseValue)
{
string numericSourceValue = null;
if (call.ValidatedArgs.SrcValue is SqlIntTypeValue intValue)
{
numericSourceValue = intValue.Value.ToString();
}
else if (call.ValidatedArgs.SrcValue is SqlDateTimeValue datetimeValue)
{
numericSourceValue = datetimeValue.Value.ToString();
}
else if (call.ValidatedArgs.SrcValue is SqlDateOnlyValue dateValue)
{
numericSourceValue = dateValue.Value.ToString();
}
else if (call.ValidatedArgs.SrcValue is SqlTimeOnlyValue timeValue)
{
numericSourceValue = timeValue.Value.ToString();
}
msg = string.Format(Strings.ViolationDetails_NumbersHaveNoUnicode_LiteralValueIsNumber, FunctionName, numericSourceValue);
}
else
{
msg = string.Format(Strings.ViolationDetails_NumbersHaveNoUnicode_ExpressionIsNumber, FunctionName);
}
call.Context.Violations.RegisterViolation(new NumbersHaveNoUnicodeViolation(msg, call.Context.NewSource));
}
if (call.ValidatedArgs.SrcValue.IsNull)
{
return call.ValidatedArgs.TargetType.MakeNullValue();
}
return call.Context.Converter.ExplicitlyConvertTo(
call.ValidatedArgs.TargetType,
call.ValidatedArgs.SrcValue,
alreadyOfTypeName => RegisterRedundantConversionViolation(call.ValidatedArgs.SrcValue, call.Context));
}
private static void RegisterRedundantConversionViolation(SqlValue srcValue, EvaluationContext context)
{
if (srcValue.SourceKind == SqlValueSourceKind.Literal)
{
// Explicit type definition for hardcoded literal is fine
// and is a preferred style for selected column definitions
return;
}
var violationMessage = string.Format(MsgSourceIsAlreadyOfThatType, srcValue.Source.ToString(), srcValue.TypeReference.ToString());
context.Violations.RegisterViolation(new RedundantTypeConversionViolation(violationMessage, context.NewSource));
}
[ExcludeFromCodeCoverage]
public class ConvertArgs
{
public SqlTypeReference TargetType { get; set; }
public SqlValue SrcValue { get; set; }
public SqlIntTypeValue ConvertionStyle { get; set; }
}
}
}