当前位置: 首页 > news >正文

CodeQL学习笔记(2)-QL语法(递归)

最近在学习CodeQL,对于CodeQL就不介绍了,目前网上一搜一大把。本系列是学习CodeQL的个人学习笔记,根据个人知识库笔记修改整理而来的,分享出来共同学习。个人觉得QL的语法比较反人类,至少与目前主流的这些OOP语言相比,还是有一定难度的。与现在网上的大多数所谓CodeQL教程不同,本系列基于官方文档和情景实例,包含大量的个人理解、思考和延伸,直入主题,只切要害,几乎没有废话,并且坚持用从每一个实例中学习总结归纳,再到实例中验证。希望能给各位一点不一样的见解和思路。当然,也正是如此必定会包含一定的错误,希望各位大佬能在评论区留言指正。

CodeQL学习笔记(1)


递归

我们逐渐来理解递归。先设定一个目标–查询国王是否有活着的兄弟姐妹

// 表示p和“King Basil”的父母是相同的,则p是其兄弟姐妹
from Person p 
where parentOf(p) = parentOf("King Basil") and    // parentOf(Person p)返回p的父母not p = "King Basil"and not p.isDeceased()    // isDeceased()谓词,判断是否死亡
select p
// 没有结果

然后尝试查找这些兄弟姐妹们是否有孩子

根据paretOf定义一个childOf谓词(反逻辑)

Person childOf(Person p){    // 这里的返回值是一个Person类型p = paretOf(result)
}
select childOf(p)
// 依旧没有结果
  1. 在QL谓词中,约定result为返回值。

  2. 理解代码:这里有两个身份,p和result,要搞清楚它们间的关系。定义的childOf谓词在这里表示–childOf§ = result,p的孩子是result。那么在谓词实现中,要把这个逻辑反过来,类似于数学中的反函数的概念,p = parentOf(result),当且仅当 p 是某人的父亲时,某人是 p 的孩子

  3. 在谓词中不一定要在谓词定义中直接写明result = ,也可以把result放到后面来作为一个“已知”变量来表达关系。(引用官方文档:相反,您还可以通过用 result 来“反向”表示 p 和 result 之间的关系。)

再尝试找是否有活着的堂兄弟、堂兄弟的孩子、二表兄弟等……这里面会涉及到非常复杂的关系,最好是能够定义一个谓词列出所有的亲戚relativeOf(Person p):如果两个人拥有同一个祖先,那么他们就是亲戚

如果我们定义一个谓词ancestorOf(Person p),列出某个人所有的祖先,这其中包含它的parent,parent的parent……无穷尽,这里就存在了一个递归的思想。我们可以把祖先的定义进行拆分,分为直接父母和已经确定是祖先的直接父母

对于祖先定义的拆分的理解,如果某人已经被确定是祖先了,那么它的直接父母肯定也是祖先

Person ancestorOf(Person p){result = parentOf(p) orresult = parentOf(ancestorOf(p))
}

下面看一个递归的具体例子

// 列出0-100
int getANumber(){result = 0orresult <= 100 and result = getANumber() + 1
}
select getANumber() as number    // 列名为“number”,列名不用加引号

这里需要与传统函数式编程严格区分开,不是按传统编程语言的“从上到下执行”模式。ql中在处理递归时,会根据查询逻辑查找所有满足条件的可能结果。因此,select getANumber() 其实是在寻找 所有可能满足 getANumber() 定义条件的 result 值,而不是只运行一次 getANumber() 函数。

  1. QL 会首先满足 result = 0,这是递归的基础条件。所以getANumber()在没有其他限制的情况下,可以返回 0。
  2. 然后CodeQL 会继续尝试满足其他条件,以找到 所有可能的 result 值。
    • 递归条件result = getANumber() + 1​意味着 QL 还会去找 比 0 大的所有整数结果,直到达到 result = 100 的上限。
    • 例如,在 result = 0 确定之后,递归条件允许 result = 1(通过 result = getANumber() + 1 得到),接着再允许 result = 2,以此类推,直到 100。(可以看成是result <= 100 and result = 1​, result <= 100 and result = 2​……)

闭包

我们把上面的递归ancestorOf(Person p)改写一下

Person getAncestor(){result = this.getAParent()orresult = this.getAParent().getAnAncestor()
}

其实它的本质就是不断的在找parent,parent的parent……,在QL里有个概念叫做闭包,简单理解就是对某个类的谓词重复执行的简化,不需要我们再去写复杂的递归谓词。

传递闭包(+)

直接对getParent这个谓词使用传递闭包p.getAParent+(),这里得到的是p的parent、p的parent的parent……,是不包含p自身的,即递归一次或多次(>=1,和正则表达式中+、*的用法一致)。

from Person p 
where p = "Cornelius"
select p.getAParent+() as name    // 找到Cornelius的所有ancestor,getAnAncestor()等价于getAParent+()
自反传递闭包(*)

与传递闭包唯一的差别是包含p本身,递归0次或多次(>=0)

select p.getAParent*() as name

其他详细的递归深入用法等到后期专题再展开。当前学习的目的仅是快速入门。

完善代码

在学习完闭包之后,就可以实现前面我们的需求–找出国王的所有亲戚relativeOf(Person p)

Person relativeOf(Person p){parentOf*(p) = parentOf*(result)andnot result = p     // 由于这个具体例子中的国王已经死了,在where中也已经过滤了,所以可以不加。正常来说自反传递闭包需要排除自身
}from Person p 
wherenot p.isDeceased() andp = relativeOf("King Basil")
select p
http://www.lryc.cn/news/470636.html

相关文章:

  • Video-XL:面向小时级视频理解的超长视觉语言模型
  • postgresql subtransaction以及他的效能
  • 新手逆向实战三部曲之二——通过更改关键跳注册软件(爆破)
  • 高级SQL技巧:提升数据查询与分析能力的关键
  • IntelliJ IDEA 安装 Maven 工具并更换阿里源
  • MIT 6.824 Lab1记录
  • C语言数据结构学习:[汇总]
  • unity游戏开发之塔防游戏
  • 前端项目接入sqlite轻量级数据库sql.js指南
  • 模拟退火算法(Simulated Annealing)详细解读
  • (二十一)、Docker 部署 Minikube 使用可视化管理工具 Kuboard
  • 代码编辑组件
  • 裴蜀定理与欧几里得算法——蓝桥杯真题中的应用
  • 冯诺依曼架构及CPU相关概念
  • 智能管线巡检系统:强化巡检质量,确保安全高效运维
  • React写关键字高亮的三个方案
  • 重塑在线软件开发新纪元:集成高效安全特性,深度解析与评估会员与促销管理系统的系统架构设计
  • 多层感知机的从零实现与softmax的从零实现(真·0000零基础)
  • 【Rust练习】18.特征 Trait
  • 【自动化测试之oracle数据库】MacOs如何安装oracle- client
  • Spring MVC的MultipartFile
  • ●Leetcode| 242.有效的字母异位词 ● 349. 两个数组的交集 ● 202. 快乐数● 1. 两数之和
  • 关于算法的时间复杂度和空间复杂度的分析
  • 深入浅出 C++ STL:解锁高效编程的秘密武器
  • 2024年1024程序人生总结
  • 【p2p、分布式,区块链笔记 分布式容错算法】: 拜占庭将军问题+实用拜占庭容错算法PBFT
  • 鸿蒙NEXT开发-应用数据持久化之用户首选项(基于最新api12稳定版)
  • 人工智能_神经网络103_感知机_感知机工作原理_感知机具备学习能力_在学习过程中自我调整权重_优化效果_多元线性回归_逻辑回归---人工智能工作笔记0228
  • WISE:重新思考大语言模型的终身模型编辑与知识记忆机制
  • 网络安全证书介绍