> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bytebase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Review Rules

Bytebase provides customizable SQL lint rules to check common issues in database change and query process.

Different sets of rules can form different [SQL Review Policies](/sql-review/review-policy) for the respective environment.

## Supported rules

* Engine
  * [Require InnoDB](/sql-review/review-rules#engine.mysql.use-innodb)
* Naming
  * [Fully qualified object name](/sql-review/review-rules#naming.fully-qualified)
  * [Table naming convention](/sql-review/review-rules#naming.table)
  * [Column naming convention](/sql-review/review-rules#naming.column)
  * [Auto-increment column naming convention](/sql-review/review-rules#naming.column.auto-increment)
  * [Index naming convention](/sql-review/review-rules#naming.index.idx)
  * [Primary key naming convention](/sql-review/review-rules#naming.index.pk)
  * [Unique key naming convention](/sql-review/review-rules#naming.index.uk)
  * [Foreign key naming convention](/sql-review/review-rules#naming.index.fk)
* Statement
  * [Disallow SELECT \*](/sql-review/review-rules#statement.select.no-select-all)
  * [Require WHERE](/sql-review/review-rules#statement.where.require)
  * [Disallow leading % in LIKE](/sql-review/review-rules#statement.where.no-leading-wildcard-like)
  * [Disallow COMMIT](/sql-review/review-rules#statement.disallow-commit)
  * [Disallow LIMIT](/sql-review/review-rules#statement.disallow-limit)
  * [Disallow ORDER BY](/sql-review/review-rules#statement.disallow-order-by)
  * [Merge ALTER TABLE](/sql-review/review-rules#statement.merge-alter-table)
  * [INSERT statements must specify columns](/sql-review/review-rules#statement.insert.must-specify-column)
  * [Disallow ORDER BY RAND in INSERT statements](/sql-review/review-rules#statement.insert.disallow-order-by-rand)
  * [Limit the inserted rows](/sql-review/review-rules#statement.insert.row-limit)
  * [Limit affected rows](/sql-review/review-rules#statement.affected-row-limit)
  * [Dry run DML statements](/sql-review/review-rules#statement.dml-dry-run)
  * [Disallow add column with default](/sql-review/review-rules#statement.disallow-add-column-with-default)
  * [Add CHECK constraints with NOT VALID option](/sql-review/review-rules#statement.add-check-not-valid)
  * [Disallow add NOT NULL constraints to an existing column](/sql-review/review-rules#statement.disallow-add-not-null)
* Table
  * [Limit DDL operations on tables with large data volumes](/sql-review/review-rules#table.limit-size)
  * [Require primary key](/sql-review/review-rules#table.require-pk)
  * [Disallow foreign key](/sql-review/review-rules#table.no-foreign-key)
  * [Drop naming convention](/sql-review/review-rules#table.drop-naming-convention)
  * [Disallow partition table](/sql-review/review-rules#table.disallow-partition)
  * [Table comment convention](/sql-review/review-rules#table.comment)
* Schema
  * [Backward incompatible schema change](/sql-review/review-rules#schema.backward-compatibility)
* Column
  * [Enforce the required columns in each table](/sql-review/review-rules#column.required)
  * [Column type disallow list](/sql-review/review-rules#column.type-disallow-list)
  * [Columns no NULL value](/sql-review/review-rules#column.no-null)
  * [Disallow changing column type](/sql-review/review-rules#column.disallow-change-type)
  * [Set DEFAULT value for NOT NULL columns](/sql-review/review-rules#column.set-default-for-not-null)
  * [Disallow ALTER TABLE CHANGE COLUMN statements](/sql-review/review-rules#column.disallow-change)
  * [Disallow changing column order](/sql-review/review-rules#column.disallow-changing-order)
  * [Use integer for auto-increment columns](/sql-review/review-rules#column.auto-increment-must-integer)
  * [Disallow set charset for columns](/sql-review/review-rules#column.disallow-set-charset)
  * [Set unsigned attribute on auto-increment columns](/sql-review/review-rules#column.auto-increment-must-unsigned)
  * [Column comment convention](/sql-review/review-rules#column.comment)
  * [Maximum CHAR length](/sql-review/review-rules#column.maximum-character-length)
  * [Auto-increment initial value](/sql-review/review-rules#column.auto-increment-initial-value)
  * [Limit the count of current time columns](/sql-review/review-rules#column.current-time-count-limit)
  * [Require column default value](/sql-review/review-rules#column.require-default)
  * [Prohibit dropping columns in indexes](/sql-review/review-rules#column.disallow-drop-in-index)
* Index
  * [Disallow duplicate column in index keys](/sql-review/review-rules#index.no-duplicate-column)
  * [Limit the count of index keys](/sql-review/review-rules#index.key-number-limit)
  * [Limit key type for primary keys](/sql-review/review-rules#index.pk-type-limit)
  * [Disallow BLOB and TEXT for index keys](/sql-review/review-rules#index.type-no-blob)
  * [Index count limit](/sql-review/review-rules#index.total-number-limit)
  * [Primary key type allowlist](/sql-review/review-rules#index.primary-key-type-allowlist)
  * [Create index concurrently](/sql-review/review-rules#index.create-concurrently)
* Database
  * [Drop database restriction](/sql-review/review-rules#database.drop-empty-database)
* System
  * [Charset allow list](/sql-review/review-rules#system.charset.allowlist)
  * [Collation allow list](/sql-review/review-rules#system.collation.allowlist)
  * [Comment length limit](/sql-review/review-rules#system.comment.length)
* Built-in Rules
  * [Prior backup feasibility check](/sql-review/review-rules#builtin.prior-backup-check)

## Engine

<div id="engine.mysql.use-innodb" />

### Require InnoDB

InnoDB is the default storage engine of MySQL 5.5+. It provides powerful transaction features. Normally, using InnoDB as the storage engine is the only option. Bytebase provides this rule to catch all scenarios where other engines are attempted.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-engine-mysql-use-innodb.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=bfcc38702589d234fd38ccc0c13e15bb" alt="schema-review-engine-mysql-use-innodb" width="959" height="361" data-path="content/docs/sql-review/schema-review-engine-mysql-use-innodb.webp" />

#### How the rule works

Bytebase defaults MySQL to use InnoDB storage engine.

So if the following situation occurs, Bytebase considers this rule to be violated:

* Explicitly specify other storage engines when creating tables. e.g. `CREATE TABLE t(id int) ENGINE = CSV`
* Explicitly specify other storage engines when `ALTER TABLE`. e.g. `ALTER TABLE t ENGINE = CSV`
* Try to set `default_storage_engine` other than InnoDB. e.g. `SET default_storage_engine = CSV`

#### Support database engine

* MySQL

## Naming

<div id="naming.fully-qualified" />

### Fully qualified object name

Using fully qualified object names in SQL ensures clarity and precision. It helps the database system to quickly locate and distinguish between objects, even if they have the same name but exist in different schemas or databases. This practice can improve performance by reducing ambiguity and aiding in the efficient execution of queries.

#### How the rule works

Bytebase checks whether the object name appearing in the SQL statement is fully qualified. The exception is that bytebase does not check pseudo table names in common table expressions (CTE), such as `foo` in `WITH foo AS (SELECT * FROM public.pokes) SELECT * FROM foo`.

##### Some typical format

| Object Name                             | Fully qualified |
| --------------------------------------- | --------------- |
| table\_name                             | no              |
| schema\_name.table\_name                | yes             |
| database\_name.schema\_name.table\_name | yes             |

#### Support database engine

* PostgreSQL

<div id="naming.table" />

### Table naming convention

The unified naming convention is desired by developers. And the same applies to the database space. Bytebase provides this rule to unify the table naming convention.

#### About convention format

`Table Naming Convention` uses [regular expression](https://en.wikipedia.org/wiki/Regular_expression) as the format for naming pattern, and also limits the naming max length. The default maximum length is 64 characters. Length limit does not support PostgreSQL.

##### Some typical format

| Name               | Regular Expression       |
| ------------------ | ------------------------ |
| snake\_lower\_case | `^[a-z]+(_[a-z]+)*$`     |
| CamelCase          | `^([A-Z][a-z]*)+$`       |
| lowerCamelCase     | `^[a-z]+([A-Z][a-z]*)*$` |
| kebab-case         | `^[a-z]+(-[a-z]+)*$`     |

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-naming-table.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=5c900201ffce1f6e6863576ef295be6d" alt="schema-review-naming-table" width="1922" height="978" data-path="content/docs/sql-review/schema-review-naming-table.webp" />

#### How the rule works

Bytebase checks that all table names in DDL conform to the naming conventions.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE RENAME TO` statements
* `RENAME TABLE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase
* Snowflake

<div id="naming.column" />

### Column naming convention

The unified naming convention is desired by developers. And the same applies to the database space. Bytebase provides this rule to unify the column naming convention.

#### About convention format

`Column Naming Convention` uses [regular expression](https://en.wikipedia.org/wiki/Regular_expression) format for naming pattern, and also limits the naming max length. The default maximum length is 64 characters. Length limit does not support PostgreSQL.

##### Some typical format

| Name               | Regular Expression       |
| ------------------ | ------------------------ |
| snake\_lower\_case | `^[a-z]+(_[a-z]+)*$`     |
| CamelCase          | `^([A-Z][a-z]*)+$`       |
| lowerCamelCase     | `^[a-z]+([A-Z][a-z]*)*$` |
| kebab-case         | `^[a-z]+(-[a-z]+)*$`     |

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-naming-column.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=7b8b2f9ed7c27dbba7078e8dc1779dfe" alt="schema-review-naming-column" width="967" height="372" data-path="content/docs/sql-review/schema-review-naming-column.webp" />

#### How the rule works

Bytebase checks that all column names in DDL conform to the naming conventions.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE RENAME COLUMN` statements
* `ALTER TABLE ADD COLUMNS` statements
* `ALTER TABLE CHANGE COLUMN` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="naming.column.auto-increment" />

### Auto-increment column naming convention

The unified naming convention is desired by developers. And the same applies to the database space. Bytebase provides this rule to unify the auto-increment column naming convention.

#### About convention format

`Auto-increment Column Naming Convention` uses [regular expression](https://en.wikipedia.org/wiki/Regular_expression) format for naming pattern, and also limits the naming maximum length. The default maximum length is 64 characters.

##### Some typical format

| Name | Regular Expression |
| ---- | ------------------ |
| id   | `^id$`             |

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/sql-reivew-naming-auto-increment.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=99583429b3ded735766f0c1891707866" alt="sql-review-naming-auto-increment" width="931" height="357" data-path="content/docs/sql-review/sql-reivew-naming-auto-increment.webp" />

#### How the rule works

Bytebase checks all auto-increment column names in DDL conforming to the naming conventions.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

<div id="naming.index.idx" />

### Index naming convention

The unified naming convention is desired by developers. And the same applies to the database space. Bytebase provides this rule to unify the index naming convention.

#### About convention format

`Index Naming Convention` uses `template` format. Specifically, the `template` is an extended [regular expression](https://en.wikipedia.org/wiki/Regular_expression). The rest follows the regular expression rules except the part with curly braces.

For example, `^idx_{{table}}_{{column_list}}$` is a `template` where `{{table}}` is the table name and `{{column_list}}` is the list of the column name. So for index on `user(id, name)`, the legal name is `idx_user_id_name`.

It also limits the naming max length. The default maximum length is 64 characters. Length limit does not support PostgreSQL.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-naming-index-idx.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=f0e68ed535eff5fc0e2017e317bc8655" alt="schema-review-naming-index-idx" width="928" height="369" data-path="content/docs/sql-review/schema-review-naming-index-idx.webp" />

#### How the rule works

Bytebase checks that all index names in DDL conform to the naming conventions.

<Note>
  `Index Naming Convention` rule is only valid for index, which means it does **NOT** work for unique key, foreign key and primary key.
  Also see primary key naming, unique key naming convention and foreign key naming convention.
</Note>

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE RENAME INDEX` statements
* `ALTER TABLE ADD CONSTRAINT` statements
* `CREATE INDEX` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="naming.index.pk" />

### Primary key naming convention

The unified naming convention is desired by developers. And the same applies to the database space. Bytebase provides this rule to unify the primary key naming convention.
This rule does **NOT** support MySQL and TiDB. Because the name of a PRIMARY KEY is always PRIMARY in MySQL and TiDB.

#### About convention format

`Primary Key Naming Convention` uses `template` format. Specifically, the `template` is an extended [regular expression](https://en.wikipedia.org/wiki/Regular_expression). The rest follows the regular expression rules except the part with curly braces.

For example, `^pk_{{table}}_{{column_list}}$` is a `template` where `{{table}}` is the table name and `{{column_list}}` is the list of the column name. So for primary key on `user(id, name)`, the legal name is `pk_user_id_name`.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-naming-index-pk.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=6e5c22c31be39df427c90a37166cfdd9" alt="schema-review-naming-index-pk" width="1366" height="520" data-path="content/docs/sql-review/schema-review-naming-index-pk.webp" />

#### How the rule works

Bytebase checks that all index names in DDL conform to the naming conventions.

<Note>
  `Primary Key Naming Convention` rule is only valid for primary key, which means it does **NOT** work for unique key, foreign key and normal index.
  Also see index naming convention, unique key naming convention and foreign key naming convention.
</Note>

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER INDEX RENAME TO` statements
* `ALTER TABLE ADD CONSTRAINT` statements

#### Support database engine

* PostgreSQL

<div id="naming.index.uk" />

### Unique key naming convention

The unified naming convention is desired by developers. And the same applies to the database space. Bytebase provides this rule to unify the unique key naming convention.

#### About convention format

`Unique Key Naming Convention` uses `template` format. Specifically, the `template` is an extended [regular expression](https://en.wikipedia.org/wiki/Regular_expression). The rest follows the regular expression rules except the part with curly braces.

For example, `^uk_{{table}}_{{column_list}}$` is a `template` where `{{table}}` is the table name and `{{column_list}}` is the list of the column name. So for unique key on `user(id, name)`, the legal name is `uk_user_id_name`.

It also limits the naming max length. The default maximum length is 64 characters. Length limit does not support PostgreSQL.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-naming-index-uk.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=4a07e4edec086a8dfd5f1f80544367c4" alt="schema-review-naming-index-uk" width="941" height="359" data-path="content/docs/sql-review/schema-review-naming-index-uk.webp" />

#### How the rule works

Bytebase checks that all unique key names in DDL conform to the naming conventions.

<Note>
  `Unique Key Naming Convention` rule is only valid for unique key, which means it does **NOT** work for index, foreign key and primary key.
  Also see index naming convention, primary key naming convention and foreign key naming convention.
</Note>

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE RENAME INDEX` statements
* `ALTER TABLE ADD CONSTRAINT` statements
* `CREATE INDEX` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="naming.index.fk" />

### Foreign key naming convention

The unified naming convention is desired by developers. And the same applies to the database space. Bytebase provides this rule to unify the foreign key naming convention.

#### About convention format

`Foreign Key Naming Convention` uses `template` format. Specifically, the `template` is an extended [regular expression](https://en.wikipedia.org/wiki/Regular_expression). The rest follows the regular expression rules except the part with curly braces.

For example, `^fk_{{referencing_table}}_{{referencing_column}}_{{referenced_table}}_{{referenced_column}}$` is a `template` where `{{referencing_table}}` is the name of the referencing table, `{{referencing_column}}` is the list of the referencing column name, `{{referenced_table}}` is the name of the referenced table and `{{referenced_column}}` is the list of the referencing column name. So for unique key on `user(id, name)`, the legal name is `uk_user_id_name`.

It also limits the naming max length. The default maximum length is 64 characters. Length limit does not support PostgreSQL.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-naming-index-fk.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=ea0adbb7523b460bed316e64b4add8a4" alt="schema-review-naming-index-fk" width="923" height="354" data-path="content/docs/sql-review/schema-review-naming-index-fk.webp" />

#### How the rule works

Bytebase checks that all foreign key names in DDL conform to the naming conventions.

<Note>
  `Foreign Key Naming Convention` rule is only valid for foreign key, which means it does **NOT** work for index, unique key and primary key.
  Also see index naming convention, primary key naming convention and unique key naming convention.
</Note>

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE ADD CONSTRAINT` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="naming.table.no-keyword" />

### Disallow keywords as table names

Using keywords as table names in Oracle, or any other database management system, is generally not recommended for several reasons:

1. Reserved Keywords: Database systems have a set of reserved keywords that are used for defining the structure and operations of the database. These keywords have specific meanings and functionalities within the system. If you use a reserved keyword as a table name, it can lead to conflicts and ambiguity when executing queries or performing operations on the table.

2. Query Conflicts: When you use a reserved keyword as a table name, it can cause conflicts and confusion when constructing SQL queries. The database may interpret the keyword as a command or function instead of a table name, resulting in unexpected behavior or errors. It becomes necessary to use special techniques or syntax to differentiate the table name from the keyword, which can make the queries more complex and error-prone.

3. Code Readability: Using keywords as table names can make the code less readable and maintainable. Table names are meant to represent the entities or concepts they represent in the system. Choosing descriptive and meaningful names for tables improves code clarity and understanding. When keywords are used, it can be challenging for developers, administrators, or future maintainers to grasp the purpose and usage of the tables quickly.

4. Portability: If you decide to migrate your database from one DBMS to another in the future, using keywords as table names can cause compatibility issues. Different database systems have different sets of reserved keywords, and these keywords may vary in meaning and functionality. Migrating a database containing table names that are keywords in the target DBMS may require modifying the table names or using workarounds, which can be time-consuming and error-prone.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-naming-table-no-keyword.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=2f48edf74b5600066618a13bb3b674a5" alt="sql-review-naming-table-no-keyword" width="1816" height="644" data-path="content/docs/sql-review/sql-review-naming-table-no-keyword.webp" />

#### Support database engine

* Oracle
* Snowflake

<div id="naming.identifier.no-keyword" />

### Disallow keywords as identifiers

The same reason as `Disallow keywords as table names` above.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-naming-identifier-no-keyword.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=1af49db8842064730dcd076072066f89" alt="sql-review-naming-identifier-no-keyword" width="1818" height="644" data-path="content/docs/sql-review/sql-review-naming-identifier-no-keyword.webp" />

#### Support database engine

* Oracle
* Snowfake

<div id="naming.identifier.case" />

### Identifier case

Bytebase provides this rule to unify the identifier case.

For Oracle, if the identifier is not quoted, it is converted to uppercase. In order to unify the identifier case, you can use this rule to disallow the lowercase identifier.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-naming-identifier-case.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=1396cf6378e4ffbf06ca96c521112fc2" alt="sql-review-naming-identifier-case" width="1818" height="644" data-path="content/docs/sql-review/sql-review-naming-identifier-case.webp" />

#### Support database engine

* Oracle
* Snowflake

## Statement

<div id="statement.select.no-select-all" />

### Disallow SELECT \*

`SELECT *` introduces additional performance cost or ambiguous semantics.

For scenarios where all columns are not required, you should SELECT the columns you need to avoid getting unneeded data.

For scenarios where all columns are required, you should list all column names to avoid semantic ambiguity. Otherwise, the data consumer cannot know the column information. And `SELECT *` may bring additional modifications and errors when modifying the table schema.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-query-select-no-select-all.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=b069d8782cd5abdd852f7ef04f6ffb53" alt="schema-review-query-select-no-select-all" width="933" height="355" data-path="content/docs/sql-review/schema-review-query-select-no-select-all.webp" />

#### How the rule works

Bytebase considers this rule to be violated if the SQL has `SELECT *`.

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase

<div id="statement.where.require" />

### Require WHERE

There are countless stories about people forgetting the WHERE clause in an UPDATE or DELETE and losing data. In queries, not using WHERE can also cause performance issues.

If you are sure you need to act on all data, use `WHERE 1=1` to remind yourself of the consequences of that action.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-query-where-require.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=a07962d9cb88f721dafa6c5456c68e3f" alt="schema-review-query-where-require" width="934" height="354" data-path="content/docs/sql-review/schema-review-query-where-require.webp" />

#### How the rule works

Bytebase considers this rule to be violated if the SQL has no WHERE clause.

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase
* Snowflake

<div id="statement.where.no-leading-wildcard-like" />

### Disallow leading % in LIKE

Database cannot use an index to match entries when there is a leading wildcard. It can cause serious performance problems because it may scan the entire table.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-query-where-no-leading-wildcard-like.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=db8fe695a92d05cfbd91870e61634a99" alt="schema-review-query-where-no-leading-wildcard-like" width="929" height="354" data-path="content/docs/sql-review/schema-review-query-where-no-leading-wildcard-like.webp" />

#### How the rule works

Bytebase considers this rule to be violated if the SQL has leading wildcard LIKE.

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase

<div id="statement.disallow-commit" />

### Disallow COMMIT

Disallow using COMMIT statement.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-disallow-commit.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=869db4c75cc01529cb4a359dd339c4fc" alt="sql-review-statement-disallow-commit" width="924" height="328" data-path="content/docs/sql-review/sql-review-statement-disallow-commit.webp" />

#### How the rule works

Bytebase alerts users if there exists COMMIT statement.

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="statement.disallow-limit" />

### Disallow LIMIT

Disallow LIMIT clause for INSERT, UPDATE and DELETE statements.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-disallow-limit.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=cefde673711c4be2d588da5606d01e05" alt="sql-review-statement-disallow-limit" width="921" height="349" data-path="content/docs/sql-review/sql-review-statement-disallow-limit.webp" />

#### How the rule works

Specifically, Bytebase checks:

* `INSERT` statements
* `UPDATE` statements
* `DELETE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

Support for PostgreSQL is coming soon.

<div id="statement.disallow-order-by" />

### Disallow ORDER BY

Disallow ORDER BY clause for UPDATE and DELETE statements.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-disallow-order-by.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=dcd8f4cdccd54c765b6a97f726a09416" alt="sql-review-statement-disallow-order-by" width="917" height="350" data-path="content/docs/sql-review/sql-review-statement-disallow-order-by.webp" />

#### How the rule works

Specifically, Bytebase checks:

* `UPDATE` statements
* `DELETE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

Support for PostgreSQL is coming soon.

<div id="statement.merge-alter-table" />

### Merge ALTER TABLE

For readability, it's better not to use multiple `ALTER TABLE` statements for the same table.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-merge-alter-table.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=d1ef2f7543fa4ac5c6a8f88e40b1c5c8" alt="sql-review-statement-merge-alter-table" width="918" height="348" data-path="content/docs/sql-review/sql-review-statement-merge-alter-table.webp" />

#### How the rule works

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="statement.insert.must-specify-column" />

### INSERT statements must specify columns

For readability, it's better to explicitly specify columns for INSERT statements, such as `INSERT INTO table_t(id, name) VALUES(...)`.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-insert-must-specify-column.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=c6f55b1bf604a7b3b657639a561bb7d6" alt="sql-review-statement-insert-must-specify-column" width="918" height="350" data-path="content/docs/sql-review/sql-review-statement-insert-must-specify-column.webp" />

#### How the rule works

Specifically, Bytebase checks:

* `INSERT` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase

<div id="statement.insert.disallow-order-by-rand" />

### Disallow ORDER BY RAND in INSERT statements

The `ORDER BY RAND()` clause is not necessary for INSERT statements.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-insert-disallow-order-by-rand.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=7f3d01b344a0129bab29bac3c5766dd1" alt="sql-review-statement-insert-disallow-order-by-rand" width="922" height="348" data-path="content/docs/sql-review/sql-review-statement-insert-disallow-order-by-rand.webp" />

#### How the rule works

Specifically, Bytebase checks:

* `INSERT` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="statement.insert.row-limit" />

### Limit the inserted rows

Alert users if the inserted rows exceed the limit.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-insert-row-limit.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=f373767d5633a51dc577c4ef3896412b" alt="sql-review-statement-statement-insert-row-limit" width="919" height="364" data-path="content/docs/sql-review/sql-review-statement-insert-row-limit.webp" />

#### How the rule works

* For `INSERT INTO ... VALUES(...)` statements, Bytebase checks the count of value list.
* For `INSERT INTO ... SELECT ...` statements, Bytebase runs `EXPLAIN` statements for them and check the rows in `EXPLAIN` statement results.

#### Support database engine

* MySQL
* PostgreSQL
* OceanBase

<div id="statement.affected-row-limit" />

### Limit affected row limit

Alert users if the affected rows in `UPDATE` or `DELETE` exceed the limit.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-affected-row-limit.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=ca37e7e30405f5d2dc7ef9265a77d9e0" alt="sql-review-statement-affected-row-limit" width="921" height="347" data-path="content/docs/sql-review/sql-review-statement-affected-row-limit.webp" />

#### How the rule works

For `UPDATE` and `DELETE` statements, Bytebase runs `EXPLAIN` statements for them and check the rows in `EXPLAIN` statement results.

#### Support database engine

* MySQL
* PostgreSQL
* OceanBase

<div id="statement.dml-dry-run" />

### Dry run DML statements

Dry run DML statements for validation.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-dml-dry-run.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=79532e3cec9fbe4a0a1408b20e8537be" alt="sql-review-statement-dml-dry-run" width="924" height="346" data-path="content/docs/sql-review/sql-review-statement-dml-dry-run.webp" />

#### How the rule works

Dry run DML statements by `EXPLAIN` statements. Specifically, Bytebase checks:

* `INSERT` statements
* `UPDATE` statements
* `DELETE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="statement.disallow-add-column-with-default" />

### Disallow add column with default

The PostgreSQL will lock the table and rewrite the whole table when you adding column with default value. You can separate the adding column, setting default value and backfilling all existing rows.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-disallow-add-column-with-default.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=ed1cc5f8c05494c8e8d807e5c249d8c7" alt="sql-review-statement-disallow-add-column-with-default" width="1476" height="650" data-path="content/docs/sql-review/sql-review-statement-disallow-add-column-with-default.webp" />

#### How the rule works

Bytebase checks all `ALTER TABLE ADD COLUMN` statements.

#### Support database engine

* PostgreSQL

<div id="statement.add-check-not-valid" />

### Add CHECK constraints with NOT VALID option

Adding CHECK constraints without NOT VALID can cause downtime because it blocks reads and writes. You can manually verify all rows and validate the constraint after creating.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-add-check-not-valid.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=bd5a2e35664e4015b4ac9cee51913404" alt="sql-review-statement-add-check-not-valid" width="1778" height="696" data-path="content/docs/sql-review/sql-review-statement-add-check-not-valid.webp" />

#### How the rule works

Bytebase checks all `ALTER TABLE ADD CONSTRAINT` statements.

#### Support database engine

* PostgreSQL

<div id="statement.disallow-add-not-null" />

### Disallow add NOT NULL constraints to an existing column

It can cause downtime because it blocks reads and writes. You can add CHECK(column IS NOT NULL) constraints with NOT VALID option to avoid this.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-statement-disallow-add-not-null.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=509adc3482c0184e7f71666d6944edd4" alt="sql-review-statement-disallow-add-not-null" width="1820" height="682" data-path="content/docs/sql-review/sql-review-statement-disallow-add-not-null.webp" />

#### How the rule works

Bytebase checks all `ALTER TABLE ADD CONSTRAINT` statements.

#### Support database engine

* PostgreSQL

## Table

<div id="table.limit-size" />

### Limit DDL operations on tables with large data volumes

DDL operations on large tables can cause long locks because they need exclusive access to update the table’s structure and metadata, which takes more time for bigger tables.

#### How the rule works

Bytebase considers this rule to be violated if the SQL tries to apply DDL operations on a table with sizes exceeding the set value.

#### Support database engine

* MySQL

<div id="table.require-pk" />

### Require primary key

In almost all cases, each table needs a primary key.

e.g. in MySQL, [the InnoDB storage engine always creates a primary key](https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html) if you didn't specify it explicitly or didn't create a unique key, thus making an extra column you don't have access to.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-table-require-pk.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=440f24053cfd7380f62f8299c512733d" alt="schema-review-table-require-pk" width="930" height="357" data-path="content/docs/sql-review/schema-review-table-require-pk.webp" />

#### How the rule works

Bytebase considers this rule to be violated if the SQL tries to create a no primary key table or drop the primary key. If the SQL drops all columns in the primary key, Bytebase also considers that this SQL drops the primary key.

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase
* Snowflake

<div id="table.no-foreign-key" />

### Disallow foreign key

This rule disallows users to create foreign key in the table.

A foreign key is a logical association of rows between two tables, in a parent-child relationship. A row in a "parent" table may be referenced by one or more rows in a "child" table.

`FOREIGN KEY` constraints are impossible to maintain once your data grows and is split over multiple database servers. This typically happens when you introduce functional partitioning/sharding and/or horizontal sharding.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-table-no-fk.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=97d47e679b625e43905ecd6caa706235" alt="schema-review-table-no-fk" width="2006" height="962" data-path="content/docs/sql-review/schema-review-table-no-fk.webp" />

#### How the rule works

Bytebase considers this rule to be violated if the SQL tries to:

* `CREATE TABLE` statement with foreign key
* `ALTER TABLE ADD CONSTRAINT FOREIGN KEY` statement

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase
* Snowflake

<div id="table.drop-naming-convention" />

### Drop naming convention

Only tables named with specific naming patterns can be deleted. This requires users to do a rename and then drop the table.

The naming convention uses [regular expression](https://en.wikipedia.org/wiki/Regular_expression) format. By default the table name must have `_del` suffix.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-table-drop-naming.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=59762b1143036637740ea4fa8c856f41" alt="schema-review-table-drop-naming" width="1956" height="932" data-path="content/docs/sql-review/schema-review-table-drop-naming.webp" />

#### How the rule works

Bytebase checks that the table names in DDL conform to the naming conventions.

Specifically, Bytebase checks:

* `DROP TABLE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="table.disallow-partition" />

### Disallow partition table

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-table-disallow-partition.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=9c2c0bc50f9e314096cf5c4f79112962" alt="sql-review-table-disallow-partition" width="922" height="346" data-path="content/docs/sql-review/sql-review-table-disallow-partition.webp" />

#### How the rule works

Bytebase checks if the SQL statement will create the partition table.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="table.comment" />

### Table comment convention

Configure whether the table requires comments and the maximum comment length.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-table-comment.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=effe021981347eb203104da23ab51211" alt="sql-review-table-comment" width="926" height="350" data-path="content/docs/sql-review/sql-review-table-comment.webp" />

#### How the rule works

Bytebase checks the table comment convention.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

## Schema

<div id="schema.backward-compatibility" />

### Backward incompatible schema change

Introducing backward incompatible schema changes is one of the most common mistakes made by developers. And enforcing backward compatible schema change is the standard practice adopted by many engineering organizations. Bytebase provides the built-in backward compatible check to catch all common incompatible schema change [scenarios](https://www.bytebase.com/docs/sql-review/error-codes/#compatibility).

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-schema-backward-compatibility.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=50f3effd7c2d024002ff4211c3fc5773" alt="schema-review-schema-backward-compatibility" width="950" height="369" data-path="content/docs/sql-review/schema-review-schema-backward-compatibility.webp" />

#### How the rule works

If the following situation occurs, Bytebase considers this rule to be violated:

* Drop database
* Rename table/view
* Drop table/view
* Rename column
* Drop column
* Add primary key
* Add Unique key
* Add Foreign key
* Add check enforced
* Alter check enforced
* Modify column
* Change column

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

## Column

<div id="column.required" />

### Enforce the required columns in each table

For most projects, you may want to enforce some columns for every table. For example, need `id` as identification and the primary key for each table or need `created_ts` and `updated_ts` to record creation and modification times.

You can customize which columns are required.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-column-required.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=1ac0ea4d43a48ffd46258bbca01a453d" alt="schema-review-column-required" width="930" height="362" data-path="content/docs/sql-review/schema-review-column-required.webp" />

#### How the rule works

Bytebase defaults all tables to meet the requirements. If the SQL tries to define a table not having all the required columns or attempts to drop the required column, Bytebase considers this rule to be violated.

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase
* Snowflake

<div id="column.type-disallow-list" />

### Column type disallow list

Set column type disallow list to ban column types.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-column-disallow-list.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=93bb933994fa5852217cb4be2faa1b82" alt="sql-review-column-disallow-list" width="925" height="350" data-path="content/docs/sql-review/sql-review-column-disallow-list.webp" />

#### How the rule works

Bytebase checks if the SQL statement creates the column type in the disallow list.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase

<div id="column.no-null" />

### Columns no NULL value

NULL is a special value. It can cause confusion or performance issues. Bytebase provides this rule to enforce that all columns cannot have NULL value.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-column-no-null.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=1609694febcdc3a61460252ac03be37f" alt="schema-review-column-no-null" width="931" height="361" data-path="content/docs/sql-review/schema-review-column-no-null.webp" />

#### How the rule works

Bytebase considers this rule to be violated if the SQL defines a column allowing NULL value.

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase
* Snowflake

<div id="column.disallow-change-type" />

### Disallow changing column type

Changing column type may fail because the data cannot be converted. Bytebase provides this rule to alert you that the SQL statement would change the column type.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-column-disallow-changing-type.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=546c085b855585b7af073825b6919aed" alt="sql-review-column-disallow-change-type" width="928" height="353" data-path="content/docs/sql-review/sql-review-column-disallow-changing-type.webp" />

#### How the rule works

Bytebase checks if the SQL statement will change the column type.

Specifically, Bytebase checks:

* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="column.set-default-for-not-null" />

### Set DEFAULT value for NOT NULL columns

NOT NULL columns have no default value. It requires users to manually set default values for NOT NULL columns.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-column-set-default-for-not-null.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=13d7e9ef614e512b30a0bdbdd90f83fe" alt="sql-review-column-set-default-for-not-null" width="928" height="349" data-path="content/docs/sql-review/sql-review-column-set-default-for-not-null.webp" />

#### How the rule works

Bytebase checks if setting default values for NOT NULL columns.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* Oracle
* OceanBase

Support for PostgreSQL is coming soon.

<div id="column.disallow-change" />

### Disallow ALTER TABLE CHANGE COLUMN statements

CHANGE COLUMN is a MySQL extension to standard SQL. CHANGE COLUMN can change column definition and names, or both.
Most of the time, you just want to change one of two. So you need to use RENAME COLUMN and MODIFY COLUMN instead of CHANGE COLUMN to avoid unexpected modifications.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-column-disallow-change.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=3f48284227c695e9969394ac1473d46b" alt="sql-review-column-disallow-change" width="929" height="350" data-path="content/docs/sql-review/sql-review-column-disallow-change.webp" />

#### How the rule works

Bytebase checks if using `ALTER TABLE CHANGE COLUMN` statements.

#### Support database engine

* MySQL
* TiDB
* OceanBase

<div id="column.disallow-changing-order" />

### Disallow changing column order

Changing column order may cause performance issues. Users should be cautious about this.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-column-disallow-changing-order.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=e825c1b2c56f4ebda5588942a34baf9a" alt="sql-review-column-disallow-changing-order" width="921" height="348" data-path="content/docs/sql-review/sql-review-column-disallow-changing-order.webp" />

#### How the rule works

Bytebase checks if changing column order.

Specifically, Bytebase checks:

* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

<div id="column.auto-increment-must-integer" />

### Use integer for auto-increment columns

The auto-increment column must be integer.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/sql-review-column-auto-increment-must-integer.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=1b64e5938a4a2481006b073ff38faade" alt="sql-review-column-auto-increment-must-integer" width="918" height="362" data-path="content/docs/sql-review/sql-review-column-auto-increment-must-integer.webp" />

#### How the rule works

Bytebase checks the auto-increment column type.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

Support for PostgreSQL is coming soon.

<div id="column.disallow-set-charset" />

### Disallow set charset for columns

It's better to set the charset in the table or database.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-column-disallow-set-charset.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=18a104e7847429a6919b11dd49fe8cbc" alt="sql-review-column-disallow-set-charset" width="922" height="365" data-path="content/docs/sql-review/sql-review-column-disallow-set-charset.webp" />

#### How the rule works

Bytebase checks if setting charset for columns.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

<div id="column.auto-increment-must-unsigned" />

### Set unsigned attribute on auto-increment columns

Setting unsigned attribute on auto-increment columns to avoid negative numbers.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/sql-review-column-auto-increment-must-unsigned.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=24fc00dd52f510b1b490add23f2d77e4" alt="sql-review-column-auto-increment-must-unsigned" width="923" height="368" data-path="content/docs/sql-review/sql-review-column-auto-increment-must-unsigned.webp" />

#### How the rule works

Bytebase checks the unsigned attribute for auto-increment columns.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

<div id="column.comment" />

### Column comment convention

Configure whether the column requires comments and the maximum comment length.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/sql-review-column-comment.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=e73eed8d774d48c04c558e565006ab4f" alt="sql-review-column-comment" width="920" height="362" data-path="content/docs/sql-review/sql-review-column-comment.webp" />

#### How the rule works

Bytebase checks the column comment.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

<div id="column.maximum-character-length" />

### Maximum CHAR length

The CHAR type is the fixed-length type. A longer CHAR will require more storage space.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-column-maximum-character-length.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=c9f5312af9cd5c90e236222676da3375" alt="sql-review-column-maximum-character-length" width="926" height="360" data-path="content/docs/sql-review/sql-review-column-maximum-character-length.webp" />

#### How the rule works

Bytebase checks the length for the CHAR type.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase

<div id="column.maximum-varchar-length" />

### Maximum VARCHAR length

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-column-maximum-varchar-length.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=0faebece40d8e472a42db59bc65b635b" alt="sql-review-column-maximum-varchar-length" width="1818" height="644" data-path="content/docs/sql-review/sql-review-column-maximum-varchar-length.webp" />

#### How the rule works

Bytebase checks the length for the VARCHAR type.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* Oracle
* Snowflake

<div id="column.auto-increment-initial-value" />

### Auto-increment initial value

Set initial value for auto-increment columns.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/sql-review-column-auto-increment-initial-value.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=ba1023fd50f0a35ba99610b73de6f628" alt="sql-review-column-auto-increment-initial-value" width="923" height="362" data-path="content/docs/sql-review/sql-review-column-auto-increment-initial-value.webp" />

#### How the rule works

Bytebase checks the initial value for auto-increment columns.

Specifically, Bytebase checks:

* `CREATE TABLE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

<div id="column.current-time-count-limit" />

### Limit the count of current time columns

Limit the count of `NOW()`, `CURRENT_TIME()` and `CURRENT_TIMESTAMP()` columns.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/sql-review-column-current-time-count-limit.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=6187666ccdba2dd8a47ae06cc8004c85" alt="sql-review-column-current-time-count-limit" width="928" height="367" data-path="content/docs/sql-review/sql-review-column-current-time-count-limit.webp" />

#### How the rule works

This rule will count the two types of the columns:

1. the column with default current time , such as `DEFAULT NOW()`
2. the column with ON UPDATE current time, such as `ON UPDATE NOW()`

If the count of type one columns is more than two or the count of type two columns is more than one, this rule will alert users.

The meaning of the number is:

1. A table usually has `created_ts` and `updated_ts` column with `DEFAULT NOW()`.
2. A table usually has `updated_ts` column with `ON UPDATE NOW()`

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

<div id="column.require-default" />

### Require column default value

Require default value for all columns, except PRIMARY KEY, JSON, BLOB, TEXT, GEOMETRY, AUTO\_INCREMENT, GENERATED columns.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-column-require-default.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=4edf250f52a8dbdbde7d16a0e55a24dd" alt="sql-review-column-require-default" width="924" height="365" data-path="content/docs/sql-review/sql-review-column-require-default.webp" />

#### How the rule works

Bytebase checks the column default value.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase

<div id="column.disallow-drop-in-index" />

### Prohibit dropping columns in indexes

Dropping columns in indexes may cause performance issues. Users should be cautious about this.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-column-disallow-drop-in-index.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=d497af9a1851dc761623b991d5f8d02e" alt="sql-review-column-disallow-drop-in-index" width="1864" height="432" data-path="content/docs/sql-review/sql-review-column-disallow-drop-in-index.webp" />

#### How the rule works

Bytebase checks if dropping columns in indexes.

Specifically, Bytebase checks:

* `ALTER TABLE DROP COLUMN` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

<div id="column.default-disallow-volatile" />

### Disallow setting volatile default value on columns

Volatile functions, such as `clock_timestamp()`, update each row with the value at the time of `ALTER TABLE ADD COLUMN` execution. This can lead to lengthy updates and potential performance issues.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/sql-review-column-default-disallow-volatile.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=d2d7f47aeb6660b1746dd6632c893551" alt="sql-review-column-default-disallow-volatile" width="1540" height="502" data-path="content/docs/sql-review/sql-review-column-default-disallow-volatile.webp" />

#### How the rule works

Bytebase checks if the default value of columns is volatile in `ALTER TABLE ADD COLUMN` statements.

#### Support database engine

* PostgreSQL

## Index

<div id="index.no-duplicate-column" />

### Disallow duplicate column in index keys

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-index-no-duplicate-column.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=ed5c1fd2a9b2f7f7912c94dbaba06b26" alt="sql-review-index-no-duplicate-column" width="914" height="363" data-path="content/docs/sql-review/sql-review-index-no-duplicate-column.webp" />

#### How the rule works

Bytebase checks if there exists duplicate column in index keys.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements
* `CREATE INDEX` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="index.key-number-limit" />

### Limit the count of index keys

Limit the count of index keys in one index.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-index-key-number-limit.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=1550ba9606584d19724208298e903325" alt="sql-review-index-key-number-limit" width="918" height="363" data-path="content/docs/sql-review/sql-review-index-key-number-limit.webp" />

#### How the rule works

Bytebase checks the count of index keys in each index.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements
* `CREATE INDEX` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* Oracle
* OceanBase

<div id="index.pk-type-limit" />

### Limit key type for primary keys

Alert users if key type is not INT or BIGINT in primary keys.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-index-pk-type-limit.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=2efca01d1a15e7b84de0672b30b3a693" alt="sql-review-index-pk-type-limit" width="920" height="369" data-path="content/docs/sql-review/sql-review-index-pk-type-limit.webp" />

#### How the rule works

Bytebase checks the key type for primary keys.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

Support for PostgreSQL is coming soon.

<div id="index.type-no-blob" />

### Disallow BLOB and TEXT for index keys

Disallow using BLOB and TEXT type as index keys.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-index-disallow-blob.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=fc63653da59c2a3feeb4eaf223173902" alt="sql-review-index-type-no-blob" width="916" height="347" data-path="content/docs/sql-review/sql-review-index-disallow-blob.webp" />

#### How the rule works

Bytebase checks the key type for index keys.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements
* `CREATE INDEX` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

Support for PostgreSQL is coming soon.

<div id="index.total-number-limit" />

### Index count limit

Limit the index count in one table.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-index-total-number-limit.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=b22884e96c671e7a6b506d0341460fed" alt="sql-review-index-total-number-limit" width="924" height="369" data-path="content/docs/sql-review/sql-review-index-total-number-limit.webp" />

#### How the rule works

Bytebase checks the index count for each table.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements
* `CREATE INDEX` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="index.primary-key-type-allowlist" />

### Primary key type allowlist

Limit the data type for primary key.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-index-primary-key-type-allowlist.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=42adb5d762b221938b274159f072cf92" alt="sql-review-index-primary-key-type-allowlist" width="1446" height="628" data-path="content/docs/sql-review/sql-review-index-primary-key-type-allowlist.webp" />

#### How the rule works

Bytebase checks the data type for each primary key.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE ADD CONSTRAINT` statements

#### Support database engine

* PostgreSQL
* MySQL
* TiDB
* OceanBase

<div id="index.create-concurrently" />

### Create index concurrently

Creating indexes blocks writes (but not reads) on the table until it's done. Use CONCURRENTLY when creates indexes can allow writes to continue.

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-index-create-concurrently.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=865fd0bd4363e947c295b6f520d259a6" alt="sql-review-index-create-concurrently" width="1628" height="648" data-path="content/docs/sql-review/sql-review-index-create-concurrently.webp" />

#### How the rule works

Specifically, Bytebase checks:

* `CREATE INDEX` statements

#### Support database engine

* PostgreSQL

## Database

<div id="database.drop-empty-database" />

### Drop database restriction

Can only drop the database if there's no table in it.
It requires users to drop all containing tables first before dropping the database.

<img src="https://mintcdn.com/dbx/oPldGQC4HFcCc-EY/content/docs/sql-review/schema-review-drop-empty-db.webp?fit=max&auto=format&n=oPldGQC4HFcCc-EY&q=85&s=45b9fb01bec33255f50fba34a7fae2d8" alt="schema-review-drop-empty-db" width="1984" height="1000" data-path="content/docs/sql-review/schema-review-drop-empty-db.webp" />

#### How the rule works

Bytebase checks if there exists any table in the specific database.

Specifically, Bytebase checks:

* `DROP DATABASE` statements

#### Support database engine

* MySQL
* TiDB
* OceanBase

Support for PostgreSQL is coming soon.

## System

<div id="system.charset.allowlist" />

### Charset allow list

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-system-charset-allow-list.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=99cb28caa6a8cead2e8f2eefa2ab8402" alt="sql-review-system-charset-allowlist" width="919" height="363" data-path="content/docs/sql-review/sql-review-system-charset-allow-list.webp" />

#### How the rule works

Bytebase checks if the SQL statement uses the charset outside of the allow list.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="system.collation.allowlist" />

### Collation allow list

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-system-collation-allowlist.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=2cf5334eb9978250dac12bbfc6f308ff" alt="sql-review-system-collation-allowlist" width="918" height="364" data-path="content/docs/sql-review/sql-review-system-collation-allowlist.webp" />

#### How the rule works

Bytebase checks if the SQL statement uses the collation outside of the allow list.

Specifically, Bytebase checks:

* `CREATE TABLE` statements
* `ALTER TABLE` statements

#### Support database engine

* MySQL
* TiDB
* PostgreSQL
* OceanBase

<div id="system.comment.length" />

### Comment length limit

<img src="https://mintcdn.com/dbx/8Z2Tpw0PfCAhTKDg/content/docs/sql-review/sql-review-system-comment-length.webp?fit=max&auto=format&n=8Z2Tpw0PfCAhTKDg&q=85&s=4c7e562603489634480e671e61e9ccb8" alt="sql-review-system-comment-length" width="1828" height="642" data-path="content/docs/sql-review/sql-review-system-comment-length.webp" />

#### How the rule works

Bytebase checks all `COMMENT ON` statements.

#### Support database engine

* PostgreSQL

## Built-in rules

<div id="builtin.prior-backup-check" />

### Prior backup feasibility check

Check [Data Rollback](/change-database/rollback-data-changes) for details.
