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); }
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); }
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); } }
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); }
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); }
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); }