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