一、概述
CreateRole 是 He3DB中的一项功能,用于创建新的数据库角色,并设置其权限和属性。角色可以是用户、组或其他权限实体。创建角色的过程中,系统需要更新多个系统目录,例如 pg_roles 和 pg_authid,以确保角色的存在与权限的正确设置。
二、CreateRole 命令的执行流程
- PostgresMain
- exec_simple_query →执行简单的 SQL 查询;
- StartTransactionCommand → 开始事务;
- pg_parse_query →解析为内部的抽象语法树(AST);
- PortalRun
- standard_ProcessUtility →权限检查和准备;
- CreateRloe(ParseState *pstate, CreateRoleStmt *stmt)→处理创建角色的具体逻辑;
- have_createrole_privilege→检查当前用户是否具有创建角色的权限;
- CommandCounterIncrement→增量更新当前的命令计数器;
- CommitTransactionCommand→ 提交当前事务;
- finish_xact_command→ 在事务结束时,执行必要的清理和关闭操作;
三、核心结构体介绍
(一) CreateRoleStmt 是 He3DB 中用于表示创建角色 (CREATE ROLE) 语句的结构体。它包含了与角色创建相关的各种信息,包括角色类型、角色名称和角色选项。下面是对 CreateRoleStmt 结构体各个成员的详细解释:
typedef struct CreateRoleStmt
{
NodeTag type;
RoleStmtType stmt_type; /* ROLE/USER/GROUP */
char *role; /* role name */
List *options; /* List of DefElem nodes */
} CreateRoleStmt;
-
NodeTag type 这个成员用于标识结构体的类型。在 He3DB 的抽象语法树 (AST) 中,每个节点都有一个类型标识符(NodeTag),用于区分不同类型的节点。CreateRoleStmt 会被标记为其特定的类型,以便在AST中进行正确的处理。
-
RoleStmtType stmt_type:
typedef enum RoleStmtType
{
ROLESTMT_ROLE,//表示一般的角色声明。这通常与创建、修改或删除角色的 SQL 语句相关。
ROLESTMT_USER,//表示用户声明。用户通常是具有登录权限的角色,因此这个类型特指那些允许登录的角色。
ROLESTMT_GROUP //表示组声明。组是多个角色的集合,主要用于简化权限管理,允许将权限授予一组角色。
} RoleStmtType;
-
char *role:这个成员是一个指向字符串的指针,用于存储要创建的角色的名称。例如,如果你要创建一个名为 myuser 的用户,role 就会保存 “myuser”。
-
这个成员是一个指向 List 类型的指针,表示角色创建时可能附带的一系列选项。每个选项都是 DefElem 类型的节点,可能包括诸如 LOGIN、SUPERUSER、PASSWORD 等角色选项。这使得角色的定义更加灵活,可以通过附加的选项来调整角色的属性。
(二) DefElem 是 He3DB 中用来描述 SQL 命令参数的结构体,通常用于定义带有特定选项或参数的命令。以下是对 DefElem 结构体的详细解析,包括它的字段及其含义。
typedef struct DefElem
{
NodeTag type; /* 节点类型标识 */
char *defnamespace; /* 定义的命名空间,NULL 表示没有指定命名空间 */
char *defname; /* 参数名称 */
Node *arg; /* 通常为 Integer、Float、String 或 TypeName 类型,表示参数的值 */
DefElemAction defaction; /* 指定的操作类型(如 SET、ADD、DROP 等) */
int location; /* 位置(token 位置),-1 表示位置未知 */
} DefElem;
四、核心代码解析
在 He3DB 的源代码中,CreateRole(@src\backend\commands\user.c) 函数是处理 CREATE ROLE SQL 语句的主要函数。该函数负责根据传入的 CreateRoleStmt 结构来创建一个新的数据库角色。下面讲分析该函数源码
/*
* CREATE ROLE
*/
Oid
CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
{
Relation pg_authid_rel;
TupleDesc pg_authid_dsc;
HeapTuple tuple;
Datum new_record[Natts_pg_authid] = {
0};
bool new_record_nulls[Natts_pg_authid] = {
0};
Oid roleid = 0;
ListCell *item = NULL;
ListCell *option = NULL;
char *password = NULL; /* user password */
bool issuper = false; /* Make the user a superuser? */
bool inherit = true; /* Auto inherit privileges? */
bool createrole = false; /* Can this user create roles? */
bool createdb = false; /* Can the user create databases? */
bool canlogin = false; /* Can this user login? */
bool isreplication = false; /* Is this a replication role? */
bool bypassrls = false; /* Is this a row security enabled role? */
int connlimit = -1; /* maximum connections allowed */
List *addroleto = NIL; /* roles to make this a member of */
List *rolemembers = NIL; /* roles to be members of this role */
List *adminmembers = NIL; /* roles to be admins of this role */
char *validUntil = NULL; /* time the login is valid until */
Datum validUntil_datum; /* same, as timestamptz Datum */
bool validUntil_null = false;
DefElem *dpassword = NULL;
DefElem *dissuper = NULL;
DefElem *dinherit = NULL;
DefElem *dcreaterole = NULL;
DefElem *dcreatedb = NULL;
DefElem *dcanlogin = NULL;
DefElem *disreplication = NULL;
DefElem *dconnlimit = NULL;
DefElem *daddroleto = NULL;
DefElem *drolemembers = NULL;
DefElem *dadminmembers = NULL;
DefElem *dvalidUntil = NULL;
DefElem *dbypassRLS = NULL;
/* The defaults can vary depending on the original statement type */
switch (stmt->stmt_type)
{
case ROLESTMT_ROLE:
break;
case ROLESTMT_USER:
canlogin = true;
/* may eventually want inherit to default to false here */
break;
case ROLESTMT_GROUP:
break;
}
/* Extract options from the statement node tree */
foreach(option, stmt->options)
{
DefElem *defel = (DefElem *) lfirst(option);
if (strcmp(defel->defname, "password") == 0)
{
if (dpassword)
errorConflictingDefElem(defel, pstate);
dpassword = defel;
}
else if (strcmp(defel->defname, "sysid") == 0)
{
ereport(NOTICE,
(errmsg("SYSID can no longer be specified")));
}
else if (strcmp(defel->defname, "superuser") == 0)
{
if (dissuper)
errorConflictingDefElem(defel, pstate);
dissuper = defel;
}
else if (strcmp(defel->defname, "inherit") == 0)
{
if (dinherit)
errorConflictingDefElem(defel, pstate);
dinherit = defel;
}
else if (strcmp(defel->defname, "createrole") == 0)
{
if (dcreaterole)
errorConflictingDefElem(defel, pstate);
dcreaterole = defel;
}
else if (strcmp(defel->defname, "createdb") == 0)
{
if (dcreatedb)
errorConflictingDefElem(defel, pstate);
dcreatedb = defel;
}
else if (strcmp(defel->defname, "canlogin") == 0)
{
if (dcanlogin)
errorConflictingDefElem(defel, pstate);
dcanlogin = defel;
}
else if (strcmp(defel->defname, "isreplication") == 0)
{
if (disreplication)
errorConflictingDefElem(defel, pstate);
disreplication = defel;
}
else if (strcmp(defel->defname, "connectionlimit") == 0)
{
if (dconnlimit)
errorConflictingDefElem(defel, pstate);
dconnlimit = defel;
}
else if (strcmp(defel->defname, "addroleto") == 0)
{
if (daddroleto)
errorConflictingDefElem(defel, pstate);
daddroleto = defel;
}
else if (strcmp(defel->defname, "rolemembers") == 0)
{
if (drolemembers)
errorConflictingDefElem(defel, pstate);
drolemembers = defel;
}
else if (strcmp(defel->defname, "adminmembers") == 0)
{
if (dadminmembers)
errorConflictingDefElem(defel, pstate);
dadminmembers = defel;
}
else if (strcmp(defel->defname, "validUntil") == 0)
{
if (dvalidUntil)
errorConflictingDefElem(defel, pstate);
dvalidUntil = defel;
}
else if (strcmp(defel->defname, "bypassrls") ==