Repository: ariga/atlas (go)

Issue:
gosec showing various errors. 
I dont know if this is checked somewhere else because there is no makefile, but i found the following.



``` 
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

golangci-lint run -E gosec -E sqlclosecheck ./... --skip-dirs-use-default
sql/internal/sqlx/twin.go:9:2: G501: Blocklisted import crypto/md5: weak cryptographic primitive (gosec)
        "crypto/md5"
        ^
sql/internal/sqlx/twin.go:113:64: G401: Use of weak cryptographic primitive (gosec)
        return fmt.Sprintf("%s_%x", twin[:t.MaxNameLen-1-md5.Size*2], md5.Sum([]byte(twin)))
                                                                      ^
sql/sqlite/migrate.go:415:19: Close should use defer (sqlclosecheck)
                err = rows.Close()
                                ^
cmd/action/fmt_test.go:144:9: G306: Expect WriteFile permissions to be 0600 or less (gosec)
                err = os.WriteFile(file, []byte(contents), 0644)
                      ^
sql/migrate/migrate.go:263:9: G306: Expect WriteFile permissions to be 0600 or less (gosec)
        return os.WriteFile(filepath.Join(d.dir, name), b, 0644)
               ^
cmd/action/internal/update/update.go:149:9: G306: Expect WriteFile permissions to be 0600 or less (gosec)
        return ioutil.WriteFile(fileLocation(path), b, 0644)
               ^
make: *** [lint] Error 1

``` 


Pull request:
sql/sqlx: replace md5 hash and run gosec on ci
fix #606 

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 27d527b6..64490dd8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,7 +14,7 @@ jobs:
         uses: golangci/golangci-lint-action@v2.5.2
         with:
           version: v1.42.1
-          args: --verbose --enable revive
+          args: --verbose
   generate-cmp:
     runs-on: ubuntu-latest
     steps:
diff --git a/.golangci.yml b/.golangci.yml
index ca0cb701..57a6a160 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -4,3 +4,9 @@ run:
 issues:
   include:
     - EXC0012
+
+linters:
+  disable-all: true
+  enable:
+    - gosec
+    - revive
diff --git a/README.md b/README.md
index 417c571b..0a4190ea 100644
--- a/README.md
+++ b/README.md
@@ -35,13 +35,13 @@ Instructions for other platforms and databases: [Getting Started](https://atlasg
 
 Inspect and save output to a schema file.
 ```shell
-atlas schema inspect -d "mysql://root:pass@tcp(localhost:3306)/example" > atlas.hcl
+atlas schema inspect -u "mysql://root:pass@tcp(localhost:3306)/example" > atlas.hcl
 ```
 
 ## Apply change to Schema
 
 ```shell
-atlas schema apply -d "mysql://root:pass@tcp(localhost:3306)/example" -f atlas.hcl
+atlas schema apply -u "mysql://root:pass@tcp(localhost:3306)/example" -f atlas.hcl
 ```
 
 Full [CLI documentation](https://atlasgo.io/cli/reference). 
diff --git a/cmd/action/apply.go b/cmd/action/apply.go
index 8e849b83..e51ecb82 100644
--- a/cmd/action/apply.go
+++ b/cmd/action/apply.go
@@ -6,6 +6,7 @@ package action
 
 import (
 	"context"
+	"errors"
 	"io/ioutil"
 	"strings"
 
@@ -18,7 +19,7 @@ import (
 var (
 	// ApplyFlags are the flags used in Apply command.
 	ApplyFlags struct {
-		DSN         string
+		URL         string
 		File        string
 		Web         bool
 		Addr        string
@@ -36,12 +37,12 @@ plan and prompt the user for approval.
 
 If run with the "--dry-run" flag, atlas will exit after printing out the planned migration.`,
 		Run: CmdApplyRun,
-		Example: `atlas schema apply -d "mysql://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
-atlas schema apply -d "mysql://user:pass@tcp(localhost:3306)/" -f atlas.hcl --schema prod --schema staging
-atlas schema apply -d "mysql://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl --dry-run 
-atlas schema apply -d "mariadb://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
-atlas schema apply --dsn "postgres://user:pass@host:port/dbname?sslmode=disable" -f atlas.hcl
-atlas schema apply -d "sqlite://file:ex1.db?_fk=1" -f atlas.hcl`,
+		Example: `  atlas schema apply -u "mysql://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
+  atlas schema apply -u "mysql://user:pass@tcp(localhost:3306)/" -f atlas.hcl --schema prod --schema staging
+  atlas schema apply -u "mysql://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl --dry-run
+  atlas schema apply -u "mariadb://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
+  atlas schema apply --url "postgres://user:pass@host:port/dbname?sslmode=disable" -f atlas.hcl
+  atlas schema apply -u "sqlite://file:ex1.db?_fk=1" -f atlas.hcl`,
 	}
 )
 
@@ -52,15 +53,16 @@ const (
 
 func init() {
 	schemaCmd.AddCommand(ApplyCmd)
-	ApplyCmd.Flags().StringVarP(&ApplyFlags.DSN, "dsn", "d", "", "[driver://username:password@protocol(address)/dbname?param=value] Select data source using the dsn format")
+	ApplyCmd.Flags().StringVarP(&ApplyFlags.URL, "url", "u", "", "[driver://username:password@protocol(address)/dbname?param=value] Select data source using the url format")
 	ApplyCmd.Flags().StringVarP(&ApplyFlags.File, "file", "f", "", "[/path/to/file] file containing schema")
 	ApplyCmd.Flags().BoolVarP(&ApplyFlags.Web, "web", "w", false, "Open in a local Atlas UI")
 	ApplyCmd.Flags().BoolVarP(&ApplyFlags.DryRun, "dry-run", "", false, "Dry-run. Print SQL plan without prompting for execution")
 	ApplyCmd.Flags().StringVarP(&ApplyFlags.Addr, "addr", "", "127.0.0.1:5800", "used with -w, local address to bind the server to")
 	ApplyCmd.Flags().StringSliceVarP(&ApplyFlags.Schema, "schema", "s", nil, "Set schema name")
 	ApplyCmd.Flags().BoolVarP(&ApplyFlags.AutoApprove, "auto-approve", "", false, "Auto approve. Apply the schema changes without prompting for approval")
-	cobra.CheckErr(ApplyCmd.MarkFlagRequired("dsn"))
+	cobra.CheckErr(ApplyCmd.MarkFlagRequired("url"))
 	cobra.CheckErr(ApplyCmd.MarkFlagRequired("file"))
+	dsn2url(ApplyCmd, &ApplyFlags.URL)
 }
 
 // CmdApplyRun is the command used when running CLI.
@@ -69,15 +71,15 @@ func CmdApplyRun(cmd *cobra.Command, args []string) {
 		schemaCmd.PrintErrln("The Atlas UI is not available in this release.")
 		return
 	}
-	d, err := defaultMux.OpenAtlas(ApplyFlags.DSN)
+	d, err := defaultMux.OpenAtlas(ApplyFlags.URL)
 	cobra.CheckErr(err)
-	applyRun(d, ApplyFlags.DSN, ApplyFlags.File, ApplyFlags.DryRun, ApplyFlags.AutoApprove)
+	applyRun(d, ApplyFlags.URL, ApplyFlags.File, ApplyFlags.DryRun, ApplyFlags.AutoApprove)
 }
 
-func applyRun(d *Driver, dsn string, file string, dryRun bool, autoApprove bool) {
+func applyRun(d *Driver, url string, file string, dryRun bool, autoApprove bool) {
 	ctx := context.Background()
 	schemas := ApplyFlags.Schema
-	if n, err := SchemaNameFromDSN(dsn); n != "" {
+	if n, err := SchemaNameFromURL(url); n != "" {
 		cobra.CheckErr(err)
 		schemas = append(schemas, n)
 	}
@@ -135,3 +137,21 @@ func promptUser() bool {
 	cobra.CheckErr(err)
 	return result == answerApply
 }
+
+func dsn2url(cmd *cobra.Command, p *string) {
+	cmd.Flags().StringVarP(p, "dsn", "d", "", "")
+	cobra.CheckErr(cmd.Flags().MarkHidden("dsn"))
+	cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
+		dsnF, urlF := cmd.Flag("dsn"), cmd.Flag("url")
+		switch {
+		case !dsnF.Changed && !urlF.Changed:
+			return errors.New(`required flag "url" was not set`)
+		case dsnF.Changed && urlF.Changed:
+			return errors.New(`both flags "url" and "dsn" were set`)
+		case dsnF.Changed && !urlF.Changed:
+			urlF.Changed = true
+			urlF.Value = dsnF.Value
+		}
+		return nil
+	}
+}
diff --git a/cmd/action/diff.go b/cmd/action/diff.go
index 01a6485e..df5dbd41 100644
--- a/cmd/action/diff.go
+++ b/cmd/action/diff.go
@@ -12,8 +12,8 @@ import (
 )
 
 type diffCmdOpts struct {
-	fromDSN string
-	toDSN   string
+	fromURL string
+	toURL   string
 }
 
 // newDiffCmd returns a new *cobra.Command that runs cmdDiffRun with the given flags and mux.
@@ -29,8 +29,8 @@ SQL statements to migrate the "from" database to the schema of the "to" database
 			cmdDiffRun(cmd, &opts)
 		},
 	}
-	cmd.Flags().StringVarP(&opts.fromDSN, "from", "", "", "[driver://username:password@protocol(address)/dbname?param=value] Select data source using the dsn format")
-	cmd.Flags().StringVarP(&opts.toDSN, "to", "", "", "[driver://username:password@protocol(address)/dbname?param=value] Select data source using the dsn format")
+	cmd.Flags().StringVarP(&opts.fromURL, "from", "", "", "[driver://username:password@protocol(address)/dbname?param=value] Select data source using the url format")
+	cmd.Flags().StringVarP(&opts.toURL, "to", "", "", "[driver://username:password@protocol(address)/dbname?param=value] Select data source using the url format")
 	cobra.CheckErr(cmd.MarkFlagRequired("from"))
 	cobra.CheckErr(cmd.MarkFlagRequired("to"))
 	return cmd
@@ -44,13 +44,13 @@ func init() {
 // cmdDiffRun connects to the given databases, and prints an SQL plan to get from
 // the "from" schema to the "to" schema.
 func cmdDiffRun(cmd *cobra.Command, flags *diffCmdOpts) {
-	fromDriver, err := defaultMux.OpenAtlas(flags.fromDSN)
+	fromDriver, err := defaultMux.OpenAtlas(flags.fromURL)
 	cobra.CheckErr(err)
-	toDriver, err := defaultMux.OpenAtlas(flags.toDSN)
+	toDriver, err := defaultMux.OpenAtlas(flags.toURL)
 	cobra.CheckErr(err)
-	fromName, err := SchemaNameFromDSN(flags.fromDSN)
+	fromName, err := SchemaNameFromURL(flags.fromURL)
 	cobra.CheckErr(err)
-	toName, err := SchemaNameFromDSN(flags.toDSN)
+	toName, err := SchemaNameFromURL(flags.toURL)
 	cobra.CheckErr(err)
 	ctx := context.Background()
 	fromSchema, err := fromDriver.InspectSchema(ctx, fromName, nil)
diff --git a/cmd/action/inspect.go b/cmd/action/inspect.go
index fe5cecb7..3ce49644 100644
--- a/cmd/action/inspect.go
+++ b/cmd/action/inspect.go
@@ -14,7 +14,7 @@ import (
 var (
 	// InspectFlags are the flags used in Inspect command.
 	InspectFlags struct {
-		DSN    string
+		URL    string
 		Web    bool
 		Addr   string
 		Schema []string
@@ -27,37 +27,31 @@ var (
 It then prints to the screen the schema of that database in Atlas DDL syntax. This output can be 
 saved to a file, commonly by redirecting the output to a file named with a ".hcl" suffix:
 
-	atlas schema inspect -d "mysql://user:pass@tcp(localhost:3306)/dbname" > atlas.hcl
+  atlas schema inspect -u "mysql://user:pass@tcp(localhost:3306)/dbname" > atlas.hcl
 
 This file can then be edited and used with the` + " `atlas schema apply` " + `command to plan
 and execute schema migrations against the given database. In cases where users wish to inspect
 all multiple schemas in a given database (for instance a MySQL server may contain multiple named
-databases), omit the relevant part from the dsn, e.g. "mysql://user:pass@tcp(localhost:3306)/".
+databases), omit the relevant part from the url, e.g. "mysql://user:pass@tcp(localhost:3306)/".
 To select specific schemas from the databases, users may use the "--schema" (or "-s" shorthand)
 flag.
 	`,
 		Run: CmdInspectRun,
-		Example: `
-atlas schema inspect -d "mysql://user:pass@tcp(localhost:3306)/dbname"
-atlas schema inspect -d "mariadb://user:pass@tcp(localhost:3306)/" --schema=schemaA,schemaB -s schemaC
-atlas schema inspect --dsn "postgres://user:pass@host:port/dbname?sslmode=disable"
-atlas schema inspect -d "sqlite://file:ex1.db?_fk=1"`,
+		Example: `  atlas schema inspect -u "mysql://user:pass@tcp(localhost:3306)/dbname"
+  atlas schema inspect -u "mariadb://user:pass@tcp(localhost:3306)/" --schema=schemaA,schemaB -s schemaC
+  atlas schema inspect --url "postgres://user:pass@host:port/dbname?sslmode=disable"
+  atlas schema inspect -u "sqlite://file:ex1.db?_fk=1"`,
 	}
 )
 
 func init() {
 	schemaCmd.AddCommand(InspectCmd)
-	InspectCmd.Flags().StringVarP(
-		&InspectFlags.DSN,
-		"dsn",
-		"d",
-		"",
-		"[driver://username:password@protocol(address)/dbname?param=value] Select data source using the dsn format",
-	)
+	InspectCmd.Flags().StringVarP(&InspectFlags.URL, "url", "u", "", "[driver://username:password@protocol(address)/dbname?param=value] Select data source using the url format")
 	InspectCmd.Flags().BoolVarP(&InspectFlags.Web, "web", "w", false, "Open in a local Atlas UI")
 	InspectCmd.Flags().StringVarP(&InspectFlags.Addr, "addr", "", "127.0.0.1:5800", "Used with -w, local address to bind the server to")
 	InspectCmd.Flags().StringSliceVarP(&InspectFlags.Schema, "schema", "s", nil, "Set schema name")
-	cobra.CheckErr(InspectCmd.MarkFlagRequired("dsn"))
+	cobra.CheckErr(InspectCmd.MarkFlagRequired("url"))
+	dsn2url(InspectCmd, &InspectFlags.URL)
 }
 
 // CmdInspectRun is the command used when running CLI.
@@ -66,15 +60,15 @@ func CmdInspectRun(_ *cobra.Command, _ []string) {
 		schemaCmd.PrintErrln("The Atlas UI is not available in this release.")
 		return
 	}
-	d, err := defaultMux.OpenAtlas(InspectFlags.DSN)
+	d, err := defaultMux.OpenAtlas(InspectFlags.URL)
 	cobra.CheckErr(err)
-	inspectRun(d, InspectFlags.DSN)
+	inspectRun(d, InspectFlags.URL)
 }
 
-func inspectRun(d *Driver, dsn string) {
+func inspectRun(d *Driver, url string) {
 	ctx := context.Background()
 	schemas := InspectFlags.Schema
-	if n, err := SchemaNameFromDSN(dsn); n != "" {
+	if n, err := SchemaNameFromURL(url); n != "" {
 		cobra.CheckErr(err)
 		schemas = append(schemas, n)
 	}
diff --git a/cmd/action/internal/update/update.go b/cmd/action/internal/update/update.go
index 9d2a8327..da467db8 100644
--- a/cmd/action/internal/update/update.go
+++ b/cmd/action/internal/update/update.go
@@ -146,7 +146,7 @@ func saveStore(path string, l LatestRelease, t time.Time) error {
 	if err != nil {
 		return err
 	}
-	return ioutil.WriteFile(fileLocation(path), b, 0644)
+	return ioutil.WriteFile(fileLocation(path), b, 0600)
 }
 
 func fileLocation(p string) string {
diff --git a/cmd/action/mux.go b/cmd/action/mux.go
index 158e316f..8d49c9eb 100644
--- a/cmd/action/mux.go
+++ b/cmd/action/mux.go
@@ -22,7 +22,7 @@ import (
 )
 
 type (
-	// Mux is used for routing dsn to correct provider.
+	// Mux is used for routing URLs to their correct provider.
 	Mux struct {
 		providers map[string]func(string) (*Driver, error)
 	}
@@ -56,8 +56,8 @@ func (u *Mux) RegisterProvider(key string, p func(string) (*Driver, error)) {
 }
 
 // OpenAtlas is used for opening an atlas driver on a specific data source.
-func (u *Mux) OpenAtlas(dsn string) (*Driver, error) {
-	key, dsn, err := parseDSN(dsn)
+func (u *Mux) OpenAtlas(url string) (*Driver, error) {
+	key, dsn, err := urlParts(url)
 	if err != nil {
 		return nil, fmt.Errorf("failed to init atlas driver, %s", err)
 	}
@@ -73,17 +73,17 @@ func (u *Mux) OpenAtlas(dsn string) (*Driver, error) {
 	return p(dsn)
 }
 
-func parseDSN(url string) (string, string, error) {
+func urlParts(url string) (string, string, error) {
 	a := strings.SplitN(url, "://", 2)
 	if len(a) != 2 {
-		return "", "", fmt.Errorf(`failed to parse dsn: "%s"`, url)
+		return "", "", fmt.Errorf(`failed to parse url: "%s"`, url)
 	}
 	return a[0], a[1], nil
 }
 
-// SchemaNameFromDSN parses the dsn the returns schema name
-func SchemaNameFromDSN(url string) (string, error) {
-	key, dsn, err := parseDSN(url)
+// SchemaNameFromURL parses the url the returns schema name
+func SchemaNameFromURL(url string) (string, error) {
+	key, dsn, err := urlParts(url)
 	if err != nil {
 		return "", err
 	}
@@ -104,15 +104,15 @@ func SchemaNameFromDSN(url string) (string, error) {
 }
 
 func postgresSchema(dsn string) (string, error) {
-	url, err := url.Parse(dsn)
+	u, err := url.Parse(dsn)
 	if err != nil {
 		return "", err
 	}
 	// lib/pq supports setting default schemas via the `search_path` parameter
-	// in a dsn.
+	// in a url.
 	//
 	// See: https://github.com/lib/pq/blob/8446d16b8935fdf2b5c0fe333538ac395e3e1e4b/conn.go#L1155-L1165
-	if schema := url.Query().Get("search_path"); schema != "" {
+	if schema := u.Query().Get("search_path"); schema != "" {
 		return schema, nil
 	}
 	return "", nil
diff --git a/doc/md/cli/reference.md b/doc/md/cli/reference.md
index 0ca12622..e7187a3e 100644
--- a/doc/md/cli/reference.md
+++ b/doc/md/cli/reference.md
@@ -78,21 +78,21 @@ If run with the "--dry-run" flag, atlas will exit after printing out the planned
 #### Example
 
 ```
-atlas schema apply -d "mysql://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
-atlas schema apply -d "mysql://user:pass@tcp(localhost:3306)/" -f atlas.hcl --schema prod --schema staging
-atlas schema apply -d "mysql://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl --dry-run 
-atlas schema apply -d "mariadb://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
-atlas schema apply --dsn "postgres://user:pass@host:port/dbname?sslmode=disable" -f atlas.hcl
-atlas schema apply -d "sqlite://file:ex1.db?_fk=1" -f atlas.hcl
+  atlas schema apply -u "mysql://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
+  atlas schema apply -u "mysql://user:pass@tcp(localhost:3306)/" -f atlas.hcl --schema prod --schema staging
+  atlas schema apply -u "mysql://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl --dry-run
+  atlas schema apply -u "mariadb://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
+  atlas schema apply --url "postgres://user:pass@host:port/dbname?sslmode=disable" -f atlas.hcl
+  atlas schema apply -u "sqlite://file:ex1.db?_fk=1" -f atlas.hcl
 ```
 #### Flags
 ```
       --addr string      used with -w, local address to bind the server to (default "127.0.0.1:5800")
       --auto-approve     Auto approve. Apply the schema changes without prompting for approval
       --dry-run          Dry-run. Print SQL plan without prompting for execution
-  -d, --dsn string       [driver://username:password@protocol(address)/dbname?param=value] Select data source using the dsn format
   -f, --file string      [/path/to/file] file containing schema
   -s, --schema strings   Set schema name
+  -u, --url string       [driver://username:password@protocol(address)/dbname?param=value] Select data source using the url format
   -w, --web              Open in a local Atlas UI
 
 ```
@@ -114,8 +114,8 @@ SQL statements to migrate the "from" database to the schema of the "to" database
 
 #### Flags
 ```
-      --from string   [driver://username:password@protocol(address)/dbname?param=value] Select data source using the dsn format
-      --to string     [driver://username:password@protocol(address)/dbname?param=value] Select data source using the dsn format
+      --from string   [driver://username:password@protocol(address)/dbname?param=value] Select data source using the url format
+      --to string     [driver://username:password@protocol(address)/dbname?param=value] Select data source using the url format
 
 ```
 
@@ -153,12 +153,12 @@ atlas schema inspect [flags]
 It then prints to the screen the schema of that database in Atlas DDL syntax. This output can be 
 saved to a file, commonly by redirecting the output to a file named with a ".hcl" suffix:
 
-	atlas schema inspect -d "mysql://user:pass@tcp(localhost:3306)/dbname" > atlas.hcl
+  atlas schema inspect -u "mysql://user:pass@tcp(localhost:3306)/dbname" > atlas.hcl
 
 This file can then be edited and used with the `atlas schema apply` command to plan
 and execute schema migrations against the given database. In cases where users wish to inspect
 all multiple schemas in a given database (for instance a MySQL server may contain multiple named
-databases), omit the relevant part from the dsn, e.g. "mysql://user:pass@tcp(localhost:3306)/".
+databases), omit the relevant part from the url, e.g. "mysql://user:pass@tcp(localhost:3306)/".
 To select specific schemas from the databases, users may use the "--schema" (or "-s" shorthand)
 flag.
 	
@@ -166,17 +166,16 @@ flag.
 #### Example
 
 ```
-
-atlas schema inspect -d "mysql://user:pass@tcp(localhost:3306)/dbname"
-atlas schema inspect -d "mariadb://user:pass@tcp(localhost:3306)/" --schema=schemaA,schemaB -s schemaC
-atlas schema inspect --dsn "postgres://user:pass@host:port/dbname?sslmode=disable"
-atlas schema inspect -d "sqlite://file:ex1.db?_fk=1"
+  atlas schema inspect -u "mysql://user:pass@tcp(localhost:3306)/dbname"
+  atlas schema inspect -u "mariadb://user:pass@tcp(localhost:3306)/" --schema=schemaA,schemaB -s schemaC
+  atlas schema inspect --url "postgres://user:pass@host:port/dbname?sslmode=disable"
+  atlas schema inspect -u "sqlite://file:ex1.db?_fk=1"
 ```
 #### Flags
 ```
       --addr string      Used with -w, local address to bind the server to (default "127.0.0.1:5800")
-  -d, --dsn string       [driver://username:password@protocol(address)/dbname?param=value] Select data source using the dsn format
   -s, --schema strings   Set schema name
+  -u, --url string       [driver://username:password@protocol(address)/dbname?param=value] Select data source using the url format
   -w, --web              Open in a local Atlas UI
 
 ```
diff --git a/doc/md/cli/dsn.md b/doc/md/cli/url.md
similarity index 80%
rename from doc/md/cli/dsn.md
rename to doc/md/cli/url.md
index e6e26eea..63057eab 100644
--- a/doc/md/cli/dsn.md
+++ b/doc/md/cli/url.md
@@ -1,12 +1,12 @@
 ---
-title: DSNs
-id: cli-dsn
-slug: /cli/dsn
+title: URLs
+id: cli-url
+slug: /cli/url
 ---
-## What is a DSN?
+## What is a URL?
 
-Data Source Names, or DSNs, are a standardized method for specifying a database connection.
-All the component parts of a DSN are shown below:
+A Uniform Resource Locators, or URLs are a standardized method for specifying a location to a resource.
+All the component parts of a URL are shown below:
 
 ```
 driver://[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]
diff --git a/doc/md/getting-started/02-inspecting.mdx b/doc/md/getting-started/02-inspecting.mdx
index 6b84af17..d38433f8 100644
--- a/doc/md/getting-started/02-inspecting.mdx
+++ b/doc/md/getting-started/02-inspecting.mdx
@@ -21,20 +21,20 @@ Usage:
 
 Examples:
 
-atlas schema inspect -d "mysql://user:pass@tcp(localhost:3306)/dbname"
-atlas schema inspect -d "mariadb://user:pass@tcp(localhost:3306)/dbname"
-atlas schema inspect --dsn "postgres://user:pass@host:port/dbname?sslmode=disable"
-atlas schema inspect -d "sqlite://file:ex1.db?_fk=1"
+atlas schema inspect -u "mysql://user:pass@tcp(localhost:3306)/dbname"
+atlas schema inspect -u "mariadb://user:pass@tcp(localhost:3306)/dbname"
+atlas schema inspect --url "postgres://user:pass@host:port/dbname?sslmode=disable"
+atlas schema inspect -u "sqlite://file:ex1.db?_fk=1"
 
 Flags:
-  -d, --dsn string   [driver://username:password@protocol(address)/dbname?param=value] Select data source using the dsn format
+  -u, --url string   [driver://username:password@protocol(address)/dbname?param=value] Select data source using the url format
   -h, --help         help for inspect
   -w, --web          open in UI server
 ```
 
 :::info
 
-If you're using PostgreSQL, there is [additional documentation about building the connection string](../dsn#ssltls-mode)
+If you're using PostgreSQL, there is [additional documentation about building the connection string](../url#ssltls-mode)
 
 :::
 
diff --git a/doc/md/getting-started/03-applying.md b/doc/md/getting-started/03-applying.md
index 3f31472f..ed57c864 100644
--- a/doc/md/getting-started/03-applying.md
+++ b/doc/md/getting-started/03-applying.md
@@ -27,18 +27,18 @@ Usage:
 
 Examples:
 
-atlas schema apply -d "mysql://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
-atlas schema apply -d "mariadb://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
-atlas schema apply --dsn "postgres://user:pass@host:port/dbname?sslmode=disable" -f atlas.hcl
-atlas schema apply -d "sqlite://file:ex1.db?_fk=1" -f atlas.hcl
+atlas schema apply -u "mysql://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
+atlas schema apply -u "mariadb://user:pass@tcp(localhost:3306)/dbname" -f atlas.hcl
+atlas schema apply --url "postgres://user:pass@host:port/dbname?sslmode=disable" -f atlas.hcl
+atlas schema apply -u "sqlite://file:ex1.db?_fk=1" -f atlas.hcl
 
 Flags:
-  -d, --dsn string    [driver://username:password@protocol(address)/dbname?param=value] Select data source using the dsn format
+  -u, --url string    [driver://username:password@protocol(address)/dbname?param=value] Select data source using the url format
   -f, --file string   [/path/to/file] file containing schema
   -h, --help          help for apply
 ```
 As you can see, similar to the `inspect` command, the `-d` flag is used to define the
-DSN to connect to the database, and an additional flag `-f` specifies the path to
+URL to connect to the database, and an additional flag `-f` specifies the path to
 the file containing the desired schema. 
 
 ### Adding new tables to our database
diff --git a/doc/website/sidebars.js b/doc/website/sidebars.js
index 2bacfc95..1490d91c 100644
--- a/doc/website/sidebars.js
+++ b/doc/website/sidebars.js
@@ -38,7 +38,7 @@ module.exports = {
             label: 'CLI',
             items: [
                 {type: 'doc', id: 'cli/cli-reference', label: 'Reference'},
-                {type: 'doc', id: 'cli/cli-dsn', label: 'DSN'},
+                {type: 'doc', id: 'cli/cli-url', label: 'URLs'},
             ]
         },
         {
diff --git a/go.mod b/go.mod
index 664cb2a7..396b4d84 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,7 @@ require (
 	github.com/go-openapi/inflect v0.19.0
 	github.com/mattn/go-sqlite3 v1.14.10
 	github.com/mitchellh/go-homedir v1.1.0
+	github.com/spf13/pflag v1.0.5
 )
 
 require (
@@ -31,6 +32,5 @@ require (
 	github.com/inconshreveable/mousetrap v1.0.0 // indirect
 	github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/spf13/pflag v1.0.5 // indirect
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
 )
diff --git a/sql/internal/sqlx/twin.go b/sql/internal/sqlx/twin.go
index 2a785d22..643d7044 100644
--- a/sql/internal/sqlx/twin.go
+++ b/sql/internal/sqlx/twin.go
@@ -6,8 +6,8 @@ package sqlx
 
 import (
 	"context"
-	"crypto/md5"
 	"fmt"
+	"hash/fnv"
 	"time"
 
 	"ariga.io/atlas/sql/migrate"
@@ -110,5 +110,6 @@ func (t *TwinDriver) formatName(name string) string {
 	if t.MaxNameLen == 0 || len(twin) <= t.MaxNameLen {
 		return twin
 	}
-	return fmt.Sprintf("%s_%x", twin[:t.MaxNameLen-1-md5.Size*2], md5.Sum([]byte(twin)))
+	h := fnv.New128()
+	return fmt.Sprintf("%s_%x", twin[:t.MaxNameLen-1-h.Size()*2], h.Sum([]byte(twin)))
 }
diff --git a/sql/migrate/migrate.go b/sql/migrate/migrate.go
index 2235963d..65e7d4bf 100644
--- a/sql/migrate/migrate.go
+++ b/sql/migrate/migrate.go
@@ -260,7 +260,7 @@ func (d *LocalDir) Open(name string) (fs.File, error) {
 
 // WriteFile implements Dir.WriteFile.
 func (d *LocalDir) WriteFile(name string, b []byte) error {
-	return os.WriteFile(filepath.Join(d.dir, name), b, 0644)
+	return os.WriteFile(filepath.Join(d.dir, name), b, 0600)
 }
 
 var (
diff --git a/sql/mysql/tidb.go b/sql/mysql/tidb.go
index 660e5b38..860eb60b 100644
--- a/sql/mysql/tidb.go
+++ b/sql/mysql/tidb.go
@@ -6,6 +6,7 @@ package mysql
 
 import (
 	"context"
+	"encoding/binary"
 	"fmt"
 	"regexp"
 	"sort"
@@ -157,11 +158,37 @@ func (i *tinspect) patchSchema(ctx context.Context, s *schema.Schema) (*schema.S
 		if err := i.setFKs(s, t); err != nil {
 			return nil, err
 		}
+		for _, c := range t.Columns {
+			i.patchColumn(ctx, c)
+		}
 	}
 	return s, nil
 }
 
-// e.g CONSTRAINT "" FOREIGN KEY ("foo_id") REFERENCES "foo" ("id")
+func (i *tinspect) patchColumn(ctx context.Context, c *schema.Column) {
+	_, ok := c.Type.Type.(*BitType)
+	if !ok {
+		return
+	}
+	// TiDB has a bug where it does not format bit default value correctly.
+	if lit, ok := c.Default.(*schema.Literal); ok {
+		lit.V = bytesToBitLiteral([]byte(lit.V))
+	}
+}
+
+// bytesToBitLiteral converts a bytes to MySQL bit literal.
+// e.g. []byte{4} -> b'100', []byte{2,1} -> b'1000000001'.
+// See: https://github.com/pingcap/tidb/issues/32655.
+func bytesToBitLiteral(b []byte) string {
+	bytes := make([]byte, 8)
+	for i := 0; i < len(b); i++ {
+		bytes[8-len(b)+i] = b[i]
+	}
+	val := binary.BigEndian.Uint64(bytes)
+	return fmt.Sprintf("b'%b'", val)
+}
+
+// e.g. CONSTRAINT "" FOREIGN KEY ("foo_id") REFERENCES "foo" ("id").
 var reFK = regexp.MustCompile("(?i)CONSTRAINT\\s+[\"`]*(\\w+)[\"`]*\\s+FOREIGN\\s+KEY\\s*\\(([,\"` \\w]+)\\)\\s+REFERENCES\\s+[\"`]*(\\w+)[\"`]*\\s*\\(([,\"` \\w]+)\\).*")
 var reActions = regexp.MustCompile(fmt.Sprintf("(?i)(ON)\\s+(UPDATE|DELETE)\\s+(%s|%s|%s|%s|%s)", schema.NoAction, schema.Restrict, schema.SetNull, schema.SetDefault, schema.Cascade))
 


Test patch:
diff --git a/cmd/action/diff_test.go b/cmd/action/diff_test.go
index d94107e6..f950da7e 100644
--- a/cmd/action/diff_test.go
+++ b/cmd/action/diff_test.go
@@ -36,7 +36,7 @@ func TestDiffCmd_Synced(t *testing.T) {
 	require.EqualValues(t, "Schemas are synced, no changes to be made.\n", s)
 }
 
-// openSQLite creates a sqlite db, seeds it with the seed query and returns the dsn to it.
+// openSQLite creates a sqlite db, seeds it with the seed query and returns the url to it.
 func openSQLite(t *testing.T, seed string) string {
 	f, err := ioutil.TempFile("", "sqlite.db")
 	require.NoError(t, err)
@@ -58,7 +58,7 @@ func openSQLite(t *testing.T, seed string) string {
 	return fmt.Sprintf("sqlite://%s", dsn)
 }
 
-func runCmd(cmd *cobra.Command, args... string) (string, error) {
+func runCmd(cmd *cobra.Command, args ...string) (string, error) {
 	var out bytes.Buffer
 	cmd.SetOut(&out)
 	cmd.SetArgs(args)
diff --git a/cmd/action/fmt_test.go b/cmd/action/fmt_test.go
index 92d1acbd..a267934c 100644
--- a/cmd/action/fmt_test.go
+++ b/cmd/action/fmt_test.go
@@ -141,7 +141,7 @@ func setupFmtTest(t *testing.T, inputDir map[string]string) string {
 	})
 	for name, contents := range inputDir {
 		file := path.Join(dir, name)
-		err = os.WriteFile(file, []byte(contents), 0644)
+		err = os.WriteFile(file, []byte(contents), 0600)
 	}
 	require.NoError(t, err)
 	return dir
diff --git a/cmd/action/mux_test.go b/cmd/action/mux_test.go
index 954183f1..3fe55252 100644
--- a/cmd/action/mux_test.go
+++ b/cmd/action/mux_test.go
@@ -50,53 +50,53 @@ func Test_GetDriverSuccess(t *testing.T) {
 
 func Test_SQLiteFileDoestNotExist(t *testing.T) {
 	var tests = []struct {
-		dsn      string
+		url      string
 		expected string
 	}{
 		{
-			dsn:      "sqlite://test.db",
+			url:      "sqlite://test.db",
 			expected: `failed opening "test.db": stat test.db: no such file or directory`,
 		},
 		{
-			dsn:      "sqlite://some_random_string_like_this",
+			url:      "sqlite://some_random_string_like_this",
 			expected: `failed opening "some_random_string_like_this": stat some_random_string_like_this: no such file or directory`,
 		},
 		{
-			dsn:      "sqlite://file:/home/fred/data.db",
+			url:      "sqlite://file:/home/fred/data.db",
 			expected: `failed opening "/home/fred/data.db": stat /home/fred/data.db: no such file or directory`,
 		},
 		{
-			dsn:      "sqlite://file:///home/fred/data.db",
+			url:      "sqlite://file:///home/fred/data.db",
 			expected: `failed opening "/home/fred/data.db": stat /home/fred/data.db: no such file or directory`,
 		},
 		{
-			dsn:      "sqlite://file://localhost/home/fred/data.db",
+			url:      "sqlite://file://localhost/home/fred/data.db",
 			expected: `failed opening "/localhost/home/fred/data.db": stat /localhost/home/fred/data.db: no such file or directory`,
 		},
 		{
-			dsn:      "sqlite://file://darkstar/home/fred/data.db",
+			url:      "sqlite://file://darkstar/home/fred/data.db",
 			expected: `failed opening "/darkstar/home/fred/data.db": stat /darkstar/home/fred/data.db: no such file or directory`,
 		},
 		{
-			dsn:      "sqlite://file:data.db?mode=ro&cache=private",
+			url:      "sqlite://file:data.db?mode=ro&cache=private",
 			expected: `failed opening "data.db": stat data.db: no such file or directory`,
 		},
 		{
-			dsn:      "sqlite://file:/home/fred/data.db?vfs=unix-dotfile",
+			url:      "sqlite://file:/home/fred/data.db?vfs=unix-dotfile",
 			expected: `failed opening "/home/fred/data.db": stat /home/fred/data.db: no such file or directory`,
 		},
 		{
-			dsn:      "sqlite://file:data.db?mode=readonly",
+			url:      "sqlite://file:data.db?mode=readonly",
 			expected: `failed opening "data.db": stat data.db: no such file or directory`,
 		},
 		{
-			dsn:      "sqlite://asdad?cache=shared&mode=memory",
+			url:      "sqlite://asdad?cache=shared&mode=memory",
 			expected: `failed opening "asdad": stat asdad: no such file or directory`,
 		},
 	}
 	for _, tt := range tests {
-		t.Run(tt.dsn, func(t *testing.T) {
-			_, err := action.SchemaNameFromDSN(tt.dsn)
+		t.Run(tt.url, func(t *testing.T) {
+			_, err := action.SchemaNameFromURL(tt.url)
 			require.EqualError(t, err, tt.expected)
 		})
 	}
@@ -111,43 +111,43 @@ func Test_SQLiteFileExist(t *testing.T) {
 		r.NoError(err)
 	})
 	dsn := "sqlite://file://" + file.Name()
-	_, err = action.SchemaNameFromDSN(dsn)
+	_, err = action.SchemaNameFromURL(dsn)
 	r.NoError(err)
 }
 
 func Test_SQLiteInMemory(t *testing.T) {
 	r := require.New(t)
-	_, err := action.SchemaNameFromDSN("sqlite://file:test.db?cache=shared&mode=memory")
+	_, err := action.SchemaNameFromURL("sqlite://file:test.db?cache=shared&mode=memory")
 	r.NoError(err)
 }
 
 func Test_PostgresSchemaDSN(t *testing.T) {
 	var tests = []struct {
-		dsn      string
+		url      string
 		expected string
 		wantErr  bool
 	}{
 		{
-			dsn:      "postgres://localhost:5432/dbname?search_path=foo",
+			url:      "postgres://localhost:5432/dbname?search_path=foo",
 			expected: "foo",
 		},
 		{
-			dsn:      "postgres://localhost:5432/dbname",
+			url:      "postgres://localhost:5432/dbname",
 			expected: "",
 		},
 		{
-			dsn:      "postgres://(bad:host)?search_path=foo",
+			url:      "postgres://(bad:host)?search_path=foo",
 			expected: "",
 			wantErr:  true,
 		},
 		{
-			dsn:      "postgres://localhost:5432/dbname?search_path=",
+			url:      "postgres://localhost:5432/dbname?search_path=",
 			expected: "",
 		},
 	}
 	for _, tt := range tests {
-		t.Run(tt.dsn, func(t *testing.T) {
-			schema, err := action.SchemaNameFromDSN(tt.dsn)
+		t.Run(tt.url, func(t *testing.T) {
+			schema, err := action.SchemaNameFromURL(tt.url)
 			require.Equal(t, tt.wantErr, err != nil)
 			require.Equal(t, tt.expected, schema)
 		})
diff --git a/internal/integration/tidb_test.go b/internal/integration/tidb_test.go
index e3fc7d85..e530de37 100644
--- a/internal/integration/tidb_test.go
+++ b/internal/integration/tidb_test.go
@@ -5,6 +5,7 @@
 package integration
 
 import (
+	"context"
 	"database/sql"
 	"fmt"
 	"testing"
@@ -429,3 +430,391 @@ func TestTiDB_ForeignKey(t *testing.T) {
 		})
 	})
 }
+
+func TestTiDB_HCL_Realm(t *testing.T) {
+	tidbRun(t, func(t *myTest) {
+		t.dropDB("second")
+		realm := t.loadRealm()
+		hcl, err := mysql.MarshalHCL(realm)
+		require.NoError(t, err)
+		wa := string(hcl) + `
+schema "second" {
+}
+`
+		t.applyRealmHcl(wa)
+		realm, err = t.drv.InspectRealm(context.Background(), &schema.InspectRealmOption{})
+		require.NoError(t, err)
+		_, ok := realm.Schema("test")
+		require.True(t, ok)
+		_, ok = realm.Schema("second")
+		require.True(t, ok)
+	})
+}
+
+func TestTiDB_DefaultsHCL(t *testing.T) {
+	n := "atlas_defaults"
+	tidbRun(t, func(t *myTest) {
+		ddl := `
+create table atlas_defaults
+(
+	string varchar(255) default "hello_world",
+	quoted varchar(100) default 'never say "never"',
+	tBit bit(10) default b'10101',
+	ts timestamp default CURRENT_TIMESTAMP,
+	number int default 42
+)
+`
+		t.dropTables(n)
+		_, err := t.db.Exec(ddl)
+		require.NoError(t, err)
+		realm := t.loadRealm()
+		spec, err := mysql.MarshalHCL(realm.Schemas[0])
+		require.NoError(t, err)
+		var s schema.Realm
+		err = mysql.UnmarshalHCL(spec, &s)
+		require.NoError(t, err)
+		t.dropTables(n)
+		t.applyHcl(string(spec))
+		ensureNoChange(t, realm.Schemas[0].Tables[0])
+	})
+}
+
+func TestTiDB_Sanity(t *testing.T) {
+	n := "atlas_types_sanity"
+	t.Run("Common", func(t *testing.T) {
+		ddl := `
+create table atlas_types_sanity
+(
+    tBit                        bit(10)              default b'1000000001'                                       null,
+		tInt                        int(10)              default 4                                               not null,
+		tTinyInt                    tinyint(10)          default 8                                                   null,
+		tSmallInt                   smallint(10)         default 2                                                   null,
+		tMediumInt                  mediumint(10)        default 11                                                  null,
+		tBigInt                     bigint(10)           default 4                                                   null,
+		tDecimal                    decimal              default 4                                                   null,
+		tNumeric                    numeric              default 4                                               not null,
+		tFloat                      float         			 default 4                                                   null,
+		tDouble                     double(10, 0)        default 4                                                   null,
+		tReal                       double(10, 0)        default 4                                                   null,
+		tTimestamp                  timestamp            default CURRENT_TIMESTAMP                                   null,
+		tTimestampFraction          timestamp(6)         default CURRENT_TIMESTAMP(6)                                null,
+		tTimestampOnUpdate          timestamp            default CURRENT_TIMESTAMP    ON UPDATE CURRENT_TIMESTAMP    null,
+		tTimestampFractionOnUpdate  timestamp(6)         default CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) null,
+		tDate                       date                                                                             null,
+		tTime                       time                                                                             null,
+		tDateTime                   datetime                                                                         null,
+		tYear                       year                                                                             null,
+		tVarchar                    varchar(10)          default 'Titan'                                             null,
+		tChar                       char(25)             default 'Olimpia'                                       not null,
+		tVarBinary                  varbinary(30)        default 'Titan'                                             null,
+		tBinary                     binary(5)            default 'Titan'                                             null,
+		tBlob                       blob(5)              default                                                     null,
+		tTinyBlob                   tinyblob                                                                         null,
+		tMediumBlob                 mediumblob           default                                                     null,
+		tLongBlob                   longblob             default                                                     null,
+		tText                       text(13)             default                                                     null,
+		tTinyText                   tinytext             default                                                     null,
+		tMediumText                 mediumtext           default                                                     null,
+		tLongText                   longtext             default                                                     null,
+		tEnum                       enum('a','b')        default                                                     null,
+		tSet                        set('a','b')         default                                                     null
+) CHARSET = latin1 COLLATE = latin1_swedish_ci;
+`
+		tidbRun(t, func(t *myTest) {
+			t.dropTables(n)
+			_, err := t.db.Exec(ddl)
+			require.NoError(t, err)
+			realm := t.loadRealm()
+			require.Len(t, realm.Schemas, 1)
+			ts, ok := realm.Schemas[0].Table(n)
+			require.True(t, ok)
+			expected := schema.Table{
+				Name: n,
+				Attrs: []schema.Attr{
+					&schema.Collation{V: "latin1_swedish_ci"},
+					&schema.Charset{V: "latin1"},
+				},
+				Schema: realm.Schemas[0],
+				Columns: []*schema.Column{
+					{
+						Name:    "tBit",
+						Type:    &schema.ColumnType{Type: &mysql.BitType{T: "bit"}, Raw: "bit(10) unsigned", Null: true},
+						Default: &schema.Literal{V: "b'1000000001'"},
+					},
+					{
+						Name: "tInt",
+						Type: &schema.ColumnType{Type: &schema.IntegerType{T: "int", Unsigned: false},
+							Raw: t.valueByVersion(map[string]string{"8": "int"}, "int(10)"), Null: false},
+						Default: &schema.Literal{V: "4"},
+					},
+					{
+						Name: "tTinyInt",
+						Type: &schema.ColumnType{Type: &schema.IntegerType{T: "tinyint", Unsigned: false},
+							Raw: t.valueByVersion(map[string]string{"8": "tinyint"}, "tinyint(10)"), Null: true},
+						Default: &schema.Literal{V: "8"},
+					},
+					{
+						Name: "tSmallInt",
+						Type: &schema.ColumnType{Type: &schema.IntegerType{T: "smallint", Unsigned: false},
+							Raw: t.valueByVersion(map[string]string{"8": "smallint"}, "smallint(10)"), Null: true},
+						Default: &schema.Literal{V: "2"},
+					},
+					{
+						Name: "tMediumInt",
+						Type: &schema.ColumnType{Type: &schema.IntegerType{T: "mediumint", Unsigned: false},
+							Raw: t.valueByVersion(map[string]string{"8": "mediumint"}, "mediumint(10)"), Null: true},
+						Default: &schema.Literal{V: "11"},
+					},
+					{
+						Name: "tBigInt",
+						Type: &schema.ColumnType{Type: &schema.IntegerType{T: "bigint", Unsigned: false},
+							Raw: t.valueByVersion(map[string]string{"8": "bigint"}, "bigint(10)"), Null: true},
+						Default: &schema.Literal{V: "4"},
+					},
+					{
+						Name: "tDecimal",
+						Type: &schema.ColumnType{Type: &schema.DecimalType{T: "decimal", Precision: 10},
+							Raw: "decimal(10,0)", Null: true},
+						Default: &schema.Literal{V: "4"},
+					},
+					{
+						Name: "tNumeric",
+						Type: &schema.ColumnType{Type: &schema.DecimalType{T: "decimal", Precision: 10},
+							Raw: "decimal(10,0)", Null: false},
+						Default: &schema.Literal{V: "4"},
+					},
+					{
+						Name: "tFloat",
+						Type: &schema.ColumnType{Type: &schema.FloatType{T: "float"},
+							Raw: "float", Null: true},
+						Default: &schema.Literal{V: "4"},
+					},
+					{
+						Name: "tDouble",
+						Type: &schema.ColumnType{Type: &schema.FloatType{T: "double"},
+							Raw: "double", Null: true},
+						Default: &schema.Literal{V: "4"},
+					},
+					{
+						Name: "tReal",
+						Type: &schema.ColumnType{Type: &schema.FloatType{T: "double"},
+							Raw: "double", Null: true},
+						Default: &schema.Literal{V: "4"},
+					},
+					{
+						Name: "tTimestamp",
+						Type: &schema.ColumnType{Type: &schema.TimeType{T: "timestamp"},
+							Raw: "timestamp", Null: true},
+						Default: &schema.RawExpr{
+							X: "CURRENT_TIMESTAMP",
+						},
+					},
+					{
+						Name: "tTimestampFraction",
+						Type: &schema.ColumnType{Type: &schema.TimeType{T: "timestamp", Precision: 6},
+							Raw: "timestamp(6)", Null: true},
+						Default: &schema.RawExpr{
+							X: "CURRENT_TIMESTAMP(6)",
+						},
+					},
+					{
+						Name: "tTimestampOnUpdate",
+						Type: &schema.ColumnType{Type: &schema.TimeType{T: "timestamp"},
+							Raw: "timestamp", Null: true},
+						Default: &schema.RawExpr{
+							X: "CURRENT_TIMESTAMP",
+						},
+						Attrs: []schema.Attr{
+							&mysql.OnUpdate{
+								A: "CURRENT_TIMESTAMP",
+							},
+						},
+					},
+					{
+						Name: "tTimestampFractionOnUpdate",
+						Type: &schema.ColumnType{Type: &schema.TimeType{T: "timestamp", Precision: 6},
+							Raw: "timestamp(6)", Null: true},
+						Default: &schema.RawExpr{
+							X: "CURRENT_TIMESTAMP(6)",
+						},
+						Attrs: []schema.Attr{
+							&mysql.OnUpdate{
+								A: "CURRENT_TIMESTAMP(6)",
+							},
+						},
+					},
+					{
+						Name: "tDate",
+						Type: &schema.ColumnType{Type: &schema.TimeType{T: "date"},
+							Raw: "date", Null: true},
+					},
+					{
+						Name: "tTime",
+						Type: &schema.ColumnType{Type: &schema.TimeType{T: "time"},
+							Raw: "time", Null: true},
+					},
+					{
+						Name: "tDateTime",
+						Type: &schema.ColumnType{Type: &schema.TimeType{T: "datetime"},
+							Raw: "datetime", Null: true},
+					},
+					{
+						Name: "tYear",
+						Type: &schema.ColumnType{Type: &schema.TimeType{T: "year", Precision: t.intByVersion(map[string]int{"8": 0}, 4)},
+							Raw: t.valueByVersion(map[string]string{"8": "year"}, "year(4) unsigned"), Null: true},
+					},
+					{
+						Name: "tVarchar",
+						Type: &schema.ColumnType{Type: &schema.StringType{T: "varchar", Size: 10},
+							Raw: "varchar(10)", Null: true},
+						Default: &schema.Literal{V: t.quoted("Titan")},
+						Attrs: []schema.Attr{
+							&schema.Charset{V: "latin1"},
+							&schema.Collation{V: "latin1_swedish_ci"},
+						},
+					},
+					{
+						Name: "tChar",
+						Type: &schema.ColumnType{Type: &schema.StringType{T: "char", Size: 25},
+							Raw: "char(25)", Null: false},
+						Default: &schema.Literal{V: t.quoted("Olimpia")},
+						Attrs: []schema.Attr{
+							&schema.Charset{V: "latin1"},
+							&schema.Collation{V: "latin1_swedish_ci"},
+						},
+					},
+					{
+						Name: "tVarBinary",
+						Type: &schema.ColumnType{Type: &schema.BinaryType{T: "varbinary", Size: 30},
+							Raw: "varbinary(30)", Null: true},
+						Default: &schema.Literal{V: t.valueByVersion(map[string]string{"8": "0x546974616E"}, t.quoted("Titan"))},
+					},
+					{
+						Name: "tBinary",
+						Type: &schema.ColumnType{Type: &schema.BinaryType{T: "binary", Size: 5},
+							Raw: "binary(5)", Null: true},
+						Default: &schema.Literal{V: t.valueByVersion(map[string]string{"8": "0x546974616E"}, t.quoted("Titan"))},
+					},
+					{
+						Name: "tBlob",
+						Type: &schema.ColumnType{Type: &schema.BinaryType{T: "tinyblob", Size: 0},
+							Raw: "tinyblob", Null: true},
+					},
+					{
+						Name: "tTinyBlob",
+						Type: &schema.ColumnType{Type: &schema.BinaryType{T: "tinyblob", Size: 0},
+							Raw: "tinyblob", Null: true},
+					},
+					{
+						Name: "tMediumBlob",
+						Type: &schema.ColumnType{Type: &schema.BinaryType{T: "mediumblob", Size: 0},
+							Raw: "mediumblob", Null: true},
+					},
+					{
+						Name: "tLongBlob",
+						Type: &schema.ColumnType{Type: &schema.BinaryType{T: "longblob", Size: 0},
+							Raw: "longblob", Null: true},
+					},
+					{
+						Name: "tText",
+						Type: &schema.ColumnType{Type: &schema.StringType{T: "tinytext", Size: 0},
+							Raw: "tinytext", Null: true},
+						Attrs: []schema.Attr{
+							&schema.Charset{V: "latin1"},
+							&schema.Collation{V: "latin1_swedish_ci"},
+						},
+					},
+					{
+						Name: "tTinyText",
+						Type: &schema.ColumnType{Type: &schema.StringType{T: "tinytext", Size: 0},
+							Raw: "tinytext", Null: true},
+						Attrs: []schema.Attr{
+							&schema.Charset{V: "latin1"},
+							&schema.Collation{V: "latin1_swedish_ci"},
+						},
+					},
+					{
+						Name: "tMediumText",
+						Type: &schema.ColumnType{Type: &schema.StringType{T: "mediumtext", Size: 0},
+							Raw: "mediumtext", Null: true},
+						Attrs: []schema.Attr{
+							&schema.Charset{V: "latin1"},
+							&schema.Collation{V: "latin1_swedish_ci"},
+						},
+					},
+					{
+						Name: "tLongText",
+						Type: &schema.ColumnType{Type: &schema.StringType{T: "longtext", Size: 0},
+							Raw: "longtext", Null: true},
+						Attrs: []schema.Attr{
+							&schema.Charset{V: "latin1"},
+							&schema.Collation{V: "latin1_swedish_ci"},
+						},
+					},
+					{
+						Name: "tEnum",
+						Type: &schema.ColumnType{Type: &schema.EnumType{T: "enum", Values: []string{"a", "b"}},
+							Raw: "enum('a','b')", Null: true},
+						Attrs: []schema.Attr{
+							&schema.Charset{V: "latin1"},
+							&schema.Collation{V: "latin1_swedish_ci"},
+						},
+					},
+					{
+						Name: "tSet",
+						Type: &schema.ColumnType{Type: &mysql.SetType{Values: []string{"a", "b"}},
+							Raw: "set('a','b')", Null: true},
+						Attrs: []schema.Attr{
+							&schema.Charset{V: "latin1"},
+							&schema.Collation{V: "latin1_swedish_ci"},
+						},
+					},
+				},
+			}
+			rmCreateStmt(ts)
+			require.EqualValues(t, &expected, ts)
+			t.hclDriftTest(n, realm, expected)
+		})
+	})
+	t.Run("JSON", func(t *testing.T) {
+		ddl := `
+	create table atlas_types_sanity
+	(
+	    tJSON         json          default                   null
+	) CHARSET = latin1 COLLATE latin1_swedish_ci;
+	`
+		tidbRun(t, func(t *myTest) {
+			if t.version == "56" {
+				return
+			}
+			t.dropTables(n)
+			_, err := t.db.Exec(ddl)
+			require.NoError(t, err)
+			realm := t.loadRealm()
+			require.Len(t, realm.Schemas, 1)
+			ts, ok := realm.Schemas[0].Table(n)
+			require.True(t, ok)
+			expected := schema.Table{
+				Name: n,
+				Attrs: func() []schema.Attr {
+					return []schema.Attr{
+						&schema.Collation{V: "latin1_swedish_ci"},
+						&schema.Charset{V: "latin1"},
+					}
+				}(),
+				Schema: realm.Schemas[0],
+				Columns: []*schema.Column{
+					&schema.Column{Name: "tJSON", Type: &schema.ColumnType{Type: &schema.JSONType{T: "json"}, Raw: "json", Null: true}},
+				},
+			}
+			rmCreateStmt(ts)
+			require.EqualValues(t, &expected, ts)
+		})
+	})
+
+	t.Run("ImplicitIndexes", func(t *testing.T) {
+		tidbRun(t, func(t *myTest) {
+			testImplicitIndexes(t, t.db)
+		})
+	})
+}