Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions TOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,7 @@
- 权限
- [与 MySQL 安全特性差异](/security-compatibility-with-mysql.md)
- [权限管理](/privilege-management.md)
- [列级权限管理](/column-privilege-management.md)
- [TiDB 用户账户管理](/user-account-management.md)
- [TiDB 密码管理](/password-management.md)
- [基于角色的访问控制](/role-based-access-control.md)
Expand Down
172 changes: 172 additions & 0 deletions column-privilege-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
---
title: 列级权限管理
summary: TiDB 支持兼容 MySQL 的列级权限管理机制,可通过 `GRANT` 或 `REVOKE` 在表级别对指定列授予或回收 `SELECT`、`INSERT`、`UPDATE`、`REFERENCES` 权限,实现更细粒度的访问控制。
---

# 列级权限管理

从 v8.5.6 版本开始,TiDB 支持兼容 MySQL 的列级权限管理机制。通过列级权限,你可以在表级别为指定列授予或回收 `SELECT`、`INSERT`、`UPDATE`、`REFERENCES` 权限,从而实现更细粒度的数据访问控制。

> **注意:**
>
> 虽然 MySQL 语法允许 `REFERENCES(col_name)` 这种列级写法,但 `REFERENCES` 本身属于数据库/表级权限,用于外键相关的权限检查。因此,列级 `REFERENCES` 在 MySQL 中并不会真正生效。TiDB 的行为与 MySQL 保持一致。

## 语法

列级权限的授予和回收与表级权限类似,区别如下:

- 列名列表位于**权限类型**后面,而不是位于**表名**后面。
- 多个列名之间使用逗号(`,`)分隔。

```sql
GRANT priv_type(col_name [, col_name] ...) [, priv_type(col_name [, col_name] ...)] ...
ON db_name.tbl_name
TO 'user'@'host';

REVOKE priv_type(col_name [, col_name] ...) [, priv_type(col_name [, col_name] ...)] ...
ON db_name.tbl_name
FROM 'user'@'host';
```

其中:

* `priv_type` 支持 `SELECT`、`INSERT`、`UPDATE` 和 `REFERENCES`。
* `ON` 后需要指定具体表,例如 `test.tbl`。
* 同一条 `GRANT` 或 `REVOKE` 语句可以包含多个权限项,每个权限项都可以指定自己的列名列表。

例如,以下语句表示将 `col1`、`col2` 的 `SELECT` 权限和 `col3` 的 `UPDATE` 权限授予用户:

```sql
GRANT SELECT(col1, col2), UPDATE(col3) ON test.tbl TO 'user'@'host';
```

## 授予列级权限

以下示例将表 `test.tbl` 的 `col1` 和 `col2` 的 `SELECT` 权限授予用户 `newuser`,并将 `col3` 的 `UPDATE` 权限授予该用户:

```sql
CREATE DATABASE IF NOT EXISTS test;
USE test;

DROP TABLE IF EXISTS tbl;
CREATE TABLE tbl (col1 INT, col2 INT, col3 INT);

DROP USER IF EXISTS 'newuser'@'%';
CREATE USER 'newuser'@'%';

GRANT SELECT(col1, col2), UPDATE(col3) ON test.tbl TO 'newuser'@'%';
SHOW GRANTS FOR 'newuser'@'%';
```

```
+---------------------------------------------------------------------+
| Grants for newuser@% |
+---------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'newuser'@'%' |
| GRANT SELECT(col1, col2), UPDATE(col3) ON test.tbl TO 'newuser'@'%' |
+---------------------------------------------------------------------+
```

除了使用 `SHOW GRANTS`,你还可以通过查询 `INFORMATION_SCHEMA.COLUMN_PRIVILEGES` 查看列级权限信息。

## 回收列级权限

以下示例从用户 `newuser` 收回列 `col2` 的 `SELECT` 权限:

```sql
REVOKE SELECT(col2) ON test.tbl FROM 'newuser'@'%';
SHOW GRANTS FOR 'newuser'@'%';
```

```
+---------------------------------------------------------------+
| Grants for newuser@% |
+---------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'newuser'@'%' |
| GRANT SELECT(col1), UPDATE(col3) ON test.tbl TO 'newuser'@'%' |
+---------------------------------------------------------------+
```

## 列级权限访问控制示例

在授予或回收列级权限后,TiDB 会对 SQL 中引用的列进行权限检查。例如:

* `SELECT` 语句:`SELECT` 列权限会影响 `SELECT` 列表以及 `WHERE`、`ORDER BY` 等子句中引用的列。
* `UPDATE` 语句:`SET` 中被更新的列需要 `UPDATE` 列权限。在表达式、条件中被读取的列通常还需要 `SELECT` 列权限。
* `INSERT` 语句:被写入的列需要 `INSERT` 列权限。`INSERT INTO t VALUES (...)` 等价于写入所有列。

以下示例中,用户 `newuser` 仅能查询 `col1`,并更新 `col3`:

```sql
-- 以 newuser 登录执行
SELECT col1 FROM tbl;
SELECT * FROM tbl; -- 报错(缺少 col2、col3 的 SELECT 列权限)

UPDATE tbl SET col3 = 1;
UPDATE tbl SET col1 = 2; -- 报错(缺少 col1 的 UPDATE 列权限)

UPDATE tbl SET col3 = col1;
UPDATE tbl SET col3 = col3 + 1; -- 报错(缺少 col3 的 SELECT 列权限)
UPDATE tbl SET col3 = col1 WHERE col1 > 0;
```

## 与 MySQL 的兼容性差异

TiDB 的列级权限整体与 MySQL 兼容,但在以下场景存在差异:

| 场景 | TiDB | MySQL |
| :----------------------- | :-------------------------- | :---------------------------- |
| 收回用户未被授予的列级权限 | `REVOKE` 可以成功执行 | `REVOKE` 会报错 |
| 列裁剪与 `SELECT` 列权限检查的执行顺序 | 先检查 `SELECT` 列权限,再进行列裁剪。例如:执行 `SELECT a FROM (SELECT a, b FROM t) s` 需要同时拥有 `t.a` 和 `t.b` 的 `SELECT` 列权限。 | 先进行列裁剪,再检查 `SELECT` 列权限。例如:执行 `SELECT a FROM (SELECT a, b FROM t) s` 只需要 `t.a` 的 `SELECT` 列权限。 |

### 视图场景的列裁剪与权限检查

在对视图进行 `SELECT` 权限检查时,MySQL 和 TiDB 存在以下差异:

- MySQL 会先对视图内部查询做列裁剪,再检查内部表的列权限,因此在某些场景下检查相对宽松。
- TiDB 不会在权限检查之前做列裁剪,因此可能需要额外的列权限。

```sql
-- 以 root 登录准备环境
DROP USER IF EXISTS 'u'@'%';
CREATE USER 'u'@'%';

DROP TABLE IF EXISTS t;
CREATE TABLE t (a INT, b INT, c INT, d INT);

DROP VIEW IF EXISTS v;
CREATE SQL SECURITY INVOKER VIEW v AS SELECT a, b FROM t WHERE c = 0 ORDER BY d;

GRANT SELECT ON v TO 'u'@'%';

-- 以 u 登录
SELECT a FROM v;
-- MySQL:报错,缺少对 t.a、t.c、t.d 的访问权限
-- TiDB:报错,缺少对 t.a、t.b、t.c、t.d 的访问权限

-- 以 root 登录
GRANT SELECT(a, c, d) ON t TO 'u'@'%';

-- 以 u 登录
SELECT a FROM v;
-- MySQL:成功(会将内部查询裁剪为 `SELECT a FROM t WHERE c = 0 ORDER BY d`)
-- TiDB:报错,缺少对 t.b 的访问权限

SELECT * FROM v;
-- MySQL:报错,缺少对 t.b 的访问权限
-- TiDB:报错,缺少对 t.b 的访问权限

-- 以 root 登录
GRANT SELECT(b) ON t TO 'u'@'%';

-- 以 u 登录
SELECT * FROM v;
-- MySQL:成功
-- TiDB:成功
```

## 另请参阅

* [权限管理](/privilege-management.md)
* [`GRANT <privileges>`](/sql-statements/sql-statement-grant-privileges.md)
* [`REVOKE <privileges>`](/sql-statements/sql-statement-revoke-privileges.md)
6 changes: 3 additions & 3 deletions information-schema/information-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Information Schema 提供了一种查看系统元数据的 ANSI 标准方法。
| [`COLLATIONS`](/information-schema/information-schema-collations.md) | 提供 TiDB 支持的排序规则列表。 |
| [`COLLATION_CHARACTER_SET_APPLICABILITY`](/information-schema/information-schema-collation-character-set-applicability.md) | 说明哪些排序规则适用于哪些字符集。 |
| [`COLUMNS`](/information-schema/information-schema-columns.md) | 提供所有表中列的列表。 |
| `COLUMN_PRIVILEGES` | TiDB 未实现,返回零行。 |
| `COLUMN_PRIVILEGES` | 汇总当前用户可见的列权限信息。 |
| `COLUMN_STATISTICS` | TiDB 未实现,返回零行。 |
| [`ENGINES`](/information-schema/information-schema-engines.md) | 提供支持的存储引擎列表。 |
| `EVENTS` | TiDB 未实现,返回零行。 |
Expand All @@ -36,14 +36,14 @@ Information Schema 提供了一种查看系统元数据的 ANSI 标准方法。
| `REFERENTIAL_CONSTRAINTS` | 提供有关 `FOREIGN KEY` 约束的信息。 |
| `ROUTINES` | TiDB 未实现,返回零行。 |
| [`SCHEMATA`](/information-schema/information-schema-schemata.md) | 提供与 `SHOW DATABASES` 命令类似的信息。 |
| `SCHEMA_PRIVILEGES` | TiDB 未实现,返回零行。 |
| `SCHEMA_PRIVILEGES` | 汇总当前用户可见的数据库权限信息。 |
| `SESSION_STATUS` | TiDB 未实现,返回零行。 |
| [`SESSION_VARIABLES`](/information-schema/information-schema-session-variables.md) | 提供与 `SHOW SESSION VARIABLES` 命令类似的功能。 |
| [`STATISTICS`](/information-schema/information-schema-statistics.md) | 提供有关表索引的信息。 |
| [`TABLES`](/information-schema/information-schema-tables.md) | 提供当前用户可见的表的列表。 类似于 `SHOW TABLES`。 |
| `TABLESPACES` | TiDB 未实现,返回零行。 |
| [`TABLE_CONSTRAINTS`](/information-schema/information-schema-table-constraints.md) | 提供有关主键、唯一索引和外键的信息。 |
| `TABLE_PRIVILEGES` | TiDB 未实现,返回零行。 |
| `TABLE_PRIVILEGES` | 汇总当前用户可见的表权限信息。 |
| `TRIGGERS` | TiDB 未实现,返回零行。 |
| [`USER_ATTRIBUTES`](/information-schema/information-schema-user-attributes.md) | 汇总用户的注释和属性信息。 |
| [`USER_PRIVILEGES`](/information-schema/information-schema-user-privileges.md) | 汇总与当前用户相关的权限。 |
Expand Down
1 change: 0 additions & 1 deletion mysql-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ TiDB 高度兼容 MySQL 协议,以及 MySQL 5.7 和 MySQL 8.0 常用的功能
* MySQL 追踪优化器
* XML 函数
* X-Protocol [#1109](https://github.com/pingcap/tidb/issues/1109)
* 列级权限 [#9766](https://github.com/pingcap/tidb/issues/9766)
* `XA` 语法(TiDB 内部使用两阶段提交,但并没有通过 SQL 接口公开)
* `CREATE TABLE tblName AS SELECT stmt` 语法 [#4754](https://github.com/pingcap/tidb/issues/4754)
* `CHECK TABLE` 语法 [#4673](https://github.com/pingcap/tidb/issues/4673)
Expand Down
4 changes: 3 additions & 1 deletion privilege-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ GRANT SELECT ON test.* TO 'xxx'@'%';
GRANT ALL PRIVILEGES ON *.* TO 'xxx'@'%';
```

从 v8.5.6 版本开始,TiDB 支持兼容 MySQL 的列级权限管理机制。你可以在表级别为指定列授予或回收 `SELECT`、`INSERT`、`UPDATE`、`REFERENCES` 权限。更多信息参见[列级权限管理](/column-privilege-management.md)。

默认情况下,如果指定的用户不存在,[`GRANT`](/sql-statements/sql-statement-grant-privileges.md) 语句将报错。该行为受 [SQL 模式](/system-variables.md#sql_mode)中的 `NO_AUTO_CREATE_USER` 控制。

```sql
Expand Down Expand Up @@ -514,7 +516,7 @@ SELECT * FROM INFORMATION_SCHEMA.USER_PRIVILEGES WHERE grantee = "'root'@'%'";
- `mysql.user`:用户账户,全局权限
- `mysql.db`:数据库级别的权限
- `mysql.tables_priv`:表级别的权限
- `mysql.columns_priv`:列级别的权限,当前暂不支持
- `mysql.columns_priv`:列级别的权限(从 v8.5.6 开始支持)

这几张表包含了数据的生效范围和权限信息。例如,`mysql.user` 表的部分数据:

Expand Down
2 changes: 1 addition & 1 deletion sql-statements/sql-statement-grant-privileges.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ SHOW GRANTS FOR 'newuser';
## MySQL 兼容性

* 与 MySQL 类似,`USAGE` 权限表示登录 TiDB 服务器的能力。
* 目前不支持列级权限
* 从 v8.5.6 版本开始,TiDB 支持兼容 MySQL 的列级权限管理机制。你可以在表级别为指定列授予或回收 `SELECT`、`INSERT`、`UPDATE`、`REFERENCES` 权限。更多信息参见[列级权限管理](/column-privilege-management.md)
* 与 MySQL 类似,不存在 `NO_AUTO_CREATE_USER` sql 模式时,`GRANT` 语句将在用户不存在时自动创建一个空密码的新用户。删除此 sql-mode(默认情况下已启用)会带来安全风险。
* `GRANT <privileges>` 语句执行成功后,在 TiDB 中语句执行的结果会在当前连接立即生效,而 [MySQL 中部分权限的结果需要等到之后的连接才生效](https://dev.mysql.com/doc/refman/8.0/en/privilege-changes.html)。见 [TiDB #39356](https://github.com/pingcap/tidb/issues/39356)。

Expand Down
2 changes: 2 additions & 0 deletions sql-statements/sql-statement-revoke-privileges.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ summary: TiDB 数据库中 REVOKE <privileges> 的使用概况。

`REVOKE <privileges>` 语句用于删除已有用户的权限。执行 `REVOKE <privileges>` 语句需要拥有分配的权限,并且拥有 `GRANT OPTION` 权限。

从 v8.5.6 版本开始,TiDB 支持兼容 MySQL 的列级权限管理机制,你可以在 `REVOKE` 中指定列名列表,例如,`REVOKE SELECT(col2) ON test.tbl FROM 'user'@'host';`。更多信息参见[列级权限管理](/column-privilege-management.md)

## 语法图

```ebnf+diagram
Expand Down
Loading