How Pg knows what I want to do? (2)

本文深入探讨了 PostgreSQL 如何解析 SQL 语句并将其转化为执行所需的内部结构,详细介绍了语法解析、语义分析及优化的过程,旨在帮助开发者理解数据库查询执行的底层逻辑。

MORE INFO AT : WWW.LEEHAO.ORG

___________________________________________________________________________________________________________________________________

#0. Prerequisite.

Before we discussion, you must know some basic knowledge about lexical and grammatical knowledge, which can give you some basic description of that.

Parsing what you typed and translated into another language known by PostgreSQL.
   In human being world, we can know what you said; catch up your mind, even you speaking English or other languages. Why do I know you real idea? In computer world, how do we teach the computer to understand it to understand our language? In our world, we have rules to abide. By following the rules, we can understand what your said; what you want; that's we called communication. In computer world, the rules are needed too. All the rules are set in advance. These rules we call grammar rules, which used to define how the express our idea. In database, we also have a language, SQL. SQL describes how the database manipulates t the data. For example, using the statement "create table tableName(...)" to tell the database to create a table which name is tableName; and the statement "select * from tableName" used to tell the database that we want to get all data which stored in tableName.
   How to understand the SQL? A set of rules is defined, and by using Lexeme analysis tool and grammar analysis tool to help our understanding.
   From scratch, a SQL statement will divide into some piece of unit, lexeme. In pg, the function, exec_simple_query, is the entry of the execution of a SQL statement. When the server frame pass a query string into QueryEnine, mainly pass into the function exec_simple_query. The function, exec_simple_query, takes only one parameter, query string. The prototype of the exec_simple_query can be found in "src/backend/tcop/postgres.c".

   "static void exec_simple_query(const char *query_string)".

From the definition of that function, we know the string input parameter, query_string, stores the user request. And, goes further, we can draw conclusion that if a user has the permission to execute queries, the server framework will pass the query string into low layer, otherwise, then refuses to execute. By the way, the permission check module maybe does not exist in a smiple database system, or we can put permission check module down to when the query statment be executed. You can find some clues about exec_query_string in its caller function "PostgresMain" .



We will take "select * from my_first_table where id=1" as an exmpale to discuss how the postgreSQL. know what i want to do in more detail. The .l and .y file can be found in "src/backend/parser/gram.y" and "src/backend/parser/scan.l".. For more specific details, pls refere to those two files. Here, we mainly put our foucs on selection query statement.



From the pics above, we know that ,after the parser reading a words, the parser will do the defined action. For example, when "*" is read, the parser will do "*"" atcion as following.


  | '*'
  {
    ColumnRef *n = makeNode(ColumnRef);
    n->fields = list_make1(makeNode(A_Star));
    n->location = @1;

    $$ = makeNode(ResTarget);
    $$->name = NULL;
    $$->indirection = NIL;
    $$->val = (Node *)n;
    $$->location = @1;
  }
  


First of all, a node is created, then sets fields of that node, and returns to its parent node.Before we go into deeper, i think some important data sturcture must be described. These data stutures are used by query engine when it does optimization.
typedef struct Query
{
   NodeTag type; //The type of the node, which can refere to nodes.h

   CmdType commandType; /* select|insert|update|delete|utility */

   QuerySource querySource; /* where did I come from? */

   uint32 queryId; /* query identifier*/

   bool canSetTag; /* do I set the command result tag? */

   Node *utilityStmt; /* non-null if this is DECLARE CURSOR or a * non-optimizable statement */

   int resultRelation; /* rtable index of target relation for * INSERT/UPDATE/DELETE; 0 for SELECT */

   bool hasAggs; /* has aggregates in tlist or havingQual */

   bool hasWindowFuncs; /* has window functions in tlist */

   bool hasSubLinks; /* has subquery SubLink */

   bool hasDistinctOn; /* distinctClause is from DISTINCT ON */

   bool hasRecursive; /* WITH RECURSIVE was specified */

   bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */

   bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */

   List *cteList; /* WITH list (of CommonTableExpr's) */

   List *rtable; /* list of range table entries */

   FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */

   List *targetList; /* target list (of TargetEntry) */

   List *returningList; /* return-values list (of TargetEntry) */

   List *groupClause; /* a list of SortGroupClause's */

   Node *havingQual; /* qualifications applied to groups */

   List *windowClause; /* a list of WindowClause's */

   List *distinctClause; /* a list of SortGroupClause's */

   List *sortClause; /* a list of SortGroupClause's */

   Node *limitOffset; /* # of result tuples to skip (int8 expr) */

   Node *limitCount; /* # of result tuples to return (int8 expr) */

   List *rowMarks; /* a list of RowMarkClause's */

   Node *setOperations; /* set-operation tree if this is top level of * a UNION/INTERSECT/EXCEPT query */

   List *constraintDeps; /* a list of pg_constraint OIDs that the query * depends on to be semantically valid */

} Query;

With respective to SQL (structual query language), the select statement is one fo the basic SQL statements. Without considering the definition of Query, which defined as above. We can know that when using a SQL to query data from a database, SELECT, FROM and WHERE are three main components for a SQL, some SQLs may have HAVING, GROUP BY, ORDER BY, etc. Therefore, in parsing phrase, the Yacc makes nodes for holding these parts. All these can be found in gramm.y, which has been discussed above. Considering the SELECT, FROM and WHERE parts, it may takes more than one items. Taking "select name, age, gender, score from student, score where student.id = score.studentid" as an instance, each parts takes more than one items, and how to orgnize these items? List structure perhaps is a suitable choice to store these items. Therefore, we have a raw definition of Query as following.

typedef struct Query
{
   ...
   List *cteList; /* WITH list (of CommonTableExpr's) */
   List *rtable; /* list of range table entries */
   FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */
   List *targetList; /* target list (of TargetEntry) */
   List *returningList; /* return-values list (of TargetEntry) */
   List *groupClause; /* a list of SortGroupClause's */
   Node *havingQual; /* qualifications applied to groups */
   List *windowClause; /* a list of WindowClause's */
   List *distinctClause; /* a list of SortGroupClause's */
   List *sortClause; /* a list of SortGroupClause's */
   ...
   List *rowMarks; /* a list of RowMarkClause's */
   ...
   List *constraintDeps; /* a list of pg_constraint OIDs that the query * depends on to be semantically valid */
} Query;

From the definition of Query, the fields with List* take the linked lists, which point to the sub-clauses. If the query statement has the sub-clauses, these values of these fields are not NULL, otherwise, are NULL. More specific details will be discussed in next pragraph. The "Query" is key data sturcture in Query Engine. When a query statement is processed by Parser, A raw query-parse tree will generated. The "Query" takes the raw query-parsing tree or called abstract syntax tree(AST). The definiton of List is given as following:

typedef struct ListCell ListCell;
typedef struct List
{
   NodeTag type;/* T_List, T_IntList, or T_OidList */
   int length;
   ListCell *head;
   ListCell *tail;
} List;
struct ListCell
{
   union
   {
      void *ptr_value;
      int int_value;
      Oid oid_value;
   } data;
   ListCell *next;
};


A Query will be returned after parsing done. The more complicated query statement please refere to gramm.y. Here we don't spent much time on this parsing phrase, the main focus will be put on how to OPTIMIZE the AST. The fucntion "List *pg_parse_query(const char *query_string)" in postgres.c performs the parsing, which takes a query string as input parameter, List* as output parameter.



After a raw parse tree generated, it will go to the next stage: Analyzing the raw parse tree and transform it to Query form, the result is a Query node.

In this section, i will present the function call-flow in detail to make you know how postgresql query egnine transform the raw syntax tree (AST) to 'Query' clearer.If the server gest a query statement, it enter into 'exec_simple_query' funtion to execute the query statement.
    In exec_simple_query fuction, first of all, the query engine starts a new transction, and drop the unnamed statement, do memory context switching for paring the raw syntax tree, then invoking the function 'pg_parse_query' to parse the query string.After the function pg_parse_query parsing the query string, a List returns to take the returned Raw Syntax Tree. the variable 'parsetree_list' holds the returned Raw syntax tree.     When the query string has been processed, it will goes to transform phrase and rewrite the Query.This is done by function, 'pg_analyze_and_rewrite'.



    Now, we are in function 'pg_analyze_and_rewrite'. Firstly, it invokes 'parse_analyze' to perform parse analysis, then rewrite the queries, as necessary.



    In function 'parse_analyze', it calls funtion 'make_parsestate(NULL)' to allocate a new ParseState object, ParseState *pstate, which can hold the parsing state when perform parsing, then sets its p_sourcetext to query string, which can be used to tell where and why your query statement is wrong format if your query statement is malformat.
    Secondly, if some parameters exist, sytem will call function 'parse_fixed_parameters' to process at first. Then, function transformTopLevelStmt will be called to process the original pars tree. At last, if user reistered function is registered, then, it will call that function handler to do user registered behavior. The function handler is stored by 'post_parse_analyze_hook'. the return value of parse_analyze is 'Query*'.

内容概要:本研究针对微电网在遭受拒绝服务(DoS)攻击时面临的功率分配不均与电能质量问题,提出了一种兼顾功率精确均分与电压频率质量恢复的抗攻击混合动态事件触发二次控制策略。该策略通过设计新型混合动态事件触发机制,有效减少控制器与分布式单元间的网络通信负担,同时增强系统对DoS攻击的鲁棒性。研究构建了完整的微电网二次控制框架,整合了分布式协同控制算法与事件触发通信机制,在保证系统稳定性的同时,实现了对频率、电压偏差的快速调节和有功/无功功率的精确分配。通过Simulink平台进行仿真实验,验证了所提方法在遭受DoS攻击及正常运行工况下均能有效维持微电网的稳定运行与高质量电能输出。; 适合人群:具备电力系统自动化、分布式控制或微电网相关基础知识,从事新能源、智能电网领域研究的研发人员及高年级研究生。; 使用场景及目标:① 解决微电网在通信受限及网络攻击场景下的协同控制难题;② 实现微电网在异常工况下功率均分与电能质量的双重优化;③ 为设计高安全性、高可靠性的智能微电网控制系统提供理论依据与仿真验证方案。; 阅读建议:本资源侧重于控制策略的设计与仿真验证,建议读者结合微电网基础理论与Simulink仿真技术,深入理解事件触发机制与抗DoS攻击控制算法的实现细节,并动手复现仿真案例以加深对系统动态性能与鲁棒性的认识。
内容概要:本文围绕《【太阳能学报EI复现】基于粒子群优化算法的风-水电联合优化运行分析(Matlab代码实现)》展开,系统阐述了采用粒子群优化算法(PSO)对风能与水力发电系统进行联合优化调度的研究方法与技术路径。研究聚焦于构建多能源互补协调的优化模型,详细论述了目标函数的设计、系统约束条件的处理、算法求解流程及收敛性分析,并通过Matlab编程实现了完整的仿真验证过程,有效提升了可再生能源系统的运行效率与稳定性。该工作属于电力系统智能优化领域,强调对高水平期刊论文的高精度复现,兼具理论深度与工程实用性,适用于科研复现、学术研究与教学参考。; 适合人群:具备一定电力系统基础知识和Matlab编程能力的研究生、科研人员及从事新能源优化调度、智能算法应用的工程技术人员。; 使用场景及目标:①用于复现《太阳能学报》等高水平期刊中关于风-水电联合调度的EI/SCI论文;②掌握粒子群算法在多源协同优化中的建模、编码与求解关键技术;③辅助完成学位论文、科研项目申报或学术竞赛中的仿真建模任务; 阅读建议:建议结合文中提供的网盘资源下载完整代码与文档资料,按照目录结构循序渐进学习,重点关注算法实现细节、电力系统建模逻辑与参数设置方法,同时可延伸学习灰狼优化算法、YALMIP工具包等先进优化技术,以全面提升科研仿真与创新能力。
内容概要:本文聚焦“基于源网荷储一体化的配电网协同优化研究”,提出一种面向高渗透率电动汽车接入场景的双层优化模型,并采用Matlab实现完整的仿真与求解。研究系统整合电源、电网、负荷与储能四大环节,构建多时段、多约束条件下的协同调度框架,涵盖电动汽车有序充电、V2G(车网互动)技术、分布式能源并网、无功优化及储能协同配置等关键要素。通过引入二阶锥松弛或凸规划方法对非线性模型进行线性化处理,有效提升优化求解效率与收敛性。同时,结合熵权法与模糊综合评价方法,建立多维度的配电网承载能力量化评估体系,实现对系统运行状态的科学评判。文中配套提供完整Matlab代码,具有较强的可复现性与工程应用价值,适用于科研仿真与实际项目开发。; 适合人群:具备电力系统分析基础和Matlab编程能力,从事新能源接入、智能配电网、综合能源系统优化等方向的研究生、科研人员及电力行业工程技术开发者。; 使用场景及目标:①用于高比例可再生能源与大规模电动汽车接入背景下配电网承载能力的量化评估;②实现源-网-荷-储多主体参与的协同优化调度建模与仿真分析;③支撑硕博学位论文撰写、高水平期刊论文结果复现及科研项目的算法验证与系统开发。; 阅读建议:建议结合文中提供的Matlab代码与相关参考文献同步研习,重点关注双层优化架构的设计逻辑、二阶锥松弛的数学处理技巧以及多指标综合评价体系的构建流程,建议动手调试代码以深入掌握模型实现细节与算法运行机制。
源码链接: https://pan.quark.cn/s/a4b39357ea24 DMA(直接内存访问)是计算机系统中一种关键的数据传输机制,它使得特定的硬件子系统得以直接对系统内存进行读写操作,无需CPU的介入。这种机制对于提高I/O操作的效能具有极其重要的作用,特别是在网络设备、存储设备等驱动程序的编写过程中占据着核心地位。Cache(缓存)则是一种用于暂存频繁访问的数据和指令的存储结构,其目的是减少处理器对主存储器的访问次数,进而增强系统的整体性能。然而,DMA和Cache之间存在着一致性的挑战,特别是在部分嵌入式系统中,DMA操作可能绕过Cache机制,从而引发数据不一致的情况,这就需要采取一系列策略来维护Cache的一致性。 在DMA的运作模式中,主要存在两种Cache一致性问题:流式DMA(streaming DMA)与一致性DMA(coherent DMA)。流式DMA通常应用于需要大量数据传输的场景,它不关注Cache的一致性,因此传输速度较快,但要求软件开发者自行管理数据的一致性。而一致性DMA则保证了在DMA传输期间,数据在Cache与主内存之间保持同步,通常适用于对一致性要求较高的应用场景。 在Linux内核中,为了有效管理DMA操作,提供了一系列接口函数。其中,一致性DMA接口负责维护数据的一致性,而流式DMA接口则提供了更快的传输速度,但要求开发者自行解决数据一致性的问题。开发者在选用这些接口时,必须依据硬件平台的特点和性能需求,选择合适的DMA模式。 Cache一致性的解决方案通常取决于硬件平台的属性。在某些先进的处理器架构中,Cache对程序员而言是透明的,即处理器与Cache控制器之间的交互对程序员不可见,从而简化了编程的复...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值