Exemple #1
0
 public Task Write(
     NpgsqlTsQueryFollowedBy value,
     NpgsqlWriteBuffer buf,
     NpgsqlLengthCache lengthCache,
     NpgsqlParameter parameter,
     bool async)
 => Write((NpgsqlTsQuery)value, buf, lengthCache, parameter, async);
Exemple #2
0
        /// <summary>
        /// Parses a tsquery in PostgreSQL's text format.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static NpgsqlTsQuery Parse(string value)
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            var valStack = new Stack <NpgsqlTsQuery>();
            var opStack  = new Stack <NpgsqlTsQueryOperator>();

            var sb             = new StringBuilder();
            var pos            = 0;
            var expectingBinOp = false;

            var lastFollowedByOpDistance = -1;

NextToken:
            if (pos >= value.Length)
            {
                goto Finish;
            }
            var ch = value[pos++];

            if (ch == '\'')
            {
                goto WaitEndComplex;
            }
            if ((ch == ')' || ch == '|' || ch == '&') && !expectingBinOp || (ch == '(' || ch == '!') && expectingBinOp)
            {
                throw new FormatException("Syntax error in tsquery. Unexpected token.");
            }

            if (ch == '<')
            {
                var endOfOperatorConsumed = false;
                var sbCurrentLength       = sb.Length;

                while (pos < value.Length)
                {
                    var c = value[pos++];
                    if (c == '>')
                    {
                        endOfOperatorConsumed = true;
                        break;
                    }

                    sb.Append(c);
                }

                if (sb.Length == sbCurrentLength || !endOfOperatorConsumed)
                {
                    throw new FormatException("Syntax error in tsquery. Malformed 'followed by' operator.");
                }

                var followedByOpDistanceString = sb.ToString(sbCurrentLength, sb.Length - sbCurrentLength);
                if (followedByOpDistanceString == "-")
                {
                    lastFollowedByOpDistance = 1;
                }
                else if (!int.TryParse(followedByOpDistanceString, out lastFollowedByOpDistance) ||
                         lastFollowedByOpDistance < 0)
                {
                    throw new FormatException("Syntax error in tsquery. Malformed distance in 'followed by' operator.");
                }

                sb.Length -= followedByOpDistanceString.Length;
            }

            if (ch == '(' || ch == '!' || ch == '&' || ch == '<')
            {
                opStack.Push(new NpgsqlTsQueryOperator(ch, lastFollowedByOpDistance));
                expectingBinOp           = false;
                lastFollowedByOpDistance = 0;
                goto NextToken;
            }

            if (ch == '|')
            {
                if (opStack.Count > 0 && opStack.Peek() == '|')
                {
                    if (valStack.Count < 2)
                    {
                        throw new FormatException("Syntax error in tsquery");
                    }
                    var right = valStack.Pop();
                    var left  = valStack.Pop();
                    valStack.Push(new NpgsqlTsQueryOr(left, right));
                    // Implicit pop and repush |
                }
                else
                {
                    opStack.Push('|');
                }
                expectingBinOp = false;
                goto NextToken;
            }

            if (ch == ')')
            {
                while (opStack.Count > 0 && opStack.Peek() != '(')
                {
                    if (valStack.Count < 2 || opStack.Peek() == '!')
                    {
                        throw new FormatException("Syntax error in tsquery");
                    }

                    var right = valStack.Pop();
                    var left  = valStack.Pop();

                    var tsOp = opStack.Pop();
                    switch (tsOp)
                    {
                    case '&':
                        valStack.Push(new NpgsqlTsQueryAnd(left, right));
                        break;

                    case '|':
                        valStack.Push(new NpgsqlTsQueryOr(left, right));
                        break;

                    case '<':
                        valStack.Push(new NpgsqlTsQueryFollowedBy(left, tsOp.FollowedByDistance, right));
                        break;

                    default:
                        throw new FormatException("Syntax error in tsquery");
                    }
                }
                if (opStack.Count == 0)
                {
                    throw new FormatException("Syntax error in tsquery: closing parenthesis without an opening parenthesis");
                }
                opStack.Pop();
                goto PushedVal;
            }

            if (ch == ':')
            {
                throw new FormatException("Unexpected : while parsing tsquery");
            }

            if (char.IsWhiteSpace(ch))
            {
                goto NextToken;
            }

            pos--;
            if (expectingBinOp)
            {
                throw new FormatException("Unexpected lexeme while parsing tsquery");
            }
            // Proceed to WaitEnd

WaitEnd:
            if (pos >= value.Length || char.IsWhiteSpace(ch = value[pos]) || ch == '!' || ch == '&' || ch == '|' || ch == '(' || ch == ')')
            {
                valStack.Push(new NpgsqlTsQueryLexeme(sb.ToString()));
                goto PushedVal;
            }
            pos++;
            if (ch == ':')
            {
                valStack.Push(new NpgsqlTsQueryLexeme(sb.ToString()));
                sb.Clear();
                goto InWeightInfo;
            }
            if (ch == '\\')
            {
                if (pos >= value.Length)
                {
                    throw new FormatException(@"Unexpected \ in end of value");
                }
                ch = value[pos++];
            }
            sb.Append(ch);
            goto WaitEnd;

WaitEndComplex:
            if (pos >= value.Length)
            {
                throw new FormatException("Missing terminating ' in string literal");
            }
            ch = value[pos++];
            if (ch == '\'')
            {
                if (pos < value.Length && value[pos] == '\'')
                {
                    ch = '\'';
                    pos++;
                }
                else
                {
                    valStack.Push(new NpgsqlTsQueryLexeme(sb.ToString()));
                    if (pos < value.Length && value[pos] == ':')
                    {
                        pos++;
                        goto InWeightInfo;
                    }
                    goto PushedVal;
                }
            }
            if (ch == '\\')
            {
                if (pos >= value.Length)
                {
                    throw new FormatException(@"Unexpected \ in end of value");
                }
                ch = value[pos++];
            }
            sb.Append(ch);
            goto WaitEndComplex;


InWeightInfo:
            if (pos >= value.Length)
            {
                goto Finish;
            }
            ch = value[pos];
            if (ch == '*')
            {
                ((NpgsqlTsQueryLexeme)valStack.Peek()).IsPrefixSearch = true;
            }
            else if (ch == 'a' || ch == 'A')
            {
                ((NpgsqlTsQueryLexeme)valStack.Peek()).Weights |= NpgsqlTsQueryLexeme.Weight.A;
            }
            else if (ch == 'b' || ch == 'B')
            {
                ((NpgsqlTsQueryLexeme)valStack.Peek()).Weights |= NpgsqlTsQueryLexeme.Weight.B;
            }
            else if (ch == 'c' || ch == 'C')
            {
                ((NpgsqlTsQueryLexeme)valStack.Peek()).Weights |= NpgsqlTsQueryLexeme.Weight.C;
            }
            else if (ch == 'd' || ch == 'D')
            {
                ((NpgsqlTsQueryLexeme)valStack.Peek()).Weights |= NpgsqlTsQueryLexeme.Weight.D;
            }
            else
            {
                goto PushedVal;
            }
            pos++;
            goto InWeightInfo;

PushedVal:
            sb.Clear();
            var processTightBindingOperator = true;

            while (opStack.Count > 0 && processTightBindingOperator)
            {
                var tsOp = opStack.Peek();
                switch (tsOp)
                {
                case '&':
                    if (valStack.Count < 2)
                    {
                        throw new FormatException("Syntax error in tsquery");
                    }
                    var andRight = valStack.Pop();
                    var andLeft  = valStack.Pop();
                    valStack.Push(new NpgsqlTsQueryAnd(andLeft, andRight));
                    opStack.Pop();
                    break;

                case '!':
                    if (valStack.Count == 0)
                    {
                        throw new FormatException("Syntax error in tsquery");
                    }
                    valStack.Push(new NpgsqlTsQueryNot(valStack.Pop()));
                    opStack.Pop();
                    break;

                case '<':
                    if (valStack.Count < 2)
                    {
                        throw new FormatException("Syntax error in tsquery");
                    }
                    var followedByRight = valStack.Pop();
                    var followedByLeft  = valStack.Pop();
                    valStack.Push(
                        new NpgsqlTsQueryFollowedBy(
                            followedByLeft,
                            tsOp.FollowedByDistance,
                            followedByRight));
                    opStack.Pop();
                    break;

                default:
                    processTightBindingOperator = false;
                    break;
                }
            }
            expectingBinOp = true;
            goto NextToken;

Finish:
            while (opStack.Count > 0)
            {
                if (valStack.Count < 2)
                {
                    throw new FormatException("Syntax error in tsquery");
                }

                var right = valStack.Pop();
                var left  = valStack.Pop();

                NpgsqlTsQuery query;
                var           tsOp = opStack.Pop();
                switch (tsOp)
                {
                case '&':
                    query = new NpgsqlTsQueryAnd(left, right);
                    break;

                case '|':
                    query = new NpgsqlTsQueryOr(left, right);
                    break;

                case '<':
                    query = new NpgsqlTsQueryFollowedBy(left, tsOp.FollowedByDistance, right);
                    break;

                default:
                    throw new FormatException("Syntax error in tsquery");
                }

                valStack.Push(query);
            }
            if (valStack.Count != 1)
            {
                throw new FormatException("Syntax error in tsquery");
            }
            return(valStack.Pop());
        }
Exemple #3
0
        public override async ValueTask <NpgsqlTsQuery> Read(NpgsqlReadBuffer buf, int len, bool async, FieldDescription fieldDescription = null)
        {
            await buf.Ensure(4, async);

            var numTokens = buf.ReadInt32();

            if (numTokens == 0)
            {
                return(new NpgsqlTsQueryEmpty());
            }

            _nodes = new Stack <Tuple <NpgsqlTsQuery, int> >();
            len   -= 4;

            for (var tokenPos = 0; tokenPos < numTokens; tokenPos++)
            {
                await buf.Ensure(Math.Min(len, MaxSingleTokenBytes), async);

                var readPos = buf.ReadPosition;

                var isOper = buf.ReadByte() == 2;
                if (isOper)
                {
                    var operKind = (NpgsqlTsQuery.NodeKind)buf.ReadByte();
                    if (operKind == NpgsqlTsQuery.NodeKind.Not)
                    {
                        var node = new NpgsqlTsQueryNot(null);
                        InsertInTree(node);
                        _nodes.Push(new Tuple <NpgsqlTsQuery, int>(node, 0));
                    }
                    else
                    {
                        NpgsqlTsQuery node;
                        switch (operKind)
                        {
                        case NpgsqlTsQuery.NodeKind.And:
                            node = new NpgsqlTsQueryAnd(null, null);
                            break;

                        case NpgsqlTsQuery.NodeKind.Or:
                            node = new NpgsqlTsQueryOr(null, null);
                            break;

                        case NpgsqlTsQuery.NodeKind.Phrase:
                            var distance = buf.ReadInt16();
                            node = new NpgsqlTsQueryFollowedBy(null, distance, null);
                            break;

                        default:
                            throw new InvalidOperationException($"Internal Npgsql bug: unexpected value {operKind} of enum {nameof(NpgsqlTsQuery.NodeKind)}. Please file a bug.");
                        }

                        InsertInTree(node);

                        _nodes.Push(new Tuple <NpgsqlTsQuery, int>(node, 2));
                        _nodes.Push(new Tuple <NpgsqlTsQuery, int>(node, 1));
                    }
                }
                else
                {
                    var weight = (NpgsqlTsQueryLexeme.Weight)buf.ReadByte();
                    var prefix = buf.ReadByte() != 0;
                    var str    = buf.ReadNullTerminatedString();
                    InsertInTree(new NpgsqlTsQueryLexeme(str, weight, prefix));
                }

                len -= buf.ReadPosition - readPos;
            }

            if (_nodes.Count != 0)
            {
                throw new InvalidOperationException("Internal Npgsql bug, please report.");
            }

            return(_value);
        }
Exemple #4
0
 public int ValidateAndGetLength(NpgsqlTsQueryFollowedBy value, ref NpgsqlLengthCache lengthCache, NpgsqlParameter parameter)
 => ValidateAndGetLength((NpgsqlTsQuery)value, ref lengthCache, parameter);