1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 using System.Collections.Generic;
7 using System.Data.SqlTypes;
8 using System.Data.Common;
9 
10 namespace System.Data
11 {
12     internal sealed class DataExpression : IFilter
13     {
14         internal string _originalExpression = null;  // original, unoptimized string
15 
16         private bool _parsed = false;
17         private bool _bound = false;
18         private ExpressionNode _expr = null;
19         private DataTable _table = null;
20         private readonly StorageType _storageType;
21         private readonly Type _dataType;  // This set if the expression is part of ExpressionCoulmn
22         private DataColumn[] _dependency = Array.Empty<DataColumn>();
23 
DataExpression(DataTable table, string expression)24         internal DataExpression(DataTable table, string expression) : this(table, expression, null)
25         {
26         }
27 
DataExpression(DataTable table, string expression, Type type)28         internal DataExpression(DataTable table, string expression, Type type)
29         {
30             ExpressionParser parser = new ExpressionParser(table);
31             parser.LoadExpression(expression);
32 
33             _originalExpression = expression;
34             _expr = null;
35 
36             if (expression != null)
37             {
38                 _storageType = DataStorage.GetStorageType(type);
39                 if (_storageType == StorageType.BigInteger)
40                 {
41                     throw ExprException.UnsupportedDataType(type);
42                 }
43 
44                 _dataType = type;
45                 _expr = parser.Parse();
46                 _parsed = true;
47                 if (_expr != null && table != null)
48                 {
49                     Bind(table);
50                 }
51                 else
52                 {
53                     _bound = false;
54                 }
55             }
56         }
57 
58         internal string Expression
59         {
60             get
61             {
62                 return (_originalExpression != null ? _originalExpression : ""); // CONSIDER: return optimized expression here (if bound)
63             }
64         }
65 
66         internal ExpressionNode ExpressionNode
67         {
68             get
69             {
70                 return _expr;
71             }
72         }
73 
74         internal bool HasValue
75         {
76             get
77             {
78                 return (null != _expr);
79             }
80         }
81 
Bind(DataTable table)82         internal void Bind(DataTable table)
83         {
84             _table = table;
85 
86             if (table == null)
87                 return;
88 
89             if (_expr != null)
90             {
91                 Debug.Assert(_parsed, "Invalid calling order: Bind() before Parse()");
92                 List<DataColumn> list = new List<DataColumn>();
93                 _expr.Bind(table, list);
94                 _expr = _expr.Optimize();
95                 _table = table;
96                 _bound = true;
97                 _dependency = list.ToArray();
98             }
99         }
100 
DependsOn(DataColumn column)101         internal bool DependsOn(DataColumn column)
102         {
103             if (_expr != null)
104             {
105                 return _expr.DependsOn(column);
106             }
107             else
108             {
109                 return false;
110             }
111         }
112 
Evaluate()113         internal object Evaluate()
114         {
115             return Evaluate((DataRow)null, DataRowVersion.Default);
116         }
117 
Evaluate(DataRow row, DataRowVersion version)118         internal object Evaluate(DataRow row, DataRowVersion version)
119         {
120             object result;
121 
122             if (!_bound)
123             {
124                 Bind(_table);
125             }
126             if (_expr != null)
127             {
128                 result = _expr.Eval(row, version);
129                 // if the type is a SqlType (StorageType.Uri < _storageType), convert DBNull values.
130                 if (result != DBNull.Value || StorageType.Uri < _storageType)
131                 {
132                     // we need to convert the return value to the column.Type;
133                     try
134                     {
135                         if (StorageType.Object != _storageType)
136                         {
137                             result = SqlConvert.ChangeType2(result, _storageType, _dataType, _table.FormatProvider);
138                         }
139                     }
140                     catch (Exception e) when (ADP.IsCatchableExceptionType(e))
141                     {
142                         ExceptionBuilder.TraceExceptionForCapture(e);
143                         throw ExprException.DatavalueConvertion(result, _dataType, e);
144                     }
145                 }
146             }
147             else
148             {
149                 result = null;
150             }
151             return result;
152         }
153 
Evaluate(DataRow[] rows)154         internal object Evaluate(DataRow[] rows)
155         {
156             return Evaluate(rows, DataRowVersion.Default);
157         }
158 
159 
Evaluate(DataRow[] rows, DataRowVersion version)160         internal object Evaluate(DataRow[] rows, DataRowVersion version)
161         {
162             if (!_bound)
163             {
164                 Bind(_table);
165             }
166             if (_expr != null)
167             {
168                 List<int> recordList = new List<int>();
169                 foreach (DataRow row in rows)
170                 {
171                     if (row.RowState == DataRowState.Deleted)
172                         continue;
173                     if (version == DataRowVersion.Original && row._oldRecord == -1)
174                         continue;
175                     recordList.Add(row.GetRecordFromVersion(version));
176                 }
177                 int[] records = recordList.ToArray();
178                 return _expr.Eval(records);
179             }
180             else
181             {
182                 return DBNull.Value;
183             }
184         }
185 
Invoke(DataRow row, DataRowVersion version)186         public bool Invoke(DataRow row, DataRowVersion version)
187         {
188             if (_expr == null)
189                 return true;
190 
191             if (row == null)
192             {
193                 throw ExprException.InvokeArgument();
194             }
195             object val = _expr.Eval(row, version);
196             bool result;
197             try
198             {
199                 result = ToBoolean(val);
200             }
201             catch (EvaluateException)
202             {
203                 throw ExprException.FilterConvertion(Expression);
204             }
205             return result;
206         }
207 
GetDependency()208         internal DataColumn[] GetDependency()
209         {
210             Debug.Assert(_dependency != null, "GetDependencies: null, we should have created an empty list");
211             return _dependency;
212         }
213 
IsTableAggregate()214         internal bool IsTableAggregate()
215         {
216             if (_expr != null)
217                 return _expr.IsTableConstant();
218             else
219                 return false;
220         }
221 
IsUnknown(object value)222         internal static bool IsUnknown(object value)
223         {
224             return DataStorage.IsObjectNull(value);
225         }
226 
HasLocalAggregate()227         internal bool HasLocalAggregate()
228         {
229             if (_expr != null)
230                 return _expr.HasLocalAggregate();
231             else
232                 return false;
233         }
234 
HasRemoteAggregate()235         internal bool HasRemoteAggregate()
236         {
237             if (_expr != null)
238                 return _expr.HasRemoteAggregate();
239             else
240                 return false;
241         }
242 
ToBoolean(object value)243         internal static bool ToBoolean(object value)
244         {
245             if (IsUnknown(value))
246                 return false;
247             if (value is bool)
248                 return (bool)value;
249             if (value is SqlBoolean)
250             {
251                 return (((SqlBoolean)value).IsTrue);
252             }
253             //check for SqlString is not added, value for true and false should be given with String, not with SqlString
254             if (value is string)
255             {
256                 try
257                 {
258                     return bool.Parse((string)value);
259                 }
260                 catch (Exception e) when (ADP.IsCatchableExceptionType(e))
261                 {
262                     ExceptionBuilder.TraceExceptionForCapture(e);
263                     throw ExprException.DatavalueConvertion(value, typeof(bool), e);
264                 }
265             }
266 
267             throw ExprException.DatavalueConvertion(value, typeof(bool), null);
268         }
269     }
270 }
271