Beispiel #1
0
        /// <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);
                    };
                }
            }
        }