// 基本结构
from /* variable declarations */
where /* logical formulas */
select /* expressions */
from int a, int b
where x = 3, y = 4
select x, y
// 找出1-10内的勾股数
from int x, int y, int z
where x in [1..10], y in [1..10], z in [1..10] and
x * x + y * y = z * z
select x, y, z
// 或下面这种类写法,封装和方法复用
class SmallInt extends int {
SmallInt(){
this in [1..10]
}
int square(){
result = this * this
}
}
from SmallInt x, SmallInt y, SmallInt z
where x.sqrt() + y.square() = z.sqrt()
select x, y, z
逻辑连接词、量词、聚合词
from Person p
where p.getAge() = max(int i | exists(Person t | t.getAge() = i ) | i) // 通用聚合语法,比较冗长
select p
// 或用以下有序聚合方式
select max(Person p | | p order by p.getAge())
e.g. exists( Person p | p.getName() = "test" ),判断是否存在一个人的名字为test
max(int i | exists(Person p | p.getAge() = i) | i),第二部分的意思是拿到所有人的年龄放入i,第三部分是作用范围为i,目前i为int数组,存放所有人的年龄,即最终计算的是max(i)
select max(Person p | | p order by p.getAge()),考虑每个人,取出年龄最大的人。过程是按照年龄来取最大值,换句话说,order by p.getAge() 是告诉 max() 函数要基于 getAge() 来找最大值,并不涉及对所有对象的排序操作。
// 其他有序聚合练习
select min(Person p | p.getLocation() = "east" | p order by p.getHeight()) // 村东最矮的人
select count(Person p | p.getLocation() = "south" | p) // 村南的人数
select avg(Person p | | p.getHeight()) // 村民平均身高
select sum(Person p | p.getHairColor() = "brown" | p.getAge()) // 所有棕色头发的村民年龄总和
// 综合练习,https://codeql.github.com/docs/writing-codeql-queries/find-the-thief/#the-real-investigation
import tutorial
from Person p
where
p.getHeight() > 150 and // 身高超过150
not p.getHairColor() = "blond" and // 头发颜色不是金发
exists(string c | p.getHairColor() = c) and // 不是秃头。这里表示这个人存在某种发色,但不用具象化
not p.getAge() < 30 and // 年龄满30岁。也可以是p.getAge() >= 30
p.getLocation() = "east" and // 住在东边
( p.getHairColor() = "black" or p.getHairColor() = "brown" ) and // 头发是黑色或棕色
not (p.getHeight() > 180 and p.getHeight() < 190) and // 没有(超过180且矮于190)
exists(Person t | t.getAge() > p.getAge()) and // 不是最年长的人。这里用存在语法是,存在一个人比他的年龄大
exists(Person t | t.getHeight() > p.getHeight()) and // 不是最高的人
p.getHeight() < avg(Person t | | t.getHeight()) and // 比平均身高要矮。所有人,没有限制范围
p = max(Person t | t.getLocation() = "east" | t order by t.getAge()) // 东部年纪最大的人。这一行是官方给的参考,但是官方文档中说 "Note that if there are several people with the same maximum age, the query lists all of them.",如果存在最大年龄相同的两个人会同时列出,可能会造成不可控的后果。
// p.getAge() = max(Person t | t.getLocation() = "east" | t.getAge()) // 按照个人理解和chatgpt的解答,应该使用这种方式
select p
// 从所有Person中取出当前在South的,然后取出其中能去north的。因为把child限定了只能呆在当地,因此取出的这部分Southerner中的Child全都没法去north,因此就把这部分(原本在South的)Child过滤了
from Southerner s
where s.isAllowedIn("north")
select s
// 取出所有Child,因此他们都只能呆在原地,因此找谁能去north就是找谁原本呆在north
from Child c
where c.isAllowedIn("north")
select c
// 取出所有Person,要找谁能去north的,即找所有成年人(默认所有人都可以前往所有地区)和找本来就呆在north的Child
from Person p
where p.isAllowedIn("north")
select p
class OneTwoThree extends int {
OneTwoThree() { // 特征谓词
this = 1 or this = 2 or this = 3
}
string getAString() { // 成员谓词
result = "One, two or three: " + this.toString()
}
}
class OneTwo extends OneTwoThree {
OneTwo() {
this = 1 or this = 2
}
override string getAString() {
result = "One or two: " + this.toString()
}
}
from OneTwoThree o
select o, o.getAString()
/* result:
o getAString() result
1 One or two: 1
2 One or two: 2
3 One, two or three: 3
// 理解:onetwothree类定义了1 2 3,onetwo重构了onetwothree中1和2的成员谓词。因此onetwothree o中有3个,其中的1和2使用onetwo的成员谓词,3使用onetwothree的成员谓词
*/
情况1: 在这个基础上加上另一个类别(重要),A->B, A->C
class TwoThree extends OneTwoThree{
TwoThree() {
this = 2 or this = 3
}
override string getAString() {
result = "Two or three: " + this.toString()
}
}
/*
command:
from OneTwoThree o
select o, o.getAString()
result:
o getAString() result
1 One or two: 1
2 One or two: 2
2 Two or three: 2
3 Two or three: 3
// 理解:twothree和onetwo重合了two,但是不像其他ool,ql并不会冲突,而是并存。
---
command:
from OneTwo o
select o, o.getAString()
result:
1 One or two: 1
2 One or two: 2
2 Two or three: 2
// 理解:twothree和onetwo都重构了其中的2,由于ql不会冲突,所以并存。由于o的类型是onetwo,因此"地基"是1和2,然后再加上twothree重构的2
---
command:
from TwoThree o
select o, o.getAString()
result:
2 One or two: 2
2 Two or three: 2
3 Two or three: 3
// 理解: twothree和onetwo都重构了2,由于ql不会冲突,会并存。由于o的类型是twothree,所以“地基”是2和3,然后再加上onetwo重构的2
*/
情况2: A->B->C(继承链)
class Two extends TwoThree {
Two() {
this = 2
}
override string getAString() {
result = "Two: " + this.toString()
}
}
from TwoThree o
select o, o.getAString()
/* result:
o getAString() result
1 One or two: 2
2 Two: 2
3 Two or three: 3
// 理解:在上面的例子的基础上,Two重构了twothree中的成员谓词,因此与twothree不是共存关系
*/
from OneTwo o
select o, o.getAString()
/* result:
o getAString() result
1 One or two: 1
2 One or two: 2
3 Two: 2
// 理解:在之前例子的基础上,OneTwo和TwoThree共存,但是Two把TwoThree中的一部分给override了(即Two和TwoThree并不是共存关系)
*/
class Two extends OneTwo, TwoThree {
Two() {
this = 2
}
override string getAString() {
result = "Two: " + this.toString()
}
}
// 解释1:Two同时继承TwoThree和OneTwo,如果不写条件谓词,则默认为同时满足两个父类条件,如果写,则范围也要小于等于这个交集范围。
// 解释2:如果多重继承的父类中同一名称的成员谓词有多重定义,则必须覆盖这些定义避免歧义。在这里的Two的getAString()是不能省略的
from OneTwoThree o
select o, o.getAString()
/* result:
o getAString() result
1 One or two: 1
2 Two: 2
3 Two or three: 3
// 理解:由于two与onetwo和twothree是父子关系,因此直接把共有的two全部覆盖,不是并存关系
*/
在这个基础上再去创建一个谓词,用于判断是否是秃头isBald
predicate isBald(Person p) {
not exists(string c | p.getHairColor() = c) // 不加not表示某人有头发
}
// 获得最终结果,允许进入北方的南方秃头
from Southerner s
where s.isAllowedIn("north") and isBald(s)
select s, s.getAge()