示例#1
0
        private bool Operator(bool fieldRequired, out QueryExpression op)
        {
            OperatorType    type;
            FieldExpression field = null;

            if (Scanner.TryScan("true"))
            {
                op = new TrueExpression();
                return(true);
            }
            else
            {
                if (fieldRequired && Field(out field) == false)
                {
                    op = null;
                    return(false);
                }

                if (Scanner.TryScan(OperatorStartMatches, out var match) == false)
                {
                    if (fieldRequired == false)
                    {
                        op = null;
                        return(false);
                    }
                    ThrowParseException("Invalid operator expected any of (In, Between, =, <, >, <=, >=)");
                }


                switch (match)
                {
                case "<":
                    type = OperatorType.LessThan;
                    break;

                case ">":
                    type = OperatorType.GreaterThan;
                    break;

                case "<=":
                    type = OperatorType.LessThanEqual;
                    break;

                case ">=":
                    type = OperatorType.GreaterThanEqual;
                    break;

                case "=":
                case "==":
                    type = OperatorType.Equal;
                    break;

                case "!=":
                case "<>":
                    type = OperatorType.NotEqual;
                    break;

                case "BETWEEN":
                    if (Value(out var fst) == false)
                    {
                        ThrowParseException("parsing Between, expected value (1st)");
                    }
                    if (Scanner.TryScan("AND") == false)
                    {
                        ThrowParseException("parsing Between, expected AND");
                    }
                    if (Value(out var snd) == false)
                    {
                        ThrowParseException("parsing Between, expected value (2nd)");
                    }

                    if (fst.Type != snd.Type)
                    {
                        ThrowQueryException(
                            $"Invalid Between expression, values must have the same type but got {fst.Type} and {snd.Type}");
                    }

                    op = new BetweenExpression(field, fst, snd);
                    return(true);

                case "IN":
                case "ALL IN":
                    if (Scanner.TryScan('(') == false)
                    {
                        ThrowParseException("parsing In, expected '('");
                    }

                    var list = new List <QueryExpression>();
                    do
                    {
                        if (Scanner.TryScan(')'))
                        {
                            break;
                        }

                        if (list.Count != 0)
                        {
                            if (Scanner.TryScan(',') == false)
                            {
                                ThrowParseException("parsing In expression, expected ','");
                            }
                        }

                        if (Value(out var inVal) == false)
                        {
                            ThrowParseException("parsing In, expected a value");
                        }

                        if (list.Count > 0)
                        {
                            if (list[0].Type != inVal.Type)
                            {
                                ThrowQueryException(
                                    $"Invalid In expression, all values must have the same type, expected {list[0].Type} but got {inVal.Type}");
                            }
                        }
                        list.Add(inVal);
                    } while (true);

                    op = new InExpression(field, list, match == "ALL IN");

                    return(true);

                case "(":
                    var isMethod = Method(field, out var method);
                    op = method;

                    if (isMethod && Operator(false, out var methodOperator))
                    {
                        if (methodOperator is BinaryExpression be)
                        {
                            be.Left = method;
                            op      = be;
                            return(true);
                        }

                        if (methodOperator is InExpression ie)
                        {
                            ie.Source = method;
                            op        = ie;
                            return(true);
                        }
                        if (methodOperator is BetweenExpression between)
                        {
                            between.Source = method;
                            op             = between;
                            return(true);
                        }
                        if (methodOperator is MethodExpression me)
                        {
                            op = me;
                            return(true);
                        }
                        ThrowParseException("Unexpected operator after method call: " + methodOperator);
                    }

                    return(isMethod);

                default:
                    op = null;
                    return(false);
                }
            }

            if (Value(out var val))
            {
                op = new BinaryExpression(field, val, type);
                return(true);
            }
            if (Operator(true, out var op2))
            {
                op = new BinaryExpression(field, op2, type);
                return(true);
            }
            op = null;
            return(false);
        }
示例#2
0
        private bool Binary(out QueryExpression op)
        {
            switch (_state)
            {
            case NextTokenOptions.Parenthesis:
                if (Parenthesis(out op) == false)
                {
                    return(false);
                }
                break;

            case NextTokenOptions.BinaryOp:
                _state = NextTokenOptions.Parenthesis;
                if (Operator(true, out op) == false)
                {
                    return(false);
                }
                break;

            default:
                op = null;
                return(false);
            }


            if (Scanner.TryScan(BinaryOperators, out var found) == false)
            {
                return(true); // found simple
            }
            var negate = Scanner.TryScan("NOT");
            var type   = found == "OR"
                ? (negate ? OperatorType.OrNot : OperatorType.Or)
                : (negate ? OperatorType.AndNot : OperatorType.And);

            _state = NextTokenOptions.Parenthesis;

            var parenthesis = Scanner.TryPeek('(');

            if (Binary(out var right) == false)
            {
                ThrowParseException($"Failed to find second part of {type} expression");
            }

            if (parenthesis == false)
            {
                // if the other arg isn't parenthesis, use operator precedence rules
                // to re-write the query
                switch (type)
                {
                case OperatorType.And:
                case OperatorType.AndNot:
                    if (right is BinaryExpression rightOp)
                    {
                        switch (rightOp.Operator)
                        {
                        case OperatorType.AndNot:
                        case OperatorType.OrNot:
                        case OperatorType.Or:
                        case OperatorType.And:

                            rightOp.Left = new BinaryExpression(op, rightOp.Left, type);
                            op           = right;
                            return(true);
                        }
                    }
                    break;
                }
            }


            op = new BinaryExpression(op, right, type);

            return(true);
        }
示例#3
0
        private bool Operator(bool fieldRequired, out QueryExpression op)
        {
            OperatorType type;
            FieldToken   field = null;

            if (Scanner.TryScan("true"))
            {
                type = OperatorType.True;
            }
            else
            {
                if (fieldRequired && Field(out field) == false)
                {
                    op = null;
                    return(false);
                }

                if (Scanner.TryScan(OperatorStartMatches, out var match) == false)
                {
                    if (fieldRequired == false)
                    {
                        op = null;
                        return(false);
                    }
                    ThrowParseException("Invalid operator expected any of (In, Between, =, <, >, <=, >=)");
                }

                switch (match)
                {
                case "<":
                    type = OperatorType.LessThan;
                    break;

                case ">":
                    type = OperatorType.GreaterThan;
                    break;

                case "<=":
                    type = OperatorType.LessThanEqual;
                    break;

                case ">=":
                    type = OperatorType.GreaterThanEqual;
                    break;

                case "=":
                case "==":
                    type = OperatorType.Equal;
                    break;

                case "!=":
                case "<>":
                    type = OperatorType.NotEqual;
                    break;

                case "BETWEEN":
                    type = OperatorType.Between;
                    break;

                case "IN":
                    type = OperatorType.In;
                    break;

                case "ALL IN":
                    type = OperatorType.AllIn;
                    break;

                case "(":
                    type = OperatorType.Method;
                    break;

                default:
                    op = null;
                    return(false);
                }
            }

            switch (type)
            {
            case OperatorType.True:
                op = new QueryExpression
                {
                    Type = type
                };
                return(true);

            case OperatorType.Method:
                var method = Method(field, op: out op);

                if (method && Operator(false, out var methodOperator))
                {
                    if (op.Arguments == null)
                    {
                        op.Arguments = new List <object>();
                    }

                    op.Arguments.Add(methodOperator);
                    return(true);
                }

                return(method);

            case OperatorType.Between:
                if (Value(out var fst) == false)
                {
                    ThrowParseException("parsing Between, expected value (1st)");
                }
                if (Scanner.TryScan("AND") == false)
                {
                    ThrowParseException("parsing Between, expected AND");
                }
                if (Value(out var snd) == false)
                {
                    ThrowParseException("parsing Between, expected value (2nd)");
                }

                if (fst.Type != snd.Type)
                {
                    ThrowQueryException(
                        $"Invalid Between expression, values must have the same type but got {fst.Type} and {snd.Type}");
                }

                op = new QueryExpression
                {
                    Field  = field,
                    Type   = OperatorType.Between,
                    First  = fst,
                    Second = snd
                };
                return(true);

            case OperatorType.In:
            case OperatorType.AllIn:
                if (Scanner.TryScan('(') == false)
                {
                    ThrowParseException("parsing In, expected '('");
                }

                var list = new List <ValueToken>();
                do
                {
                    if (Scanner.TryScan(')'))
                    {
                        break;
                    }

                    if (list.Count != 0)
                    {
                        if (Scanner.TryScan(',') == false)
                        {
                            ThrowParseException("parsing In expression, expected ','");
                        }
                    }

                    if (Value(out var inVal) == false)
                    {
                        ThrowParseException("parsing In, expected a value");
                    }

                    if (list.Count > 0)
                    {
                        if (list[0].Type != inVal.Type)
                        {
                            ThrowQueryException(
                                $"Invalid In expression, all values must have the same type, expected {list[0].Type} but got {inVal.Type}");
                        }
                    }
                    list.Add(inVal);
                } while (true);

                op = new QueryExpression
                {
                    Field  = field,
                    Type   = type,
                    Values = list
                };

                return(true);

            default:
                if (Value(out var val) == false)
                {
                    ThrowParseException($"parsing {type} expression, expected a value (operators only work on scalar / parameters values)");
                }

                op = new QueryExpression
                {
                    Field = field,
                    Type  = type,
                    Value = val
                };
                return(true);
            }
        }
示例#4
0
        private (FieldToken From, FieldToken Alias, QueryExpression Filter, bool Index) FromClause()
        {
            if (Scanner.TryScan("FROM") == false)
            {
                ThrowParseException("Expected FROM clause");
            }

            FieldToken      field;
            QueryExpression filter = null;
            bool            index  = false;
            bool            isQuoted;

            if (Scanner.TryScan("INDEX"))
            {
                isQuoted = false;
                if (!Scanner.Identifier() && !(isQuoted = Scanner.String()))
                {
                    ThrowParseException("Expected FROM INDEX source");
                }

                field = new FieldToken
                {
                    TokenLength = Scanner.TokenLength,
                    TokenStart  = Scanner.TokenStart,
                    EscapeChars = Scanner.EscapeChars,
                    IsQuoted    = isQuoted
                };

                index = true;
            }
            else
            {
                isQuoted = false;
                if (!Scanner.Identifier() && !(isQuoted = Scanner.String()))
                {
                    ThrowParseException("Expected FROM source");
                }

                field = new FieldToken
                {
                    TokenLength = Scanner.TokenLength,
                    TokenStart  = Scanner.TokenStart,
                    EscapeChars = Scanner.EscapeChars,
                    IsQuoted    = isQuoted
                };

                if (Scanner.TryScan('(')) // FROM  Collection ( filter )
                {
                    if (Expression(out filter) == false)
                    {
                        ThrowParseException("Expected filter in filtered FORM clause");
                    }

                    if (Scanner.TryScan(')') == false)
                    {
                        ThrowParseException("Expected closing parenthesis in filtered FORM clause after filter");
                    }
                }
            }


            Alias(false, out var alias);

            return(field, alias, filter, index);
        }
示例#5
0
        public Query Parse(QueryType queryType = QueryType.Select)
        {
            var q = new Query
            {
                QueryText = Scanner.Input
            };

            while (Scanner.TryScan("DECLARE"))
            {
                var(name, func) = DeclaredFunction();

                if (q.TryAddFunction(name, QueryExpression.Extract(Scanner.Input, func)) == false)
                {
                    ThrowParseException(name + " function was declared multiple times");
                }
            }

            q.From = FromClause();

            if (Scanner.TryScan("GROUP BY"))
            {
                q.GroupBy = GroupBy();
            }

            if (Scanner.TryScan("WHERE") && Expression(out q.Where) == false)
            {
                ThrowParseException("Unable to parse WHERE clause");
            }

            if (Scanner.TryScan("ORDER BY"))
            {
                q.OrderBy = OrderBy();
            }

            if (Scanner.TryScan("LOAD"))
            {
                q.Load = SelectClauseExpressions("LOAD", false);
            }

            switch (queryType)
            {
            case QueryType.Select:
                if (Scanner.TryScan("SELECT"))
                {
                    q.Select = SelectClause("SELECT", q);
                }
                if (Scanner.TryScan("INCLUDE"))
                {
                    q.Include = IncludeClause();
                }
                break;

            case QueryType.Update:
                if (Scanner.TryScan("UPDATE") == false)
                {
                    ThrowParseException("Update operations must end with UPDATE clause");
                }

                var functionStart = Scanner.Position;
                if (Scanner.FunctionBody() == false)
                {
                    ThrowParseException("Update clause must have a single function body");
                }

                q.UpdateBody = new ValueToken
                {
                    Type        = ValueTokenType.String,
                    TokenStart  = functionStart,
                    TokenLength = Scanner.Position - functionStart
                };
                break;

            default:
                ThrowUnknownQueryType(queryType);
                break;
            }

            if (Scanner.AtEndOfInput() == false)
            {
                ThrowParseException("Expected end of query");
            }

            return(q);
        }
示例#6
0
        private List <(QueryExpression Expression, OrderByFieldType OrderingType, bool Ascending)> OrderBy()
        {
            var orderBy = new List <(QueryExpression Expression, OrderByFieldType OrderingType, bool Ascending)>();

            do
            {
                if (Field(out var field) == false)
                {
                    ThrowParseException("Unable to get field for ORDER BY");
                }

                OrderByFieldType type = OrderByFieldType.Implicit;

                QueryExpression op;
                if (Scanner.TryScan('('))
                {
                    if (Method(field, out op) == false)
                    {
                        ThrowParseException($"Unable to parse method call {QueryExpression.Extract(Scanner.Input, field)}for ORDER BY");
                    }
                }
                else
                {
                    op = new QueryExpression
                    {
                        Field = field,
                        Type  = OperatorType.Field
                    };
                }

                if (Scanner.TryScan("AS") && Scanner.TryScan(OrderByAsOptions, out var asMatch))
                {
                    switch (asMatch)
                    {
                    case "string":
                        type = OrderByFieldType.String;
                        break;

                    case "long":
                        type = OrderByFieldType.Long;
                        break;

                    case "double":
                        type = OrderByFieldType.Double;
                        break;

                    case "alphaNumeric":
                        type = OrderByFieldType.AlphaNumeric;
                        break;
                    }
                }

                var asc = true;

                if (Scanner.TryScan(OrderByOptions, out var match))
                {
                    if (match == "DESC" || match == "DESCENDING")
                    {
                        asc = false;
                    }
                }

                orderBy.Add((op, type, asc));

                if (Scanner.TryScan(",") == false)
                {
                    break;
                }
            } while (true);
            return(orderBy);
        }