Example #1
0
    def testApplication(self):
        self.assertQueryMatches(
            "f(x, y)", ast.Apply(ast.Var("f"), ast.Var("x"), ast.Var("y")))

        self.assertQueryRaises("f(x, ,)")
        self.assertQueryRaises("f(x, y")
        self.assertQueryRaises("f (x, y)")
Example #2
0
 def testFormatters(self):
     """Creating a query with raw AST should generate the source."""
     q = query.Query(
         ast.Complement(
             ast.Equivalence(ast.Map(ast.Var("Process"), ast.Var("pid")),
                             ast.Literal(10))))
     self.assertEqual(q.source, "Process.pid != 10")
Example #3
0
    def testComplexSelect(self):
        query = ("(SELECT proc.parent.pid AS ppid, proc.pid FROM pslist(10) "
                 "WHERE COUNT(proc.open_files) > 10) and True")

        expected = ast.Intersection(
            ast.Map(
                ast.Filter(
                    ast.Apply(ast.Var("pslist"), ast.Literal(10)),
                    ast.StrictOrderedSet(
                        ast.Apply(
                            ast.Var("COUNT"),
                            ast.Resolve(ast.Var("proc"),
                                        ast.Literal("open_files"))),
                        ast.Literal(10))),
                ast.Bind(
                    ast.Pair(
                        ast.Literal("ppid"),
                        ast.Resolve(
                            ast.Resolve(ast.Var("proc"),
                                        ast.Literal("parent")),
                            ast.Literal("pid"))),
                    ast.Pair(ast.Literal("pid"),
                             ast.Resolve(ast.Var("proc"),
                                         ast.Literal("pid"))))),
            ast.Literal(True))

        self.assertQueryMatches(query, expected)
Example #4
0
 def testInfix(self):
     self.assertQueryMatches(
         "x == 'foo' and y.z contains 'bar'",
         ast.Intersection(
             ast.Equivalence(ast.Var("x"), ast.Literal("foo")),
             ast.Membership(ast.Literal("bar"),
                            ast.Resolve(ast.Var("y"), ast.Literal("z")))))
Example #5
0
    def select_limit(self, source_expression):
        """Match LIMIT take [OFFSET drop]."""
        start = self.tokens.matched.start

        # The expression right after LIMIT is the count to take.
        limit_count_expression = self.expression()

        # Optional OFFSET follows.
        if self.tokens.accept(grammar.select_offset):
            offset_start = self.tokens.matched.start
            offset_end = self.tokens.matched.end

            # Next thing is the count to drop.
            offset_count_expression = self.expression()

            # We have a new source expression, which is drop(count, original).
            offset_source_expression = ast.Apply(
                ast.Var("drop", start=offset_start, end=offset_end,
                        source=self.original),
                offset_count_expression,
                source_expression,
                start=offset_start, end=offset_count_expression.end,
                source=self.original)

            # Drop before taking, because obviously.
            source_expression = offset_source_expression

        limit_expression = ast.Apply(
            ast.Var("take", start=start, end=limit_count_expression.end,
                    source=self.original),
            limit_count_expression,
            source_expression,
            start=start, end=self.tokens.matched.end, source=self.original)

        return limit_expression
Example #6
0
    def testSelectOrder(self):
        # Order expressions.
        self.assertQueryMatches(
            "SELECT * FROM pslist() ORDER BY pid",
            ast.Sort(ast.Apply(ast.Var("pslist")), ast.Var("pid")))

        self.assertQueryMatches(
            "SELECT * FROM pslist() ORDER BY pid DESC",
            ast.Apply(ast.Var("reverse"),
                      ast.Sort(ast.Apply(ast.Var("pslist")), ast.Var("pid"))))
Example #7
0
 def testSelectWhereOrder(self):
     self.assertQueryMatches(
         "SELECT * FROM pslist() WHERE pid == 1 ORDER BY command DESC",
         ast.Apply(
             ast.Var("reverse"),
             ast.Sort(
                 ast.Filter(ast.Apply(ast.Var("pslist")),
                            ast.Equivalence(ast.Var("pid"),
                                            ast.Literal(1))),
                 ast.Var("command"))))
Example #8
0
    def testSelectLimit(self):
        self.assertQueryMatches(
            "SELECT * FROM pslist LIMIT 10",
            ast.Apply(ast.Var("take"), ast.Literal(10), ast.Var("pslist")))

        self.assertQueryMatches(
            "SELECT * FROM pslist LIMIT 10 OFFSET 5",
            ast.Apply(
                ast.Var("take"), ast.Literal(10),
                ast.Apply(ast.Var("drop"), ast.Literal(5), ast.Var("pslist"))))
Example #9
0
    def testListLiterals(self):
        self.assertQueryMatches(
            "[1, 2, 3]",
            ast.Tuple(ast.Literal(1), ast.Literal(2), ast.Literal(3)))

        # Empty list literals should work.
        self.assertQueryMatches("[]", ast.Tuple())

        # Arbitrary AST should now be allowed in lists.
        self.assertQueryMatches(
            "[x, f(x)]",
            ast.Tuple(ast.Var("x"), ast.Apply(ast.Var("f"), ast.Var("x"))))
Example #10
0
    def testSelectAny(self):
        self.assertQueryMatches("SELECT ANY FROM pslist()",
                                ast.Any(ast.Apply(ast.Var("pslist"))))

        # Shorthands for any should work.
        self.assertQueryMatches("SELECT ANY FROM pslist()",
                                ast.Any(ast.Apply(ast.Var("pslist"))))

        self.assertQueryMatches("ANY pslist()",
                                ast.Any(ast.Apply(ast.Var("pslist"))))

        # Any doesn't allow ORDER BY.
        self.assertQueryRaises("SELECT ANY FROM pslist() ORDER BY pid")
Example #11
0
    def testLet(self):
        self.assertEqual(
            solve.solve(
                ast.Let(ast.Bind(ast.Pair(ast.Literal("x"), ast.Literal(5))),
                        ast.Sum(ast.Var("x"), ast.Var("x"))), {}).value, [10])

        # Previous binding should be made available to subsequent bindings.
        self.assertEqual(
            solve.solve(
                ast.Let(
                    ast.Bind(
                        ast.Pair(ast.Literal("x"), ast.Literal(5)),
                        ast.Pair(ast.Literal("y"),
                                 ast.Sum(ast.Var("x"), ast.Literal(5)))),
                    ast.Var("y")), {}).value, [10])
Example #12
0
    def testKVPairs(self):
        self.assertQueryMatches("x: y", ast.Pair(ast.Var("x"), ast.Var("y")))

        # KV pairs are used in named function arguments:
        self.assertQueryMatches(
            "f(10, 'strings': ['foo', 'bar'])",
            ast.Apply(
                ast.Var("f"), ast.Literal(10),
                ast.Pair(ast.Literal("strings"),
                         ast.Tuple(ast.Literal("foo"), ast.Literal("bar")))))

        # They can also appear in repeated values, forming a logical dictionary:
        self.assertQueryMatches(
            "('foo': foo, 'bar': bar)",
            ast.Repeat(ast.Pair(ast.Literal("foo"), ast.Var("foo")),
                       ast.Pair(ast.Literal("bar"), ast.Var("bar"))))
Example #13
0
    def select_order(self, source_expression):
        start = self.tokens.matched.start
        sort_expression = ast.Sort(source_expression, self.expression(),
                                   start=start, end=self.tokens.matched.end,
                                   source=self.original)

        if self.tokens.accept(grammar.select_asc):
            sort_expression.end = self.tokens.matched.end
            return sort_expression

        if self.tokens.accept(grammar.select_desc):
            # Descending sort uses the stdlib function 'reverse' on the sorted
            # results. Standard library's core functions should ALWAYS be
            # available.
            sort_expression = ast.Apply(
                ast.Var("reverse",
                        start=sort_expression.start,
                        end=self.tokens.matched.end,
                        source=self.original),
                sort_expression,
                start=sort_expression.start,
                end=self.tokens.matched.end,
                source=self.original)

        if self.tokens.accept(grammar.select_limit):
            return self.select_limit(sort_expression)

        if self.tokens.accept(grammar.select_limit):
            return self.select_limit(sort_expression)

        return sort_expression
Example #14
0
    def testBasicSelect(self):
        self.assertQueryMatches("SELECT * FROM pslist()",
                                ast.Apply(ast.Var("pslist")))

        # The dotty-like where doesn't exist. SQL keywords are not permitted
        # outside of a SELECT expression.
        self.assertQueryRaises("pslist where pid == 1")
Example #15
0
    def testSubscript(self):
        self.assertQueryMatches("d['foo']",
                                ast.Select(ast.Var("d"), ast.Literal("foo")))

        self.assertQueryMatches(
            "d['foo'] + 10",
            ast.Sum(ast.Select(ast.Var("d"), ast.Literal("foo")),
                    ast.Literal(10)))

        self.assertQueryMatches(
            "obj.props[0]",
            ast.Select(ast.Resolve(ast.Var("obj"), ast.Literal("props")),
                       ast.Literal(0)))

        self.assertQueryMatches(
            "obj.props[0].foo",
            ast.Resolve(
                ast.Select(ast.Resolve(ast.Var("obj"), ast.Literal("props")),
                           ast.Literal(0)), ast.Literal("foo")))

        self.assertQueryMatches(
            "obj.props[10 + 10].foo",
            ast.Resolve(
                ast.Select(ast.Resolve(ast.Var("obj"), ast.Literal("props")),
                           ast.Sum(ast.Literal(10), ast.Literal(10))),
                ast.Literal("foo")))

        self.assertQueryMatches(
            "w['x'][y[5] + 5] * 10",
            ast.Product(
                ast.Select(
                    ast.Select(ast.Var("w"), ast.Literal("x")),
                    ast.Sum(ast.Select(ast.Var("y"), ast.Literal(5)),
                            ast.Literal(5))), ast.Literal(10)))
Example #16
0
def asdottysql(expr):
    if not isinstance(expr.rhs, ast.Literal):
        return "<expression cannot be formatted as DottySQL>"

    return _format_binary(expr.lhs,
                          ast.Var(expr.rhs.value),
                          grammar.OPERATORS.by_handler[ast.Resolve],
                          lspace="",
                          rspace="")
Example #17
0
    def testOperatorPrecedence(self):
        # Prefix operator, like the unary minus sign, should respect operator
        # precedence order.
        self.assertQueryMatches(
            "-x + y",
            ast.Sum(ast.Product(ast.Literal(-1), ast.Var("x")), ast.Var("y")))

        self.assertQueryMatches(
            "not x and y",
            ast.Intersection(ast.Complement(ast.Var("x")), ast.Var("y")))

        self.assertQueryMatches(
            "x / -f(y) or not z(a, b)",
            ast.Union(
                ast.Quotient(
                    ast.Var("x"),
                    ast.Product(ast.Literal(-1),
                                ast.Apply(ast.Var("f"), ast.Var("y")))),
                ast.Complement(
                    ast.Apply(ast.Var("z"), ast.Var("a"), ast.Var("b")))))
Example #18
0
    def testInfix(self):
        self.assertQueryMatches("x + y", ast.Sum(ast.Var("x"), ast.Var("y")))

        self.assertQueryMatches(
            "w.x.y.z",
            ast.Resolve(
                ast.Resolve(ast.Resolve(ast.Var("w"), ast.Literal("x")),
                            ast.Literal("y")), ast.Literal("z")))

        # Operator precedence should work correctly.
        self.assertQueryMatches(
            "x + y * z",
            ast.Sum(ast.Var("x"), ast.Product(ast.Var("y"), ast.Var("z"))))

        self.assertQueryMatches(
            "x * y + z",
            ast.Sum(ast.Product(ast.Var("x"), ast.Var("y")), ast.Var("z")))
Example #19
0
 def testSelectWhat(self):
     self.assertQueryMatches(
         "SELECT proc.parent.pid AS ppid,"
         "proc.pid,"
         "'foo',"
         "asdate(proc.starttime),"
         "proc.fd[5]"
         "FROM pslist()",
         ast.Map(
             ast.Apply(ast.Var("pslist")),
             ast.Bind(
                 ast.Pair(
                     ast.Literal("ppid"),
                     ast.Resolve(
                         ast.Resolve(ast.Var("proc"),
                                     ast.Literal("parent")),
                         ast.Literal("pid"))),
                 ast.Pair(ast.Literal("pid"),
                          ast.Resolve(ast.Var("proc"), ast.Literal("pid"))),
                 ast.Pair(ast.Literal("column_2"), ast.Literal("foo")),
                 ast.Pair(
                     ast.Literal("asdate"),
                     ast.Apply(
                         ast.Var("asdate"),
                         ast.Resolve(ast.Var("proc"),
                                     ast.Literal("starttime")))),
                 ast.Pair(
                     ast.Literal("fd_5"),
                     ast.Select(
                         ast.Resolve(ast.Var("proc"), ast.Literal("fd")),
                         ast.Literal(5))))))
Example #20
0
    def _parse_s_expression(self, atom):
        car = atom[0]
        cdr = atom[1:]

        # Vars are a little special. Don't make the value a Literal.
        if car == "var":
            return ast.Var(cdr[0])

        # Params are interpolated right away.
        if car == "param":
            return ast.Literal(self.params[cdr[0]])

        return EXPRESSIONS[car](*[self._parse_atom(a) for a in cdr])
Example #21
0
    def testLiterals(self):
        # Numbers:
        self.assertQueryMatches("5", ast.Literal(5))
        self.assertQueryRaises("5)")

        # Strings:
        self.assertQueryMatches("'foo'", ast.Literal("foo"))

        # Booleans:
        self.assertQueryMatches("true", ast.Literal(True))
        self.assertQueryMatches("false", ast.Literal(False))
        self.assertQueryMatches("TRUE", ast.Literal(True))
        self.assertQueryMatches("TRU", ast.Var("TRU"))
Example #22
0
    def testLet(self):
        self.assertQueryMatches(
            "let x = 5, y = 10 x + y",
            ast.Let(
                ast.Bind(ast.Pair(ast.Literal("x"), ast.Literal(5)),
                         ast.Pair(ast.Literal("y"), ast.Literal(10))),
                ast.Sum(ast.Var("x"), ast.Var("y"))))

        self.assertQueryMatches(
            "let( (x = 5 - 3,y=(10+(10)) ) )x + y",
            ast.Let(
                ast.Bind(
                    ast.Pair(ast.Literal("x"),
                             ast.Difference(ast.Literal(5), ast.Literal(3))),
                    ast.Pair(ast.Literal("y"),
                             ast.Sum(ast.Literal(10), ast.Literal(10)))),
                ast.Sum(ast.Var("x"), ast.Var("y"))))

        self.assertQueryRaises("let x = 5) x + 5")
        self.assertQueryRaises("let ((x = 5) x + 5")
        self.assertQueryRaises("let (x = 5)) x + 5")
        self.assertQueryRaises("let (x = 5 x + 5")
        self.assertQueryRaises("let (x = 5)")
Example #23
0
    def atom(self):
        # Unary operator.
        if self.tokens.accept(grammar.prefix, self.operators):
            operator = self.tokens.matched.operator
            start = self.tokens.matched.start
            children = [self.expression(operator.precedence)]

            # Allow infix to be repeated in circumfix operators.
            if operator.infix:
                while self.tokens.accept(grammar.match_tokens(operator.infix)):
                    children.append(self.expression())

            # If we have a suffix expect it now.
            if operator.suffix:
                self.tokens.expect(grammar.match_tokens(operator.suffix))

            return operator.handler(*children,
                                    start=start,
                                    end=self.tokens.matched.end,
                                    source=self.original)

        if self.tokens.accept(grammar.literal):
            return ast.Literal(self.tokens.matched.value,
                               source=self.original,
                               start=self.tokens.matched.start,
                               end=self.tokens.matched.end)

        if self.tokens.accept(grammar.symbol):
            return ast.Var(self.tokens.matched.value,
                           source=self.original,
                           start=self.tokens.matched.start,
                           end=self.tokens.matched.end)

        if self.tokens.accept(grammar.lparen):
            expr = self.expression()
            self.tokens.expect(grammar.rparen)
            return expr

        if self.tokens.peek(0):
            raise errors.EfilterParseError(
                message="Was not expecting %r here." %
                self.tokens.peek(0).name,
                token=self.tokens.peek(0))
        else:
            raise errors.EfilterParseError("Unexpected end of input.")
Example #24
0
    def testAny(self):
        self.assertTrue(
            solve.solve(
                q.Query("any Process.parent where (pid == 1)"), {
                    "Process": {
                        "parent":
                        repeated.meld(mocks.Process(1, None, None),
                                      mocks.Process(2, None, None))
                    }
                }).value)

        # Test that unary ANY works as expected.
        query = q.Query(ast.Any(ast.Var("x")))
        self.assertFalse(solve.solve(query, {"x": None}).value)
        self.assertTrue(solve.solve(query, {"x": 1}).value)
        self.assertTrue(
            solve.solve(query, {
                "x": repeated.meld(1, 2, 3)
            }).value)
Example #25
0
    def testParens(self):
        # Base case.
        self.assertQueryMatches(
            "x + y * z",
            ast.Sum(ast.Var("x"), ast.Product(ast.Var("y"), ast.Var("z"))))

        # With parens.
        self.assertQueryMatches(
            "(x + y) * z",
            ast.Product(ast.Sum(ast.Var("x"), ast.Var("y")), ast.Var("z")))

        # Missing rparen.
        self.assertQueryRaises("(x + y")

        # Empty expressions make no sense.
        self.assertQueryRaises("()")
Example #26
0
    def testBuiltins(self):
        self.assertQueryMatches(
            "filter(pslist(), proc.pid == 1)",
            ast.Filter(
                ast.Apply(ast.Var("pslist")),
                ast.Equivalence(
                    ast.Resolve(ast.Var("proc"), ast.Literal("pid")),
                    ast.Literal(1))))

        self.assertQueryMatches(
            "map(pslist(), [proc.pid, proc['command']])",
            ast.Map(
                ast.Apply(ast.Var("pslist")),
                ast.Tuple(ast.Resolve(ast.Var("proc"), ast.Literal("pid")),
                          ast.Select(ast.Var("proc"),
                                     ast.Literal("command")))))

        self.assertQueryMatches(
            "bind(x: 1, y: 2)",
            ast.Bind(ast.Pair(ast.Var("x"), ast.Literal(1)),
                     ast.Pair(ast.Var("y"), ast.Literal(2))))

        self.assertQueryRaises("bind (x: 1, y: 2)")
Example #27
0
    def atom(self):
        """Parse an atom, which is most things.

        Grammar:
            atom =
                [ prefix ]
                ( select_expression
                | any_expression
                | func_application
                | let_expr
                | var
                | literal
                | list
                | "(" expression ")" ) .
        """
        # Parameter replacement with literals.
        if self.tokens.accept(grammar.param):
            return self.param()

        # Let expressions (let(x = 5, y = 10) x + y)
        if self.tokens.accept(grammar.let):
            return self.let()

        # At the top level, we try to see if we are recursing into an SQL query.
        if self.tokens.accept(grammar.select):
            return self.select()

        # A SELECT query can also start with 'ANY'.
        if self.tokens.accept(grammar.select_any):
            return self.select_any()

        # Explicitly reject any keywords from SQL other than SELECT and ANY.
        # If we don't do this they will match as valid symbols (variables)
        # and that might be confusing to the user.
        self.tokens.reject(grammar.sql_keyword)

        # Match if-else before other things that consume symbols.
        if self.tokens.accept(grammar.if_if):
            return self.if_if()

        # Operators must be matched first because the same symbols could also
        # be vars or applications.
        if self.tokens.accept(grammar.prefix):
            operator = self.tokens.matched.operator
            start = self.tokens.matched.start
            expr = self.expression(operator.precedence)
            return operator.handler(expr, start=start, end=expr.end,
                                    source=self.original)

        if self.tokens.accept(grammar.literal):
            return ast.Literal(self.tokens.matched.value, source=self.original,
                               start=self.tokens.matched.start,
                               end=self.tokens.matched.end)

        # Match builtin pseudo-functions before functions and vars to prevent
        # overrides.
        if self.tokens.accept(grammar.builtin):
            return self.builtin(self.tokens.matched.value)

        # Match applications before vars, because obviously.
        if self.tokens.accept(grammar.application):
            return self.application(
                ast.Var(self.tokens.matched.value, source=self.original,
                        start=self.tokens.matched.start,
                        end=self.tokens.matched.first.end))

        if self.tokens.accept(common_grammar.symbol):
            name = self.tokens.matched.value
            if self.aliases and name in self.aliases[-1]:
                return self.aliases[-1][name]

            return ast.Var(self.tokens.matched.value, source=self.original,
                           start=self.tokens.matched.start,
                           end=self.tokens.matched.end)

        if self.tokens.accept(common_grammar.lparen):
            # Parens will contain one or more expressions. If there are several
            # expressions, separated by commas, then they are a repeated value.
            #
            # Unlike lists, repeated values must all be of the same type,
            # otherwise evaluation of the query will fail at runtime (or
            # type-check time, for simple cases.)
            start = self.tokens.matched.start
            expressions = [self.expression()]

            while self.tokens.accept(common_grammar.comma):
                expressions.append(self.expression())

            self.tokens.expect(common_grammar.rparen)

            if len(expressions) == 1:
                return expressions[0]

            return ast.Repeat(*expressions, source=self.original,
                              start=start, end=self.tokens.matched.end)

        if self.tokens.accept(common_grammar.lbracket):
            return self.list()

        # We've run out of things we know the next atom could be. If there is
        # still input left then it's illegal syntax. If there is nothing then
        # the input cuts off when we still need an atom. Either is an error.
        if self.tokens.peek(0):
            return self.error(
                "Was not expecting %r here." % self.tokens.peek(0).name,
                start_token=self.tokens.peek(0))

        return self.error("Unexpected end of input.")
Example #28
0
 def testSelectAnyWhere(self):
     self.assertQueryMatches(
         "SELECT ANY FROM pslist() WHERE pid == 1",
         ast.Any(ast.Apply(ast.Var("pslist")),
                 ast.Equivalence(ast.Var("pid"), ast.Literal(1))))
Example #29
0
    def testFullSelect(self):
        query = ("SELECT proc.parent.pid AS ppid, proc.pid"
                 " FROM pslist(pid: 10, ppid: 20)"
                 " WHERE count(proc.open_files) > 10"
                 " ORDER BY proc.command DESC"
                 " LIMIT 10 - 9 OFFSET add(5, 10)")

        expected = ast.Map(
            ast.Apply(
                ast.Var("take"), ast.Difference(ast.Literal(10),
                                                ast.Literal(9)),
                ast.Apply(
                    ast.Var("drop"),
                    ast.Apply(ast.Var("add"), ast.Literal(5), ast.Literal(10)),
                    ast.Apply(
                        ast.Var("reverse"),
                        ast.Sort(
                            ast.Filter(
                                ast.Apply(
                                    ast.Var("pslist"),
                                    ast.Pair(ast.Var("pid"), ast.Literal(10)),
                                    ast.Pair(ast.Var("ppid"),
                                             ast.Literal(20))),
                                ast.StrictOrderedSet(
                                    ast.Apply(
                                        ast.Var("count"),
                                        ast.Resolve(
                                            ast.Var("proc"),
                                            ast.Literal("open_files"))),
                                    ast.Literal(10))),
                            ast.Resolve(ast.Var("proc"),
                                        ast.Literal("command")))))),
            ast.Bind(
                ast.Pair(
                    ast.Literal("ppid"),
                    ast.Resolve(
                        ast.Resolve(ast.Var("proc"), ast.Literal("parent")),
                        ast.Literal("pid"))),
                ast.Pair(ast.Literal("pid"),
                         ast.Resolve(ast.Var("proc"), ast.Literal("pid")))))

        self.assertQueryMatches(query, expected)
Example #30
0
 def testReverse(self):
     query = q.Query(
         ast.Apply(
             ast.Var("reverse"),
             ast.Repeat(ast.Literal(1), ast.Literal(2), ast.Literal(3))))
     self.assertEqual(solve.solve(query, {}).value, repeated.meld(3, 2, 1))