PostgreSQL的学习心得和知识总结(一百四十一)|深入理解PostgreSQL数据库数据库角色的使用及预定义角色的原理


注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:

1、参考书籍:《PostgreSQL数据库内核分析》
2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》
3、PostgreSQL数据库仓库链接,点击前往
4、日本著名PostgreSQL数据库专家 铃木启修 网站主页,点击前往
5、参考书籍:《PostgreSQL中文手册》
6、参考书籍:《PostgreSQL指南:内幕探索》,点击前往
7、参考书籍:《事务处理 概念与技术》


1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
2、本文目的:开源共享 抛砖引玉 一起学习
3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)
5、本文内容基于PostgreSQL master源码开发而成


PostgreSQL数据库数据库角色的使用及预定义角色的原理

  • 文章快速说明索引
  • 功能使用背景说明
    • 数据库角色
    • 角色属性
    • 角色成员关系
    • 删除角色
    • 预定义角色
    • 函数和触发器安全性
  • 预定义角色深入
    • 相关系统表和视图
    • pg_read_all_stats
    • 新增预定义角色



文章快速说明索引

学习目标:

做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于PostgreSQL数据库的兼容功能开发,近段时间 将着重于学习分享Postgres的基础知识和实践内幕。


学习内容:(详见目录)

1、PostgreSQL数据库数据库角色的使用及预定义角色的原理


学习时间:

2024年04月30日 21:32:22


学习产出:

1、PostgreSQL数据库基础知识回顾 1个
2、CSDN 技术博客 1篇
3、PostgreSQL数据库内核深入学习


注:下面我们所有的学习环境是Centos8+PostgreSQL master +Oracle19C+MySQL8.0

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>

功能使用背景说明

数据库角色

首先看一下官方文档的说明,如下:

PostgreSQL使用角色的概念管理数据库访问权限。一个角色可以被看成是一个数据库用户或者是一个数据库用户组,这取决于角色被怎样设置。角色可以拥有数据库对象(例如,表和函数)并且能够把那些对象上的权限赋予给其他角色来控制谁能访问哪些对象。此外,还可以把一个角色中的成员资格授予给另一个角色,这样允许成员角色使用被赋予给另一个角色的权限

角色的概念把“用户”和“组”的概念都包括在内。在PostgreSQL版本 8.1 之前,用户和组是完全不同的两种实体,但是现在只有角色。任意角色都可以扮演用户、组或者两者

数据库角色在概念上已经完全与操作系统用户独立开来。事实上可能维护一个对应关系会比较方便,但是这并非必需。数据库角色在一个数据库集簇安装范围内是全局的(而不是独立数据库内)。要创建一个角色,可使用CREATE ROLE SQL 命令:

CREATE ROLE name;

name遵循 SQL 标识符的规则:或是未经装饰没有特殊字符,或是用双引号包围(实际上,你将总是给该命令要加上额外选项,例如LOGIN。更多细节可见下文)。要移除一个已有的角色,使用相似的DROP ROLE命令:

DROP ROLE name;

为了方便,createuser和dropuser程序被提供作为这些 SQL 命令的包装器,它们可以从 shell 命令行调用:

createuser name
dropuser name

要决定现有角色的集合,检查pg_roles系统目录,例如:

[postgres@localhost:~/test/bin]$ ./pg_ctl start -D test/
waiting for server to start....2024-04-30 00:44:52.507 PDT [27569] LOG:  starting PostgreSQL 17devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-21), 64-bit
2024-04-30 00:44:52.508 PDT [27569] LOG:  listening on IPv6 address "::1", port 5432
2024-04-30 00:44:52.508 PDT [27569] LOG:  listening on IPv4 address "127.0.0.1", port 5432
2024-04-30 00:44:52.512 PDT [27569] LOG:  listening on Unix socket "/tmp/.s.PGSQL.5432"
2024-04-30 00:44:52.532 PDT [27572] LOG:  database system was shut down at 2024-04-30 00:39:54 PDT
2024-04-30 00:44:52.537 PDT [27569] LOG:  database system is ready to accept connectionsdone
server started
[postgres@localhost:~/test/bin]$ ./psql 
psql (17devel)
Type "help" for help.postgres=# select * from pg_roles ;rolname           | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolconnlimit | rolpassword | rolvaliduntil | rolbypassrls | rolconfig | oid  
-----------------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+-------------+---------------+--------------+-----------+------postgres                    | t        | t          | t             | t           | t           | t              |           -1 | ********    |               | t            |           |   10pg_database_owner           | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 6171pg_read_all_data            | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 6181pg_write_all_data           | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 6182pg_monitor                  | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 3373pg_read_all_settings        | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 3374pg_read_all_stats           | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 3375pg_stat_scan_tables         | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 3377pg_read_server_files        | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 4569pg_write_server_files       | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 4570pg_execute_server_program   | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 4571pg_signal_backend           | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 4200pg_checkpoint               | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 4544pg_maintain                 | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 9256pg_use_reserved_connections | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 4550pg_create_subscription      | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           | 6304
(16 rows)postgres=#

psql程序的\du元命令也可以用来列出现有角色,如下:

[postgres@localhost:~/test/bin]$ ./psql 
psql (17devel)
Type "help" for help.postgres=# \duList of rolesRole name |                         Attributes                         
-----------+------------------------------------------------------------postgres  | Superuser, Create role, Create DB, Replication, Bypass RLSpostgres=#

为了引导数据库系统,一个刚刚被初始化好的系统总是包含一个预定义角色。这个角色总是一个superuser,并且默认情况下(除非在运行initdb时修改)它的名字和初始化数据库集簇的操作系统用户相同。习惯上,这个角色将被命名为postgres。为了创建更多角色,你首先必须以初始角色的身份连接。

每一个到数据库服务器的连接都是使用某个特定角色名建立的,并且这个角色决定发起连接的命令的初始访问权限。要使用一个特定数据库连接的角色名由客户端指示,该客户端以一种应用相关的风格发起连接请求。例如,psql程序使用-U命令行选项来指定要以哪个角色连接。很多应用假定该名字默认是当前操作系统用户(包括createuser和psql)。因此在角色和操作系统用户之间维护一个名字对应关系通常是很方便的。

一个给定客户端连接能够用来连接的数据库角色的集合由该客户端的认证设置决定,这些在其他章中有解释(因此,一个客户端不止限于以匹配其操作系统用户的角色连接,就像一个人的登录名不需要匹配她的真实名字一样)。因为角色身份决定一个已连接客户端可用的权限集合,在设置一个多用户环境时要小心地配置权限。


角色属性

一个数据库角色可以有一些属性,它们定义角色的权限并且与客户端认证系统交互。

login privilege:只有具有LOGIN属性的角色才能被用于一个数据库连接的初始角色名称。一个带有LOGIN属性的角色可以被认为和一个 数据库用户 相同。要创建一个带有登录权限的角色,使用两者之一:

CREATE ROLE name LOGIN;
CREATE USER name;

CREATE USER和CREATE ROLE等效,除了CREATE USER默认假定有LOGIN,而CREATE ROLE不这样认为,如下:

// src/backend/parser/gram.y/******************************************************************************* Create a new Postgres DBMS user (role with implied login ability)******************************************************************************/CreateUserStmt:CREATE USER RoleId opt_with OptRoleList{CreateRoleStmt *n = makeNode(CreateRoleStmt);n->stmt_type = ROLESTMT_USER;n->role = $3;n->options = $5;$$ = (Node *) n;};
.../******************************************************************************* Create a new Postgres DBMS role******************************************************************************/CreateRoleStmt:CREATE ROLE RoleId opt_with OptRoleList{CreateRoleStmt *n = makeNode(CreateRoleStmt);n->stmt_type = ROLESTMT_ROLE;n->role = $3;n->options = $5;$$ = (Node *) n;};

这两者的区别,如下:

// src/backend/commands/user.c/** CREATE ROLE*/
Oid
CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
{.../* 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;}...
}

superuser status:一个数据库超级用户会绕开所有权限检查,除了登入的权利。这是一个危险的权限并且应该小心使用,最好用一个不是超级用户的角色来完成你的大部分工作。要创建一个新数据库超级用户,使用CREATE ROLE name SUPERUSER。你必须作为一个超级用户来完成这些。

database creation:一个角色必须被显式给予权限才能创建数据库(除了超级用户,因为它们会绕开所有权限检查)。要创建这样一个角色,使用CREATE ROLE name CREATEDB。

role creation:一个角色必须被显式给予权限才能创建更多角色(除了超级用户,因为它们会绕开所有权限检查)。要创建这样一个角色,使用CREATE ROLE name CREATEROLE。一个带有CREATEROLE权限的角色也可以修改和删除其他角色,还可以授予或回收角色中的成员关系。然而,要创建、修改、删除或修改一个超级用户角色的成员关系,需要以超级用户的身份操作。CREATEROLE不足以完成这一切。

initiating replication:一个角色必须被显式给予权限才能发起流复制(除了超级用户,因为它们会绕开所有权限检查)。一个被用于流复制的角色必须也具有LOGIN权限。要创建这样一个角色,使用CREATE ROLE name REPLICATION LOGIN。

password:只有当客户端认证方法要求用户在连接数据库时提供一个口令时,一个口令才有意义。password和md5认证方法使用口令。数据库口令与操作系统命令独立。在角色创建时指定一个口令:CREATE ROLE name PASSWORD ‘string’。

注:一个好习惯是创建一个具有CREATEDB和CREATEROLE权限的角色,而不是创建一个超级用户,并且然后用这个角色来完成对数据库和角色的例行管理。这种方法避免了在非必要时作为超级用户操作任务的风险。


在创建后可以用ALTER ROLE修改一个角色属性。一个角色也可以有角色相关的默认值。例如,如果出于某些原因你希望在每次连接时禁用索引扫描(提示:不是好主意),你可以使用:

ALTER ROLE myname SET enable_indexscan TO off;

这将保存设置(但是不会立刻设置它)。在这个角色的后续连接中,它就表现得像在会话开始之前执行过SET enable_indexscan TO off。你也可以在会话期间改变该设置,它将只是作为默认值。要移除一个角色相关的默认设置,使用ALTER ROLE rolename RESET varname。注意附加到没有LOGIN权限的角色的角色相关默认值相当无用,因为它们从不会被调用。

:后面这个我会另起一篇博客去详解!


角色成员关系

把用户分组在一起来便于管理权限常常很方便:那样,权限可以被授予一整个组或从一整个组回收。在PostgreSQL中通过创建一个表示组的角色来实现,并且然后将在该组角色中的成员关系授予给单独的用户角色。

要建立一个组角色,首先创建该角色:

CREATE ROLE name;

通常被用作一个组的角色不需要有LOGIN属性,不过如果你希望你也可以设置它。一旦组角色存在,你可以使用GRANT 和 REVOKE 命令增加和移除成员:

GRANT group_role TO role1, ... ;
REVOKE group_role FROM role1, ... ;

你也可以为其他组角色授予成员关系(因为组角色和非组角色之间其实没有任何区别)。数据库将不会让你设置环状的成员关系。另外,不允许把一个角色中的成员关系授予给PUBLIC。

组角色的成员可以以两种方式使用角色的权限:

  • 第一,一个组的每一个成员可以显式地做SET ROLE来临时 成为 组角色。在这种状态中,数据库会话可以访问组角色而不是原始登录角色的权限,并且任何被创建的数据库对象被认为属于组角色而不是登录角色
  • 第二,有INHERIT属性的成员角色自动地具有它们所属角色的权限,包括任何组角色继承得到的权限。作为一个例子,假设我们已经有:
CREATE ROLE joe LOGIN INHERIT;
CREATE ROLE admin NOINHERIT;
CREATE ROLE wheel NOINHERIT;
GRANT admin TO joe;
GRANT wheel TO admin;

在作为角色joe连接后,一个数据库会话将立即拥有直接授予给joe的权限,外加任何授予给admin的权限,因为joe 继承了 admin的权限。然而,授予给wheel的权限不可用,因为即使joe是wheel的一个间接成员,但是该成员关系是通过带NOINHERIT属性的admin得到的。在:

SET ROLE admin;

之后,该会话将只拥有授予给admin的权限,但是没有授予给joe的权限。在执行:

SET ROLE wheel;

之后,该会话将只拥有授予给wheel的权限,但是没有授予给joe或admin的权限。初始的权限状态可以使用下面命令之一恢复:

SET ROLE joe;
SET ROLE NONE;
RESET ROLE;

注意:SET ROLE命令总是允许选择原始登录角色的直接或间接组角色。因此,在上面的例子中,在成为wheel之前不必先成为admin。

注意:在 SQL 标准中,用户和角色之间的区别很清楚,并且用户不会自动继承权限而角色会继承。这种行为在PostgreSQL中也可以实现:为要用作 SQL 角色的角色给予INHERIT属性,而为要用作 SQL 用户的角色给予NOINHERIT属性。不过,为了向后兼容 8.1 以前的发布(在其中用户总是拥有它们所在组的权限),PostgreSQL默认给所有的角色INHERIT属性。


角色属性LOGIN、SUPERUSER、CREATEDB和CREATEROLE可以被认为是一种特殊权限,但是它们从来不会像数据库对象上的普通权限那样被继承。要使用这些属性,你必须实际SET ROLE到一个有这些属性之一的特定角色。继续上述例子,我们可以选择授予CREATEDB和CREATEROLE给admin角色。然后一个以joe角色连接的会话将不会立即有这些权限,只有在执行了SET ROLE admin之后才会拥有。

要销毁一个组角色,使用DROP ROLE:

DROP ROLE name;

任何在该组角色中的成员关系会被自动撤销(但是成员角色不会受到影响)。


删除角色

由于角色可以拥有数据库对象并且能持有访问其他对象的特权,删除一个角色常常并非一次DROP ROLE就能解决。任何被该用户所拥有的对象必须首先被删除或者转移给其他拥有者,并且任何已被授予给该角色的权限必须被收回。

对象的拥有关系可以使用重新分配拥有的命令一次转移出去,例如:

ALTER TABLE bobs_table OWNER TO alice;

此外,重新分配拥有的命令可以被用来把要被删除的角色所拥有的所有对象的拥有关系转移给另一个角色。由于 重新分配拥有 不能访问其他数据库中的对象,有必要在每一个包含该角色所拥有对象的数据库中运行该命令(注意第一个这样的 重新分配拥有 将更改任何在数据库间共享的该角色拥有的对象的拥有关系,即数据库或者表空间)。

一旦任何有价值的对象已经被转移给新的拥有者,任何由被删除角色拥有的剩余对象就可以用DROP OWNED命令删除。再次,由于这个命令不能访问其他数据库中的对象,有必要在每一个包含该角色所拥有对象的数据库中运行该命令。还有,DROP OWNED将不会删除整个数据库或者表空间,因此如果该角色拥有任何还没有被转移给新拥有者的数据库或者表空间,有必要手工删除它们。

DROP OWNED 还负责删除授予目标角色的不属于目标角色的对象的任何权限。因为REASSIGN OWNED不会涉及这类对象,通常有必要运行REASSIGN OWNED(重新分配拥有的)和 DROP OWNED(按照这个顺序!)以完全地移除要被删除角色的从属物。

总之,移除曾经拥有过对象的角色的方法是:

REASSIGN OWNED BY doomed_role TO successor_role;
DROP OWNED BY doomed_role;-- 在集簇中的每一个数据库中重复上述命令
DROP ROLE doomed_role;

如果不是所有的拥有对象都被转移给了同一个后继拥有者,最好手工处理异常然后执行上述步骤直到结束。如果在依赖对象还存在时尝试了DROP ROLE,它将发出消息标识哪些对象需要被重新授予或者删除。


预定义角色

PostgreSQL提供了一组预定义角色,它们提供对特定的、通常需要的、需要特权的功能和信息的访问。管理员(包括具有CREATEROLE 权限的角色)可以把这些角色GRANT给其环境中的用户或者其他角色,让这些用户能够访问指定的功能和信息。

下表中描述了预定义的角色。注意由于额外功能的增加,每一种角色相关的权限可能会在未来被改变。管理员应该关注发行注记中提到的这方面的变化。

角色允许的访问
pg_read_all_data读所有数据(表,视图,序列),如同在那些对象上有 SELECT 权限,在所有模式上有USAGE权限,即使没有显式拥有它。这个角色没有角色属性 BYPASSRLS 集。如果使用了RLS,管理员可能希望设置角色上的被GRANTED给该角色的 BYPASSRLS
pg_write_all_data写全部数据(表,视图,序列),如果在那些对象上有 INSERT、UPDATE和DELETE权限,以及在全部模式上有USAGE权限,即使没有显式拥有它。这个角色没有角色属性 BYPASSRLS 集。如果使用了RLS,管理员可能希望设置角色上的被GRANTED给该角色的 BYPASSRLS
pg_read_all_settings读取所有配置变量,甚至是那些通常只对超级用户可见的变量
pg_read_all_stats读取所有的pg_stat_*视图并且使用与扩展相关的各种统计信息,甚至是那些通常只对超级用户可见的信息
pg_stat_scan_tables执行可能会在表上取得 ACCESS SHARE 锁的监控函数(可能会持锁很长时间)
pg_monitor读取/执行各种不同的监控视图和函数。这角色是 pg_read_all_settings,pg_read_all_stats和pg_stat_scan_tables的成员
pg_database_owner无 成员构成,隐式的,当前数据库的所有者
pg_signal_backend发信号到其他后端以取消查询或中止它的会话
pg_read_server_files允许使用COPY以及其他文件访问函数从服务器上该数据库可访问的任意位置读取文件
pg_write_server_files允许使用COPY以及其他文件访问函数在服务器上该数据库可访问的任意位置中写入文件
pg_execute_server_program允许用运行该数据库的用户执行数据库服务器上的程序来配合COPY和其他允许执行服务器端程序的函数
  • pg_monitor、pg_read_all_settings、pg_read_all_stats和pg_stat_scan_tables角色的目的是允许管理员能为监控数据库服务器的目的很容易地配置角色。它们授予一组常用的特权,这些特权允许角色读取各种有用的配置设置、统计信息以及通常仅限于超级用户的其他系统信息
  • pg_database_owner 角色有一个隐式 且 情况依赖的成员,也就是当前数据库的所有者。该角色开始没有传递任何特权。就像任何角色一样,它可以拥有对象或者接受访问权限的授予。因此,当pg_database_owner有模板数据库的权限,从该模板实例化的数据库的每个所有者都将运用这些权限。pg_database_owner不能成为任何角色的成员,并且它不能有非隐式成员
  • pg_signal_backend角色想要允许管理员启用受信任的、但是非超级用户的、发送信号给其他后端的角色。当前,此角色允许发送信号以取消另一个后端上的查询或终止其会话。不过授予此角色的用户不能向属于超级用户的后端发送信号
  • pg_read_server_files、pg_write_server_files以及pg_execute_server_program角色的目的是允许管理员有一些可信但不是超级用户的角色来访问文件以及以运行数据库的用户在数据库服务器上运行程序。由于这些角色能够访问服务器文件系统上的任何文件,因此在直接访问文件时它们会绕过任何数据库级别的权限检查并且它们可以被用来得到超级用户级别的访问,因此在把这些角色授予给用户时应当特别小心。

在授予这些角色时应当非常小心,以确保它们只被用在需要的地方,并且要理解这些角色会授予对特权信息的访问。管理员可以用GRANT命令把对这些角色的访问授予给用户,例如:

GRANT pg_signal_backend TO admin_user;

函数和触发器安全性

函数、触发器以及行级安全性策略允许用户在后端服务器中插入代码,其他用户不会注意到这些代码的执行。因此,这些机制允许用户相对容易地为其他人设置“特洛伊木马”。最强的保护是严格控制哪些人能定义对象。如果做不到,则编写查询时应该只引用具有可信任拥有者的对象。可以从search_path中去除public方案以及任何其他允许不可信用户创建对象的方案。

在后端服务器进程中运行的函数带有数据库服务器守护进程的操作系统权限。如果用于函数的编程语言允许非检查的内存访问,它就可能改变服务器的内部数据结构。因此,在很多其他事情中,这些函数可能绕开任何系统访问控制。允许这种访问的函数语言被认为是“不可信的”,并且PostgreSQL只允许超级用户创建用这些语言编写的函数。


预定义角色深入

相关系统表和视图

关于预定义角色的解释,上面已经说的非常清楚了 我们接下来看一些例子,深入研究一下:

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=# select * from pg_authid ;oid  |           rolname           | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil 
------+-----------------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------+---------------10 | postgres                    | t        | t          | t             | t           | t           | t              | t            |           -1 |             | 6171 | pg_database_owner           | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 6181 | pg_read_all_data            | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 6182 | pg_write_all_data           | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 3373 | pg_monitor                  | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 3374 | pg_read_all_settings        | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 3375 | pg_read_all_stats           | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 3377 | pg_stat_scan_tables         | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 4569 | pg_read_server_files        | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 4570 | pg_write_server_files       | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 4571 | pg_execute_server_program   | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 4200 | pg_signal_backend           | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 4544 | pg_checkpoint               | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 9256 | pg_maintain                 | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 4550 | pg_use_reserved_connections | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 6304 | pg_create_subscription      | f        | t          | f             | f           | f           | f              | f            |           -1 |             | 
(16 rows)postgres=#

这里区分一下相关的系统表和视图,如下:

## pg_authid包含关于数据库授权标识符(角色)的信息。角色把“用户”和“组”的概念包含在内
## pg_db_role_setting为每一个角色和数据库组合记录被设置到运行时配置变量的默认值## 视图pg_user提供关于数据库用户的信息。这是pg_shadow的一个公共可读的视图,它消除了口令域
## 视图pg_roles提供了关于数据库角色的信息。这是pg_authid的一个公共可读视图,它隐去了口令域
## 视图pg_shadow的存在是为了向后兼容:它模拟了在PostgreSQL版本8.1之前的一个系统目录。它显示pg_authid中所有被标记为rolcanlogin的角色的属性。由于这个表包含口令,所以不能是公众可读的,这也是采用pg_shadow这个名字的原因。 而pg_user是pg_shadow上的一个公共可读视图,它屏蔽了口令域。
postgres=# \d+ pg_authidTable "pg_catalog.pg_authid"Column     |           Type           | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
----------------+--------------------------+-----------+----------+---------+----------+-------------+--------------+-------------oid            | oid                      |           | not null |         | plain    |             |              | rolname        | name                     |           | not null |         | plain    |             |              | rolsuper       | boolean                  |           | not null |         | plain    |             |              | rolinherit     | boolean                  |           | not null |         | plain    |             |              | rolcreaterole  | boolean                  |           | not null |         | plain    |             |              | rolcreatedb    | boolean                  |           | not null |         | plain    |             |              | rolcanlogin    | boolean                  |           | not null |         | plain    |             |              | rolreplication | boolean                  |           | not null |         | plain    |             |              | rolbypassrls   | boolean                  |           | not null |         | plain    |             |              | rolconnlimit   | integer                  |           | not null |         | plain    |             |              | rolpassword    | text                     | C         |          |         | extended |             |              | rolvaliduntil  | timestamp with time zone |           |          |         | plain    |             |              | 
Indexes:"pg_authid_oid_index" PRIMARY KEY, btree (oid), tablespace "pg_global""pg_authid_rolname_index" UNIQUE CONSTRAINT, btree (rolname), tablespace "pg_global"
Tablespace: "pg_global"
Access method: heappostgres=#
postgres=# \d+ pg_db_role_settingTable "pg_catalog.pg_db_role_setting"Column    |  Type  | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
-------------+--------+-----------+----------+---------+----------+-------------+--------------+-------------setdatabase | oid    |           | not null |         | plain    |             |              | setrole     | oid    |           | not null |         | plain    |             |              | setconfig   | text[] | C         |          |         | extended |             |              | 
Indexes:"pg_db_role_setting_databaseid_rol_index" PRIMARY KEY, btree (setdatabase, setrole), tablespace "pg_global"
Tablespace: "pg_global"
Access method: heappostgres=#
postgres=# \d+ pg_userView "pg_catalog.pg_user"Column    |           Type           | Collation | Nullable | Default | Storage  | Description 
--------------+--------------------------+-----------+----------+---------+----------+-------------usename      | name                     |           |          |         | plain    | usesysid     | oid                      |           |          |         | plain    | usecreatedb  | boolean                  |           |          |         | plain    | usesuper     | boolean                  |           |          |         | plain    | userepl      | boolean                  |           |          |         | plain    | usebypassrls | boolean                  |           |          |         | plain    | passwd       | text                     |           |          |         | extended | valuntil     | timestamp with time zone |           |          |         | plain    | useconfig    | text[]                   | C         |          |         | extended | 
View definition:SELECT usename,usesysid,usecreatedb,usesuper,userepl,usebypassrls,'********'::text AS passwd,valuntil,useconfigFROM pg_shadow;postgres=# \d+ pg_roles View "pg_catalog.pg_roles"Column     |           Type           | Collation | Nullable | Default | Storage  | Description 
----------------+--------------------------+-----------+----------+---------+----------+-------------rolname        | name                     |           |          |         | plain    | rolsuper       | boolean                  |           |          |         | plain    | rolinherit     | boolean                  |           |          |         | plain    | rolcreaterole  | boolean                  |           |          |         | plain    | rolcreatedb    | boolean                  |           |          |         | plain    | rolcanlogin    | boolean                  |           |          |         | plain    | rolreplication | boolean                  |           |          |         | plain    | rolconnlimit   | integer                  |           |          |         | plain    | rolpassword    | text                     |           |          |         | extended | rolvaliduntil  | timestamp with time zone |           |          |         | plain    | rolbypassrls   | boolean                  |           |          |         | plain    | rolconfig      | text[]                   | C         |          |         | extended | oid            | oid                      |           |          |         | plain    | 
View definition:SELECT pg_authid.rolname,pg_authid.rolsuper,pg_authid.rolinherit,pg_authid.rolcreaterole,pg_authid.rolcreatedb,pg_authid.rolcanlogin,pg_authid.rolreplication,pg_authid.rolconnlimit,'********'::text AS rolpassword,pg_authid.rolvaliduntil,pg_authid.rolbypassrls,s.setconfig AS rolconfig,pg_authid.oidFROM pg_authidLEFT JOIN pg_db_role_setting s ON pg_authid.oid = s.setrole AND s.setdatabase = 0::oid;postgres=#
postgres=# \d+ pg_shadowView "pg_catalog.pg_shadow"Column    |           Type           | Collation | Nullable | Default | Storage  | Description 
--------------+--------------------------+-----------+----------+---------+----------+-------------usename      | name                     |           |          |         | plain    | usesysid     | oid                      |           |          |         | plain    | usecreatedb  | boolean                  |           |          |         | plain    | usesuper     | boolean                  |           |          |         | plain    | userepl      | boolean                  |           |          |         | plain    | usebypassrls | boolean                  |           |          |         | plain    | passwd       | text                     | C         |          |         | extended | valuntil     | timestamp with time zone |           |          |         | plain    | useconfig    | text[]                   | C         |          |         | extended | 
View definition:SELECT pg_authid.rolname AS usename,pg_authid.oid AS usesysid,pg_authid.rolcreatedb AS usecreatedb,pg_authid.rolsuper AS usesuper,pg_authid.rolreplication AS userepl,pg_authid.rolbypassrls AS usebypassrls,pg_authid.rolpassword AS passwd,pg_authid.rolvaliduntil AS valuntil,s.setconfig AS useconfigFROM pg_authidLEFT JOIN pg_db_role_setting s ON pg_authid.oid = s.setrole AND s.setdatabase = 0::oidWHERE pg_authid.rolcanlogin;postgres=#

pg_read_all_stats

下面我们以pg_read_all_stats为例,来看一下预定义角色的内部使用、实现逻辑,如下:

[postgres@localhost:~/test/bin]$ ./psql 
psql (17devel)
Type "help" for help.postgres=# \dxList of installed extensionsName        | Version |   Schema   |                              Description                               
--------------------+---------+------------+------------------------------------------------------------------------pg_stat_statements | 1.11    | public     | track planning and execution statistics of all SQL statements executedplpgsql            | 1.0     | pg_catalog | PL/pgSQL procedural language
(2 rows)postgres=#
postgres=# create role r1 superuser password '1' login;
CREATE ROLE
postgres=# create role r2 password '1' login;
CREATE ROLE
postgres=# create role r3 password '1';
CREATE ROLE
postgres=# \q
[postgres@localhost:~/test/bin]$ ./psql -U r3 -d postgres
psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: FATAL:  role "r3" is not permitted to log in
[postgres@localhost:~/test/bin]$

示例如下:

[postgres@localhost:~/test/bin]$ ./psql -U r1 -d postgres
psql (17devel)
Type "help" for help.postgres=# select * from pg_stat_statements_reset();pg_stat_statements_reset   
------------------------------2024-05-07 23:59:07.24913-07
(1 row)postgres=# select userid, queryid, query from pg_stat_statements(true);userid |       queryid        |                  query                   
--------+----------------------+------------------------------------------16388 | -4138155974000909952 | select * from pg_stat_statements_reset()
(1 row)postgres=# set role r2;
SET
postgres=> select userid, queryid, query from pg_stat_statements(true);userid |       queryid       |          query           
--------+---------------------+--------------------------16388 |                     | <insufficient privilege>16388 |                     | <insufficient privilege>16389 | 7964835675605767119 | set role r2
(3 rows)postgres=> reset role ;
RESET
postgres=# grant pg_read_all_stats to r2;
GRANT ROLE
postgres=# set role r2;
SET
postgres=> select userid, queryid, query from pg_stat_statements(true);userid |       queryid        |                                                                    query                                                                     
--------+----------------------+----------------------------------------------------------------------------------------------------------------------------------------------16388 |  5989446405197177395 | grant pg_read_all_stats to r216389 |  4672340091951134412 | select userid, queryid, query from pg_stat_statements($1)16389 |  5234989915205660419 | SELECT pg_catalog.lower(name) FROM pg_catalog.pg_settings  WHERE context IN ($1, $2)    AND pg_catalog.lower(name) LIKE pg_catalog.lower($3)+|                      | LIMIT $416388 | -4138155974000909952 | select * from pg_stat_statements_reset()16388 |  4672340091951134412 | select userid, queryid, query from pg_stat_statements($1)16388 |  8467575322926743907 | reset role16389 |  7964835675605767119 | set role r2
(7 rows)postgres=>

下面来看一下缺少此权限的内部控制逻辑,如下:

在这里插入图片描述

也即:

// contrib/pg_stat_statements/pg_stat_statements.c/* Common code for all versions of pg_stat_statements() */
static void
pg_stat_statements_internal(FunctionCallInfo fcinfo,pgssVersion api_version,bool showtext)
{ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;Oid			userid = GetUserId();bool		is_allowed_role = false;char	   *qbuffer = NULL;Size		qbuffer_size = 0;Size		extent = 0;int			gc_count = 0;HASH_SEQ_STATUS hash_seq;pgssEntry  *entry;/** Superusers or roles with the privileges of pg_read_all_stats members* are allowed* 允许具有 pg_read_all_stats 成员权限的超级用户或角色*/is_allowed_role = has_privs_of_role(userid, ROLE_PG_READ_ALL_STATS);...

当然统计信息不止上面一处,如下:

在这里插入图片描述


新增预定义角色

我这里选择一个来源于社区的patch,目前该patch应该属于不成熟 不会合入的。但是作为今天学习的材料来说,还是非常好的。有兴趣的小伙伴们可以自行前去查看,如下:

  • [PATCH] New predefined role pg_manage_extensions,点击前往

其目的是:向 Postgres 添加一个新的预定义角色 pg_manage_extensions。这个想法是,它允许超级用户将创建、更新或删除扩展的权限委托给其他角色,即使这些扩展不受信任或者这些用户不是数据库所有者。

支持者:我同意扩展创建是人们需要超级用户访问权限的主要原因之一,我认为尝试减少这种情况是有益的。但我不确定这样的 pg_manage_extensions 角色在实践中是否会比超级用户拥有更少的权限。据我所知,许多未标记为可信的扩展是不可信的,因为如果它们是可信的,它们将允许相当微不足道的权限升级到超级用户。

反对者:请注意,仅仅能够创建扩展并不能授予使用它的全面权限。我对我认为可能有问题的东西(例如 adminpack 或 plpython3u)做了一些检查,并且不允许 pg_manage_extensions 用户调用这些函数或使用不受信任的语言。虽然这可能是真的(或者说我们应该谨慎行事),但我认为更多的理由是,它们披露的数据库服务器信息比我们希望向普通用户披露的信息要多,或者说它们允许访问文件系统等。我认为,如果我们在 contrib 中有扩展,只需安装即可轻松地允许非超级用户成为超级用户,那么这应该是一个错误,并且可以通过使普通用户无法在不被授予对它们的某些访问权限的情况下使用这些扩展来修复 。毕竟,由于用户需求而对 DBA 进行社会工程以安装扩展无论如何都是一件事(即使大多数 DBA 可能会拒绝它),并且至少 DBA 应该意识到特定扩展的特定风险?


接下来,我们只关注其内部的实现,如下:

[postgres@localhost:~/postgres → master]$ wget https://www.postgresql.org/message-id/attachment/154187/0001-Add-new-pg_manage_extensions-predefined-role.patch
--2024-04-30 02:56:24--  https://www.postgresql.org/message-id/attachment/154187/0001-Add-new-pg_manage_extensions-predefined-role.patch
Resolving www.postgresql.org (www.postgresql.org)... 2001:4800:3e1:1::230, 2a02:16a8:dc51::50, 2a02:c0:301:0:ffff::32, ...
Connecting to www.postgresql.org (www.postgresql.org)|2001:4800:3e1:1::230|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3997 (3.9K) [text/x-diff]
Saving to: ‘0001-Add-new-pg_manage_extensions-predefined-role.patch’0001-Add-new-pg_manage_extensions- 100%[================================================================>]   3.90K  --.-KB/s    in 0s      2024-04-30 02:56:30 (178 MB/s) - ‘0001-Add-new-pg_manage_extensions-predefined-role.patch’ saved [3997/3997][postgres@localhost:~/postgres → master]$ 
[postgres@localhost:~/postgres → master]$ 
[postgres@localhost:~/postgres → master]$ ls
0001-Add-new-pg_manage_extensions-predefined-role.patch  config.log     configure.ac  doc             HISTORY      meson_options.txt
aclocal.m4                                               config.status  contrib       GNUmakefile     Makefile     README.md
config                                                   configure      COPYRIGHT     GNUmakefile.in  meson.build  src
[postgres@localhost:~/postgres → master]$ 
[postgres@localhost:~/postgres → master]$ 
[postgres@localhost:~/postgres → master]$ cat 0001-Add-new-pg_manage_extensions-predefined-role.patch 
From 59497e825184f0de30a18573ffd7d331be3b233d Mon Sep 17 00:00:00 2001
From: Michael Banck <michael.banck@credativ.de>
Date: Fri, 12 Jan 2024 13:56:59 +0100
Subject: [PATCH] Add new pg_manage_extensions predefined role.This allows any role that is granted this new predefined role to CREATE, UPDATE
or DROP extensions, no matter whether they are trusted or not.
---doc/src/sgml/user-manag.sgml      |  5 +++++src/backend/commands/extension.c  | 11 ++++++-----src/include/catalog/pg_authid.dat |  5 +++++3 files changed, 16 insertions(+), 5 deletions(-)diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index 92a299d2d3..ebb82801ec 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -693,6 +693,11 @@ DROP ROLE doomed_role;database to issue<link linkend="sql-createsubscription"><command>CREATE SUBSCRIPTION</command></link>.</entry></row>
+      <row>
+       <entry>pg_manage_extensions</entry>
+       <entry>Allow creating, removing or updating extensions, even if the
+       extensions are untrusted or the user is not the database owner.</entry>
+      </row></tbody></tgroup></table>
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 226f85d0e3..71481d9a73 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -882,13 +882,14 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,ListCell   *lc2;/*
-	 * Enforce superuser-ness if appropriate.  We postpone these checks until
-	 * here so that the control flags are correctly associated with the right
+	 * Enforce superuser-ness/membership of the pg_manage_extensions
+	 * predefined role if appropriate.  We postpone these checks until here
+	 * so that the control flags are correctly associated with the right* script(s) if they happen to be set in secondary control files.*/if (control->superuser && !superuser()){
-		if (extension_is_trusted(control))
+		if (extension_is_trusted(control) || has_privs_of_role(GetUserId(), ROLE_PG_MANAGE_EXTENSIONS))switch_to_superuser = true;else if (from_version == NULL)ereport(ERROR,
@@ -897,7 +898,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,control->name),control->trusted? errhint("Must have CREATE privilege on current database to create this extension.")
-					 : errhint("Must be superuser to create this extension.")));
+					 : errhint("Only roles with privileges of the \"%s\" role are allowed to create this extension.", "pg_manage_extensions")));elseereport(ERROR,(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -905,7 +906,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,control->name),control->trusted? errhint("Must have CREATE privilege on current database to update this extension.")
-					 : errhint("Must be superuser to update this extension.")));
+					 : errhint("Only roles with privileges of the \"%s\" role are allowed to update this extension.", "pg_manage_extensions")));}filename = get_extension_script_filename(control, from_version, version);
diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.dat
index 82a2ec2862..ac70603d26 100644
--- a/src/include/catalog/pg_authid.dat
+++ b/src/include/catalog/pg_authid.dat
@@ -94,5 +94,10 @@rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',rolpassword => '_null_', rolvaliduntil => '_null_' },
+{ oid => '8801', oid_symbol => 'ROLE_PG_MANAGE_EXTENSIONS',
+  rolname => 'pg_manage_extensions', rolsuper => 'f', rolinherit => 't',
+  rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
+  rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
+  rolpassword => '_null_', rolvaliduntil => '_null_' },]
-- 
2.39.2[postgres@localhost:~/postgres → master]$ git apply 0001-Add-new-pg_manage_extensions-predefined-role.patch
[postgres@localhost:~/postgres → master]$

使用superuser才可以安装的插件,如下:

在这里插入图片描述


其使用,如下:

[postgres@localhost:~/test/bin]$ ./psql 
psql (17devel)
Type "help" for help.postgres=# create role r1 superuser password '1' login;
CREATE ROLE
postgres=# create role r2 password '1' login;
CREATE ROLE
postgres=# \q
[postgres@localhost:~/test/bin]$ 
[postgres@localhost:~/test/bin]$ ./psql -U r1 -d postgres
psql (17devel)
Type "help" for help.postgres=# create extension plperl;
CREATE EXTENSION
postgres=# drop extension plperl ;
DROP EXTENSION
postgres=# \q
[postgres@localhost:~/test/bin]$ ./psql -U r2 -d postgres
psql (17devel)
Type "help" for help.postgres=> create extension plperl;
2024-05-08 01:37:30.248 PDT [125519] ERROR:  permission denied to create extension "plperl"
2024-05-08 01:37:30.248 PDT [125519] HINT:  Must have CREATE privilege on current database to create this extension.
2024-05-08 01:37:30.248 PDT [125519] STATEMENT:  create extension plperl;
ERROR:  permission denied to create extension "plperl"
HINT:  Must have CREATE privilege on current database to create this extension.
postgres=> \q
[postgres@localhost:~/test/bin]$ ./psql
psql (17devel)
Type "help" for help.postgres=# grant pg_manage_extensions to r2;
GRANT ROLE
postgres=# set role r2;
SET
postgres=> create extension plperl;
CREATE EXTENSION
postgres=>

上面校验权限的逻辑,如下:

在这里插入图片描述


最后来思考一个问题:为什么上面control->trusted = trueextension_is_trusted_ret仍然为假?

// src/backend/commands/extension.c/** Policy function: is the given extension trusted for installation by a* non-superuser?** (Update the errhint logic below if you change this.)*/
static bool
extension_is_trusted(ExtensionControlFile *control)
{AclResult	aclresult;/* Never trust unless extension's control file says it's okay */if (!control->trusted)return false;/* Allow if user has CREATE privilege on current database */aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(), ACL_CREATE);if (aclresult == ACLCHECK_OK)return true;return false;
}

原因如下:

[postgres@localhost:~/test/bin]$ ./psql 
psql (17devel)
Type "help" for help.postgres=# create role r4 password '1' login;
CREATE ROLE
postgres=# set role r4;
SET
postgres=> create extension plperl;
2024-05-08 02:04:43.067 PDT [129938] ERROR:  permission denied to create extension "plperl"
2024-05-08 02:04:43.067 PDT [129938] HINT:  Must have CREATE privilege on current database to create this extension.
2024-05-08 02:04:43.067 PDT [129938] STATEMENT:  create extension plperl;
ERROR:  permission denied to create extension "plperl"
HINT:  Must have CREATE privilege on current database to create this extension.
postgres=> 
postgres=> reset role;
RESET
postgres=# select * from pg_database ;oid |  datname  | datdba | encoding | datlocprovider | datistemplate | datallowconn | dathasloginevt | datconnlimit | datfrozenxid | datminmxid | dattablespace | datcollate  |  datctype   | datlocale | daticurules | datcollversion |               datacl                
-----+-----------+--------+----------+----------------+---------------+--------------+----------------+--------------+--------------+------------+---------------+-------------+-------------+-----------+-------------+----------------+-------------------------------------5 | postgres  |     10 |        6 | c              | f             | t            | f              |           -1 |          730 |          1 |          1663 | zh_CN.UTF-8 | zh_CN.UTF-8 |           |             | 2.28           | 1 | template1 |     10 |        6 | c              | t             | t            | f              |           -1 |          730 |          1 |          1663 | zh_CN.UTF-8 | zh_CN.UTF-8 |           |             | 2.28           | {=c/postgres,postgres=CTc/postgres}4 | template0 |     10 |        6 | c              | t             | f            | f              |           -1 |          730 |          1 |          1663 | zh_CN.UTF-8 | zh_CN.UTF-8 |           |             |                | {=c/postgres,postgres=CTc/postgres}
(3 rows)postgres=#

于是:

postgres=# grant CREATE on database postgres to r4;
GRANT
postgres=# set role r4;
SET
postgres=> create extension plperl;
CREATE EXTENSION
postgres=> select * from pg_database ;oid |  datname  | datdba | encoding | datlocprovider | datistemplate | datallowconn | dathasloginevt | datconnlimit | datfrozenxid | datminmxid | dattablespace | datcollate  |  datctype   | datlocale | daticurules | datcollversion |                       datacl                       
-----+-----------+--------+----------+----------------+---------------+--------------+----------------+--------------+--------------+------------+---------------+-------------+-------------+-----------+-------------+----------------+----------------------------------------------------5 | postgres  |     10 |        6 | c              | f             | t            | f              |           -1 |          730 |          1 |          1663 | zh_CN.UTF-8 | zh_CN.UTF-8 |           |             | 2.28           | {=Tc/postgres,postgres=CTc/postgres,r4=C/postgres}1 | template1 |     10 |        6 | c              | t             | t            | f              |           -1 |          730 |          1 |          1663 | zh_CN.UTF-8 | zh_CN.UTF-8 |           |             | 2.28           | {=c/postgres,postgres=CTc/postgres}4 | template0 |     10 |        6 | c              | t             | f            | f              |           -1 |          730 |          1 |          1663 | zh_CN.UTF-8 | zh_CN.UTF-8 |           |             |                | {=c/postgres,postgres=CTc/postgres}
(3 rows)postgres=>

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

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

相关文章

Mysql 基础 - 常见 子句

算数运算符 > < > < !/<> 逻辑运算符 3i in is null is not null 2l limit like 2o or 、order by 1a and ib between and 1n not and、or 、not、 in、 orderby、 limit、 like、 between...and、 is null 、is not null

【C++】C++11--- 列表初始化|关键字

目录 前言 列表初始化 创建对象时的列表初始化 单参数隐式类型转换 多参数的隐式类型转换 new表达式中使用列表初始化 列表初始化适用于STL 容器 模板类initializer_list 关键字auto 关键字decltype 关键字nullptr 前言 C标准10年磨一剑&#xff0c;第二个真正意义上…

学习软考----数据库系统工程师20

数据库技术基础 主要内容如下&#xff1a; DBMS的功能和特点 课本上&#xff1a; 数据库系统的三级模式结构 数据模型 E-R图

AI讲师大模型培训老师叶梓:大模型应用的方向探讨

大模型应用的关键方向及其落地案例可以从多个角度进行探讨&#xff0c;结合最新的研究和实际应用案例&#xff0c;我们可以更全面地理解这些技术如何推动社会和经济的发展。 Agent&#xff08;数字代理&#xff09;: 方向说明:Agent方向的AI技术旨在创建能够独立执行任务、做出…

Git系列:Git Stash临时保存与恢复工作进度

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

掌控网络流量,优化网络性能 - AnaTraf网络流量分析仪登场

在当今日新月异的网络环境中,网络流量监控和性能诊断已成为企业IT部门不可或缺的重要工作。只有充分了解网络流量状况,才能有效优化网络性能,提高业务运营效率。针对这一需求,全新推出的AnaTraf网络流量分析仪应运而生,为企业提供全面的网络监控和性能诊断解决方案。 快速定位…

嵌入式Linux的QT项目CMake工程模板分享及使用指南

在嵌入式linux开发板上跑QT应用&#xff0c;不同于PC上的开发过程。最大的区别就是需要交叉编译&#xff0c;才能在板子上运行。 这里总结下嵌入式linux环境下使用CMake&#xff0c;嵌入式QT的CMake工程模板配置及如何使用&#xff0c;分享给有需要的小伙伴&#xff0c;有用到的…

在做题中学习(50):搜索插入位置

35. 搜索插入位置 - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a;二分查找 思路&#xff1a;题目是有序的&#xff0c;时间复杂度O(logN),二分没跑了&#xff0c;题目说如果找不到target&#xff0c;返回它应该被插入位置的下标&#xff0c;所以可以分析一下示例2&…

后教培时代的新东方,正在找寻更大的教育驱动力?

近段时间&#xff0c;K12教育主要上市公司的阶段性业绩皆已出炉。从具体数据来看&#xff0c;随着时间推移&#xff0c;教培机构的转型之路已愈走愈顺。 财报显示&#xff0c;2023年12月1日-2024年2月29日&#xff0c;好未来实现营收4.3亿美元&#xff0c;同比增长59.7%&#…

Parallels Desktop 19 for Mac v19.3.0.54924中文破解版

Parallels Desktop 19 for Mac v19.3.0.54924中文破解版是一款强大的虚拟机软件&#xff0c;支持多操作系统&#xff0c;提供卓越的虚拟化技术&#xff0c;确保流畅稳定的运行。新增特色功能如共享打印、TouchID集成等&#xff0c;提供便捷高效的虚拟机体验。界面美观现代&…

认识大模型提示词

一、写作助理 &#x1f4a5;最常使用的 prompt&#xff0c;用于优化文本的语法、清晰度和简洁度&#xff0c;提高可读性。 输入&#xff1a;作为一名写作改进助理&#xff0c;你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性&#xff0c;同时分解长句&#xff…

Google Earth Engine谷歌地球引擎计算遥感影像在每个8天间隔内的多年平均值

本文介绍在谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;中&#xff0c;求取多年时间中&#xff0c;遥感影像在每1个8天时间间隔内的多年平均值的方法。 本文是谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;系列教学文章…

[机器学习-01]一文了解|机器学习简介、工具选择和Python包基础应用

目录 前言 正文 01-机器学习简介 &#xff08;1&#xff09;诞生过程 &#xff08;2&#xff09;人工智能、机器学习和深度学习之间的关系 &#xff08;3&#xff09;机器学习核心 02-机器学习工具 &#xff08;1&#xff09;Anaconda简介 &#xff08;2&#xff09;Jupyte…

【千帆平台】使用AppBuilder零代码创建应用,Excel表格数据转为Markdown格式文本

欢迎来到《小5讲堂》 这是《千帆平台》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言创建应用应用名称应用描述应用头像角色指令组件能力开场白推…

springcloud报错:Failed to start bean‘webServerStartStop‘

如果你正在使用nacos进行服务注册&#xff0c;然后报一下错误&#xff1a; 那就说明的nacos没有打开&#xff0c;所以找到你的下载nacos的文件夹 好了&#xff0c;错误完美解决~

Leetcode—387. 字符串中的第一个唯一字符【简单】

2024每日刷题&#xff08;127&#xff09; Leetcode—387. 字符串中的第一个唯一字符 实现代码 class Solution { public:int firstUniqChar(string s) {int count[26] {0};for(char c: s) {count[c - a];}for(int i 0; i < s.length(); i) {if(count[s[i] - a] 1) {re…

射频无源器件之耦合器

一. 耦合器的作用 在射频电路中,射频耦合器将一路微波功率按比例分成几路,用于检测或监测信号,如功率测量和波检测,还可改变信号的幅度、相位等特性,以满足不同的通信需求。根据输入与耦合端的功率差,常被分为5dB、6dB、10dB等耦合器。射频耦合器的类型主要包括定向耦合…

OSPF Stub区域

原理概述 OSPF 协议定义了多种区域&#xff08; Area &#xff09;类型&#xff0c;其中比较常见的有 Stub 区域和 Totally Stub 区域。区域的类型决定了在这个区域当中所存在的 LSA 的类型。 Stub 区域不允许 Type-4和 Type-5 LSA 进入&#xff0c;该区域会通过 Type-3 LSA…

BFS专题——FloodFill算法:200.岛屿数量

文章目录 题目描述算法原理代码实现CJava 题目描述 题目链接&#xff1a;200.岛屿数量 PS:注意题目中每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。也就是说斜角是不算了&#xff0c; 例如示例二&#xff0c;是三个岛屿。 算法原理 这道题目是 DFS&#xff0…

实战BACnet/IP标准通信网关在楼宇自动化中的应用

智慧楼宇建设实现不同设备间的互联互通是一项巨大挑战&#xff0c;尤其是在那些历史悠久的建筑中&#xff0c;新旧系统并存的情况尤为普遍。某大型商业综合体就面临着这样的困境&#xff1a;老旧的暖通空调系统采用Modbus RTU协议&#xff0c;而新部署的能源管理系统却要求BACn…