Repository: ariga/atlas
Pull request #1416 (2023-01-23 15:37:36)

Doc/md/guides/mysql: Add doc about CHECK constraint


Patch:
diff --git a/doc/md/cloud/getting-started.md b/doc/md/cloud/getting-started.md
index fd76dfe1..894a9bb4 100644
--- a/doc/md/cloud/getting-started.md
+++ b/doc/md/cloud/getting-started.md
@@ -1,13 +1,13 @@
 ---
 id: getting-started
-title: Getting Started with Ariga Cloud
+title: Getting Started with Atlas Cloud
 sidebar_label: Getting Started
 ---
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 import Discord from '../../website/src/assets/icons/discord-white.svg'
 
-[Ariga Cloud](https://ariga.cloud) is an online platform that allows developers to view their Atlas projects and track
+[Atlas Cloud](https://atlasgo.cloud) is an online platform that allows developers to view their Atlas projects and track
 their [Atlas GitHub Action](/integrations/github-actions) CI runs. The cloud platform gives full visibility into your
 Atlas schema, by displaying an entity relation diagram (ERD) that shows the schema changes, as well as the entire
 database schema.
@@ -15,20 +15,20 @@ database schema.
 ### Signing Up
 
 ![Login](https://release.ariga.io/images/assets/login1.png)
-1. To get started with [Ariga Cloud](https://ariga.cloud/), create an account by clicking 'sign up' on the homepage.
+1. To get started with [Atlas Cloud](https://atlasgo.cloud/), create an account by clicking 'sign up' on the homepage.
 2. In the sign up screen, enter your work email and choose a password.
-3. Next, you will receive an email to verify your account. From your email, [Ariga Cloud](https://ariga.cloud/)
+3. Next, you will receive an email to verify your account. From your email, [Atlas Cloud](https://atlasgo.cloud/)
 will open in a new tab, and you will need to sign in to access your account.
 4. Once signed in, you will be asked to confirm the service agreement in order to continue with the login process.
 5. Next, you will be prompted to create an organization. After creating the organization, you will be able
 to invite team members to join. Choose a meaningful name for the organization, as it will also be your subdomain.
-For example, "Acme Corp" will be available at "acme-corp.ariga.cloud".  
+For example, "Acme Corp" will be available at "acme-corp.atlasgo.cloud".
 
 ### Choosing an organization
 ![Choosing organization](https://atlasgo.io/uploads/images/choose-org1.png)
-Ariga Cloud allows you to be a part of multiple organizations.
+Atlas Cloud allows you to be a part of multiple organizations.
 After logging in (not including the first login), you will see all the organizations you have access to. Choose the organization you would to log in to.
-If you would like to create a new organization, click 'create a new organization'.  
+If you would like to create a new organization, click 'create a new organization'.
 While logged in to one organization, you would be able to switch to another by clicking on your avatar on the top right corner and choose the organization you would like to switch to.
 
 ![Switching organization](https://atlasgo.io/uploads/images/choose-org2.png)
@@ -211,7 +211,7 @@ jobs:
 </Tabs>
 
 6. After merging the workflow to your mainline branch, the workflow will be triggered.
-7. Refresh Ariga Cloud and your project will appear!
+7. Refresh Atlas Cloud and your project will appear!
 
 ![Setup Projects](https://release.ariga.io/images/assets/setup-projects.png)
 
@@ -276,7 +276,7 @@ lint {
 
 ### Inviting Members
 Under 'Settings' > 'Members', you can invite team members to your organization.
-These members will receive an email with a link to Ariga Cloud, and will be required to sign up with the same email
+These members will receive an email with a link to Atlas Cloud, and will be required to sign up with the same email
 in order to access the organization.
 
 ### Regenerating Tokens
diff --git a/doc/md/declarative/inspect.mdx b/doc/md/declarative/inspect.mdx
index c8cd4fd0..1ff4cc97 100644
--- a/doc/md/declarative/inspect.mdx
+++ b/doc/md/declarative/inspect.mdx
@@ -1,7 +1,7 @@
 ---
 id: inspect
 slug: /declarative/inspect
-title: Inspecting existing schemas with Atlas 
+title: Inspecting existing schemas with Atlas
 ---
 
 import Tabs from '@theme/Tabs';
@@ -22,7 +22,7 @@ language that they can use as the starting point for working with this database.
 When using `schema inspect` to inspect an existing database, users may supply multiple
 parameters:
 * `--url` (`-u` accepted as well) - the [URL](/concepts/url) of database to be inspected.
-* `--schema` (optional, may be supplied multiple times) - schemas to inspect within 
+* `--schema` (optional, may be supplied multiple times) - schemas to inspect within
  the target database.
 * `--exclude` (optional) - filter out resources matching the given glob pattern.
 * `--log` (optional) - [Go template](https://pkg.go.dev/text/template) to use to format the output.
@@ -210,9 +210,9 @@ atlas schema inspect -u '<url>' --log '{{ json . }}'
 ### Visualize schemas
 
 Ariga's Explore tool, available in a web browser, can generate an Entity-Relationship Diagram (ERD) for the database
-schema. Visit [gh.ariga.cloud/explore](https://gh.ariga.cloud/explore) and follow the instructions.
+schema. Visit [gh.atlasgo.cloud/explore](https://gh.atlasgo.cloud/explore) and follow the instructions.
 
-<p style={{textAlign: "center"}}><a href="https://gh.ariga.cloud/explore/0083bc5b"><img src="https://atlasgo.io/uploads/images/atlas-erd-screenshot.jpg" alt="Schema ERD"/></a></p>
+<p style={{textAlign: "center"}}><a href="https://gh.atlasgo.cloud/explore/0083bc5b"><img src="https://atlasgo.io/uploads/images/atlas-erd-screenshot.jpg" alt="Schema ERD"/></a></p>
 
 ## Reference
 
diff --git a/doc/md/getting-started/getting-started.mdx b/doc/md/getting-started/getting-started.mdx
index 3fa2e4ad..6c5fc79c 100644
--- a/doc/md/getting-started/getting-started.mdx
+++ b/doc/md/getting-started/getting-started.mdx
@@ -104,7 +104,7 @@ Now, consider we want to add a `blog_posts` table and have our schema represent
 blogging system.
 
 
-<p style={{textAlign: "center"}}><a href="https://gh.ariga.cloud/explore/9717d499"><img src="https://atlasgo.io/uploads/images/blog-erd.png" alt="Blog ERD"/></a></p>
+<p style={{textAlign: "center"}}><a href="https://gh.atlasgo.cloud/explore/9717d499"><img src="https://atlasgo.io/uploads/images/blog-erd.png" alt="Blog ERD"/></a></p>
 
 Let's add the following to our inspected schema, and use Atlas to plan and apply the changes to our database.
 
@@ -315,4 +315,4 @@ For web-based, free, and fun (GIFs included) support:
     </span>
 </a>
 
-:::
\ No newline at end of file
+:::
diff --git a/doc/md/guides/cloud/explore-inspection.md b/doc/md/guides/cloud/explore-inspection.md
index 4b65d946..50fb43d3 100644
--- a/doc/md/guides/cloud/explore-inspection.md
+++ b/doc/md/guides/cloud/explore-inspection.md
@@ -9,7 +9,7 @@ import TabItem from '@theme/TabItem';
 import CodeBlock from '@theme/CodeBlock';
 
 
-Atlas has a [Cloud platform](https://ariga.cloud/signup) that offers an [Explore feature](https://gh.ariga.cloud/explore)
+Atlas has a [Cloud platform](https://atlasgo.cloud/signup) that offers an [Explore feature](https://gh.atlasgo.cloud/explore)
 without having to sign up. The Explore feature allows you to generate an Entity Relationship Diagram (ERD) from your
 database by either manually typing in your schema, or importing from an existing database.
 
@@ -94,7 +94,7 @@ mysql://root:pass@4.tcp.ngrok.io:16496/db_name
 Make sure to remove the 'tcp://' from the URL and write in your local username and password to access the database.
 :::
 
-Open the [Explore feature](https://gh.ariga.cloud/explore) and click on `import`.
+Open the [Explore feature](https://gh.atlasgo.cloud/explore) and click on `import`.
 
 ![Explore Page](https://atlasgo.io/uploads/images/explore_page.png)
 
diff --git a/doc/md/guides/deploying/fly-io.mdx b/doc/md/guides/deploying/fly-io.mdx
index c8b4731d..9d4b646f 100644
--- a/doc/md/guides/deploying/fly-io.mdx
+++ b/doc/md/guides/deploying/fly-io.mdx
@@ -164,4 +164,4 @@ Error release command failed, deployment aborted
 
 You can always improve the deployment pipeline by leveraging [Atlas](https://atlasgo.io/integrations/github-actions)
 and [Fly](https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/) Github actions. For additional
-insights on the database schema and migrations, we recommend giving [Ariga Cloud](https://atlasgo.io/cloud/getting-started) a try.
\ No newline at end of file
+insights on the database schema and migrations, we recommend giving [Atlas Cloud](https://atlasgo.io/cloud/getting-started) a try.
diff --git a/doc/md/guides/guides.md b/doc/md/guides/guides.md
index 6be06fb9..81dc5942 100644
--- a/doc/md/guides/guides.md
+++ b/doc/md/guides/guides.md
@@ -32,6 +32,8 @@ Welcome to the Atlas Guides.
 
 [Prefix Indexes](mysql/prefix-indexes.md)
 
+[CHECK Constraint](mysql/check-constraint.md)
+
 [Generated Columns](mysql/generated-columns.md)
 
 [Managing MySQL schemas on RDS with Terraform](mysql/terraform.md)
diff --git a/doc/md/guides/mysql/check-constraint.md b/doc/md/guides/mysql/check-constraint.md
new file mode 100644
index 00000000..f899c985
--- /dev/null
+++ b/doc/md/guides/mysql/check-constraint.md
@@ -0,0 +1,297 @@
+---
+id: check-constraint
+title: CHECK Constraint in MySQL
+slug: /guides/mysql/check-constraint
+---
+
+### Introduction
+
+A `CHECK` constraint is a very useful tool that can be used in MySQL to prevent invalid data from being inserted into a table.
+
+It works by checking the values of certain columns in the table against certain conditions that you specify. If a row of data does not meet the conditions of the `CHECK` constraint, then it is rejected and not inserted into the table.
+
+This can be a very helpful tool for preventing bad data from being inserted into your database, and it can save you a lot of time and hassle later on. In this article, we will show you how to use a `CHECK` constraint in MySQL.
+
+### What is the `CHECK` constraint in MySQL?
+
+MySQL 8.0.16 introduces the `CHECK` constraint, which you can use to protect the data in your tables. A `CHECK` constraint is an integrity constraint used to limit the value range for a column. It ensures that the inserted or updated value in a column must follow certain criteria.
+
+### When do we need `CHECK` constraints?
+
+The following are some examples of when you can use `CHECK` constraints:
+
+- A column must be greater than or equal to a specified value.
+- A date field must be in the format of MM/DD/YYYY.
+- An employee’s joining date must not precede their DOB.
+- The value of an employee’s salary must be a positive integer.
+
+The `CHECK` constraint is commonly used along with the NOT NULL constraint for ensuring that a column contains only valid data and doesn’t contain any NULL values.
+
+## Syntax
+
+```sql
+CHECK (expression)
+```
+
+The expression defines list of values that the column can have. You can have multiple expressions in a single `CHECK` constraint.
+
+If you want to specify multiple expressions, you need to use the OR keyword between each expression. For example:
+
+```sql
+CHECK (expression_1 OR expression_2)
+```
+
+You can use the AND keyword between each expression to make the `CHECK` constraint more restrictive. For example:
+
+```sql
+CHECK (expression_1 AND expression_2)
+```
+
+The condition for an expression can be any valid MySQL expression. If the condition is not met, the insert or update operation will fail.
+
+## Adding and removing the `CHECK` constraint in MySQL
+
+#### To add a `CHECK` constraint to a table in MySQL, use the `ALTER TABLE` statement:
+
+```sql title="Syntax"
+ALTER TABLE table_name
+ADD CONSTRAINT constraint_name CHECK (expression);
+```
+
+```sql title="Example"
+CREATE TABLE example (
+  id INT PRIMARY KEY,
+  value INT
+);
+
+ALTER TABLE example
+ADD CONSTRAINT positive_value CHECK (value > 0);
+
+INSERT INTO example (id, value) VALUES (1, -1);  
+-- This will trigger an "ERROR 3819 (HY000): Check constraint 'positive_value' is violated." error
+```
+
+#### You can also use the `MODIFY COLUMN` statement to add a `CHECK` constraint to a column in MySQL. The syntax is as follows:
+
+```sql title="Syntax"
+ALTER TABLE table_name
+MODIFY COLUMN column_name data_type CHECK (expression);
+```
+
+```sql title="Example"
+CREATE TABLE example (
+  id INT PRIMARY KEY,
+  value INT
+);
+
+ALTER TABLE example
+MODIFY COLUMN value INT CHECK (value > 0);
+
+INSERT INTO example (id, value) VALUES (1, -1);  
+-- This will trigger an "ERROR 3819 (HY000): Check constraint 'example_chk_1' is violated." error
+```
+
+#### If you want to add a `CHECK` constraint to multiple columns, you can use syntax similar to this:
+
+```sql title="Syntax"
+ALTER TABLE table_name
+ADD CONSTRAINT constraint_name
+CHECK ((c1_expression) AND (c2_expression) AND (c3_expression));
+```
+
+```sql title="Example"
+CREATE TABLE example (
+  id INT PRIMARY KEY,
+  value1 INT,
+  value2 INT,
+  value3 INT
+);
+
+ALTER TABLE example
+ADD CONSTRAINT multi_condition CHECK ((value1 > 0) AND (value2 > 0) AND (value3 > 0));
+
+INSERT INTO example (id, value1, value2, value3) VALUES (1, -1, 1, 1);  
+-- This will trigger an "ERROR 3819 (HY000): Check constraint 'multi_condition' is violated." error
+```
+
+#### To remove a `CHECK` constraint, you can use the following statements:
+
+```sql title="Syntax"
+ALTER TABLE table_name
+DROP CONSTRAINT constraint_name;
+```
+
+```sql title="Example"
+CREATE TABLE example (
+  id INT PRIMARY KEY,
+  value INT,
+  CHECK (value > 0)
+);
+
+ALTER TABLE example
+DROP CONSTRAINT value;
+
+INSERT INTO example (id, value) VALUES (1, -1);  
+-- This will not trigger an error
+```
+
+### Benefits and drawbacks of using a `CHECK` constraint
+Using a `CHECK` constraint comes with several benefits. A `CHECK` constraint can enforce the data to be valid according to the `CHECK` condition, protecting from poor data quality. This means that any values that do not comply with the `CHECK` condition fail the insertion or modification processes, thus making it easier to troubleshoot any eventual issues. It is worth noting that this comes at a certain cost in terms of performance, as MySQL needs to validate each value before applying it to the database.
+
+### Common mistakes to avoid when using the `CHECK` constraint
+
+When using the `CHECK` constraint in MySQL, it's important to be aware of a few common mistakes. 
+
+1. Be aware of the impact of `CHECK` constraints on data manipulation: `CHECK` constraints can impact the way you manipulate data in the database. For example, you may need to modify multiple rows in order to satisfy a `CHECK` constraint, or you may need to temporarily disable a `CHECK` constraint in order to perform certain operations.
+2. Older MySQL versions (8.0.15 and below) ignore `CHECK` constraints. Thus, one needs to keep in mind that the constraint is neither created nor evaluated even if it has been defined in a table definition while using older versions of MySQL. For example:
+
+```sql title="MySQL 5.7"
+CREATE TABLE example (
+  id INT PRIMARY KEY,
+  value INT,
+  CHECK (value > 0)
+);
+
+INSERT INTO example (id, value) VALUES (1, -1);  
+-- This will not trigger an error, even though the CHECK constraint specifies that value must be greater than 0
+```
+
+## Managing `CHECK` constraints is easy with Atlas​
+
+Atlas is an open-source project which allows us to manage our database using a simple and easy-to-understand declarative syntax (similar to Terraform).
+
+:::info
+If you are just getting started, install the latest version of Atlas using the guide to [setting up Atlas](https://atlasgo.io/getting-started/).
+:::
+
+We will first use the `atlas schema inspect command` to get an [HCL](https://atlasgo.io/guides/ddl#hcl) representation of the table we created earlier (without any indexes) by using the Atlas CLI:
+
+```console title="Terminal"
+atlas schema inspect -u "mysql://root:@localhost:3306/check_constraint" > schema.hcl
+```
+```hcl title="schema.hcl"
+schema "check_constraint" {
+  charset = "utf8mb4"
+  collate = "utf8mb4_0900_ai_ci"
+}
+```
+
+There are no tables in our schema yet, so let’s create tables by adding the following table definitions to the schema.
+To create a `CHECK` constraint that ensures a column contains only positive integers, you can use the following syntax:
+
+```hcl title="schema.hcl"
+table "users" {
+  schema = schema.check_constraint
+  column "id" {
+    null = false
+    type = int
+  }
+  column "value" {
+    null = true
+    type = int
+  }
+  check "user_id" {
+    expr = "value > 0"
+  }
+}
+```
+
+To create a `CHECK` constraint that ensures a column contains only values within a certain range, you can use the following syntax:
+
+```hcl title="schema.hcl"
+table "blog_posts" {
+  schema = schema.check_constraint
+  column "id" {
+    null = false
+    type = int
+  }
+  column "value" {
+    null = true
+    type = int
+  }
+  check "post_id" {
+    expr = "value BETWEEN 1 AND 10"
+  }
+}
+```
+
+To create a `CHECK` constraint that ensures a column contains a value that matches a specific pattern, you can use the following syntax:
+
+```hcl title="schema.hcl"
+table "friends" {
+  schema = schema.check_constraint
+  column "id" {
+    null = false
+    type = int
+  }
+  column "email" {
+    null = true
+    type = varchar(255)
+  }
+  check "friend_id" {
+    expr = "email LIKE '%@%.%'"
+  }
+}
+```
+
+Save the file and apply the schema changes on the database by using the following command:
+
+```console title="Terminal"
+atlas schema apply -u "mysql://root:password@localhost:3306/check_constraint" -f schema.hcl --dev-url docker://mysql/8/check_constraint
+```
+
+:::note
+If you get `Error: pulling image: exit status 1` error, ensure that Docker Desktop is up and running.
+:::
+
+Atlas generates the necessary SQL statements to add the new index to the database schema. 
+
+```console title="Output"
+-- Planned Changes:
+-- Create "users" table
+CREATE TABLE `check_constraint`.`users` (`id` int NOT NULL, `value` int NULL, CONSTRAINT `user_id` CHECK (`value` > 0)) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci
+-- Create "blog_posts" table
+CREATE TABLE `check_constraint`.`blog_posts` (`id` int NOT NULL, `value` int NULL, CONSTRAINT `post_id` CHECK (`value` between 1 and 10)) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci
+-- Create "friends" table
+CREATE TABLE `check_constraint`.`friends` (`id` int NOT NULL, `email` varchar(255) NULL, CONSTRAINT `friend_id` CHECK (`email` like _utf8mb4'%@%.%')) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci
+Use the arrow keys to navigate: ↓ ↑ → ← 
+? Are you sure?: 
+  ▸ Apply
+    Abort
+```
+
+From the MySQL terminal, let’s verify that our tables are created successfully:
+
+```sql
+SHOW tables;
+```
+```console title="Output"
++----------------------------+
+| Tables_in_check_constraint |
++----------------------------+
+| users                      |
+| blog_posts                 |
+| friends                    |
++----------------------------+
+```
+```sql
+SHOW CREATE TABLE users;
+```
+```console title="Output"
+| users | CREATE TABLE `users` (
+  `id` int NOT NULL,
+  `value` int DEFAULT NULL,
+  CONSTRAINT `user_id` CHECK ((`value` > 0))
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+```
+
+Amazing! Our tables with `CHECK` constraints were created!
+
+### Wrapping up​
+In this guide, we have demonstrated how to configure our columns to accept and store only desired sets of values using the `CHECK` constraint.
+
+## Need More Help?​
+
+[Join the Ariga Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling. 
+
+[Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io), our OSS database schema management tool, and our cloud platform [Atlas Cloud](https://atlasgo.cloud).
diff --git a/doc/md/guides/mysql/descending-indexes.md b/doc/md/guides/mysql/descending-indexes.md
index 83612bb7..3f9f7789 100644
--- a/doc/md/guides/mysql/descending-indexes.md
+++ b/doc/md/guides/mysql/descending-indexes.md
@@ -8,7 +8,7 @@ slug: /guides/mysql/descending-indexes
 In general, indexes with ascending or descending order help increase the performance of queries with the “ORDER BY” clause. Descending indexes are indexes where key values are stored in descending order.
 
 #### When are descending indexes helpful?
-In versions prior to MySQL 8.0, scanning an index in reverse order had a very high cost, which resulted in reduced performance for certain queries. 
+In versions prior to MySQL 8.0, scanning an index in reverse order had a very high cost, which resulted in reduced performance for certain queries.
 Since the release of MySQL version 8.0, users can now create descending indexes, which can be scanned in forward order, thus increasing efficiency of scanning for certain queries with the ORDER BY clause.
 
 ### Syntax
@@ -24,9 +24,9 @@ CREATE TABLE t (
 ```
 Or, you can add a descending index to an existing table with the following syntax:
 ```sql
-CREATE INDEX 
-    index_name_idx 
-ON 
+CREATE INDEX
+    index_name_idx
+ON
     table_name(column_name DESC);
 ```
 :::info
@@ -203,7 +203,7 @@ SHOW STATUS LIKE 'Last_query_cost';
 ```
 (Note: The results will vary, ​​depending on the data that is stored in the database)
 
-Amazing! Now our query cost only 52.01 units, compared to 108034.65 units earlier when the descending index was not used. 
+Amazing! Now our query cost only 52.01 units, compared to 108034.65 units earlier when the descending index was not used.
 
 Let's check how MySQL resolves the query in this case:
 
@@ -314,8 +314,8 @@ Atlas generates the necessary SQL statements to add the new descending index to
 -- Planned Changes:
 -- Modify "telecom_data" table
 ALTER TABLE `telecom_data`.`telecom_data` ADD INDEX `fastscan_idx` (`megabytes_used` DESC)
-Use the arrow keys to navigate: ↓ ↑ → ← 
-? Are you sure?: 
+Use the arrow keys to navigate: ↓ ↑ → ←
+? Are you sure?:
   ▸ Apply
     Abort
 ```
@@ -345,8 +345,8 @@ show index from telecom_data\G
        Packed: NULL
          Null: YES
    Index_type: BTREE
-      Comment: 
-Index_comment: 
+      Comment:
+Index_comment:
       Visible: YES
    Expression: NULL
 2 rows in set (0.00 sec)
@@ -359,6 +359,6 @@ In this guide, we demonstrated how to create and use descending indexes in MySQL
 
 ## Need More Help?​
 
-[Join the Ariga Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling. 
+[Join the Ariga Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling.
 
-[Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io), our OSS database schema management tool, and our cloud platform [Ariga Cloud](https://ariga.cloud).
+[Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io), our OSS database schema management tool, and our cloud platform [Atlas Cloud](https://atlasgo.cloud).
diff --git a/doc/md/guides/mysql/functional-indexes.md b/doc/md/guides/mysql/functional-indexes.md
index 6d734a76..0d7cfe6f 100644
--- a/doc/md/guides/mysql/functional-indexes.md
+++ b/doc/md/guides/mysql/functional-indexes.md
@@ -351,8 +351,8 @@ Press `Enter` key while the Apply option is highlighted to apply the changes:
 -- Planned Changes:
 -- Modify "scorecard" table
 ALTER TABLE `scorecard`.`scorecard` ADD INDEX `sci_math_avg_idx` (((`science` + `mathematics`) / 2))
-Use the arrow keys to navigate: ↓ ↑ → ← 
-? Are you sure?: 
+Use the arrow keys to navigate: ↓ ↑ → ←
+? Are you sure?:
   ▸ Apply
     Abort
 ```
@@ -374,10 +374,10 @@ SHOW INDEXES FROM scorecard;
   Cardinality: 997438
      Sub_part: NULL
        Packed: NULL
-         Null: 
+         Null:
    Index_type: BTREE
-      Comment: 
-Index_comment: 
+      Comment:
+Index_comment:
       Visible: YES
    Expression: NULL
 *************************** 2. row ***************************
@@ -392,8 +392,8 @@ Index_comment:
        Packed: NULL
          Null: YES
    Index_type: BTREE
-      Comment: 
-Index_comment: 
+      Comment:
+Index_comment:
       Visible: YES
    Expression: ((`science` + `mathematics`) / 2)
 2 rows in set (0.00 sec)
@@ -407,4 +407,4 @@ In this guide, we demonstrated how using functional indexes with an appropriate
 # Need More Help?​​
 [Join the Ariga Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling.
 
-[Sign up](https://www.getrevue.co/profile/ariga) to our newsletter to stay up to date about [Atlas](https://atlasgo.io/), our OSS database schema management tool, and our cloud platform [Ariga Cloud](https://ariga.cloud/).
+[Sign up](https://www.getrevue.co/profile/ariga) to our newsletter to stay up to date about [Atlas](https://atlasgo.io/), our OSS database schema management tool, and our cloud platform [Atlas Cloud](https://atlasgo.cloud/).
diff --git a/doc/md/guides/mysql/prefix-indexes.md b/doc/md/guides/mysql/prefix-indexes.md
index 59149ed0..0a78e6da 100644
--- a/doc/md/guides/mysql/prefix-indexes.md
+++ b/doc/md/guides/mysql/prefix-indexes.md
@@ -9,7 +9,7 @@ slug: /guides/mysql/prefix-indexes
 With MySQL, users may create secondary indexes for string columns which use the first N characters of the values stored in the column, instead of using the entire value. If used correctly, prefix indexes improve performance and reduce costs, all while minimizing the amount of storage space they take up on the disk.
 When do we need them?
 
-Let’s assume you have a lengthy column. If you have many records in an indexed table, the number of records the index needs to track also grows. If the index grows in size, the disk space needed to store the index itself increases as well. In many tables, lengthy records are not accessed with uniform search queries. One might prefer to use the `LIKE` operator to fetch content from a lengthy column, instead of writing the full value itself in the query. 
+Let’s assume you have a lengthy column. If you have many records in an indexed table, the number of records the index needs to track also grows. If the index grows in size, the disk space needed to store the index itself increases as well. In many tables, lengthy records are not accessed with uniform search queries. One might prefer to use the `LIKE` operator to fetch content from a lengthy column, instead of writing the full value itself in the query.
 
 Some data types (such as `BLOB` and `TEXT`) are not allowed to be indexed (unless the prefix value is specified). Also, The maximum length of the indexed value is 767 bytes. If the indexed value exceeds this length, it will be truncated.
 
@@ -20,16 +20,16 @@ In such cases, the prefix index can become useful to filter unsearched parts of
 Here is how you can create a prefix index:
 
 ```sql
-CREATE INDEX 
-    index_name 
-ON 
+CREATE INDEX
+    index_name
+ON
     table_name(column_name(length));
 ```
 
 Here is how you can define a prefix index in a table definition:
 
 ```sql
-CREATE TABLE 
+CREATE TABLE
     table_name(column_name BLOB, index_name(column_name(length)));
 ```
 
@@ -133,9 +133,9 @@ Notice that the query cost 108430.088807 units.
 Now, suppose we want to optimize this query but we do not know about prefix indexes yet. In this case, we will make an index on column `message` with the following command:
 
 ```sql
-CREATE INDEX 
-    message_idx 
-ON 
+CREATE INDEX
+    message_idx
+ON
     comm_database(message);
 ```
 
@@ -146,9 +146,9 @@ ERROR 1170 (42000): BLOB/TEXT column 'message' used in key specification without
 That didn’t work! As mentioned in the [Syntax](#syntax) section above and the error message, you must specify length when indexing `BLOB` and `TEXT` type columns. Let’s specify the length and try to check the performance for the same query again:
 
 ```sql
-CREATE INDEX 
-    message_idx 
-ON 
+CREATE INDEX
+    message_idx
+ON
     comm_database(message(30));
 ```
 
@@ -230,9 +230,9 @@ WHERE
 To optimize this query, usually we would create the following index:
 
 ```sql
-CREATE INDEX 
-    email_idx 
-ON 
+CREATE INDEX
+    email_idx
+ON
     comm_database(email);
 ```
 
@@ -254,11 +254,11 @@ The cost for the query after creating this index is as follows:
 The storage space used by the index is as following:
 
 ```sql
-SELECT 
+SELECT
     stat_value
-FROM 
-    mysql.innodb_index_stats 
-WHERE 
+FROM
+    mysql.innodb_index_stats
+WHERE
     index_name = 'email_idx' AND stat_name= 'size';
 ```
 
@@ -271,7 +271,7 @@ WHERE
 ```
 
 :::info
-The `innodb_page_size` variable specifies the size of the pages used by the InnoDB storage engine, and the `stat_value` column contains statistics about the index, such as the number of pages used by the index. When `stat_name` is 'size', the `stat_value` column contains the size of the index in terms of number of pages. 
+The `innodb_page_size` variable specifies the size of the pages used by the InnoDB storage engine, and the `stat_value` column contains statistics about the index, such as the number of pages used by the index. When `stat_name` is 'size', the `stat_value` column contains the size of the index in terms of number of pages.
 
 For example, if the `innodb_page_size` system variable is set to 16 KB and the `stat_value` column contains the value 10, this means that the index uses 10 pages, or 160KB of disk space.
 :::
@@ -279,11 +279,11 @@ For example, if the `innodb_page_size` system variable is set to 16 KB and the `
 For our example, keeping `innodb_page_size` as 16KB in mind, our index uses ~32MB disk space. Now, we can further improve the query performance as well as reduce the storage used by the index by using a prefix index on the email column. Let’s create a prefix index with the following command:
 
 ```sql
-ALTER TABLE 
-    comm_database 
-DROP INDEX 
+ALTER TABLE
+    comm_database
+DROP INDEX
     email_idx,
-ADD INDEX 
+ADD INDEX
     email_prefix_idx (email(5));
 ```
 
@@ -307,11 +307,11 @@ Our cost has reduced by 5% by using the prefix index.
 And the storage space used by the index is as follows:
 
 ```sql
-SELECT 
+SELECT
     stat_value
-FROM 
-    mysql.innodb_index_stats 
-WHERE 
+FROM
+    mysql.innodb_index_stats
+WHERE
     index_name = 'email_prefix_idx' AND stat_name= 'size';
 ```
 
@@ -413,14 +413,14 @@ atlas schema apply -u "mysql://root:password@localhost:3306/comm_database" -f sc
 If you get `Error: pulling image: exit status 1` error, ensure that Docker Desktop is up and running.
 :::
 
-Atlas generates the necessary SQL statements to add the new index to the database schema. 
+Atlas generates the necessary SQL statements to add the new index to the database schema.
 
 ```console
 -- Planned Changes:
 -- Modify "comm_database" table
 ALTER TABLE `comm_database`.`comm_database` ADD INDEX `message_idx` (`message` (30)), ADD INDEX `email_prefix_idx` (`email` (5))
-Use the arrow keys to navigate: ↓ ↑ → ← 
-? Are you sure?: 
+Use the arrow keys to navigate: ↓ ↑ → ←
+? Are you sure?:
   ▸ Apply
     Abort
 ```
@@ -454,4 +454,4 @@ In this guide, we have demonstrated that creating a prefix index is inevitable w
 
 ## Need More Help?​​
 [Join](https://discord.com/invite/zZ6sWVg6NT) the Ariga Discord Server for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling.
-[Sign up](https://www.getrevue.co/profile/ariga) to our newsletter to stay up to date about [Atlas](https://atlasgo.io/), our OSS database schema management tool, and our cloud platform [Ariga Cloud](https://ariga.cloud/).
+[Sign up](https://www.getrevue.co/profile/ariga) to our newsletter to stay up to date about [Atlas](https://atlasgo.io/), our OSS database schema management tool, and our cloud platform [Atlas Cloud](https://atlasgo.cloud/).
diff --git a/doc/md/guides/postgres/ar-101.md b/doc/md/guides/postgres/ar-101.md
index 0375df7b..12c774eb 100644
--- a/doc/md/guides/postgres/ar-101.md
+++ b/doc/md/guides/postgres/ar-101.md
@@ -31,21 +31,21 @@ CREATE TABLE users (
     user_id bigint PRIMARY KEY,
     active_status boolean,
     data_balance integer,
-    plan_period smallint                  
+    plan_period smallint
 );
 ```
 
 Here is what a portion of the table might look like after inserting values:
 
 ```sql
-SELECT 
-    * 
+SELECT
+    *
 FROM
     users
 ```
 
 ```console title="Output"
- user_id | active_status | data_balance | plan_period 
+ user_id | active_status | data_balance | plan_period
 ---------+---------------+--------------+-------------
        0 | f             |       731147 |         267
        1 | t             |       901448 |          26
@@ -62,9 +62,9 @@ FROM
  1999999 | t             |       550192 |         146
 ```
 
-Each tuple in the table takes up 24 bytes of total memory without the header. The `user_id` occupies the first 8 bytes. 
+Each tuple in the table takes up 24 bytes of total memory without the header. The `user_id` occupies the first 8 bytes.
 
-Here, the `active_status` column is of type boolean and takes up only one byte. However, because the next consecutive column `data_balance` is an `integer` with size of 4 bytes, PostgreSQL automatically adds 3 bytes of padding to `active_status` so its size can match with the 4 bytes size of the consecutive `data_balance` column. 
+Here, the `active_status` column is of type boolean and takes up only one byte. However, because the next consecutive column `data_balance` is an `integer` with size of 4 bytes, PostgreSQL automatically adds 3 bytes of padding to `active_status` so its size can match with the 4 bytes size of the consecutive `data_balance` column.
 
 The second 8 bytes of the row are occupied by 1 byte of `active_status`, 3 bytes of padding, and then 4 bytes of  `data_balance`.
 
@@ -86,7 +86,7 @@ SELECT pg_size_pretty(pg_relation_size('users'));
 ```
 
 ```console title="Output"
- pg_size_pretty 
+ pg_size_pretty
 ----------------
  100 MB
 (1 row)
@@ -101,20 +101,20 @@ CREATE TABLE users_optimized (
     user_id bigint PRIMARY KEY,
     data_balance integer,
     plan_period smallint,
-    active_status boolean             
+    active_status boolean
 );
 ```
 
 Here is what a portion of the table might look like after inserting values:
 
 ```sql
-SELECT 
-    * 
+SELECT
+    *
 FROM
     users_optimized
 ```
 ```console title="Output"
- user_id | data_balance | plan_period | active_status 
+ user_id | data_balance | plan_period | active_status
 ---------+--------------+-------------+---------------
        0 |       730878 |         267 | f
        1 |       410081 |         329 | t
@@ -138,7 +138,7 @@ SELECT pg_size_pretty(pg_relation_size(users_optimized));
 ```
 
 ```console title="Output"
- pg_size_pretty 
+ pg_size_pretty
 ----------------
  84 MB
 (1 row)
@@ -164,12 +164,12 @@ In this case, each tuple in the table takes 16 bytes of total memory without the
 
 ## Determining optimal data alignment for a table is easy with Atlas CI
 
-As we have observed in the previous section, creating a table with non-optimal data alignment can cost more space, and thus more money. At the same time, calculating the disk space and determining the best arrangement of columns manually can be confusing and error-prone. 
+As we have observed in the previous section, creating a table with non-optimal data alignment can cost more space, and thus more money. At the same time, calculating the disk space and determining the best arrangement of columns manually can be confusing and error-prone.
 
 With the help of Atlas CI, you can now easily check whether your table alignment is optimal. If the alignment is not optimal, Atlas CI will suggest the best possible order of columns to make the alignment optimal and thus help you save maximum space (and money!).
 
 :::info
-Teams using GitHub that wish to ensure all changes to their database schema are safe can use the [Atlas GitHub Action](https://github.com/ariga/atlas-action). If you’re just getting started, you can visit the documentation to [set-up Atlas CI GitHub action](https://atlasgo.io/integrations/github-actions#usage) 
+Teams using GitHub that wish to ensure all changes to their database schema are safe can use the [Atlas GitHub Action](https://github.com/ariga/atlas-action). If you’re just getting started, you can visit the documentation to [set-up Atlas CI GitHub action](https://atlasgo.io/integrations/github-actions#usage)
 :::
 
 Let’s create a migration file in Atlas format with the following SQL command:
@@ -179,7 +179,7 @@ CREATE TABLE users (
     user_id bigint PRIMARY KEY,
     active_status boolean,
     data_balance integer,
-    plan_period smallint                  
+    plan_period smallint
 );
 ```
 
@@ -195,7 +195,7 @@ Atlas CI provides different Analyzer implementations that are useful for determi
 
 ### Conclusion
 
-In this section, we learned about how we can reduce disk space costs without removing any data, just by arranging the table columns in an efficient order to reduce padding space. 
+In this section, we learned about how we can reduce disk space costs without removing any data, just by arranging the table columns in an efficient order to reduce padding space.
 
 Additionally, we have also learned how using Atlas CI can help us determine the best optimal order of columns in a table, removing the hassle of manually calculating the disk cost before and after rearranging columns.
 
@@ -203,4 +203,4 @@ Additionally, we have also learned how using Atlas CI can help us determine the
 
 [Join the Ariga Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling.
 
-[Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io/), our OSS database schema management tool, and our cloud platform [Ariga Cloud](https://ariga.cloud/).
\ No newline at end of file
+[Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io/), our OSS database schema management tool, and our cloud platform [Atlas Cloud](https://atlasgo.cloud/).
diff --git a/doc/md/guides/postgres/included-columns.md b/doc/md/guides/postgres/included-columns.md
index 1cd2e840..9f23d4c4 100644
--- a/doc/md/guides/postgres/included-columns.md
+++ b/doc/md/guides/postgres/included-columns.md
@@ -91,20 +91,20 @@ Now, suppose we want to find the ID of a user by their email address. Let’s ch
 
 ```sql
 EXPLAIN ANALYZE
-SELECT    
+SELECT
     first_name,
     last_name,
     email
-FROM    
+FROM
     "bankdb"
-WHERE 
+WHERE
     email = 'd-abbott3425@google.edu';
 ```
 
 ```console title="Output"
-QUERY PLAN                            
+QUERY PLAN
 ----------------
- Seq Scan on bankdb  (cost=0.00..38.75 rows=1 width=37) (actual time=0.180..0.181 
+ Seq Scan on bankdb  (cost=0.00..38.75 rows=1 width=37) (actual time=0.180..0.181
 rows=1 loops=1)
    Filter: ((email)::text = 'd-abbott3425@google.edu'::text)
    Rows Removed by Filter: 1499
@@ -118,7 +118,7 @@ Time: 0.626 ms
 Notice that the total cost is 38.75 units. If we want to use a unique index to accelerate the query, we can create it on the `email` column with the following command:
 
 ```sql
-CREATE UNIQUE INDEX emails_idx 
+CREATE UNIQUE INDEX emails_idx
 ON bankdb(email);
 ```
 ```console title="Output"
@@ -130,19 +130,19 @@ Now, let's check the performance of querying data of first and last names based
 
 ```sql
 EXPLAIN ANALYZE
-SELECT    
+SELECT
     first_name,
     last_name,
     email
-FROM    
+FROM
     "bankdb"
-WHERE 
+WHERE
     email = 'd-abbott3425@google.edu';
 ```
 ```console title="Output"
-QUERY PLAN                   
+QUERY PLAN
 ----------------------------------
- Index Scan using emails_idx on bankdb  (cost=0.28..8.29 rows=1 width=37) (actual 
+ Index Scan using emails_idx on bankdb  (cost=0.28..8.29 rows=1 width=37) (actual
 time=0.200..0.203 rows=1 loops=1)
    Index Cond: ((email)::text = 'd-abbott3425@google.edu'::text)
  Planning Time: 0.081 ms
@@ -167,7 +167,7 @@ Suppose we want to accelerate the same query using the `INCLUDE` clause.
 In the following command, we will create an index with an `INCLUDE` clause that precisely covers `first_name` and `last_name` columns which are part of the query for which we are trying to improve performance.
 
 ```sql
-CREATE UNIQUE INDEX emails_idx 
+CREATE UNIQUE INDEX emails_idx
 ON bankdb(email)
 INCLUDE(first_name,last_name);
 ```
@@ -181,17 +181,17 @@ Now, let's check the performance of querying data of first and last names based
 
 ```sql
 EXPLAIN ANALYZE
-SELECT    
+SELECT
     first_name,
     last_name,
     email
-FROM    
+FROM
     "bankdb"
-WHERE 
+WHERE
     email = 'd-abbott3425@google.edu';
 ```
 ```console title="Output"
-QUERY PLAN                 
+QUERY PLAN
 ---------------------------------------
  Index Only Scan using emails_idx on bankdb  (cost=0.28..4.29 rows=1 width=37) (ac
 tual time=0.228..0.231 rows=1 loops=1)
@@ -201,7 +201,7 @@ tual time=0.228..0.231 rows=1 loops=1)
  Execution Time: 0.283 ms
 (5 rows)
 ```
-Notice that the total cost is now 4.29, which is significantly lower, compared to 8.29 which we got while using a unique index without the `INCLUDE` clause. We were able to reduce the total cost because the query only scanned the index in order to get the data. As a result, `heap fetches` is also zero, which means the query does not access any tables to retrieve the records. 
+Notice that the total cost is now 4.29, which is significantly lower, compared to 8.29 which we got while using a unique index without the `INCLUDE` clause. We were able to reduce the total cost because the query only scanned the index in order to get the data. As a result, `heap fetches` is also zero, which means the query does not access any tables to retrieve the records.
 
 :::info
 You might be wondering why we didn’t just use `CREATE INDEX ON bankdb(email,first_name,last_name)` instead of using the `INCLUDE` clause. One of the advantages of using the `INCLUDE` clause is having fewer levels in a  B-tree. All `INCLUDE` columns are stored in the doubly linked list of the B-tree index.
@@ -287,8 +287,8 @@ Atlas generates the necessary SQL statements to add the new index to the databas
 -- Planned Changes:
 -- Create index "emails_idx" to table: "bankdb"
 CREATE UNIQUE INDEX "emails_idx" ON "public"."bankdb" ("email") INCLUDE ("first_name", "last_name")
-Use the arrow keys to navigate: ↓ ↑ → ← 
-? Are you sure?: 
+Use the arrow keys to navigate: ↓ ↑ → ←
+? Are you sure?:
   ▸ Apply
     Abort
 ```
@@ -301,15 +301,15 @@ To verify that our new index was created, open the database command line tool fr
 
 ```console title="Output"
                                     Table "public.bankdb"
- Column     |          Type         |Collation| Nullable |              Default   
-            
+ Column     |          Type         |Collation| Nullable |              Default
+
 ------------+-----------------------+---------+----------+------------------------------------
  id         | integer               |         | not null | nextval('bankdb_id_seq'::regclass)
  savings    | character varying(100)|         |          | NULL::character varying
  first_name | character varying(255)|         |          | NULL::character varying
  last_name  | character varying(255)|         |          | NULL::character varying
  email      | character varying(255)|         |          | NULL::character varying
- bank       | character varying(34) |         |          | 
+ bank       | character varying(34) |         |          |
 Indexes:
     "bankdb_pkey" PRIMARY KEY, btree (id)
     "emails_idx" UNIQUE, btree (email) INCLUDE (first_name, last_name)
@@ -320,6 +320,6 @@ Amazing! Our new index with included columns is now created!
 
 In this section, we learned about PostgreSQL indexes with included columns and how we can easily create them in our database by using Atlas.
 
-## Need More Help?​
+## Need More Help?
 
-[Join the Ariga Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling. [Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io), our OSS database schema management tool, and our cloud platform [Ariga Cloud](https://ariga.cloud).
+[Join the Ariga Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling. [Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io), our OSS database schema management tool, and our cloud platform [Atlas Cloud](https://atlasgo.cloud).
diff --git a/doc/md/guides/postgres/index-operator-classes.md b/doc/md/guides/postgres/index-operator-classes.md
index a1652601..f97114ed 100644
--- a/doc/md/guides/postgres/index-operator-classes.md
+++ b/doc/md/guides/postgres/index-operator-classes.md
@@ -13,9 +13,9 @@ An operator class identifies the operators to be used by the index for the index
 Here is how you can specify an operator class for a column in an index definition:
 
 ```sql
-CREATE INDEX 
-    name 
-ON 
+CREATE INDEX
+    name
+ON
     table (column opclass [ ( opclass_options ) ] [sort options] [, ...]);
 ```
 
@@ -42,11 +42,11 @@ Here is how a portion of the table might look like after inserting values:
 ```sql
 SELECT
         *
-FROM    
+FROM
         internet_provider
 ```
 ```console title="Output"
- id | subscriber_name  |      email_address      | payment_pending | active_user 
+ id | subscriber_name  |      email_address      | payment_pending | active_user
 ----+------------------+-------------------------+-----------------+-------------
   0 | Abel Warren      | havb@example.com        | 730             | false
   1 | Erick Valentine  | riuee@example.com       | 70              | false
@@ -61,7 +61,7 @@ FROM
 .
 .
 .
-   id   | subscriber_name  |      email_address      | payment_pending | active_user 
+   id   | subscriber_name  |      email_address      | payment_pending | active_user
 --------+------------------+-------------------------+-----------------+-------------
  999999 | James Strong     | jxgi3@example.com       | 788             | true
  999998 | Virginia Ballard | rkdzo0@example.com      | 598             | false
@@ -76,14 +76,14 @@ FROM
 (1000000 rows)
 ```
 
-We do not have any indexes other than the primary index on the `id` column. 
+We do not have any indexes other than the primary index on the `id` column.
 
 Now, let’s assume that we are not aware of the usage of operator classes in indexes just yet. We want to accelerate queries involving patterns matching expressions with a `LIKE` operator in order to search a name in the `subscriber_name` column. In this case, we would create an index on the column `subscriber_name` with the following command:
 
 ```sql
-CREATE INDEX 
+CREATE INDEX
     internet_provider_idx
-ON 
+ON
     internet_provider(subscriber_name);
 ```
 
@@ -98,7 +98,7 @@ Awesome! Our index is now created on the `subscriber_name` column. Now, suppose
 EXPLAIN ANALYZE
 SELECT
         *
-FROM    
+FROM
         internet_provider
 WHERE
         subscriber_name LIKE 'Shirley C%'
@@ -109,7 +109,7 @@ The EXPLAIN command is used for understanding the performance of a query. You ca
 :::
 
 ```console title="Output"
-                                                             QUERY PLAN                                                             
+                                                             QUERY PLAN
 ------------------------------------------------------------------------------------------------------------------------------------
  Gather  (cost=1000.00..15874.33 rows=100 width=44) (actual time=5.956..227.339 rows=78 loops=1)
    Workers Planned: 2
@@ -129,7 +129,7 @@ Notice that the `internet_provider_idx` index that we created was not used in or
 In a parallel sequential scan, the table's blocks will be divided into ranges and shared among the cooperating processes. Each worker process will complete the scanning of its given range of blocks before requesting an additional range of blocks. To learn more about Parallel Plans in PostgreSQL, visit the official documentation [here](https://www.postgresql.org/docs/current/parallel-plans.html).
 :::
 
-Now, you might be wondering why the index that we created was not being used in the execution of this query. This is when having knowledge about the usage of operator classes becomes important. 
+Now, you might be wondering why the index that we created was not being used in the execution of this query. This is when having knowledge about the usage of operator classes becomes important.
 
 As mentioned earlier, an operator class identifies the operators to be used by the index for the indexed column. Let’s see this in action by specifying an operator class in our definition with the following commands:
 
@@ -139,9 +139,9 @@ DROP INDEX internet_provider_idx;
 DROP INDEX
 Time: 43.279 ms
 
-CREATE INDEX 
+CREATE INDEX
     internet_provider_idx
-ON 
+ON
     internet_provider(subscriber_name varchar_pattern_ops)
 ```
 
@@ -156,13 +156,13 @@ This time, we specified an operator class `varchar_pattern_ops` in our index def
 EXPLAIN ANALYZE
 SELECT
         *
-FROM    
+FROM
         internet_provider
 WHERE
         subscriber_name LIKE 'Shirley C%'
 ```
 ```console title="Output"
-                                                                 QUERY PLAN                                                                  
+                                                                 QUERY PLAN
 ---------------------------------------------------------------------------------------------------------------------------------------------
  Index Scan using internet_provider_idx on internet_provider  (cost=0.42..8.45 rows=100 width=44) (actual time=0.025..0.131 rows=78 loops=1)
    Index Cond: (((subscriber_name)::text ~>=~ 'Shirley C'::text) AND ((subscriber_name)::text ~<~ 'Shirley D'::text))
@@ -172,7 +172,7 @@ WHERE
 (5 rows)
 ```
 
-Amazing! This time, Index Scan was performed using `internet_provider_idx`. As a result, the cost, planning time, and execution time for our query have reduced significantly, as we expected. 
+Amazing! This time, Index Scan was performed using `internet_provider_idx`. As a result, the cost, planning time, and execution time for our query have reduced significantly, as we expected.
 
 The previous index (without a specified operator class) could have been helpful while executing queries with WHERE clauses with operators such as  <, <=, >, or >=. Though, the same index could not be utilized when executing queries with WHERE clauses with a `LIKE` operator.
 
@@ -250,8 +250,8 @@ Atlas generates the necessary SQL statements to add the new index to the databas
 -- Planned Changes:
 -- Create index "internet_provider_idx" to table: "internet_provider"
 CREATE INDEX "internet_provider_idx" ON "public"."internet_provider" ("subscriber_name" varchar_pattern_ops)
-Use the arrow keys to navigate: ↓ ↑ → ← 
-? Are you sure?: 
+Use the arrow keys to navigate: ↓ ↑ → ←
+? Are you sure?:
   ▸ Apply
     Abort
 ```
@@ -264,13 +264,13 @@ To verify that our new index was created, open the database command line tool fr
 
 ```console title="Output"
                                         Table "public.internet_provider"
-     Column      |          Type          | Collation | Nullable |                    Default                    
+     Column      |          Type          | Collation | Nullable |                    Default
 -----------------+------------------------+-----------+----------+-----------------------------------------------
  id              | integer                |           | not null | nextval('internet_provider_id_seq'::regclass)
- subscriber_name | character varying(255) |           | not null | 
- email_address   | character varying(255) |           | not null | 
- payment_pending | character varying(100) |           | not null | 
- active_user     | character varying(255) |           | not null | 
+ subscriber_name | character varying(255) |           | not null |
+ email_address   | character varying(255) |           | not null |
+ payment_pending | character varying(100) |           | not null |
+ active_user     | character varying(255) |           | not null |
 Indexes:
     "internet_provider_pkey" PRIMARY KEY, btree (id)
     "internet_provider_idx" btree (subscriber_name varchar_pattern_ops)
@@ -280,10 +280,10 @@ Amazing! Our new index `internet_provider_idx` with operator class `varchar_patt
 
 ### Wrapping up
 
-In this guide, we demonstrated how using indexes with an appropriate operator class becomes a very crucial skill in optimizing query performance with combinations of certain clauses and operators. 
+In this guide, we demonstrated how using indexes with an appropriate operator class becomes a very crucial skill in optimizing query performance with combinations of certain clauses and operators.
 
 ## Need More Help?​
 
 [Join the Ariga Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling.
 
-[Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io/), our OSS database schema management tool, and our cloud platform [Ariga Cloud](https://ariga.cloud/).
+[Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io/), our OSS database schema management tool, and our cloud platform [Atlas Cloud](https://atlasgo.cloud/).
diff --git a/doc/md/guides/sqlite/partial-indexes.md b/doc/md/guides/sqlite/partial-indexes.md
index 5a06cac3..7cbc749b 100644
--- a/doc/md/guides/sqlite/partial-indexes.md
+++ b/doc/md/guides/sqlite/partial-indexes.md
@@ -24,11 +24,11 @@ Partial indexes come into the picture to filter unsearched values and give you,
 #### Basic SQLite syntax for using Partial Indexes
 
 ```sql
-CREATE INDEX 
+CREATE INDEX
     index_name
-ON 
+ON
     table_name(column_list)
-WHERE 
+WHERE
     expression;
 ```
 
@@ -80,9 +80,9 @@ You can also beautify tables in SQLite like shown above, by using the command `.
 In the following example, suppose we want a list of doctors from India that have taken the vaccine. If we want to use a non-partial index, we can create it on the "vaccinated" column with the following command:
 
 ```sql
-CREATE INDEX 
-    vaccinated_idx 
-ON 
+CREATE INDEX
+    vaccinated_idx
+ON
     vaccination_data(vaccinated);
 ```
 
@@ -115,20 +115,20 @@ DROP INDEX vaccinated_idx;
 In the following command, we will create an index with a `WHERE` clause that precisely describes the list of doctors from India that have taken the vaccine.
 
 ```sql
-CREATE INDEX 
+CREATE INDEX
     vaccinated_idx
-ON 
+ON
     vaccination_data(vaccinated)
-WHERE 
+WHERE
     vaccinated = 'Yes' AND country = 'India' AND title = 'Dr';
 ```
 Let’s verify if the index we created is being used in the query with a `WHERE` clause by running the following command:
 
 ```sql
-EXPLAIN QUERY PLAN 
+EXPLAIN QUERY PLAN
 SELECT
         *
-FROM    
+FROM
         vaccination_data
 WHERE
         vaccinated = 'Yes' AND country = 'India' AND title = 'Dr';
@@ -255,6 +255,6 @@ In this section, we learned about SQLite partial indexes and how we can easily c
 
 ## Need More Help?​
 
-[Join the Ariga Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling. 
+[Join the Ariga Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling.
 
-[Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io), our OSS database schema management tool, and our cloud platform [Ariga Cloud](https://Ariga.cloud).
+[Sign up](https://atlasnewsletter.substack.com/) to our newsletter to stay up to date about [Atlas](https://atlasgo.io), our OSS database schema management tool, and our cloud platform [Atlas Cloud](https://atlasgo.cloud).
diff --git a/doc/md/lint/analyzers.md b/doc/md/lint/analyzers.md
index a0b94204..ccd2e340 100644
--- a/doc/md/lint/analyzers.md
+++ b/doc/md/lint/analyzers.md
@@ -18,9 +18,9 @@ safety of migration scripts.
 
 ## Analyzers
 
-Below are the `Analyzer` implementations currently supported by Atlas. 
+Below are the `Analyzer` implementations currently supported by Atlas.
 
-### Destructive Changes 
+### Destructive Changes
 
 Destructive changes are changes to a database schema that result in loss of data. For instance,
 consider a statement such as:
@@ -30,7 +30,7 @@ ALTER TABLE `users` DROP COLUMN `email_address`;
 This statement is considered destructive because whatever data is stored in the `email_address` column
 will be deleted from disk, with no way to recover it. There are definitely situations where this type
 of change is desired, but they are relatively rare. Using the `destructive` ([GoDoc](https://pkg.go.dev/ariga.io/atlas@master/sql/sqlcheck/destructive))
-Analyzer, teams can detect this type of change and design workflows that prevent it from happening accidentally. 
+Analyzer, teams can detect this type of change and design workflows that prevent it from happening accidentally.
 
 Running migration linting locally on in CI fails with exit code 1 in case destructive changes are detected. However,
 users can disable this by configuring the `destructive` analyzer in the [`atlas.hcl`](../atlas-schema/projects#configure-migration-linting)
@@ -44,7 +44,7 @@ lint {
 }
 ```
 
-### Data-dependent Changes 
+### Data-dependent Changes
 
 Data-dependent changes are changes to a database schema that _may_ succeed or fail, depending on the
 data that is stored in the database. For instance, consider a statement such as:
@@ -52,7 +52,7 @@ data that is stored in the database. For instance, consider a statement such as:
 ```sql
 ALTER TABLE `example`.`orders` ADD UNIQUE INDEX `idx_name` (`name`);
 ```
-This statement is considered data-dependent because if the `orders` table 
+This statement is considered data-dependent because if the `orders` table
 contains duplicate values on the name column we will not be able to add a uniqueness
 constraint. Consider we added two records with the name `atlas` to the table:
 ```
@@ -75,7 +75,7 @@ This type of change is tricky because a developer trying to simulate it locally
 might succeed in performing it only to be surprised that their migration script
 fails in production, breaking a deployment sequence or causing other unexpected
 behavior. Using the `data_depend` ([GoDoc](https://pkg.go.dev/ariga.io/atlas@master/sql/sqlcheck/datadepend))
-Analyzer, teams can detect this risk early and account for it in pre-deployment checks to a database. 
+Analyzer, teams can detect this risk early and account for it in pre-deployment checks to a database.
 
 By default, data-dependent changes are reported but not cause migration linting to fail. Users can change this by
 configuring the `data_depend` analyzer in the [`atlas.hcl`](../atlas-schema/projects#configure-migration-linting) file:
@@ -109,8 +109,8 @@ The following schema change checks are provided by Atlas:
 | [MY101](#MY101)                    | Adding a non-nullable column without a `DEFAULT` value to an existing table |
 | [MY102](#MY102)                    | Adding a column with an inline `REFERENCES` clause has no actual effect     |
 | **LT**                             | SQLite specific checks                                                      |
-| [LT101](#LT101)                    | Modifying a nullable column to non-nullable without a `DEFAULT` value       |  
-| **AR**                             | Ariga cloud checks                                                          |
+| [LT101](#LT101)                    | Modifying a nullable column to non-nullable without a `DEFAULT` value       |
+| **AR**                             | Atlas cloud checks                                                          |
 | [AR101](#AR101)                    | Creating table with non-optimal data alignment                              |
 
 #### DS101 {#DS101}
@@ -245,7 +245,7 @@ PRAGMA foreign_keys = on;
 
 #### AR101 {#AR101}
 
-Creating a table with optimal data alignment may help minimize the amount of required disk space. 
+Creating a table with optimal data alignment may help minimize the amount of required disk space.
 For example consider the next Postgres table on a 64-bit system:
 
 ```postgresql
@@ -253,7 +253,7 @@ CREATE TABLE accounts (
     id bigint PRIMARY KEY,
     premium boolean,
     balance integer,
-    age     smallint                  
+    age     smallint
 );
 ```
 Each tuple in the table takes 24 bytes of successive memory without the header.
@@ -268,6 +268,6 @@ CREATE TABLE accounts (
     id bigint PRIMARY KEY,
     balance integer,
     age smallint,
-    premium boolean             
+    premium boolean
 );
-```
\ No newline at end of file
+```
diff --git a/doc/website/blog/2022-12-06-erd-and-json.md b/doc/website/blog/2022-12-06-erd-and-json.md
index ebec0875..7fd7ffb1 100644
--- a/doc/website/blog/2022-12-06-erd-and-json.md
+++ b/doc/website/blog/2022-12-06-erd-and-json.md
@@ -23,13 +23,13 @@ the schema and see how different entities are connected. This can be especially
 large databases, as it allows you to quickly identify patterns and connections that might not be immediately obvious
 when looking at the raw data.
 
-[![Using Explore to generate an ERD](https://atlasgo.io/uploads/explore-erd.gif)](https://gh.ariga.cloud/explore)
+[![Using Explore to generate an ERD](https://atlasgo.io/uploads/explore-erd.gif)](https://gh.atlasgo.cloud/explore)
 
-To automatically generate an ERD from your database, you can use the [Explore](https://gh.ariga.cloud/explore) feature
-of Ariga Cloud. To visualize a schema using the Explore feature, you need to provide your database schema in
+To automatically generate an ERD from your database, you can use the [Explore](https://gh.atlasgo.cloud/explore) feature
+of Atlas Cloud. To visualize a schema using the Explore feature, you need to provide your database schema in
 one of two ways:
 
-1. Provide a connection string to your database. This will allow Ariga Cloud to connect to your database and
+1. Provide a connection string to your database. This will allow Atlas Cloud to connect to your database and
    automatically generate a schema from the metadata tables. Note: this method only works for databases that
    are publicly accessible via the internet.
 2. Provide the schema as an [Atlas HCL](https://atlasgo.io/atlas-schema/sql-resources/) file. If you have an
diff --git a/doc/website/blog/2023-01-05-atlas-v090.mdx b/doc/website/blog/2023-01-05-atlas-v090.mdx
index 85f0a906..035a001d 100644
--- a/doc/website/blog/2023-01-05-atlas-v090.mdx
+++ b/doc/website/blog/2023-01-05-atlas-v090.mdx
@@ -200,26 +200,26 @@ Boom! Atlas automatically calculates the difference between the ***current state
 defined by our schema file, and generates the necessary changes to migrate the database to the new state. We don't need to
 specify each individual migration – we simply tell Atlas what state we want the database to be in, and it handles the rest.
 
-To see a full description of this generated migration plan, check out this diagram example in [Atlas public playground](https://gh.ariga.cloud/explore/f95ffc45...20d5152c):
+To see a full description of this generated migration plan, check out this diagram example in [Atlas public playground](https://gh.atlasgo.cloud/explore/f95ffc45...20d5152c):
 
 #### Diff ERD
 
-[![](https://atlasgo.io/uploads/images/diff-erd.png)](https://gh.ariga.cloud/explore/f95ffc45...20d5152c)
+[![](https://atlasgo.io/uploads/images/diff-erd.png)](https://gh.atlasgo.cloud/explore/f95ffc45...20d5152c)
 
 #### Diff SQL
 
-[![](https://atlasgo.io/uploads/images/diff-sql.png)](https://gh.ariga.cloud/explore/f95ffc45...20d5152c)
+[![](https://atlasgo.io/uploads/images/diff-sql.png)](https://gh.atlasgo.cloud/explore/f95ffc45...20d5152c)
 
 ## Atlas Playground
 
-As part of this version, we have released the [Atlas playground](https://gh.ariga.cloud/explore) where users can visualize their
+As part of this version, we have released the [Atlas playground](https://gh.atlasgo.cloud/explore) where users can visualize their
 database schemas in an interactive way. Simply provide an SQL or HCL schema, or import one from an existing database,
 and in return get an ERD visualizing their entire data model.
 
 Users can also compare between two schemas with the *Schema Diff* button, and get the SQL statements necessary to
-migrate from one schema to the other - [give it try!](https://gh.ariga.cloud/explore/01d2aeb1)
+migrate from one schema to the other - [give it try!](https://gh.atlasgo.cloud/explore/01d2aeb1)
 
-<p style={{textAlign: "center"}}><a href="https://gh.ariga.cloud/explore/01d2aeb1">
+<p style={{textAlign: "center"}}><a href="https://gh.atlasgo.cloud/explore/01d2aeb1">
 <img src="https://atlasgo.io/uploads/images/diff-erd-short.gif" alt="Blog ERD"/>
 </a></p>
 
diff --git a/doc/website/docusaurus.config.js b/doc/website/docusaurus.config.js
index e7810e14..47010f8f 100644
--- a/doc/website/docusaurus.config.js
+++ b/doc/website/docusaurus.config.js
@@ -127,8 +127,8 @@ module.exports = {
       `,
     },
     announcementBar: {
-      id: 'announcementBar-1', // Increment on change
-      content: `️🚀 Sign up for a user testing session and receive exclusive Atlas swag, register <a target="_blank" rel="noopener noreferrer" href="https://calendly.com/ariga-user-testing/atlas-user-testing">here!</a>`,
+      id: 'announcementBar-2', // Increment on change
+      content: `️🚀 Read the latest updates in <a target="_blank" rel="noopener noreferrer" href="https://atlasnewsletter.substack.com/archive">our newsletter!</a>`, 
       isCloseable: true,
     },
   },
diff --git a/doc/website/sidebars.js b/doc/website/sidebars.js
index 20d370b8..1dbc8e73 100644
--- a/doc/website/sidebars.js
+++ b/doc/website/sidebars.js
@@ -165,6 +165,11 @@ module.exports = {
                     id: 'guides/mysql/prefix-indexes',
                     label: 'Prefix Indexes'
                 },
+                {
+                    type: 'doc',
+                    id: 'guides/mysql/check-constraint',
+                    label: 'CHECK Constraint'
+                },
                 {
                   type: 'doc',
                   id: 'guides/mysql/generated-columns',
diff --git a/doc/website/src/assets/icons/newsletter.svg b/doc/website/src/assets/icons/newsletter.svg
deleted file mode 100644
index d9e0b363..00000000
--- a/doc/website/src/assets/icons/newsletter.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M20 4H4C2.9 4 2.01 4.9 2.01 6L2 18C2 19.1 2.9 20 4 20H20C21.1 20 22 19.1 22 18V6C22 4.9 21.1 4 20 4ZM20 8L12 13L4 8V6L12 11L20 6V8Z" fill="#82C7FF"/>
-</svg>
diff --git a/doc/website/src/pages/index.js b/doc/website/src/pages/index.js
index abc2e74f..e453591f 100644
--- a/doc/website/src/pages/index.js
+++ b/doc/website/src/pages/index.js
@@ -6,7 +6,6 @@ import index from './index.module.css';
 import GithubIcon from '../assets/icons/github.svg';
 import DiscordIcon from '../assets/icons/discord.svg';
 import TwitterIcon from '../assets/icons/twitter.svg';
-import NewsletterIcon from '../assets/icons/newsletter.svg';
 import BrowserOnly from "@docusaurus/core/lib/client/exports/BrowserOnly";
 
 function Nav() {
@@ -34,16 +33,12 @@ function SocialLinks() {
         <a href="https://github.com/ariga/atlas" target="_blank">
             <GithubIcon />
         </a>
-
         <a href="https://discord.gg/zZ6sWVg6NT" target="_blank">
             <DiscordIcon />
         </a>
         <a href="https://twitter.com/ariga_io" target="_blank">
             <TwitterIcon />
         </a>
-        <a href="https://atlasnewsletter.substack.com/" target="_blank">
-            <NewsletterIcon />
-        </a>
     </div>
 }
 
diff --git a/sql/migrate/dir.go b/sql/migrate/dir.go
index ab4e1634..787ce788 100644
--- a/sql/migrate/dir.go
+++ b/sql/migrate/dir.go
@@ -179,6 +179,37 @@ func (f LocalFile) Bytes() []byte {
 	return f.b
 }
 
+// Directive returns the (global) file directives that match the provided name.
+// File directives are located at the top of the file and should not be associated with any
+// statement. Hence, double new lines are used to separate file directives from its content.
+func (f LocalFile) Directive(name string) (ds []string) {
+	var (
+		comments []string
+		content  = string(f.b)
+	)
+	for strings.HasPrefix(content, "#") || strings.HasPrefix(content, "--") {
+		idx := strings.IndexByte(content, '\n')
+		if idx == -1 {
+			// Comments-only file.
+			comments = append(comments, content)
+			break
+		}
+		comments = append(comments, strings.TrimSpace(content[:idx]))
+		content = content[idx+1:]
+	}
+	// File directives are separated by
+	// double newlines from file content.
+	if !strings.HasPrefix(content, "\n") {
+		return nil
+	}
+	for _, c := range comments {
+		if d, ok := directive(c, name); ok {
+			ds = append(ds, d)
+		}
+	}
+	return ds
+}
+
 // MemDir provides an in-memory Dir implementation.
 type MemDir struct {
 	files map[string]File
diff --git a/sql/postgres/crdb.go b/sql/postgres/crdb.go
index 873929bf..c98735bf 100644
--- a/sql/postgres/crdb.go
+++ b/sql/postgres/crdb.go
@@ -12,24 +12,22 @@ import (
 	"strings"
 
 	"ariga.io/atlas/sql/internal/sqlx"
+	"ariga.io/atlas/sql/migrate"
 	"ariga.io/atlas/sql/schema"
-	"ariga.io/atlas/sql/sqlclient"
 )
 
-func init() {
-	sqlclient.Register(
-		"cockroach",
-		sqlclient.DriverOpener(Open),
-		sqlclient.RegisterCodec(MarshalHCL, EvalHCL),
-		sqlclient.RegisterFlavours("crdb"),
-		sqlclient.RegisterURLParser(parser{}),
-	)
-}
-
-// crdbDiff implements the sqlx.DiffDriver for CockroachDB.
 type (
 	crdbDiff    struct{ diff }
 	crdbInspect struct{ inspect }
+	noLocker    interface {
+		migrate.Driver
+		migrate.Snapshoter
+		migrate.CleanChecker
+		schema.Normalizer
+	}
+	noLockDriver struct {
+		noLocker
+	}
 )
 
 var _ sqlx.DiffDriver = (*crdbDiff)(nil)
@@ -266,10 +264,11 @@ const (
 	TypeGeometry = "geometry"
 )
 
-// CockroachDB query for getting schema indexes.
-// Scanning constraints is disabled due to internal CockroachDB error.
-// (internal error: unexpected type *tree.DOidWrapper for key value)
-const crdbIndexesQuery = `
+const (
+	// CockroachDB query for getting schema indexes.
+	// Scanning constraints is disabled due to internal CockroachDB error.
+	// (internal error: unexpected type *tree.DOidWrapper for key value)
+	crdbIndexesQuery = `
 SELECT
 	t.relname AS table_name,
 	i.relname AS index_name,
@@ -301,7 +300,7 @@ ORDER BY
 	table_name, index_name, idx.ord
 `
 
-const crdbColumnsQuery = `
+	crdbColumnsQuery = `
 SELECT
 	t1.table_name,
 	t1.column_name,
@@ -341,3 +340,4 @@ WHERE
 ORDER BY
 	t1.table_name, t1.ordinal_position
 `
+)
diff --git a/sql/postgres/driver.go b/sql/postgres/driver.go
index 21877b44..a978c416 100644
--- a/sql/postgres/driver.go
+++ b/sql/postgres/driver.go
@@ -28,12 +28,13 @@ type (
 		schema.Differ
 		schema.Inspector
 		migrate.PlanApplier
-		schema string // the schema given in the `search_path` parameter (if given)
 	}
 
 	// database connection and its information.
 	conn struct {
 		schema.ExecQuerier
+		// The schema in the `search_path` parameter (if given).
+		schema string
 		// System variables that are set on `Open`.
 		collate string
 		ctype   string
@@ -69,7 +70,12 @@ func opener(_ context.Context, u *url.URL) (*sqlclient.Client, error) {
 		}
 		return nil, err
 	}
-	drv.(*Driver).schema = ur.Schema
+	switch drv := drv.(type) {
+	case *Driver:
+		drv.schema = ur.Schema
+	case noLockDriver:
+		drv.noLocker.(*Driver).schema = ur.Schema
+	}
 	return &sqlclient.Client{
 		Name:   DriverName,
 		DB:     db,
@@ -101,11 +107,13 @@ func Open(db schema.ExecQuerier) (migrate.Driver, error) {
 	}
 	// Means we are connected to CockroachDB because we have a result for name='crdb_version'. see `paramsQuery`.
 	if c.crdb = len(params) == 4; c.crdb {
-		return &Driver{
-			conn:        c,
-			Differ:      &sqlx.Diff{DiffDriver: &crdbDiff{diff{c}}},
-			Inspector:   &crdbInspect{inspect{c}},
-			PlanApplier: &planApply{c},
+		return noLockDriver{
+			&Driver{
+				conn:        c,
+				Differ:      &sqlx.Diff{DiffDriver: &crdbDiff{diff{c}}},
+				Inspector:   &crdbInspect{inspect{c}},
+				PlanApplier: &planApply{c},
+			},
 		}, nil
 	}
 	return &Driver{


Test patch:
diff --git a/sql/migrate/dir_test.go b/sql/migrate/dir_test.go
index d46b1a41..7cb5de1c 100644
--- a/sql/migrate/dir_test.go
+++ b/sql/migrate/dir_test.go
@@ -215,3 +215,39 @@ func TestMemDir(t *testing.T) {
 	require.NoError(t, err)
 	require.Equal(t, hs1, hs2)
 }
+
+func TestLocalFile_Directive(t *testing.T) {
+	f := migrate.NewLocalFile("1.sql", []byte(`-- atlas:lint ignore
+alter table users drop column id;
+`))
+	require.Empty(t, f.Directive("lint"), "statement directives are ignored")
+
+	f = migrate.NewLocalFile("1.sql", []byte(`-- atlas:lint ignore
+
+alter table users drop column id;
+
+-- atlas:lint DS102
+alter table pets drop column id;
+`))
+	require.Equal(t, []string{"ignore"}, f.Directive("lint"), "single directive")
+
+	f = migrate.NewLocalFile("1.sql", []byte(`-- atlas:lint ignore
+-- atlas:txmode none
+
+alter table users drop column id;
+
+-- atlas:lint DS102
+alter table pets drop column id;
+`))
+	require.Equal(t, []string{"ignore"}, f.Directive("lint"), "first directive from two")
+	require.Equal(t, []string{"none"}, f.Directive("txmode"), "second directive from two")
+
+	f = migrate.NewLocalFile("1.sql", nil)
+	require.Empty(t, f.Directive("lint"))
+	f = migrate.NewLocalFile("1.sql", []byte("-- atlas:lint ignore"))
+	require.Empty(t, f.Directive("lint"))
+	f = migrate.NewLocalFile("1.sql", []byte("-- atlas:lint ignore\n"))
+	require.Empty(t, f.Directive("lint"))
+	f = migrate.NewLocalFile("1.sql", []byte("-- atlas:lint ignore\n\n"))
+	require.Equal(t, []string{"ignore"}, f.Directive("lint"), "double newline as directive separator")
+}
diff --git a/sql/postgres/driver_test.go b/sql/postgres/driver_test.go
index e6286a00..9c13f22f 100644
--- a/sql/postgres/driver_test.go
+++ b/sql/postgres/driver_test.go
@@ -105,7 +105,7 @@ func TestDriver_UnlockError(t *testing.T) {
 
 func TestDriver_CheckClean(t *testing.T) {
 	s := schema.New("test")
-	drv := &Driver{Inspector: &mockInspector{schema: s}, schema: "test"}
+	drv := &Driver{Inspector: &mockInspector{schema: s}, conn: conn{schema: "test"}}
 	// Empty schema.
 	err := drv.CheckClean(context.Background(), nil)
 	require.NoError(t, err)