diff --git a/TOC.md b/TOC.md index f880953edda66..487f7b0ba3f05 100644 --- a/TOC.md +++ b/TOC.md @@ -627,6 +627,7 @@ - Privileges - [Security Compatibility with MySQL](/security-compatibility-with-mysql.md) - [Privilege Management](/privilege-management.md) + - [Column-Level Privilege Management](/column-privilege-management.md) - [User Account Management](/user-account-management.md) - [TiDB Password Management](/password-management.md) - [Role-Based Access Control](/role-based-access-control.md) diff --git a/column-privilege-management.md b/column-privilege-management.md new file mode 100644 index 0000000000000..06dce59476ea3 --- /dev/null +++ b/column-privilege-management.md @@ -0,0 +1,172 @@ +--- +title: Column-Level Privilege Management +summary: TiDB supports a MySQL-compatible column-level privilege management mechanism, enabling you to grant or revoke `SELECT`, `INSERT`, `UPDATE`, and `REFERENCES` privileges on specific columns of a table using `GRANT` or `REVOKE`, thus achieving finer-grained access control. +--- + +# Column-Level Privilege Management + +Starting from v8.5.6, TiDB supports a MySQL-compatible column-level privilege management mechanism. With column-level privileges, you can grant or revoke `SELECT`, `INSERT`, `UPDATE`, and `REFERENCES` privileges on specific columns of a table, achieving finer-grained data access control. + +> **Note:** +> +> Although MySQL syntax allows column-level specification like `REFERENCES(col_name)`, `REFERENCES` itself is a database/table-level privilege used for foreign key-related permission checks. Therefore, column-level `REFERENCES` does not actually take effect in MySQL. TiDB's behavior is consistent with MySQL. + +## Syntax + +Granting and revoking column-level privileges are similar to table-level privileges, with the following differences: + +- The column name list is placed after the **privilege type**, not after the **table name**. +- Multiple column names are separated by commas (`,`). + +```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'; +``` + +Where: + +* `priv_type` supports `SELECT`, `INSERT`, `UPDATE`, and `REFERENCES`. +* The `ON` clause requires specifying the specific table, for example, `test.tbl`. +* A single `GRANT` or `REVOKE` statement can include multiple privilege items, and each privilege item can specify its own list of column names. + +For example, the following statement grants `SELECT` privileges on `col1` and `col2` and `UPDATE` privilege on `col3` to the user: + +```sql +GRANT SELECT(col1, col2), UPDATE(col3) ON test.tbl TO 'user'@'host'; +``` + +## Grant column-level privileges + +The following example grants the `SELECT` privilege on `col1` and `col2` of table `test.tbl` to user `newuser`, and grants the `UPDATE` privilege on `col3` to the same user: + +```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'@'%' | ++---------------------------------------------------------------------+ +``` + +In addition to using `SHOW GRANTS`, you can also view column-level privilege information by querying `INFORMATION_SCHEMA.COLUMN_PRIVILEGES`. + +## Revoke column-level privileges + +The following example revokes the `SELECT` privilege on column `col2` from user `newuser`: + +```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'@'%' | ++---------------------------------------------------------------+ +``` + +## Column-level privilege access control example + +After granting or revoking column-level privileges, TiDB performs privilege checks on columns referenced in SQL statements. For example: + +* `SELECT` statements: `SELECT` column privileges affect columns referenced in the `SELECT` list as well as `WHERE`, `ORDER BY`, and other clauses. +* `UPDATE` statements: columns being updated in the `SET` clause require `UPDATE` column privileges. Columns read in expressions or conditions usually also require `SELECT` column privileges. +* `INSERT` statements: columns being written to require `INSERT` column privileges. `INSERT INTO t VALUES (...)` is equivalent to writing to all columns. + +In the following example, user `newuser` can only query `col1` and update `col3`: + +```sql +-- Execute as newuser +SELECT col1 FROM tbl; +SELECT * FROM tbl; -- Error (missing SELECT column privilege for col2, col3) + +UPDATE tbl SET col3 = 1; +UPDATE tbl SET col1 = 2; -- Error (missing UPDATE column privilege for col1) + +UPDATE tbl SET col3 = col1; +UPDATE tbl SET col3 = col3 + 1; -- Error (missing SELECT column privilege for col3) +UPDATE tbl SET col3 = col1 WHERE col1 > 0; +``` + +## Compatibility differences with MySQL + +TiDB's column-level privileges are generally compatible with MySQL. However, there are differences in the following scenarios: + +| Scenario | TiDB | MySQL | +| :--------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Revoking column-level privileges not granted to a user | `REVOKE` executes successfully. | `REVOKE` throws an error. | +| Execution order of column pruning and `SELECT` privilege check | `SELECT` column privileges are checked first, then column pruning is performed. For example: executing `SELECT a FROM (SELECT a, b FROM t) s` requires `SELECT` column privileges for both `t.a` and `t.b`. | Column pruning is performed first, then `SELECT` column privileges are checked. For example: executing `SELECT a FROM (SELECT a, b FROM t) s` only requires `SELECT` column privilege for `t.a`. | + +### Column pruning and privilege checks in view scenarios + +When performing `SELECT` privilege checks on views, MySQL and TiDB differ as follows: + +- MySQL first prunes columns in the view's internal query and then checks the column privileges of the internal tables, making the checks relatively lenient in some scenarios. +- TiDB does not perform column pruning before privilege checks, so additional column privileges might be required. + +```sql +-- Prepare the environment by logging in as 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'@'%'; + +-- Log in as u +SELECT a FROM v; +-- MySQL: Error, missing access privileges for t.a, t.c, t.d +-- TiDB: Error, missing access privileges for t.a, t.b, t.c, t.d + +-- Log in as root +GRANT SELECT(a, c, d) ON t TO 'u'@'%'; + +-- Log in as u +SELECT a FROM v; +-- MySQL: Success (internal query is pruned to `SELECT a FROM t WHERE c = 0 ORDER BY d`) +-- TiDB: Error, missing access privileges for t.b + +SELECT * FROM v; +-- MySQL: Error, missing access privileges for t.b +-- TiDB: Error, missing access privileges for t.b + +-- Log in as root +GRANT SELECT(b) ON t TO 'u'@'%'; + +-- Log in as u +SELECT * FROM v; +-- MySQL: Success +-- TiDB: Success +``` + +## See also + +* [Privilege Management](/privilege-management.md) +* [`GRANT `](/sql-statements/sql-statement-grant-privileges.md) +* [`REVOKE `](/sql-statements/sql-statement-revoke-privileges.md) diff --git a/information-schema/information-schema.md b/information-schema/information-schema.md index 9f949f0cef976..5222584760617 100644 --- a/information-schema/information-schema.md +++ b/information-schema/information-schema.md @@ -20,7 +20,7 @@ Many `INFORMATION_SCHEMA` tables have a corresponding `SHOW` statement. The bene | [`COLLATIONS`](/information-schema/information-schema-collations.md) | Provides a list of collations that the server supports. | | [`COLLATION_CHARACTER_SET_APPLICABILITY`](/information-schema/information-schema-collation-character-set-applicability.md) | Explains which collations apply to which character sets. | | [`COLUMNS`](/information-schema/information-schema-columns.md) | Provides a list of columns for all tables. | -| `COLUMN_PRIVILEGES` | Not implemented by TiDB. Returns zero rows. | +| `COLUMN_PRIVILEGES` | Summarizes the information about column privileges visible to the current user. | | `COLUMN_STATISTICS` | Not implemented by TiDB. Returns zero rows. | | [`ENGINES`](/information-schema/information-schema-engines.md) | Provides a list of supported storage engines. | | `EVENTS` | Not implemented by TiDB. Returns zero rows. | @@ -38,14 +38,14 @@ Many `INFORMATION_SCHEMA` tables have a corresponding `SHOW` statement. The bene | `REFERENTIAL_CONSTRAINTS` | Provides information on `FOREIGN KEY` constraints. | | `ROUTINES` | Not implemented by TiDB. Returns zero rows. | | [`SCHEMATA`](/information-schema/information-schema-schemata.md) | Provides similar information to `SHOW DATABASES`. | -| `SCHEMA_PRIVILEGES` | Not implemented by TiDB. Returns zero rows. | +| `SCHEMA_PRIVILEGES` | Summarizes the database privileges visible to the current user. | | `SESSION_STATUS` | Not implemented by TiDB. Returns zero rows. | | [`SESSION_VARIABLES`](/information-schema/information-schema-session-variables.md) | Provides similar functionality to the command `SHOW SESSION VARIABLES` | | [`STATISTICS`](/information-schema/information-schema-statistics.md) | Provides information on table indexes. | | [`TABLES`](/information-schema/information-schema-tables.md) | Provides a list of tables that the current user has visibility of. Similar to `SHOW TABLES`. | | `TABLESPACES` | Not implemented by TiDB. Returns zero rows. | | [`TABLE_CONSTRAINTS`](/information-schema/information-schema-table-constraints.md) | Provides information on primary keys, unique indexes and foreign keys. | -| `TABLE_PRIVILEGES` | Not implemented by TiDB. Returns zero rows. | +| `TABLE_PRIVILEGES` | Summarizes the table privileges visible to the current user. | | `TRIGGERS` | Not implemented by TiDB. Returns zero rows. | | [`USER_ATTRIBUTES`](/information-schema/information-schema-user-attributes.md) | Summarizes information about user comments and user attributes. | | [`USER_PRIVILEGES`](/information-schema/information-schema-user-privileges.md) | Summarizes the privileges associated with the current user. | diff --git a/mysql-compatibility.md b/mysql-compatibility.md index bea14fd7213dd..28c3310d56018 100644 --- a/mysql-compatibility.md +++ b/mysql-compatibility.md @@ -55,7 +55,6 @@ You can try out TiDB features on [TiDB Playground](https://play.tidbcloud.com/?u + Optimizer trace + XML Functions + X-Protocol [#1109](https://github.com/pingcap/tidb/issues/1109) -+ Column-level privileges [#9766](https://github.com/pingcap/tidb/issues/9766) + `XA` syntax (TiDB uses a two-phase commit internally, but this is not exposed via an SQL interface) + `CREATE TABLE tblName AS SELECT stmt` syntax [#4754](https://github.com/pingcap/tidb/issues/4754) + `CHECK TABLE` syntax [#4673](https://github.com/pingcap/tidb/issues/4673) diff --git a/privilege-management.md b/privilege-management.md index 3d789cc54ec23..b3cd21d2b3fa5 100644 --- a/privilege-management.md +++ b/privilege-management.md @@ -30,6 +30,8 @@ Use the following statement to grant the `xxx` user all privileges on all databa GRANT ALL PRIVILEGES ON *.* TO 'xxx'@'%'; ``` +Starting from v8.5.6, TiDB supports the MySQL-compatible column-level privilege management mechanism. You can grant or revoke `SELECT`, `INSERT`, `UPDATE`, and `REFERENCES` privileges for specific columns at the table level. For more information, see [Column-Level Privilege Management](/column-privilege-management.md). + By default, [`GRANT`](/sql-statements/sql-statement-grant-privileges.md) statements will return an error if the user specified does not exist. This behavior depends on if the [SQL mode](/system-variables.md#sql_mode) `NO_AUTO_CREATE_USER` is specified: ```sql @@ -506,7 +508,7 @@ The following [`mysql` system tables](/mysql-schema/mysql-schema.md) are special - `mysql.user` (user account, global privilege) - `mysql.db` (database-level privilege) - `mysql.tables_priv` (table-level privilege) -- `mysql.columns_priv` (column-level privilege; not currently supported) +- `mysql.columns_priv` (column-level privilege; supported starting from v8.5.6) These tables contain the effective range and privilege information of the data. For example, in the `mysql.user` table: diff --git a/sql-statements/sql-statement-grant-privileges.md b/sql-statements/sql-statement-grant-privileges.md index 83b14b0f76581..3253e69f2728f 100644 --- a/sql-statements/sql-statement-grant-privileges.md +++ b/sql-statements/sql-statement-grant-privileges.md @@ -82,7 +82,7 @@ mysql> SHOW GRANTS FOR 'newuser'; ## MySQL compatibility * Similar to MySQL, the `USAGE` privilege denotes the ability to log into a TiDB server. -* Column level privileges are not currently supported. +* Starting from v8.5.6, TiDB supports a MySQL-compatible column-level privilege management mechanism. You can grant or revoke `SELECT`, `INSERT`, `UPDATE`, and `REFERENCES` privileges on specific columns at the table level. For more information, see [Column-Level Privilege Management](/column-privilege-management.md). * Similar to MySQL, when the `NO_AUTO_CREATE_USER` sql mode is not present, the `GRANT` statement will automatically create a new user with an empty password when a user does not exist. Removing this sql-mode (it is enabled by default) presents a security risk. * In TiDB, after the `GRANT ` statement is executed successfully, the execution result takes effect immediately on the current connection. Whereas [in MySQL, for some privileges, the execution results take effect only on subsequent connections](https://dev.mysql.com/doc/refman/8.0/en/privilege-changes.html). See [TiDB #39356](https://github.com/pingcap/tidb/issues/39356) for details. diff --git a/sql-statements/sql-statement-revoke-privileges.md b/sql-statements/sql-statement-revoke-privileges.md index 9fb6f6681e9f5..0ab9b47106687 100644 --- a/sql-statements/sql-statement-revoke-privileges.md +++ b/sql-statements/sql-statement-revoke-privileges.md @@ -7,6 +7,8 @@ summary: An overview of the usage of REVOKE for the TiDB database. This statement removes privileges from an existing user. Executing this statement requires the `GRANT OPTION` privilege and all privileges you revoke. +Starting from v8.5.6, TiDB supports the MySQL-compatible column-level privilege management mechanism. You can specify a list of column names in `REVOKE`, for example `REVOKE SELECT(col2) ON test.tbl FROM 'user'@'host';`. For more information, see [Column-Level Privilege Management](/column-privilege-management.md). + ## Synopsis ```ebnf+diagram