目录结构
注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:
1、参考书籍:++《PostgreSQL数据库内核分析》++
2、参考书籍:++《数据库事务处理的艺术:事务管理与并发控制》++
3、++PostgreSQL数据库仓库链接,点击前往++
4、++日本著名PostgreSQL数据库专家 铃木启修 网站主页,点击前往++
5、参考书籍:++《PostgreSQL中文手册》++
6、++参考书籍:《PostgreSQL指南:内幕探索》,点击前往++
1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
2、本文目的:开源共享 抛砖引玉 一起学习
3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)
5、本文内容基于PostgreSQL master源码开发而成
深入理解PostgreSQL数据库之 pg_get_acl 的使用和实现
文章快速说明索引
学习目标:
做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于PostgreSQL数据库的兼容功能开发,近段时间 将着重于学习分享Postgres的基础知识和实践内幕。
学习内容:(详见目录)
1、深入理解PostgreSQL数据库之pg_get_acl的使用和实现
学习时间:
2024年12月01日 17:14:30
学习产出:
1、PostgreSQL数据库基础知识回顾 1个
2、CSDN 技术博客 1篇
3、PostgreSQL数据库内核深入学习
注:下面我们所有的学习环境是Centos8+PostgreSQL master+Oracle19C+MySQL8.0
sql
postgres=# select version();
version
------------------------------------------------------------------------------------------------------------
PostgreSQL 17devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-21), 64-bit
(1 row)
postgres=#
#-----------------------------------------------------------------------------#
SQL> select * from v$version;
BANNER Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production
BANNER_FULL Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production Version 19.17.0.0.0
BANNER_LEGACY Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production
CON_ID 0
#-----------------------------------------------------------------------------#
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.27 |
+-----------+
1 row in set (0.06 sec)
mysql>
功能使用背景说明
pg_get_acl 可用于检索和检查与数据库对象关联的权限,而无需查看特定目录。
sql
postgres=# select version();
version
---------------------------------------------------------------------------------
PostgreSQL 18devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 13.1.0, 64-bit
(1 row)
postgres=# \x
Expanded display is on.
postgres=#
postgres=# \df+ pg_get_acl
List of functions
-[ RECORD 1 ]-------+-----------------------------------------
Schema | pg_catalog
Name | pg_get_acl
Result data type | aclitem[]
Argument data types | classid oid, objid oid, objsubid integer
Type | func
Volatility | stable
Parallel | safe
Owner | postgres
Security | invoker
Access privileges |
Language | internal
Internal name | pg_get_acl
Description | get ACL for SQL object
postgres=# \x
Expanded display is off.
postgres=#
案例一,如下:
sql
postgres=# CREATE ROLE alice;
CREATE ROLE
postgres=# CREATE TABLE test (id int);
CREATE TABLE
postgres=# GRANT SELECT ON test TO alice;
GRANT
postgres=#
postgres=# DROP ROLE alice;
2024-12-02 00:09:00.859 PST [27893] ERROR: role "alice" cannot be dropped because some objects depend on it
2024-12-02 00:09:00.859 PST [27893] DETAIL: privileges for table test
2024-12-02 00:09:00.859 PST [27893] STATEMENT: DROP ROLE alice;
ERROR: role "alice" cannot be dropped because some objects depend on it
DETAIL: privileges for table test
postgres=#
postgres=# select pg_get_acl(classid, objid, objsubid) from pg_shdepend;
pg_get_acl
-----------------------------------------------
{postgres=arwdDxtm/postgres,alice=r/postgres}
(1 row)
postgres=# SELECT pg_get_acl('pg_class'::regclass, 'test'::regclass::oid, 0);
pg_get_acl
-----------------------------------------------
{postgres=arwdDxtm/postgres,alice=r/postgres}
(1 row)
postgres=# GRANT DELETE ON test TO alice;
GRANT
postgres=# SELECT pg_get_acl('pg_class'::regclass, 'test'::regclass::oid, 0);
pg_get_acl
------------------------------------------------
{postgres=arwdDxtm/postgres,alice=rd/postgres}
(1 row)
postgres=# select * from pg_shdepend ;
dbid | classid | objid | objsubid | refclassid | refobjid | deptype
------+---------+-------+----------+------------+----------+---------
5 | 1259 | 16385 | 0 | 1260 | 16384 | a
(1 row)
postgres=# select pg_get_acl(classid, objid, objsubid) from pg_shdepend;
pg_get_acl
------------------------------------------------
{postgres=arwdDxtm/postgres,alice=rd/postgres}
(1 row)
postgres=# REVOKE SELECT ON test FROM alice;
REVOKE
postgres=# select * from pg_shdepend ;
dbid | classid | objid | objsubid | refclassid | refobjid | deptype
------+---------+-------+----------+------------+----------+---------
5 | 1259 | 16385 | 0 | 1260 | 16384 | a
(1 row)
postgres=# select pg_get_acl(classid, objid, objsubid) from pg_shdepend;
pg_get_acl
-----------------------------------------------
{postgres=arwdDxtm/postgres,alice=d/postgres}
(1 row)
postgres=# \dp test
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+------+-------+----------------------------+-------------------+----------
public | test | table | postgres=arwdDxtm/postgres+| |
| | | alice=d/postgres | |
(1 row)
postgres=# \du
List of roles
Role name | Attributes
-----------+------------------------------------------------------------
alice | Cannot login
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS
postgres=# SET SESSION AUTHORIZATION alice ;
SET
postgres=> \dp test
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+------+-------+----------------------------+-------------------+----------
public | test | table | postgres=arwdDxtm/postgres+| |
| | | alice=d/postgres | |
(1 row)
postgres=> select * from test ;
2024-12-02 01:33:49.853 PST [32897] ERROR: permission denied for table test
2024-12-02 01:33:49.853 PST [32897] STATEMENT: select * from test ;
ERROR: permission denied for table test
postgres=>
案例二,如下:
sql
postgres=# CREATE ROLE zhangsan;
CREATE ROLE
postgres=# CREATE TABLE test2 (one int, two int unique, three int, four int unique);
CREATE TABLE
postgres=# GRANT SELECT (one), INSERT (two), UPDATE (three) ON test2 TO zhangsan ;
GRANT
postgres=# select pg_get_acl('pg_class'::regclass, 'test2'::regclass::oid, 1);
pg_get_acl
-----------------------
{zhangsan=r/postgres}
(1 row)
postgres=# select pg_get_acl('pg_class'::regclass, 'test2'::regclass::oid, 2);
pg_get_acl
-----------------------
{zhangsan=a/postgres}
(1 row)
postgres=# select pg_get_acl('pg_class'::regclass, 'test2'::regclass::oid, 3);
pg_get_acl
-----------------------
{zhangsan=w/postgres}
(1 row)
postgres=# select pg_get_acl('pg_class'::regclass, 'test2'::regclass::oid, 4);
pg_get_acl
------------
(1 row)
postgres=# select pg_get_acl('pg_class'::regclass, 'test2'::regclass::oid, 0);
pg_get_acl
------------
(1 row)
postgres=# select pg_get_acl('pg_class'::regclass, 'test2'::regclass::oid, 5);
pg_get_acl
------------
(1 row)
postgres=# select * from pg_shdepend ;
dbid | classid | objid | objsubid | refclassid | refobjid | deptype
------+---------+-------+----------+------------+----------+---------
5 | 1259 | 16389 | 0 | 1260 | 16388 | a
5 | 1259 | 16393 | 1 | 1260 | 16392 | a
5 | 1259 | 16393 | 2 | 1260 | 16392 | a
5 | 1259 | 16393 | 3 | 1260 | 16392 | a
(4 rows)
postgres=# select pg_get_acl(classid, objid, objsubid) from pg_shdepend;
pg_get_acl
-----------------------------------------------
{postgres=arwdDxtm/postgres,alice=d/postgres}
{zhangsan=r/postgres}
{zhangsan=a/postgres}
{zhangsan=w/postgres}
(4 rows)
postgres=# \du
List of roles
Role name | Attributes
-----------+------------------------------------------------------------
alice | Cannot login
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS
zhangsan | Cannot login
postgres=# \dp test2
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+-------+-------+-------------------+-----------------------+----------
public | test2 | table | | one: +|
| | | | zhangsan=r/postgres+|
| | | | two: +|
| | | | zhangsan=a/postgres+|
| | | | three: +|
| | | | zhangsan=w/postgres |
(1 row)
postgres=# table test2 ;
one | two | three | four
-----+-----+-------+------
(0 rows)
postgres=# SET SESSION AUTHORIZATION zhangsan ;
SET
postgres=> \dp test2
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+-------+-------+-------------------+-----------------------+----------
public | test2 | table | | one: +|
| | | | zhangsan=r/postgres+|
| | | | two: +|
| | | | zhangsan=a/postgres+|
| | | | three: +|
| | | | zhangsan=w/postgres |
(1 row)
postgres=> table test2 ;
2024-12-02 01:36:51.234 PST [33353] ERROR: permission denied for table test2
2024-12-02 01:36:51.234 PST [33353] STATEMENT: table test2 ;
ERROR: permission denied for table test2
postgres=> select one from test2;
one
-----
(0 rows)
postgres=> select four from test2;
2024-12-02 01:37:10.117 PST [33353] ERROR: permission denied for table test2
2024-12-02 01:37:10.117 PST [33353] STATEMENT: select four from test2;
ERROR: permission denied for table test2
postgres=>
sql
postgres=# GRANT DELETE ON test2 TO zhangsan;
GRANT
postgres=# \dp test2
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+-------+-------+----------------------------+-----------------------+----------
public | test2 | table | postgres=arwdDxtm/postgres+| one: +|
| | | zhangsan=d/postgres | zhangsan=r/postgres+|
| | | | two: +|
| | | | zhangsan=a/postgres+|
| | | | three: +|
| | | | zhangsan=w/postgres |
(1 row)
postgres=# SET SESSION AUTHORIZATION zhangsan ;
SET
postgres=> \dp test2
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+-------+-------+----------------------------+-----------------------+----------
public | test2 | table | postgres=arwdDxtm/postgres+| one: +|
| | | zhangsan=d/postgres | zhangsan=r/postgres+|
| | | | two: +|
| | | | zhangsan=a/postgres+|
| | | | three: +|
| | | | zhangsan=w/postgres |
(1 row)
postgres=> select pg_get_acl(classid, objid, objsubid) from pg_shdepend;
pg_get_acl
--------------------------------------------------
{postgres=arwdDxtm/postgres,alice=d/postgres}
{zhangsan=r/postgres}
{zhangsan=a/postgres}
{zhangsan=w/postgres}
{postgres=arwdDxtm/postgres,zhangsan=d/postgres}
(5 rows)
postgres=> select pg_get_acl('pg_class'::regclass, 'test2'::regclass::oid, 0);
pg_get_acl
--------------------------------------------------
{postgres=arwdDxtm/postgres,zhangsan=d/postgres}
(1 row)
postgres=>
尤其是与其他系统表结合使用的时候,更加方便高效。如下:
sql
postgres=# SELECT *,
pg_identify_object(classid, objid, objsubid) AS object,
pg_identify_object(refclassid, refobjid, 0) AS ref_object,
pg_get_acl(classid, objid, objsubid)
FROM pg_shdepend;
dbid | classid | objid | objsubid | refclassid | refobjid | deptype | object | ref_object | pg_get_acl
------+---------+-------+----------+------------+----------+---------+--------------------------------------------------+---------------------------+--------------------------------------------------
5 | 1259 | 16389 | 0 | 1260 | 16388 | a | (table,public,test,public.test) | (role,,alice,alice) | {postgres=arwdDxtm/postgres,alice=d/postgres}
5 | 1259 | 16393 | 1 | 1260 | 16392 | a | ("table column",public,test2,public.test2.one) | (role,,zhangsan,zhangsan) | {zhangsan=r/postgres}
5 | 1259 | 16393 | 2 | 1260 | 16392 | a | ("table column",public,test2,public.test2.two) | (role,,zhangsan,zhangsan) | {zhangsan=a/postgres}
5 | 1259 | 16393 | 3 | 1260 | 16392 | a | ("table column",public,test2,public.test2.three) | (role,,zhangsan,zhangsan) | {zhangsan=w/postgres}
5 | 1259 | 16393 | 0 | 1260 | 16392 | a | (table,public,test2,public.test2) | (role,,zhangsan,zhangsan) | {postgres=arwdDxtm/postgres,zhangsan=d/postgres}
(5 rows)
postgres=#
功能实现源码解析
下面调试一下,如下:
sql
postgres=# SELECT pg_get_acl('pg_class'::regclass, 'test'::regclass::oid, 0);
pg_get_acl
-----------------------------------------------
{postgres=arwdDxtm/postgres,alice=d/postgres}
(1 row)
postgres=#
简单分析一下:
- 开表
pg_class
- 查询出
test
表对应的tuple
- 获取并返回
relacl
列 - ...
sql
postgres=# SELECT pg_get_acl('pg_class'::regclass, 'test'::regclass::oid, 1);
pg_get_acl
------------
(1 row)
postgres=#
如果处理关系的属性(设置了 objsubid),则从 pg_attribute 中检索 ACL。
如上其实就是等同于执行了如下:
sql
postgres=# select attacl from pg_attribute where attrelid = 16389 and attnum = 1;
attacl
--------
(1 row)
postgres=#
同理,如下:
sql
postgres=# SELECT pg_get_acl('pg_class'::regclass, 'test2'::regclass::oid, 0);
pg_get_acl
--------------------------------------------------
{postgres=arwdDxtm/postgres,zhangsan=d/postgres}
(1 row)
postgres=# SELECT pg_get_acl('pg_class'::regclass, 'test2'::regclass::oid, 1);
pg_get_acl
-----------------------
{zhangsan=r/postgres}
(1 row)
postgres=# select attacl from pg_attribute where attrelid = 16393 and attnum = 1;
attacl
-----------------------
{zhangsan=r/postgres}
(1 row)
postgres=# select relacl from pg_class where oid = 16393;
relacl
--------------------------------------------------
{postgres=arwdDxtm/postgres,zhangsan=d/postgres}
(1 row)
postgres=#
这里示例就不再调试了,接下来看一下函数的返回值问题:
c
// src/include/catalog/pg_proc.dat
{ oid => '8730', descr => 'get ACL for SQL object',
proname => 'pg_get_acl', provolatile => 's', prorettype => '_aclitem',
proargtypes => 'oid oid int4', proargnames => '{classid,objid,objsubid}',
prosrc => 'pg_get_acl' },
调试如下:
此时的函数堆栈,如下:
c
aclitemout(FunctionCallInfo fcinfo) (\home\postgres\postgres\src\backend\utils\adt\acl.c:652)
FunctionCall1Coll(FmgrInfo * flinfo, Oid collation, Datum arg1) (\home\postgres\postgres\src\backend\utils\fmgr\fmgr.c:1139)
OutputFunctionCall(FmgrInfo * flinfo, Datum val) (\home\postgres\postgres\src\backend\utils\fmgr\fmgr.c:1685)
array_out(FunctionCallInfo fcinfo) (\home\postgres\postgres\src\backend\utils\adt\arrayfuncs.c:1134)
FunctionCall1Coll(FmgrInfo * flinfo, Oid collation, Datum arg1) (\home\postgres\postgres\src\backend\utils\fmgr\fmgr.c:1139)
OutputFunctionCall(FmgrInfo * flinfo, Datum val) (\home\postgres\postgres\src\backend\utils\fmgr\fmgr.c:1685)
printtup(TupleTableSlot * slot, DestReceiver * self) (\home\postgres\postgres\src\backend\access\common\printtup.c:360)
ExecutePlan(EState * estate, PlanState * planstate, _Bool use_parallel_mode, CmdType operation, _Bool sendTuples, uint64 numberTuples, ScanDirection direction, DestReceiver * dest, _Bool execute_once) (\home\postgres\postgres\src\backend\executor\execMain.c:1686)
standard_ExecutorRun(QueryDesc * queryDesc, ScanDirection direction, uint64 count, _Bool execute_once) (\home\postgres\postgres\src\backend\executor\execMain.c:362)
ExecutorRun(QueryDesc * queryDesc, ScanDirection direction, uint64 count, _Bool execute_once) (\home\postgres\postgres\src\backend\executor\execMain.c:303)
PortalRunSelect(Portal portal, _Bool forward, long count, DestReceiver * dest) (\home\postgres\postgres\src\backend\tcop\pquery.c:924)
PortalRun(Portal portal, long count, _Bool isTopLevel, _Bool run_once, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc) (\home\postgres\postgres\src\backend\tcop\pquery.c:768)
exec_simple_query(const char * query_string) (\home\postgres\postgres\src\backend\tcop\postgres.c:1283)
...
至此_aclitem
类型的翻译工作就完成了!