| File: System\Data\Filter\DataExpression.cs | Web Access |
| Project: src\runtime\src\libraries\System.Data.Common\src\System.Data.Common.csproj (System.Data.Common) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Data.Common; using System.Data.SqlTypes; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace System.Data { internal sealed class DataExpression : IFilter { internal string? _originalExpression; // original, unoptimized string private readonly bool _parsed; private bool _bound; private ExpressionNode? _expr; private DataTable? _table; private readonly StorageType _storageType; private readonly Type? _dataType; // This set if the expression is part of ExpressionCoulmn private DataColumn[] _dependency = Array.Empty<DataColumn>(); [RequiresUnreferencedCode("Members of types used in the expression might be trimmed")] internal DataExpression(DataTable? table, string? expression) : this(table, expression, null) { } [RequiresUnreferencedCode("Members of types used in the expression might be trimmed")] internal DataExpression(DataTable? table, string? expression, Type? type) { ExpressionParser parser = new ExpressionParser(table); parser.LoadExpression(expression); _originalExpression = expression; _expr = null; // Note: nobody seems to pass a null expression in the codebase if (expression != null) { _storageType = DataStorage.GetStorageType(type); if (_storageType == StorageType.BigInteger) { throw ExprException.UnsupportedDataType(type!); } _dataType = type; _expr = parser.Parse(); _parsed = true; if (_expr != null && table != null) { Bind(table); } else { _bound = false; } } } internal string Expression { get { return _originalExpression ?? ""; // CONSIDER: return optimized expression here (if bound) } } internal ExpressionNode? ExpressionNode { get { return _expr; } } internal bool HasValue { get { return (null != _expr); } } internal void Bind(DataTable? table) { _table = table; if (table == null) return; if (_expr != null) { Debug.Assert(_parsed, "Invalid calling order: Bind() before Parse()"); List<DataColumn> list = new List<DataColumn>(); _expr.Bind(table, list); _expr = _expr.Optimize(); _table = table; _bound = true; _dependency = list.ToArray(); } } internal bool DependsOn(DataColumn column) { if (_expr != null) { return _expr.DependsOn(column); } else { return false; } } internal object Evaluate() { return Evaluate((DataRow?)null, DataRowVersion.Default); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Constructors taking expression are marked as unsafe")] internal object Evaluate(DataRow? row, DataRowVersion version) { object? result; if (!_bound) { Bind(_table); } // Note: _expr is always non-null in the current codebase if (_expr != null) { result = _expr.Eval(row, version); // if the type is a SqlType (StorageType.Uri < _storageType), convert DBNull values. if (result != DBNull.Value || StorageType.Uri < _storageType) { // we need to convert the return value to the column.Type; try { if (StorageType.Object != _storageType) { // TODO: _dataType can be null, probably a bug result = SqlConvert.ChangeType2(result, _storageType, _dataType!, _table!.FormatProvider); } } catch (Exception e) when (ADP.IsCatchableExceptionType(e)) { ExceptionBuilder.TraceExceptionForCapture(e); throw ExprException.DatavalueConversion(result, _dataType!); } } } else { result = null; } return result!; } internal object Evaluate(DataRow[] rows) { return Evaluate(rows, DataRowVersion.Default); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Constructors taking expression are marked as unsafe")] internal object Evaluate(DataRow[] rows, DataRowVersion version) { if (!_bound) { Bind(_table); } if (_expr != null) { List<int> recordList = new List<int>(); foreach (DataRow row in rows) { if (row.RowState == DataRowState.Deleted) continue; if (version == DataRowVersion.Original && row._oldRecord == -1) continue; recordList.Add(row.GetRecordFromVersion(version)); } int[] records = recordList.ToArray(); return _expr.Eval(records); } else { return DBNull.Value; } } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Constructors taking expression are marked as unsafe")] public bool Invoke(DataRow row, DataRowVersion version) { if (_expr == null) return true; if (row == null) { throw ExprException.InvokeArgument(); } object val = _expr.Eval(row, version); bool result; try { result = ToBoolean(val); } catch (EvaluateException) { throw ExprException.FilterConversion(Expression); } return result; } internal DataColumn[] GetDependency() { Debug.Assert(_dependency != null, "GetDependencies: null, we should have created an empty list"); return _dependency; } internal bool IsTableAggregate() { if (_expr != null) return _expr.IsTableConstant(); else return false; } internal static bool IsUnknown(object value) { return DataStorage.IsObjectNull(value); } internal bool HasLocalAggregate() { if (_expr != null) return _expr.HasLocalAggregate(); else return false; } internal bool HasRemoteAggregate() { if (_expr != null) return _expr.HasRemoteAggregate(); else return false; } internal static bool ToBoolean(object value) { if (IsUnknown(value)) return false; if (value is bool) return (bool)value; if (value is SqlBoolean) { return (((SqlBoolean)value).IsTrue); } //check for SqlString is not added, value for true and false should be given with String, not with SqlString if (value is string) { try { return bool.Parse((string)value); } catch (Exception e) when (ADP.IsCatchableExceptionType(e)) { ExceptionBuilder.TraceExceptionForCapture(e); throw ExprException.DatavalueConversion(value, typeof(bool)); } } throw ExprException.DatavalueConversion(value, typeof(bool)); } } }