SQL引擎 - analyze.cpp分析(四)
(一)SQL简要介绍
数据库的SQL引擎是数据库重要的子系统之一,它对上负责承接应用程序发送过来的SQL语句,对下则负责指挥执行器运行执行计划。其中优化器作为SQL引擎中最重要、最复杂的模块,被称为数据库的“大脑”,优化器产生的执行计划的优劣直接决定数据库的性能。
SQL引擎主要包括查询解析(parser)、查询分流(traffic cop)、查询优化(optimizer)、查询执行(executor)。parser源码目录为/src/common/backend/parser:
(二)transformStmt
/*
* 说明:检查删除stmt是否为plan_table。
* 参数:
* @in relname:要删除的对象名称。
* 如果对象名称为plan_table,则返回:true
*/
static bool checkDeleteStmtForPlanTable(const char* relname)
{
OnlyDeleteFromPlanTable = false;
const char* target_rel = V_PLAN_TABLE;
Oid plan_table_data_oid = RelnameGetRelid(T_PLAN_TABLE_DATA);
if (strcasecmp(relname, target_rel) == 0 && plan_table_data_oid != InvalidOid)
return true;
return false;
}
/*
* 说明:make SessionIdExpr for WhereClause
* 参数:
* @in cur_location:当前位置
* @out lexpr:左expr
* @out rexpr:正确的expr
* @out op_loc:操作位置
* Return: void
*/
static void makeSessionIdExpr(Node** lexpr, Node** rexpr, int* cur_location, int* op_loc)
{
/* 为session_id设置过滤条件 */
char* lcolname = "session_id";
ColumnRef* c = makeNode(ColumnRef);
c->fields = list_make1(makeString(lcolname));
c->location = *cur_location;
*cur_location = *cur_location + strlen("session_id=");
*op_loc = *cur_location - 1;
char* s_id = (char*)palloc0(SESSION_ID_LEN);
getSessionID(s_id,
IS_THREAD_POOL_WORKER ? u_sess->proc_cxt.MyProcPort->SessionStartTime : t_thrd.proc_cxt.MyStartTime,
IS_THREAD_POOL_WORKER ? u_sess->session_id : t_thrd.proc_cxt.MyProcPid);
A_Const* n = makeNode(A_Const);
n->val.type = T_String;
n->val.val.str = s_id;
n->location = *cur_location;
*cur_location = *cur_location + strlen(s_id) + 3;
*lexpr = (Node*)c;
*rexpr = (Node*)n;
}
/*
* 说明:make UserIdExpr for WhereClause。
* 参数:
* @in cur_location:当前位置
* @out lexpr:左expr
* @out rexpr:正确的expr
* @out op_loc:操作位置
* Return: void
*/
static void makeUserIdExpr(Node** lexpr, Node** rexpr, int* cur_location, int* op_loc)
{
/* 对于过滤条件user_id */
char* lcolname = "user_id";
ColumnRef* c = makeNode(ColumnRef);
c->fields = list_make1(makeString(lcolname));
c->location = *cur_location;
*cur_location = *cur_location + strlen("user_id=");
*op_loc = *cur_location - 1;
Oid user_id = GetCurrentUserId();
A_Const* n = makeNode(A_Const);
n->val.type = T_Integer;
n->val.val.ival = user_id;
n->location = *cur_location;
*lexpr = (Node*)c;
*rexpr = (Node*)n;
}
/*
* 说明:为plan_table detele stmt创建包含SessionIdExpr和UserIdExpr的WhereClause节点
* 参数:
* @in cur_location:当前位置
* @out where eclause:我们做的where eclause
* Return: void
*/
static void makeNewWhereClause(Node** whereClause, int* cur_location)
{
Node* lexpr = NULL;
Node* rexpr = NULL;
A_Expr* clause_1 = NULL;
A_Expr* clause_2 = NULL;
int op_loc = -1;
int and_loc = -1;
makeSessionIdExpr(&lexpr, &rexpr, cur_location, &op_loc);
clause_1 = makeSimpleA_Expr(AEXPR_OP, "=", lexpr, rexpr, op_loc);
and_loc = *cur_location;
*cur_location = *cur_location + strlen("AND ");
makeUserIdExpr(&lexpr, &rexpr, cur_location, &op_loc);
clause_2 = makeSimpleA_Expr(AEXPR_OP, "=", lexpr, rexpr, op_loc);
*whereClause = (Node*)makeA_Expr(AEXPR_AND, NIL, (Node*)clause_1, (Node*)clause_2, and_loc);
}
/*
* 说明:为plan_table detele stmt添加WhereClause。
* 参数:
* @in pstate: ParseState.
* @int stmt: delete stmt that user input.
* 返回:删除testmt加上新的WhereClause
*/
static DeleteStmt* addWhereClauseForPlanTable(ParseState* pstate, DeleteStmt* stmt)
{
int current_location = -1;
DeleteStmt* new_stmt = (DeleteStmt*)copyObject(stmt);
new_stmt->relation->relname = T_PLAN_TABLE_DATA;
current_location = strlen("delete from plan_table_data where ");
/* 创建一个新的WhereClause,其中包含session_id和user_id过滤器条件. */
if (new_stmt->whereClause == NULL) {
makeNewWhereClause(&new_stmt->whereClause, ¤t_location);
} else {
/* 在原来的WhereClause后面添加一个包含session_id和user_id的WhereClause过滤条件. */
makeNewWhereClause(&new_stmt->whereClause, ¤t_location);
new_stmt->whereClause = (Node*)makeA_Expr(AEXPR_AND, NIL, new_stmt->whereClause, stmt->whereClause, -1);
}
return new_stmt;
}
const void SendCommandIdForInsertCte(Query* qry, WithClause* withclause)
{
ListCell* tl = NULL;
/*
* For a WITH query that deletes from a parent table in the
* main query & inserts a row in the child table in the WITH query
* we need to use command ID communication to remote nodes in order
* to maintain global data visibility.
* For example
* CREATE TEMP TABLE parent ( id int, val text ) DISTRIBUTE BY REPLICATION;
* CREATE TEMP TABLE child ( ) INHERITS ( parent ) DISTRIBUTE BY REPLICATION;
* INSERT INTO parent VALUES ( 42, 'old' );
* INSERT INTO child VALUES ( 42, 'older' );
* WITH wcte AS ( INSERT INTO child VALUES ( 42, 'new' ) RETURNING id AS newid )
* DELETE FROM parent USING wcte WHERE id = newid;
* The last query gets translated into the following multi-statement
* transaction on the primary datanode
* (a) SELECT id, ctid FROM ONLY parent WHERE true
* (b) START TRANSACTION ISOLATION LEVEL read committed READ WRITE
* (c) INSERT INTO child (id, val) VALUES ($1, $2) RETURNING id -- (42, 'new')
* (d) DELETE FROM ONLY parent parent WHERE (parent.ctid = $1)
* (e) SELECT id, ctid FROM ONLY child parent WHERE true
* (f) DELETE FROM ONLY child parent WHERE (parent.ctid = $1)
* (g) COMMIT TRANSACTION
* The command id of the select in step (e), should be such that
* it does not see the insert of step (c)
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
foreach (tl, withclause->ctes) {
CommonTableExpr* cte = (CommonTableExpr*)lfirst(tl);
if (IsA(cte->ctequery, InsertStmt)) {
qry->has_to_save_cmd_id = true;
SetSendCommandId(true);
break;
}
}
}
}