An open API service indexing awesome lists of open source software.

https://github.com/hakadoriya/ddlctl

ddlctl is a tool to control RDBMS DDLs: output all RDBMS DDLs, generate DDLs from tagged Golang source code, view differences between RDBMS and your DDL, and automate migrations.
https://github.com/hakadoriya/ddlctl

create-table ddl diff go golang migration

Last synced: about 2 months ago
JSON representation

ddlctl is a tool to control RDBMS DDLs: output all RDBMS DDLs, generate DDLs from tagged Golang source code, view differences between RDBMS and your DDL, and automate migrations.

Awesome Lists containing this project

README

          

# [ddlctl](https://github.com/hakadoriya/ddlctl)

`ddlctl` is a tool to control RDBMS DDLs: output all RDBMS DDLs, generate DDLs from tagged Golang source code, view differences between RDBMS and your DDL, and automate migrations.

> [!WARNING]
> This project is experimental. It is operational in the author's environment, but it is not known if it can be operated in other environments without trouble.

[![license](https://img.shields.io/github/license/hakadoriya/ddlctl)](LICENSE)
[![pkg](https://pkg.go.dev/badge/github.com/hakadoriya/ddlctl)](https://pkg.go.dev/github.com/hakadoriya/ddlctl)
[![goreportcard](https://goreportcard.com/badge/github.com/hakadoriya/ddlctl)](https://goreportcard.com/report/github.com/hakadoriya/ddlctl)
[![workflow](https://github.com/hakadoriya/ddlctl/workflows/go-lint/badge.svg)](https://github.com/hakadoriya/ddlctl/tree/main)
[![workflow](https://github.com/hakadoriya/ddlctl/workflows/go-test/badge.svg)](https://github.com/hakadoriya/ddlctl/tree/main)
[![workflow](https://github.com/hakadoriya/ddlctl/workflows/go-vuln/badge.svg)](https://github.com/hakadoriya/ddlctl/tree/main)
[![codecov](https://codecov.io/gh/hakadoriya/ddlctl/graph/badge.svg?token=8Jtk2bpTe2)](https://codecov.io/gh/hakadoriya/ddlctl)
[![sourcegraph](https://sourcegraph.com/github.com/hakadoriya/ddlctl/-/badge.svg)](https://sourcegraph.com/github.com/hakadoriya/ddlctl)

## Demo

![ddlctl_demo](https://github.com/hakadoriya/ddlctl/assets/29125616/cdb603ad-bb44-4e18-8af0-ac5bce46e319)

## Overview

ddlctl can do the following:

- Output all RDBMS DDLs
- Generate DDL from tagged Golang source code
- Output differences between the RDBMS and your DDL
- Automated Migration

## TODO

- `generate` subcommand
- source language
- [x] Support `go` (beta)
- dialect
- [x] Support `mysql` (alpha)
- [x] Support `postgres` (alpha)
- [x] Support `cockroachdb` (alpha)
- [x] Support `spanner` (alpha)
- [ ] Support `sqlite3`
- `show` subcommand
- dialect
- [x] Support `mysql` (beta)
- [x] Support `postgres` (alpha)
- [x] Support `cockroachdb` (beta)
- [x] Support `spanner` (alpha)
- [ ] Support `sqlite3`
- `diff` subcommand
- dialect
- [x] Support `mysql` (alpha)
- [x] Support `postgres` (alpha)
- [x] Support `cockroachdb` (alpha)
- [x] Support `spanner` (alpha)
- [ ] Support `sqlite3`
- `apply` subcommand
- dialect
- [x] Support `mysql` (alpha)
- [x] Support `postgres` (alpha)
- [x] Support `cockroachdb` (alpha)
- [x] Support `spanner` (alpha)
- [ ] Support `sqlite3`

## Example: `ddlctl generate`

### 1. Prepare your annotated model source code

For example, prepare the following Go code:

```go
package sample

// User is a user model struct.
//
//pgddl:table public.users
//pgddl:constraint UNIQUE ("username")
//pgddl:index "index_users_username" ON public.users ("username")
type User struct {
UserID string `db:"user_id" pgddl:"TEXT NOT NULL" pk:"true"`
Username string `db:"username" pgddl:"TEXT NOT NULL"`
Age int `db:"age" pgddl:"INT NOT NULL"`
}

// Group is a group model struct.
//
//pgddl:table CREATE TABLE IF NOT EXISTS public.groups
//pgddl:index CREATE UNIQUE INDEX "index_groups_group_name" ON public.groups ("group_name")
type Group struct {
GroupID string `db:"group_id" pgddl:"TEXT NOT NULL" pk:"true"`
GroupName string `db:"group_name" pgddl:"TEXT NOT NULL"`
Description string `db:"description" pgddl:"TEXT NOT NULL"`
}
```

### 2. Generate DDL

Please execute the ddlctl command as follows:

```console
$ ddlctl generate --dialect postgres --go-column-tag db --go-ddl-tag pgddl --go-pk-tag pk sample.go sample.sql
INFO: 2023/11/16 16:10:39 ddlctl.go:44: source: sample.go
INFO: 2023/11/16 16:10:39 ddlctl.go:73: destination: sample.sql
```

### 3. Check generated DDL file

Please check the contents of the outputted DDL:

```sh
cat sample.sql
```

content:

```sql
-- Code generated by ddlctl. DO NOT EDIT.
--

-- source: docs/sample.go:5
-- User is a user model struct.
--
-- pgddl:table public.users
-- pgddl:constraint UNIQUE ("username")
CREATE TABLE public.users (
"user_id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"age" INT NOT NULL,
PRIMARY KEY ("user_id"),
UNIQUE ("username")
);

-- source: docs/sample.go:7
-- pgddl:index "index_users_username" ON public.users ("username")
CREATE INDEX "index_users_username" ON public.users ("username");

-- source: docs/sample.go:16
-- Group is a group model struct.
--
-- pgddl:table CREATE TABLE IF NOT EXISTS public.groups
CREATE TABLE IF NOT EXISTS public.groups (
"group_id" TEXT NOT NULL,
"group_name" TEXT NOT NULL,
"description" TEXT NOT NULL,
PRIMARY KEY ("group_id")
);

-- source: docs/sample.go:17
-- pgddl:index CREATE UNIQUE INDEX "index_groups_group_name" ON public.groups ("group_name")
CREATE UNIQUE INDEX "index_groups_group_name" ON public.groups ("group_name");
```

## Example: `ddlctl diff` and `ddlctl apply`

### 1. Prepare your DDL

```sh
cat sample.sql
```

content:

```sql
-- Code generated by ddlctl. DO NOT EDIT.
--

-- source: docs/sample.go:5
-- User is a user model struct.
--
-- pgddl:table public.users
-- pgddl:constraint UNIQUE ("username")
CREATE TABLE public.users (
"user_id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"age" INT NOT NULL,
PRIMARY KEY ("user_id"),
UNIQUE ("username")
);

-- source: docs/sample.go:7
-- pgddl:index "index_users_username" ON public.users ("username")
CREATE INDEX "index_users_username" ON public.users ("username");

-- source: docs/sample.go:16
-- Group is a group model struct.
--
-- pgddl:table CREATE TABLE IF NOT EXISTS public.groups
CREATE TABLE IF NOT EXISTS public.groups (
"group_id" TEXT NOT NULL,
"group_name" TEXT NOT NULL,
"description" TEXT NOT NULL,
PRIMARY KEY ("group_id")
);

-- source: docs/sample.go:17
-- pgddl:index CREATE UNIQUE INDEX "index_groups_group_name" ON public.groups ("group_name")
CREATE UNIQUE INDEX "index_groups_group_name" ON public.groups ("group_name");
```

### 2. Check diff from local DDL file to DSN

Please check the differences between the local DDL file and the destination database:

```console
$ ddlctl diff --dialect postgres "postgres://postgres:password@localhost/testdb?sslmode=disable" sample.sql
CREATE TABLE public.users (
"user_id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"age" INT NOT NULL,
CONSTRAINT users_pkey PRIMARY KEY ("user_id"),
CONSTRAINT users_unique_username UNIQUE ("username")
);
CREATE INDEX "index_users_username" ON public.users ("username");
CREATE TABLE IF NOT EXISTS public.groups (
"group_id" TEXT NOT NULL,
"group_name" TEXT NOT NULL,
"description" TEXT NOT NULL,
CONSTRAINT groups_pkey PRIMARY KEY ("group_id")
);
CREATE UNIQUE INDEX "index_groups_group_name" ON public.groups ("group_name");
```

### 3. Apply DDL

```console
$ ddlctl apply --dialect postgres "postgres://postgres:password@localhost/testdb?sslmode=disable" sample.sql --auto-approve

ddlctl will exec the following DDL queries:

-- 8< --

CREATE TABLE public.users (
"user_id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"age" INT NOT NULL,
CONSTRAINT users_pkey PRIMARY KEY ("user_id"),
CONSTRAINT users_unique_username UNIQUE ("username")
);
CREATE INDEX "index_users_username" ON public.users ("username");
CREATE TABLE IF NOT EXISTS public.groups (
"group_id" TEXT NOT NULL,
"group_name" TEXT NOT NULL,
"description" TEXT NOT NULL,
CONSTRAINT groups_pkey PRIMARY KEY ("group_id")
);
CREATE UNIQUE INDEX "index_groups_group_name" ON public.groups ("group_name");

-- >8 --

Do you want to apply these DDL queries?
ddlctl will exec the DDL queries described above.
Only 'yes' will be accepted to approve.

Enter a value: yes (via --auto-approve option)

executing...
done
```

### 4. (Optional) Edit DDL and apply

```diff
-- pgddl:table public.users
-- pgddl:constraint UNIQUE ("username")
CREATE TABLE public.users (
"user_id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"age" INT NOT NULL,
+ "description" TEXT NOT NULL,
PRIMARY KEY ("user_id"),
UNIQUE ("username")
);
```

apply:

```console
$ ddlctl apply --dialect postgres "postgres://postgres:password@localhost/testdb?sslmode=disable" sample.sql --auto-approve

ddlctl will exec the following DDL queries:

-- 8< --

-- -
-- +"description" TEXT NOT NULL
ALTER TABLE public.users ADD COLUMN "description" TEXT NOT NULL;

-- >8 --

Do you want to apply these DDL queries?
ddlctl will exec the DDL queries described above.
Only 'yes' will be accepted to approve.

Enter a value: yes (via --auto-approve option)

executing...
done
```

## Installation

### pre-built binary

```bash
LATEST_VERSION=$(curl -ISs https://github.com/hakadoriya/ddlctl/releases/latest | tr -d '\r' | awk -F/ '/location:/{print $NF}')
OS=$(uname | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
URL="https://github.com/hakadoriya/ddlctl/releases/download/${LATEST_VERSION}/ddlctl_${LATEST_VERSION}_${OS}_${ARCH}.zip"

# Check URL
echo "${URL}"

# Download
curl -fLROSs "${URL}"

# Unzip
unzip -j ddlctl_${LATEST_VERSION}_${OS}_${ARCH}.zip '*/ddlctl'
```

### go install

```bash
go install github.com/hakadoriya/ddlctl/cmd/ddlctl@latest
```

## Usage

### `ddlctl`

```console
$ ddlctl --help
Usage:
ddlctl [options]

Description:
ddlctl is a tool for control RDBMS DDL.

sub commands:
version: show version
generate: generate DDL from source (file or directory) to destination (file or directory).
show: show DDL from DSN like `SHOW CREATE TABLE`.
diff: diff DDL from to .
apply: apply DDL from to .

options:
--trace (env: DDLCTL_TRACE, default: false)
trace mode enabled
--debug (env: DDLCTL_DEBUG, default: false)
debug mode
--help (default: false)
show usage
```

### `ddlctl generate`

```console
$ ddlctl generate --help
Usage:
ddlctl generate [options] --dialect

Description:
generate DDL from source (file or directory) to destination (file or directory).

options:
--lang (env: DDLCTL_LANGUAGE, default: go)
programming language to generate DDL
--dialect (env: DDLCTL_DIALECT, default: )
SQL dialect to generate DDL
--go-column-tag (env: DDLCTL_GO_COLUMN_TAG, default: db)
column annotation key for Go struct tag
--go-ddl-tag (env: DDLCTL_GO_DDL_TAG, default: ddlctl)
DDL annotation key for Go struct tag
--go-pk-tag (env: DDLCTL_GO_PK_TAG, default: pk)
primary key annotation key for Go struct tag
--help (default: false)
show usage
```

### `ddlctl show`

```console
$ ddlctl show --help
Usage:
ddlctl show --dialect

Description:
show DDL from DSN like `SHOW CREATE TABLE`.

options:
--dialect (env: DDLCTL_DIALECT, default: )
SQL dialect to generate DDL
--help (default: false)
show usage
```

### `ddlctl diff`

```console
$ ddlctl diff --help
Usage:
ddlctl diff [options] --dialect

Description:
diff DDL from to .

options:
--lang (env: DDLCTL_LANGUAGE, default: go)
programming language to generate DDL
--dialect (env: DDLCTL_DIALECT, default: )
SQL dialect to generate DDL
--go-column-tag (env: DDLCTL_GO_COLUMN_TAG, default: db)
column annotation key for Go struct tag
--go-ddl-tag (env: DDLCTL_GO_DDL_TAG, default: ddlctl)
DDL annotation key for Go struct tag
--go-pk-tag (env: DDLCTL_GO_PK_TAG, default: pk)
primary key annotation key for Go struct tag
--help (default: false)
show usage
```

### `ddlctl apply`

```console
$ ddlctl apply --help
Usage:
ddlctl apply [options] --dialect

Description:
apply DDL from to .

options:
--lang (env: DDLCTL_LANGUAGE, default: go)
programming language to generate DDL
--dialect (env: DDLCTL_DIALECT, default: )
SQL dialect to generate DDL
--go-column-tag (env: DDLCTL_GO_COLUMN_TAG, default: db)
column annotation key for Go struct tag
--go-ddl-tag (env: DDLCTL_GO_DDL_TAG, default: ddlctl)
DDL annotation key for Go struct tag
--go-pk-tag (env: DDLCTL_GO_PK_TAG, default: pk)
primary key annotation key for Go struct tag
--auto-approve (env: DDLCTL_AUTO_APPROVE, default: false)
auto approve
--help (default: false)
show usage
```