Beginner question - How is this recursive predicate is evaluated? #85
-
I'm in the process of trying to learn CodeQL and I'm a little confused about how certain CodeQL code is evaluated. I'm hoping someone can help me with a more simplistic explanation. Take the following CodeQL code:
select getANeighbor("Germany")
I understand why Belgium and Austria are returned. However I'm confused as to how CodeQL determines that France is to be returned as a result. My imperative programming intuition tells me that for France to be returned, I would need an additional line that looks something like country = "Germany" and result = "France", but I'm really confused here how France is being returned without that line of code. Also, how does this line work exactly?:
With some of the simple examples that are given in the CodeQL handbook, they make it seem like that the 'result' keyword almost acts like 'return' in other languages. I feel that I have a fundamental misunderstanding of what 'result' does and how it works. I can't seem to find a good explanation after googling. Thanks in advance! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
This is a great question. I'll give the full details on recursive predicates below, but let's first deal with
You're right to observe that in many examples Note that this is quite similar to how you implicitly get a special variable called Other than its implicit declaration, there is nothing special about the predicate getANeighbor(string country, string neighbor) {
country = "France" and neighbor = "Belgium"
or
country = "France" and neighbor = "Germany"
or
country = "Germany" and neighbor = "Austria"
or
country = "Germany" and neighbor = "Belgium"
or
getANeighbor(neighbor, country)
} You do have to invoke the predicate differently if you declare it without a result type: If you get used to the idea that As a final observation, you can of course get something more similar to the The general intuition behind recursion in CodeQL is given here, but at a high level you can think of each recursive call as representing the "current" set of value tuples in the called predicate, and all predicate definitions keep being evaluated until there is no change. Let's see how this works in your example predicate: string getANeighbor(string country){
country = "France" and result = "Belgium"
or
country = "France" and result = "Germany"
or
country = "Germany" and result = "Austria"
or
country = "Germany" and result = "Belgium"
or
country = getANeighbor(result)
} The first time we try to evaluate
However, after deducing these values, we know that there was a recursive call to
Note that we re-deduced the first four lines, which do not depend on a recursive call, just like we did in iteration 1. This is fine -- we already knew that As before, we see that iteration 2 of evaluating [I've omitted some details of how to make such a recursive evaluation efficient -- in practice there are some ways to avoid re-deducing the same values again and again on each recursive iteration --, because they are not necessary to understand how recursion works in the first place.] |
Beta Was this translation helpful? Give feedback.
This is a great question. I'll give the full details on recursive predicates below, but let's first deal with
result
.You're right to observe that in many examples
result = f()
in CodeQL seems to act likereturn f()
in other languages. However, areturn
statement (with its usual meaning of actually interruptin…