PostgreSQL源码分析 —— 除法运算符

我们分析一下在PostgreSQL数据库中,是如何实现除法操作的。其他的运算符操作与之类似。以下面的语句为例:

select 2/4;
源码分析

主流程如下:

exec_simple_query
--> pg_parse_query
--> pg_analyze_and_rewrite
--> pg_plan_queries
--> PortalStart
--> PortalRun
--> PortalDrop
解析部分
exec_simple_query
--> pg_parse_query   // 构造语法解析树SelectStmt-->ResTarget-->A_Expr--> raw_parser--> base_yyparse

select 2/4的语法解析部分,比较简单。构造SelectStmt节点,只存在target部分,构造targetList字段, target为ResTarget,在ResTarget中构造val字段,为2/4的表达式节点表示A_Expr。在运算符的语法解析表示上,gram.y中定义的源码如下:

simple_select:SELECT opt_all_clause opt_target_listinto_clause from_clause where_clause group_clause having_clause window_clause{SelectStmt *n = makeNode(SelectStmt);n->targetList = $4;-- ...}
/*******************************************************************************	target list for SELECT******************************************************************************/
opt_target_list: target_list		{ $$ = $1; }| /* EMPTY */	{ $$ = NIL; };target_list:target_el	{ $$ = list_make1($1); }| target_list ',' target_el	{ $$ = lappend($1, $3); };
target_el:	a_expr{$$ = makeNode(ResTarget);$$->name = NULL;$$->indirection = NIL;$$->val = (Node *)$1;$$->location = @1;}
a_expr:		c_expr	{ $$ = $1; }/** These operators must be called out explicitly in order to make use* of bison's automatic operator-precedence handling.  All other* operator names are handled by the generic productions using "Op",* below; and all those operators will have the same precedence.** If you add more explicitly-known operators, be sure to add them* also to b_expr and to the MathOp list below.*/| '+' a_expr		%prec UMINUS{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1); }| '-' a_expr		%prec UMINUS{ $$ = doNegate($2, @1); }| a_expr '+' a_expr{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3, @2); }| a_expr '-' a_expr{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3, @2); }| a_expr '*' a_expr{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3, @2); }| a_expr '/' a_expr{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3, @2); }

构造了一个A_Expr节点:

/** makeSimpleA_Expr -*		As above, given a simple (unqualified) operator name*/
A_Expr *makeSimpleA_Expr(A_Expr_Kind kind, char *name, Node *lexpr, Node *rexpr, int location)
{A_Expr	   *a = makeNode(A_Expr);a->kind = kind;a->name = list_make1(makeString((char *) name));a->lexpr = lexpr;a->rexpr = rexpr;a->location = location;return a;
}
语义分析阶段
exec_simple_query
--> pg_parse_query
--> pg_analyze_and_rewrite--> parse_analyze--> transformStmt--> transformSelectStmt     // transforms a Select Statement--> transformTargetList /* Turns a list of ResTarget's into a list of TargetEntry's. */--> transformTargetEntry--> transformExpr--> transformAExprOp--> makeTargetEntry--> make_op // 构造操作符表达式Expr节点OpExpr--> oper // search for a binary operator、--> ket_ok = make_oper_cache_key // 先在缓存中找, 找到返回--> DeconstructQualifiedName  // 获取操作符name, namespace--> fetch_search_path_array--> recomputeNamespacePath();--> if (key_ok)--> find_oper_cache_entry   --> binary_oper_exact // 如果缓存中没有找到--> OpernameGetOprid  // 查找pg_operator系统表找--> enforce_generic_type_consistency--> make_fn_arguments
--> pg_plan_queries--> standard_planner--> subquery_planner--> pull_up_subqueries--> preprocess_expression// 化简常量表达式,如果可以的话,immutable才行--> eval_const_expressions--> set_opfuncid--> simplify_function--> expand_function_arguments--> evaluate_function   // try to pre-evaluate a function call--> evaluate_expr   // 预执行一个常量表达式--> CreateExecutorState--> ExecInitExpr--> ExecInitExprRec--> ExecInitFunc--> ExecReadyExpr--> ExecEvalExprSwitchContext // 执行获取一个常量值--> ExecInterpExpr--> int4div  // 调用除法实现函数 计算结果--> makeConst // 将执行常量表达式获得的结果转换为Const常量节点返回--> grouping_planner--> query_planner--> build_simple_rel--> create_empty_pathtarget // 构造PathTarget--> create_group_result_path  // 生成GroupResultPath--> apply_scanjoin_target_to_paths--> create_projection_path--> create_plan--> create_projection_plan--> create_group_result_plan
--> PortalStart--> ExecutorStart--> InitPlan--> ExecInitResult
--> PortalRun--> ExecutorRun--> standard_ExecutorRun--> ExecutePlan--> ExecResult--> ExecProject
--> PortalDrop

在这个阶段,会找到/除法操作符的oid,即查系统表pg_operaotr,在函数oper中,我们下面查看一下系统表,查询一下除法/操作符,这个操作符你一定意义上可以认为是函数,除法函数。需要输入参数,输出结果。

PG文档:10.2. 操作符

postgres=# select * from pg_operator where oprname = '/';oid  | oprname | oprnamespace | oprowner | oprkind | oprcanmerge | oprcanhash | oprleft | oprright | oprresult | oprcom | oprnegate |    oprcode    | oprrest | oprjoin 
------+---------+--------------+----------+---------+-------------+------------+---------+----------+-----------+--------+-----------+---------------+---------+---------527 | /       |           11 |       10 | b       | f           | f          |      21 |       21 |        21 |      0 |         0 | int2div       | -       | -528 | /       |           11 |       10 | b       | f           | f          |      23 |       23 |        23 |      0 |         0 | int4div       | -       | -546 | /       |           11 |       10 | b       | f           | f          |      21 |       23 |        23 |      0 |         0 | int24div      | -       | -547 | /       |           11 |       10 | b       | f           | f          |      23 |       21 |        23 |      0 |         0 | int42div      | -       | -588 | /       |           11 |       10 | b       | f           | f          |     700 |      700 |       700 |      0 |         0 | float4div     | -       | -593 | /       |           11 |       10 | b       | f           | f          |     701 |      701 |       701 |      0 |         0 | float8div     | -       | -687 | /       |           11 |       10 | b       | f           | f          |      20 |       20 |        20 |      0 |         0 | int8div       | -       | -691 | /       |           11 |       10 | b       | f           | f          |      20 |       23 |        20 |      0 |         0 | int84div      | -       | -695 | /       |           11 |       10 | b       | f           | f          |      23 |       20 |        20 |      0 |         0 | int48div      | -       | -821 | /       |           11 |       10 | b       | f           | f          |      20 |       21 |        20 |      0 |         0 | int82div      | -       | -825 | /       |           11 |       10 | b       | f           | f          |      21 |       20 |        20 |      0 |         0 | int28div      | -       | -734 | /       |           11 |       10 | b       | f           | f          |     600 |      600 |       600 |      0 |         0 | point_div     | -       | -739 | /       |           11 |       10 | b       | f           | f          |     602 |      600 |       602 |      0 |         0 | path_div_pt   | -       | -807 | /       |           11 |       10 | b       | f           | f          |     603 |      600 |       603 |      0 |         0 | box_div       | -       | -844 | /       |           11 |       10 | b       | f           | f          |     790 |      700 |       790 |      0 |         0 | cash_div_flt4 | -       | -909 | /       |           11 |       10 | b       | f           | f          |     790 |      701 |       790 |      0 |         0 | cash_div_flt8 | -       | -3347 | /       |           11 |       10 | b       | f           | f          |     790 |       20 |       790 |      0 |         0 | cash_div_int8 | -       | -913 | /       |           11 |       10 | b       | f           | f          |     790 |       23 |       790 |      0 |         0 | cash_div_int4 | -       | -915 | /       |           11 |       10 | b       | f           | f          |     790 |       21 |       790 |      0 |         0 | cash_div_int2 | -       | -3825 | /       |           11 |       10 | b       | f           | f          |     790 |      790 |       701 |      0 |         0 | cash_div_cash | -       | -1118 | /       |           11 |       10 | b       | f           | f          |     700 |      701 |       701 |      0 |         0 | float48div    | -       | -1128 | /       |           11 |       10 | b       | f           | f          |     701 |      700 |       701 |      0 |         0 | float84div    | -       | -1519 | /       |           11 |       10 | b       | f           | f          |     718 |      600 |       718 |      0 |         0 | circle_div_pt | -       | -1585 | /       |           11 |       10 | b       | f           | f          |    1186 |      701 |      1186 |      0 |         0 | interval_div  | -       | -1761 | /       |           11 |       10 | b       | f           | f          |    1700 |     1700 |      1700 |      0 |         0 | numeric_div   | -       | -
(25 rows)

可以查到,select 2/4;对应的是oid=528,实现该操作符的函数是int4div,oprcode表示实现该操作符的函数,可通过查看系统表pg_proc查看该函数的详细信息:

postgres@postgres=# select * from pg_proc where proname='int4div';
-[ RECORD 1 ]---+--------
oid             | 154
proname         | int4div              -- 函数名
pronamespace    | 11
proowner        | 10
prolang         | 12
procost         | 1
prorows         | 0
provariadic     | 0
prosupport      | -
prokind         | f
prosecdef       | f
proleakproof    | f
proisstrict     | t
proretset       | f
provolatile     | i
proparallel     | s
pronargs        | 2
pronargdefaults | 0
prorettype      | 23                    -- 返回参数
proargtypes     | 23 23                 -- 入参
proallargtypes  | 
proargmodes     | 
proargnames     | 
proargdefaults  | 
protrftypes     | 
prosrc          | int4div               -- 函数source 
probin          | 
proconfig       | 
proacl          | 

其中除法表达式A_Expr经过语义分析后,通过OpExpr进行表示:

/** OpExpr - expression node for an operator invocation** Semantically, this is essentially the same as a function call.* 语义上,等同于一个函数调用* Note that opfuncid is not necessarily filled in immediately on creation* of the node.  The planner makes sure it is valid before passing the node* tree to the executor, but during parsing/planning opfuncid can be 0.*/
typedef struct OpExpr
{Expr	xpr;Oid	opno;		/* PG_OPERATOR OID of the operator */   //对应pg_operator.oidOid	opfuncid;	/* PG_PROC OID of underlying function */ // 实现该操作符的函数oidOid	opresulttype;	/* PG_TYPE OID of result value */   // 返回值类型bool	opretset;	/* true if operator returns set */Oid	opcollid;	/* OID of collation of result */Oid	inputcollid;	/* OID of collation that operator should use */List	*args;		/* arguments to the operator (1 or 2) */  // 参数int	location;	/* token location, or -1 if unknown */
} OpExpr;

除法的实现函数:

Datum int4div(PG_FUNCTION_ARGS)
{int32		arg1 = PG_GETARG_INT32(0);int32		arg2 = PG_GETARG_INT32(1);int32		result;if (arg2 == 0){ereport(ERROR,(errcode(ERRCODE_DIVISION_BY_ZERO),errmsg("division by zero")));/* ensure compiler realizes we mustn't reach the division (gcc bug) */PG_RETURN_NULL();}/** INT_MIN / -1 is problematic, since the result can't be represented on a* two's-complement machine.  Some machines produce INT_MIN, some produce* zero, some throw an exception.  We can dodge the problem by recognizing* that division by -1 is the same as negation.*/if (arg2 == -1){if (unlikely(arg1 == PG_INT32_MIN))ereport(ERROR,(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range")));result = -arg1;PG_RETURN_INT32(result);}/* No overflow is possible */result = arg1 / arg2;PG_RETURN_INT32(result);
}

从上面的分析过程来看,其实运算符操作与调用一个函数是差不多的。基本可以认为是等同。

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

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

相关文章

如何在宝塔中使用命令行执行命令

一、 进入宝塔,找到网站然后点击根目录 二、进入项目中,然后点击 终端 三、如果这里会弹出输入密码的框,那就是需要你输入一下你服务器的 账号密码,然后就登录进去了,可以在这里直接执行命令即可,比如我这…

RERCS系统开发实战案例-Part08 FPM 应用程序的表单组件(From UIBB)与列表组件(List UIBB)组合的创建

1、新建From UIBB的FPM Application的快速启动面板 备注:该步骤可第一步操作,也可最后一步操作,本人习惯第一步操作。 1)使用事务码 LPD_CUST,选择对应的角色与实例进入快速启动板定制页面; 2&#xff09…

函数依赖-函数依赖、平凡函数依赖、完全与部分函数依赖、传递函数依赖

一、引言 函数依赖是关系模式中属性与属性之间存在的一种重要数据依赖 1、将关系模式R的模式结构改为 R(SNO,CNO,SN,SD,DD,GRADE) 并对属性列进行重命名 R(学生学号&#xff0c…

力扣hot100:31. 下一个排列

LeetCode:31. 下一个排列 字典序的大小排序: 从前往后对比,如果先出现更小字符的,字典序更小,如果有个字符串结束了,则它更小。string s "112233"和string t "1122334",…

无代码爬虫八爪鱼采集器-如何采集携程网指定酒店差评信息

场景描述:有一些酒店会分析同行的差评原因,以便提前做预案,避免自己酒店也放同样的错误。他们通过采集携程网指定酒店的提取中差评,使用的采集工具为无代码爬虫软件八爪鱼采集器免费版,下载链接:1.软件分享…

【PyQt5】一文向您详细介绍 self.sender() 的作用

【PyQt5】一文向您详细介绍 self.sender() 的作用 下滑即可查看博客内容 🌈 欢迎莅临我的个人主页 👈这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地!🎇 🎓 博主简介:985高校的普通本硕&a…

暑期计划打卡清单表怎么写 暑期待办计划清单

暑假来临,是不是感觉时间好像突然多了起来,但又不知道该做些什么好?别担心,列一个暑期计划打卡清单表,就能让你的暑假生活变得有条不紊、充实而有意义。 计划清单,就像是给暑假生活绘制的一张地图。没有它…

Jasper Studio制作报表,预览时候出现死循环,一直渲染页面,total pages无限渲染

目录 1.1、错误描述 1.2、解决方案 1.1、错误描述 最近遇到一个jasper报表线上预览出现死循环的问题,实施人员反馈,线上生产环境中,使用某个功能显示pdf的时候,出现了接口超时问题,在这个项目中,我们使用…

精准测试:代码覆盖率与测试覆盖率

在日常的测试过程当中,不管是人工进行接口测试还是接口自动化,以及RD写的单元测试,我们一般使用代码覆盖率来衡量测试的完备程度,这篇文章就带大家认识一下代码覆盖率这个常用质量完备度的指标 代码覆盖率测试与测试覆盖率在软件…

Elasticsearch扩展性探索:水平扩展与集群管理

在当今数据驱动的时代,搜索引擎和数据存储解决方案的重要性不言而喻。Elasticsearch,作为一款基于Lucene构建的开源、分布式、RESTful搜索引擎,凭借其强大的全文搜索、结构化搜索和分析能力,受到了众多企业和开发者的青睐。然而&a…

C++的标准容器及其应用

C的标准容器及其应用 数组(array)数组的特征应用实列 前向列表(forward_list)前向列表的特征应用实列 列表(list)列表的特征应用实列 有序映射(map)有序映射的特征应用实列 队列&…

uniapp滚动加载

uniapp实现滚动加载,先获取10条数据,滚动到底时,再获取10条数据,以此类推,直至没有数据为止。 使用scroll-view,注意一定要给一个固定高度,隐藏滚动条会更美观 2. 在data中定义 3. 获取数据 …

【Ubuntu】修改计算机名称

在Ubuntu系统中,可以通过以下步骤来修改计算机名(hostname): 方法一:临时修改计算机名 这种方法只会在当前会话中生效,重启后会恢复原来的计算机名。 sudo hostname 新计算机名请将新计算机名替换为你想…

3D三维模型展示上传VR全景创建H5开源版开发

3D三维模型展示上传VR全景创建H5开源版开发 新增三级分类(项目分类、项目、默认场景) 新增热点 前台创建项目、场景 场景跳转、提示信息 新增热点图标选择 新增预览场景是显示关联场景 新增3D模型展示功能 当然可以!以下是一个关于3D三维模…

AndroidStudio|本地生成APK|build.gradle.kts配置

1. 准备密钥库 首先,你需要生成一个密钥库,用于签署你的 APK 文件。 使用 Android Studio 生成密钥库 打开你的 Android Studio 项目。点击 Build -> Generate Signed Bundle / APK...。选择 APK,然后点击 Next。点击 Create new... 按…

Gradle相关概念

目录 基本概念1、Plugin2、Task3、Configuration4、Extension 其他1、查看gralde源码2、查看Android-Gradle-Plugin源码3、查看shadowJar源码 基本概念 1、Plugin 配置插件 apply plugin: ‘xxx’动态添加插件 project.plugins.apply(ShadowJavaPlugin)2、Task 1、创建任务…

MySQL Explain 关键字详解

概述 explain 关键字可以模拟执行 sql 查询语句,输出执行计划,分析查询语句的执行性能 使用方式如下:explain sql explain select * from t1执行计划各字段含义 1. id 如果 id 序号相同,从上往下执行如果 id 序号不同&#…

php使用sockets实现Modbus TCP串口通信

sockets文档地址:https://www.php.net/manual/zh/book.sockets.php 一:安装sockets拓展 1:windows安装sockets 找到php.ini文件,将下面这行注释即可 extensionsockets 2:Linux安装sockets pecl install sockets …

MJ绘画设计基础——如何玩转midjourney?

抽卡的时候经常有一个问题,就是整张图都还不错,但是某些地方有些小问题,比如说手很奇怪,比如下面这个图,哪都挺好看,就是左手有点问题。 这时候就可以局部重绘来拯救一下 第一次生成的图 点击图片下方的V…