/// <summary> /// WhereContains overload to compare String8 rows as a single block, if available. /// This is only available when comparing to a constant and before any other row filtering operations. /// </summary> /// <param name="left">Raw String8 byte[] and int[] for current rows</param> /// <param name="rightValue">Constant Value to compare to</param> /// <param name="vector">BitVector to record matches to</param> public void WhereContains(String8Raw left, String8 rightValue, BitVector vector) { if (rightValue.Length == 0) return; String8 all = new String8((byte[])left.Bytes.Array, 0, left.Bytes.Selector.EndIndexExclusive); Allocator.AllocateToSize(ref _indicesBuffer, 1024); int startRowIndex = left.Positions.Selector.StartIndexInclusive; int nextRowIndex = startRowIndex; int endRowIndex = left.Positions.Selector.EndIndexExclusive; int nextByteIndex = left.Bytes.Selector.StartIndexInclusive; int rightLength = rightValue.Length; int[] positions = (int[])left.Positions.Array; bool includesFirstString = (left.Selector.StartIndexInclusive == 0); int firstStringStart = (includesFirstString ? 0 : positions[left.Positions.Index(0)]); int positionOffset = left.Positions.Index((includesFirstString ? 0 : 1)); int textOffset = firstStringStart - left.Bytes.Index(0); while (true) { // Find a batch of matches int countFound; if (s_IndexOfAllNative != null) { countFound = s_IndexOfAllNative(all.Array, nextByteIndex, all.Length - nextByteIndex, rightValue.Array, rightValue.Index, rightValue.Length, true, _indicesBuffer); } else { countFound = all.IndexOfAll(rightValue, nextByteIndex, true, _indicesBuffer); } // Map the indices found to rows int countMatched = 0; // If matching the very first row, look for the full match within it if (countFound > 0 && nextRowIndex == startRowIndex) { if (_indicesBuffer[0] < (positions[nextRowIndex] - textOffset)) { if (_indicesBuffer[0] + rightLength <= (positions[nextRowIndex] - textOffset)) { vector.Set(0); countMatched++; } } nextRowIndex++; } for (; countMatched < countFound; ++countMatched) { int indexToFind = _indicesBuffer[countMatched] + rightLength + textOffset; for (; nextRowIndex < endRowIndex; ++nextRowIndex) { // If the next match is before this row... if (indexToFind <= positions[nextRowIndex]) { // If it's fully within the previous row, add it if (indexToFind - rightLength >= positions[nextRowIndex - 1]) { vector.Set(nextRowIndex - startRowIndex - 1); } // Look for the next match (in this row again) break; } } // If we're out of rows, stop if (nextRowIndex == endRowIndex) break; } // If there were matches left, they must be in the last row if (countMatched != countFound) { vector.Set(nextRowIndex - startRowIndex - 1); } // Find the next index to search for matches from if (countFound < _indicesBuffer.Length) break; nextByteIndex = _indicesBuffer[countFound - 1] + 1; } }
public TermExpression(IXTable source, IXColumn left, CompareOperator op, IXColumn right) { _evaluate = EvaluateNormal; // Save arguments as-is for ToString() _left = left; _cOp = op; _right = right; // Disallow constant <op> constant [likely error not wrapping column name] if (_left is ConstantColumn && _right is ConstantColumn) { throw new ArgumentException($"({left} {op.ToQueryForm()} {right}) is comparing two constants. Wrap [ColumnNames] in braces."); } // If the left side is a constant and the operator can be swapped, move it to the right side. // Comparers can check if the right side is constant and run a faster loop when that's the case. if (_left.IsConstantColumn() && !(_right.IsConstantColumn())) { if (op.TryInvertCompareOperator(out op)) { _left = right; _right = left; } } // Disallow unquoted constants used as strings if (_right.IsConstantColumn() && _left.ColumnDetails.Type == typeof(String8) && _right.ColumnDetails.Type == typeof(String8)) { ConstantColumn cRight = _right as ConstantColumn; if (cRight != null && !cRight.IsNull && cRight.WasUnwrappedLiteral) { throw new ArgumentException($"{right} is compared to a string, but is unquoted. Strings must be quoted."); } } // Convert the right side to the left side type if required // This means constants will always be casted to the other side type. if (_left.ColumnDetails.Type != _right.ColumnDetails.Type) { _right = CastedColumn.Build(source, _right, _left.ColumnDetails.Type, ValueKinds.Invalid); } // Get the left and right getters _leftGetter = _left.CurrentGetter(); _rightGetter = _right.CurrentGetter(); // Null comparison is generic if (_right.IsNullConstant()) { if (op == CompareOperator.Equal) { _comparer = WhereIsNull; } else if (op == CompareOperator.NotEqual) { _comparer = WhereIsNotNull; } else { throw new ArgumentException($"Only equals and not equals operators are supported against null."); } } else if (_left.IsNullConstant()) { _left = _right; if (op == CompareOperator.Equal) { _comparer = WhereIsNull; } else if (op == CompareOperator.NotEqual) { _comparer = WhereIsNotNull; } else { throw new ArgumentException($"Only equals and not equals operators are supported against null."); } } else { // Get a comparer which can compare the values _comparer = TypeProviderFactory.Get(left.ColumnDetails.Type).TryGetComparer(op); if (_comparer == null) { throw new ArgumentException($"No comparer found for type {left.ColumnDetails.Type.Name}."); } } // Optimize Enum to Constant comparisons to use the underlying indices if (_left.IsEnumColumn() && _right.IsConstantColumn()) { // Get an optimized comparer against the indices rather than values IXColumn replacedRight = _right; _comparer = SetComparer.ConvertToEnumIndexComparer(_left, _comparer, ref replacedRight, source); // Get the indices on the left side _leftGetter = _left.IndicesCurrentGetter(); // Use the updated value for the right side _rightGetter = replacedRight.CurrentGetter(); } // Allow String8 to constant Contains queries to compare on the raw byte[] and int[] if (op == CompareOperator.Contains && _right.IsConstantColumn() && _left.ColumnDetails.Type == typeof(String8) && !_left.IsEnumColumn()) { Func <object> rawGetter = _left.ComponentGetter(ColumnComponent.String8Raw); if (rawGetter != null) { String8 rightValue = (String8)_right.ValuesGetter()().Array.GetValue(0); String8Comparer string8Comparer = new String8Comparer(); _evaluate = (vector) => { String8Raw raw = (String8Raw)rawGetter(); string8Comparer.WhereContains(raw, rightValue, vector); }; } } }