PostgreSQL查询引擎——transform expressions之AEXPR_OP

news2024/11/25 7:01:19
static Node *transformAExprOp(ParseState *pstate, A_Expr *a){
	Node	   *lexpr = a->lexpr; Node	   *rexpr = a->rexpr; // 操作符左右表达式
	Node	   *result;

	/* Special-case "foo = NULL" and "NULL = foo" for compatibility with standards-broken products (like Microsoft's).  Turn these into IS NULL exprs. (If either side is a CaseTestExpr, then the expression was generated internally from a CASE-WHEN expression, and transform_null_equals does not apply.) */
	if (Transform_null_equals && list_length(a->name) == 1 && strcmp(strVal(linitial(a->name)), "=") == 0 &&(exprIsNullConstant(lexpr) || exprIsNullConstant(rexpr)) && (!IsA(lexpr, CaseTestExpr) && !IsA(rexpr, CaseTestExpr))) {
		NullTest   *n = makeNode(NullTest); n->nulltesttype = IS_NULL; n->location = a->location;
		if (exprIsNullConstant(lexpr)) n->arg = (Expr *) rexpr;
		else n->arg = (Expr *) lexpr;
		result = transformExprRecurse(pstate, (Node *) n);
	}
	else if (lexpr && IsA(lexpr, RowExpr) && rexpr && IsA(rexpr, SubLink) && ((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK) { /* Convert "row op subselect" into a ROWCOMPARE sublink. Formerly the grammar did this, but now that a row construct is allowed anywhere in expressions, it's easier to do it here. */
		SubLink    *s = (SubLink *) rexpr;
		s->subLinkType = ROWCOMPARE_SUBLINK; s->testexpr = lexpr; s->operName = a->name; s->location = a->location;
		result = transformExprRecurse(pstate, (Node *) s);
	}
	else if (lexpr && IsA(lexpr, RowExpr) && rexpr && IsA(rexpr, RowExpr)){ /* ROW() op ROW() is handled specially */
		lexpr = transformExprRecurse(pstate, lexpr);
		rexpr = transformExprRecurse(pstate, rexpr);
		result = make_row_comparison_op(pstate, a->name,castNode(RowExpr, lexpr)->args, castNode(RowExpr, rexpr)->args, a->location);
	}
	else { /* Ordinary scalar operator */
		Node	   *last_srf = pstate->p_last_srf;
		lexpr = transformExprRecurse(pstate, lexpr); rexpr = transformExprRecurse(pstate, rexpr);
		result = (Node *) make_op(pstate, a->name, lexpr, rexpr, last_srf, a->location);
	}
	return result;
}

transformAExprOp函数将对AEXPR_OP、AEXPR_LIKE、AEXPR_ILIKE和AEXPR_SIMILAR类型的A_Expr表达式进行转换,分为四种类型。

  • “foo = NULL” and “NULL = foo” 转为IS NULL表达式
  • "row op subselect"转为ROWCOMPARE sublink表达式
  • ROW() op ROW()调用make_row_comparison_op进行处理
  • scalar operator调用make_op进行处理

scalar operator

make_op函数用于转换运算符表达式,确保类型兼容性。首先获取左表达式、右表达式类型,加上操作符名,调用oper函数获取pg_operator中匹配运算符表达式的operator记录;强制实现多态参数和返回类型的一致性,可能会调整返回类型或declard_arg_types(make_fn_arguments将其用作强制转换目标);为形参设置必要的强转函数;

/* make_op() Operator expression construction.
 * Transform operator expression ensuring type compatibility. This is where some type conversion happens.
 * last_srf should be a copy of pstate->p_last_srf from just before we started transforming the operator's arguments; this is used for nested-SRF detection.  If the caller will throw an error anyway for a set-returning expression, it's okay to cheat and just pass pstate->p_last_srf. */
Expr *make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, Node *last_srf, int location){
	Oid			ltypeId, rtypeId; Oid			rettype; // 左表达式、右表达式、返回值类型
	Operator	tup; Form_pg_operator opform;
	Oid			actual_arg_types[2]; Oid			declared_arg_types[2]; int			nargs; List	   *args;	
	OpExpr	   *result;

	/* Select the operator */
	if (ltree == NULL){/* prefix operator */ // 查找一元左operator(unary left operator)
		rtypeId = exprType(rtree); ltypeId = InvalidOid; tup = left_oper(pstate, opname, rtypeId, false, location);
	}else{/* otherwise, binary operator */   // 查找二元operator
		ltypeId = exprType(ltree); rtypeId = exprType(rtree); tup = oper(pstate, opname, ltypeId, rtypeId, false, location);
	}
	opform = (Form_pg_operator) GETSTRUCT(tup);
	
	/* Do typecasting and build the expression tree */
	if (ltree == NULL){ /* prefix operator */
		args = list_make1(rtree); actual_arg_types[0] = rtypeId; declared_arg_types[0] = opform->oprright; nargs = 1;
	}else{/* otherwise, binary operator */
		args = list_make2(ltree, rtree); actual_arg_types[0] = ltypeId; actual_arg_types[1] = rtypeId;
		declared_arg_types[0] = opform->oprleft; declared_arg_types[1] = opform->oprright; nargs = 2;
	}

	/* enforce consistency with polymorphic argument and return types, possibly adjusting return type or declared_arg_types (which will be used as the cast destination by make_fn_arguments) */
	rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, nargs, opform->oprresult, false);
	/* perform the necessary typecasting of arguments */
	make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
	
	result = makeNode(OpExpr); /* and build the expression node */
	result->opno = oprid(tup); result->opfuncid = opform->oprcode; result->opresulttype = rettype;
	result->opretset = get_func_retset(opform->oprcode);
	/* opcollid and inputcollid will be set by parse_collate.c */
	result->args = args; result->location = location;	
	if (result->opretset) { /* if it returns a set, check that's OK */
		check_srf_call_placement(pstate, last_srf, location);		
		pstate->p_last_srf = (Node *) result; /* ... and remember it for error checks at higher levels */
	}
	ReleaseSysCache(tup);

	return (Expr *) result;
}

ROW() op ROW()

c_expr中指明了表达式为Row表达式的规则,而Explicit row production的规则如下所示,语法中有ROW关键字或括号:

row:		ROW '(' expr_list ')'					{ $$ = $3; }
			| ROW '(' ')'							{ $$ = NIL; }
			| '(' expr_list ',' a_expr ')'			{ $$ = lappend($2, $4); }
		;
explicit_row:	ROW '(' expr_list ')'				{ $$ = $3; }
			| ROW '(' ')'							{ $$ = NIL; }
		;
implicit_row:	'(' expr_list ',' a_expr ')'		{ $$ = lappend($2, $4); }
		;

expr_list:	a_expr { $$ = list_make1($1); }
			| expr_list ',' a_expr { $$ = lappend($1, $3); }

在这里插入图片描述
对于ROW() op ROW()调用make_row_comparison_op(pstate, a->name, castNode(RowExpr, lexpr)->args, castNode(RowExpr, rexpr)->args, a->location)进行转换处理.该函数输入是已转换表达式的列表。与coerce_type一样,如果不需要特殊的未知Param处理,pstate可能为NULL。输出可以是单个OpExprOpExpr的AND或OR组合RowCompareExpr。在所有情况下,都保证返回布尔值。AND、OR和RowCompareExpr的情况进一步暗示了运算符的行为(即,它们的行为为=、<>或<<=>=)。The inputs are lists of already-transformed expressions. As with coerce_type, pstate may be NULL if no special unknown-Param processing is wanted. The output may be a single OpExpr, an AND or OR combination of OpExprs, or a RowCompareExpr. In all cases it is guaranteed to return boolean. The AND, OR, and RowCompareExpr cases further imply things about the behavior of the operators (ie, they behave as =, <>, or < <= > >=). 首先取出左右RowExpr表达式args中的子表达式,通过make_op函数为其创建OpExpr结构。如果row()的长度为1,则只返回单个运算符OpExpr。否则需要为每对运算符OpExpr,寻找包含运算符的btree操作族;对于=和<>创建BoolExpr(AND_EXPR、OR_EXPR);对于其他运算符,查找到其对于的opfamily,创建RowCompareExpr,并关联OpExpr的中的左右表达式。

static Node *make_row_comparison_op(ParseState *pstate, List *opname, List *largs, List *rargs, int location) {
	RowCompareExpr *rcexpr; RowCompareType rctype;
	ListCell   *l, *r;
	/* Identify all the pairwise operators, using make_op so that behavior is the same as in the simple scalar case. */
	List	   *opexprs = NIL; // 使用make_op识别所有成对运算符,使其行为与简单标量情况相同。
	forboth(l, largs, r, rargs){
		Node	   *larg = (Node *) lfirst(l); Node	   *rarg = (Node *) lfirst(r);
		OpExpr	   *cmp = castNode(OpExpr, make_op(pstate, opname, larg, rarg, pstate->p_last_srf, location));
		opexprs = lappend(opexprs, cmp);
	}
    int nopers = list_length(largs);
	/* If rows are length 1, just return the single operator.  In this case we don't insist on identifying btree semantics for the operator (but we still require it to return boolean). */ //如果行的长度为1,则只返回单个运算符。在这种情况下,我们并不坚持为运算符识别btree语义(但我们仍然要求它返回布尔值)。
	if (nopers == 1)return (Node *) linitial(opexprs);

	/* Now we must determine which row comparison semantics (= <> < <= > >=) apply to this set of operators.  We look for btree opfamilies containing the operators, and see which interpretations (strategy numbers) exist for each operator. */ // 现在,我们必须确定哪一行比较语义(= <> < <= > >=)应用于这组运算符。我们寻找包含运算符的btree操作族,并查看每个运算符存在哪些解释(策略编号)。
	List	  **opinfo_lists = (List **) palloc(nopers * sizeof(List *));
	Bitmapset  *strats = NULL;
	int i = 0;
	foreach(l, opexprs){ // 从OpExpr列表中取出row左右对应位置的子表达式创建而成的OpExpr
		Oid			opno = ((OpExpr *) lfirst(l))->opno;
		opinfo_lists[i] = get_op_btree_interpretation(opno); // 寻找包含运算符的btree操作族

		/* convert strategy numbers into a Bitmapset to make the intersection calculation easy. */
		Bitmapset  *this_strats = NULL;  ListCell   *j;
		foreach(j, opinfo_lists[i]){
			OpBtreeInterpretation *opinfo = lfirst(j);
			this_strats = bms_add_member(this_strats, opinfo->strategy);
		}
		if (i == 0) strats = this_strats;
		else strats = bms_int_members(strats, this_strats);
		i++;
	}

	/* If there are multiple common interpretations, we may use any one of them ... this coding arbitrarily picks the lowest btree strategy number. */ // 如果有多种常见的解释,我们可以使用其中的任何一种。。。这种编码任意地选择最低的btree策略编号。
	i = bms_first_member(strats); rctype = (RowCompareType) i;

	/* For = and <> cases, we just combine the pairwise operators with AND or OR respectively. */
	if (rctype == ROWCOMPARE_EQ) return (Node *) makeBoolExpr(AND_EXPR, opexprs, location);
	if (rctype == ROWCOMPARE_NE) return (Node *) makeBoolExpr(OR_EXPR, opexprs, location);

	/* Otherwise we need to choose exactly which opfamily to associate with each operator. */
	List	   *opfamilies = NIL;
	for (i = 0; i < nopers; i++){
		Oid			opfamily = InvalidOid;
		ListCell   *j;
		foreach(j, opinfo_lists[i]){
			OpBtreeInterpretation *opinfo = lfirst(j);
			if (opinfo->strategy == rctype){
				opfamily = opinfo->opfamily_id;
				break;
			}
		}
		if (OidIsValid(opfamily)) opfamilies = lappend_oid(opfamilies, opfamily);
		else					/* should not happen */
			ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),errmsg("could not determine interpretation of row comparison operator %s",strVal(llast(opname))), errdetail("There are multiple equally-plausible candidates."), parser_errposition(pstate, location)));
	}

	/* Now deconstruct the OpExprs and create a RowCompareExpr. Note: can't just reuse the passed largs/rargs lists, because of possibility that make_op inserted coercion operations. */
	List	   *opnos = NIL; largs = NIL; rargs = NIL;
	foreach(l, opexprs){
		OpExpr	   *cmp = (OpExpr *) lfirst(l);
		opnos = lappend_oid(opnos, cmp->opno);
		largs = lappend(largs, linitial(cmp->args)); rargs = lappend(rargs, lsecond(cmp->args));
	}

	rcexpr = makeNode(RowCompareExpr);
	rcexpr->rctype = rctype; rcexpr->opnos = opnos; rcexpr->opfamilies = opfamilies;
	rcexpr->inputcollids = NIL; /* assign_expr_collations will fix this */
	rcexpr->largs = largs; rcexpr->rargs = rargs;

	return (Node *) rcexpr;
}

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/552299.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Bug——后端返回LocalDateTime类型数据中间出现一个T

错误如下图所示: 返回的JSON格式数据里面会有一个多出来的T. 解决方案&#xff1a; 在后端的POJO层的实体类的LocalDateTime属性上面加上一个注解 JsonFormat(pattern"yyyy-MM-dd HH:mm:ss") 如下所示&#xff0c;然后在返回JSON格式数据时就不会出现那个多余的T了…

C++之模板初阶

目录 前言 1.泛型编程 2.模板 2.1 函数模板 2.1.1 函数模板概念 2.1.2 函数模板格式 2.1.3 函数模板的原理 2.1.4 函数模板的实例化 2.1.5 模板参数的匹配原则 2.2 类模板 2.2.1 类模板定义模式 2.2.2 类模板的实例化 前言 我们会不会有疑惑为什么C语言中&#xf…

Python学习笔记——cmeans模糊聚类例程

文章目录 模糊聚类应用简介安装环境demo&#xff1a;运行结果 模糊聚类应用简介 模糊聚类即通过模糊数学&#xff08;处理模糊或不确定性信息的数学方法&#xff09;的相关算法进行聚类分析任务。 常用的模糊聚类算法包括模糊C均值聚类&#xff08;FCM&#xff0c;Fuzzy-c mea…

TryHackMe-Red Team Capstone Challenge (红队挑战)【真实红队模拟】

Red Team Capstone Challenge 注意&#xff1a;我不会在这里提及相关的flag&#xff0c;只专心打&#xff1b;flag可以自己用各个hostname尝试一遍 挑战作者的一句话 这个房间被评为坚硬&#xff0c;但因为它是你前面的一座山&#xff0c;它可能被评为疯狂。但是&#xff0c;…

【Vue3】滑动验证组件 | 滑动验证

前言 滑块验证不只判断是否滑动到尾部&#xff0c;真正的目的是检测用户行为&#xff0c;检测行为是人为、脚本、还是其它。 防止使用脚本大量注册、请求等 。比如发送请求时&#xff0c;判断用户在某个页面停留了多长时间。登录、注册时是否点击了登录、注册按钮&#xff0c;…

Lesson1——数据结构前言

前言&#xff1a; 今天我们正式开始一个新的专栏——初阶数据结构&#xff08;C语言实现&#xff09;&#xff0c;本专栏后续持续更新时间复杂度空间复杂度、顺序表、链表、栈和队列、二叉树、排序等算法的相关知识&#xff0c;欢迎大家互相学习&#xff0c;可以私信互相讨论哦…

一次oracle环境 enq: TX - allocate ITL entry锁问题分析

enq: TX - allocate ITL entry锁问题分析 通过分析问题时间段两个节点的AWR报告&#xff0c;TOP1等待为锁竞争enq: TX - allocate ITL entry&#xff0c;该等待事件是由于缺省情况下创建的表的INITRANS参数为1,索引的INITRANS参数值为2.当有太多的并发DML操作的数据行处于相同的…

日志模块封封装:单例模式+策略模式+构建者模式+bugly

日志模块封封装:单例模式策略模式构建者模式bugly 一.单例模式策略模式构建者模式二.日志模块封装1.日志等级&#xff1a;LoggerLevel枚举类2.日志输出类型&#xff1a;LoggerType枚举类3.ILogger接口4.LogCatLogger/FileLogger/NetWorkLogger/EmailLogger5.使用构建者模式创建…

相同格式相同分辨率图片不同大小分析

1、问题 有三张图片&#xff0c;如下&#xff1a; 这三张图片均为jpg格式&#xff0c;分辨率均为1851*580&#xff0c;肉眼看不出区别。但是大小不同。 2号为217KB&#xff0c;4号为1.15MB&#xff0c;5号为1.06MB。 我们看下常规信息&#xff0c;先看2号&#xff1a; 可以…

初识Linux:第五篇

初识Linux&#xff1a;第五篇 初识Linux&#xff1a;第五篇1.Linux下的用户2.Linux权限管理2.1文件权限管理2.2文件权限的更改2.21改变文件访问权限属性2.22改变文件的身份 3.三个问题 总结 初识Linux&#xff1a;第五篇 &#x1f601;本篇主要介绍Linux权限的相关知识&#x1…

vue+elementui+nodejs高校校园在线打印预约系统

在线提交文档进行打印 首页简单介绍系统 语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode 顶部或主页按钮转到打印 用户可以登录 查看历史打印记录 用户分学生和非学生 学生可以享有优惠…

基于.NetCore开源的Windows的GIF录屏工具

推荐一个Github上Start超过20K的超火、好用的屏幕截图转换为 GIF 动图开源项目。 项目简介 这是基于.Net Core WPF 开发的、开源项目&#xff0c;可将屏幕截图转为 GIF 动画。它的核心功能是能够简单、快速地截取整个屏幕或者选定区域&#xff0c;并将其转为 GIF动画&#x…

编写 ROS 消息发布订阅器(五)

执行命令&#xff0c;指定目录添加cpp文件 cd ~/catkin_ws/src/beginner_tutorials如果没有src目录&#xff0c; 就自己创建一个目录叫src cd src/ vim talker.cpp 复制代码粘贴&#xff1a; #include "ros/ros.h" #include "std_msgs/String.h" int m…

C++/R 期末冲刺3h

C 1. 基础程序 #include "iostream" // C头文件 #include "stdio.h" // C 头文件 //using namespace std; // 命名空间// main() 是程序开始执行的地方int main() {std::cout << "Hello, World!" << "\n";return 0; …

【数据结构】线性结构 之 顺序表

&#x1f331;博客主页&#xff1a;大寄一场. &#x1f331;系列专栏&#xff1a;数据结构与算法 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 目录 前言 顺序表概念及结构 静态代码实现&#xff1a; 动态代码实现&#xff1a; SeqList.h文件 SeqLi…

使用VitePress和Github搭建个人博客网站,可以自动构建和发布

之前我们写过一篇关于如何自动构建和发布个人博客的文章&#xff0c;当时是使用VuePress和GitLab。GitLab持续集成部署CI/CD初探&#xff1a;如何自动构建和发布个人前端博客 现在换了Vue3和Vite&#xff0c;使用VitePress在Github上又搭建了一个博客。博客地址&#xff1a; …

博弈Ai官网ChatGPT能力真实测评

链接&#xff1a;https://chat.bo-e.com/&#xff08;基于ChatGPT4和3.5研发的智能聊天机器人国产镜像&#xff09; 一&#xff0c;博弈Ai的UI设计样式 1、博弈Ai&#xff08;ChatGPT&#xff09;白天模式 2、博弈Ai&#xff08;ChatGPT&#xff09;黑天模式 3、博弈Ai&#x…

五、c++学习(加餐1:汇编基础学习)

经过前面几节课的学习&#xff0c;我们在一些地方都会使用汇编来分析&#xff0c;我们学习汇编&#xff0c;只是学习一些基础&#xff0c;主要是在我们需要深入分析语法的时候&#xff0c;使用汇编分析&#xff0c;这样会让我们更熟悉c编译器和语法。 从这节课开始&#xff0c…

【003hive基础】hive的数据类型

文章目录 一.数据类型1. 基础数据类型2. 复杂数据类型 二. 显式转换与隐式转换三. hive的读时模式 一.数据类型 1. 基础数据类型 2. 复杂数据类型 array: 有序相同数据类型的集合。 arrays(1, 2)map : key必须是基本数据类型&#xff0c;value不限。 map(‘a’, 1, ‘b’, 2)s…

线性回归、正规方程和梯度下降法

一、线性回归简介 1.定义与公式 线性回归是利用回归方程(函数)对一个或多个自变量(特征值)和因变量(目标值)之间关系进行建模的一种分析方式。 特点&#xff1a;只有一个自变量的情况称为单变量回归&#xff0c;多余一个自变量情况的叫做多元回归 通用公式&#xff1a; y …