myddlmaker

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 23, 2024 License: MIT Imports: 16 Imported by: 0

README

myddlmaker

Build Status Go Reference

myddlmaker generates DDL (Data Definition Language) from Go structs. It is a fork of kayac/ddl-maker that focuses to use with MySQL.

SYNOPSIS

Firstly, write your table definitions as Go structures. Here is an example: schema.go

package schema

import (
	"time"

	"github.com/shogo82148/myddlmaker"
)

//go:generate go run -tags myddlmaker gen/main.go

type User struct {
	ID        uint64 `ddl:",auto"`
	Name      string
	CreatedAt time.Time
}

func (*User) PrimaryKey() *myddlmaker.PrimaryKey {
	return myddlmaker.NewPrimaryKey("id")
}

Next, configure your DDL maker: gen/main.go

package main

import (
	"log"

	"github.com/shogo82148/myddlmaker"
	schema "github.com/shogo82148/myddlmaker/testdata/example"
)

func main() {
	// create a new DDL maker.
	m, err := myddlmaker.New(&myddlmaker.Config{
		DB: &myddlmaker.DBConfig{
			Engine:  "InnoDB",
			Charset: "utf8mb4",
			Collate: "utf8mb4_bin",
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	m.AddStructs(&schema.User{})

	// generate an SQL file.
	if err := m.GenerateFile(); err != nil {
		log.Fatal(err)
	}

	// generate Go source code for basic SQL operations
	// such as INSERT, SELECT, and UPDATE.
	if err := m.GenerateGoFile(); err != nil {
		log.Fatal(err)
	}
}

Run go generate.

$ go generate ./...

You can get the following SQL queries:

SET foreign_key_checks=0;

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
    `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(191) NOT NULL,
    `created_at` DATETIME(6) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8mb4;

SET foreign_key_checks=1;

And more, the DDL maker generates Go source code for basic SQL operations such as INSERT, SELECT, and UPDATE.

// Code generated by https://github.com/shogo82148/myddlmaker; DO NOT EDIT.

//go:build !myddlmaker

package schema

import (
	"context"
	"database/sql"
)

type execer interface {
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
}

type queryer interface {
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
	PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
}

func InsertUser(ctx context.Context, execer execer, values ...*User) error {
	const q = "INSERT INTO `user` (`name`, `created_at`) VALUES (?, ?)"
    // (snip)
	return nil
}

func SelectUser(ctx context.Context, queryer queryer, primaryKeys *User) (*User, error) {
	var v User
	row := queryer.QueryRowContext(ctx, "SELECT `id`, `name`, `created_at` FROM `user` WHERE `id` = ?", primaryKeys.ID)
	if err := row.Scan(&v.ID, &v.Name, &v.CreatedAt); err != nil {
		return nil, err
	}
	return &v, nil
}

func SelectAllUser(ctx context.Context, queryer queryer) ([]*User, error) {
	var ret []*User
	rows, err := queryer.QueryContext(ctx, "SELECT `id`, `name`, `created_at` FROM `user` ORDER BY `id`")
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		var v User
		if err := rows.Scan(&v.ID, &v.Name, &v.CreatedAt); err != nil {
			return nil, err
		}
		ret = append(ret, &v)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return ret, nil
}

func UpdateUser(ctx context.Context, execer execer, values ...*User) error {
	stmt, err := execer.PrepareContext(ctx, "UPDATE `user` SET `name` = ?, `created_at` = ? WHERE `id` = ?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	for _, value := range values {
		if _, err := stmt.ExecContext(ctx, value.Name, value.CreatedAt, value.ID); err != nil {
			return err
		}
	}
	return nil
}

You can use these generated functions in your application.

db, _ := sql.Open("mysql", "user:password@/dbname")

// INSERT INTO `user` (`name`, `created_at`) VALUES ("Alice", NOW());
schema.InsertUser(context.TODO(), db, &schema.User{
	Name:      "Alice",
	CreatedAt: time.Now(),
})

// SELECT * FROM `user` WHERE `id` = 1;
user, err := schema.SelectUser(context.TODO(), db, &schema.User{
	ID: 1,
})

// UPDATE `user` SET `name` = "Bob", `created_at` = NOW() WHERE `id` = 1;
schema.UpdateUser(context.TODO(), db, &schema.User{
	ID: 1,
	Name: "Bob",
	CreatedAt: time.Now(),
})

MySQL Types and Go Types

Golang Type MySQL Column
int8 TINYINT
int16 SMALLINT
int32 INTEGER
int64, sql.NullInt64 BIGINT
uint8, sql.NullByte TINYINT UNSIGNED
uint16 SMALLINT UNSIGNED
uint32 INTEGER UNSIGNED
uint64 BIGINT UNSIGNED
float32 FLOAT
float64, sql.NullFloat64 DOUBLE
string, sql.NullString VARCHAR
bool, sql.NullBool TINYINT(1)
[]byte VARBINARY
[N]byte BINARY(N)
time.Time, sql.NullTime DATETIME(6)
json.RawMessage JSON
sql.Null[T] Corresponding MySQL type to T

Go Struct Tag Options

Tag Value SQL Fragment
null NULL (default: NOT NULL)
auto AUTO INCREMENT
invisible INVISIBLE
unsigned UNSIGNED
size=<size> VARCHAR(<size>), DATETIME(<size>), etc.
type=<type> override field type
srid=<srid> override SRID
default=<value> DEFAULT <value>
charset=<charset> CHARACTER SET <charset>
collate=<collate> COLLATE <collate>
comment=<comment> COMMENT <comment>
Change Column Name

According to the naming conventions of Golang, acronyms formed by concatenating initial letters (e.g., HTTP for Hyper Text Transfer Protocol) are written entirely in uppercase. When defining table column names according to this convention, it may result in undesirable column names. For instance, by default, the variable NameJP generates the column name name_j_p.

To circumvent this issue, you can specify column names in the struct tags.

type User struct {
	ID        uint64 `ddl:",auto"`
	// By default, the `name` column will be generated.
	// If you want to specify a column name explicitly (e.g. `user_name`),
	// you write the column name immediately after the "ddl:" tag.
	Name      string `ddl:"user_name,auto"`
}

Primary Index

Implement the PrimaryKey method to define the primary index.

func (*User) PrimaryKey() *myddlmaker.PrimaryKey {
    // PRIMARY KEY (`id1`, `id2`)
    return myddlmaker.NewPrimaryKey("id1", "id2")
}

Indexes

Implement the Indexes method to define the indexes.

func (*User) Indexes() []*myddlmaker.Index {
    return []*myddlmaker.Index{
        // INDEX `idx_name` (`name`)
        myddlmaker.NewIndex("idx_name", "name"),

        // INDEX `idx_name` (`name`) COMMENT 'some comment'
        myddlmaker.NewIndex("idx_name", "name").Comment("some comment"),

        // INDEX `idx_name` (`name`) INVISIBLE
        myddlmaker.NewIndex("idx_name", "name").Invisible(),
    }
}

Unique Indexes

Implement the UniqueIndexes method to define the unique indexes.

func (*User) UniqueIndexes() []*myddlmaker.UniqueIndex {
    return []*myddlmaker.UniqueIndex{
        // UNIQUE INDEX `idx_name` (`name`)
        myddlmaker.NewUniqueIndex("idx_name", "name"),

        // UNIQUE INDEX `idx_name` (`name`) COMMENT 'some comment'
        myddlmaker.NewUniqueIndex("idx_name", "name").Comment("some comment"),

        // UNIQUE INDEX `idx_name` (`name`) INVISIBLE
        myddlmaker.NewUniqueIndex("idx_name", "name").Invisible(),
    }
}

Foreign Key Constraints

Implement the ForeignKeys method to define the foreign key constraints.

func (*User) ForeignKeys() []*myddlmaker.ForeignKey {
    return []*myddlmaker.ForeignKey{
        // CONSTRAINT `name_of_constraint`
        //     FOREIGN KEY (`column1`, `column2`)
        //     REFERENCES `another_table` (`id1`, `id2`)
        myddlmaker.NewForeignKey(
            "name_of_constraint",
            []string{"column1", "column2"},
            "another_table",
            []string{"id1", "id2"},
        ),

        // CONSTRAINT `name_of_constraint`
        //     FOREIGN KEY (`column1`, `column2`)
        //     REFERENCES `another_table` (`id1`, `id2`)
        //     ON DELETE CASCADE
        myddlmaker.NewForeignKey(
            "name_of_constraint",
            []string{"column1", "column2"},
            "another_table",
            []string{"id1", "id2"},
        ).OnDelete(myddlmaker.ForeignKeyOptionCascade),
    }
}

Spatial Indexes

Implement the SpatialIndexes method to define the spatial indexes.

func (*User) SpatialIndexes() []*myddlmaker.SpatialIndex {
    return []*myddlmaker.SpatialIndex{
        // SPATIAL INDEX `idx_name` (`name`)
        myddlmaker.NewSpatialIndex("idx_name", "name"),

        // SPATIAL INDEX `idx_name` (`name`) COMMENT 'some comment'
        myddlmaker.NewSpatialIndex("idx_name", "name").Comment("some comment"),

        // SPATIAL INDEX `idx_name` (`name`) INVISIBLE
        myddlmaker.NewSpatialIndex("idx_name", "name").Invisible(),
    }
}

Full Text Indexes

Implement the FullTextIndexes method to define the full-text indexes.

func (*User) FullTextIndexes() []*myddlmaker.FullTextIndex {
    return []*myddlmaker.FullTextIndex{
        // FULLTEXT INDEX `idx_name` (`name`)
        myddlmaker.NewFullTextIndex("idx_name", "name"),

        // FULLTEXT INDEX `idx_name` (`name`) COMMENT 'some comment'
        myddlmaker.NewFullTextIndex("idx_name", "name").Comment("some comment"),

        // FULLTEXT INDEX `idx_name` (`name`) INVISIBLE
        myddlmaker.NewFullTextIndex("idx_name", "name").Invisible(),
    }
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// StructTagName is the key name of the tag string.
	StructTagName = "ddl"

	// IgnoreName is the string that myddlmaker ignores.
	IgnoreName = "-"
)

Functions

This section is empty.

Types

type Config

type Config struct {
	DB *DBConfig

	// OutFilePath is a file path for SQL generated by the DDL Maker.
	// If it is empty, "schema.sql" is used.
	OutFilePath string

	// OutGoFilePath is a file path for Go source code generated by the DDL Maker.
	// If it is empty, "schema_gen.go" is used.
	OutGoFilePath string

	// PackageName is a package name for Go source code generated by the DDL Maker.
	// If it is empty, "schema" is used.
	PackageName string

	// Tag is a build constraint tag for Go source code generated by the DDL Maker.
	// If it is empty, "myddlmaker" is used.
	Tag string

	// SkipValidationFKIndex disables index validation for foreign key constraints.
	SkipValidationFKIndex bool
}

Config is a configuration of the DDL Maker.

type DBConfig

type DBConfig struct {
	// Engine is the default database engine for creating tables.
	Engine string

	// Charset is the default character set for creating tables.
	Charset string

	// Collate is the default character collate for creating tables.
	Collate string
}

type ForeignKey

type ForeignKey struct {
	// contains filtered or unexported fields
}

ForeignKey is a foreign key constraint. Implement the ForeignKeys method to define the foreign key constraints.

func (*User) ForeignKeys() []*myddlmaker.ForeignKey {
	return []*myddlmaker.ForeignKey{
		// CONSTRAINT `name_of_constraint`
		//     FOREIGN KEY (`column1`, `column2`)
		//     REFERENCES `another_table` (`id1`, `id2`)
		myddlmaker.NewForeignKey(
			"name_of_constraint",
			[]string{"column1", "column2"},
			"another_table",
			[]string{"id1", "id2"},
		),
	}
}

func NewForeignKey

func NewForeignKey(name string, columns []string, table string, references []string) *ForeignKey

NewForeignKey returns a new foreign key constraint.

func (*ForeignKey) OnDelete

func (fk *ForeignKey) OnDelete(opt ForeignKeyOption) *ForeignKey

OnDelete returns a copy of fk with the referential action option opt specified by ON DELETE cause.

func (*ForeignKey) OnUpdate

func (fk *ForeignKey) OnUpdate(opt ForeignKeyOption) *ForeignKey

OnUpdate returns a copy of fk with the referential action option opt specified by ON UPDATE cause.

type ForeignKeyOption

type ForeignKeyOption string

ForeignKeyOption is an option of a referential action.

const (
	// ForeignKeyOptionCascade deletes or updates the row from the parent table
	// and automatically delete or update the matching rows in the child table.
	ForeignKeyOptionCascade ForeignKeyOption = "CASCADE"

	// ForeignKeyOptionSetNull deletes or updates the row from the parent table
	// and set the foreign key column or columns in the child table to NULL.
	ForeignKeyOptionSetNull ForeignKeyOption = "SET NULL"

	// ForeignKeyOptionRestrict rejects the delete or update operation for the parent table.
	ForeignKeyOptionRestrict ForeignKeyOption = "RESTRICT"
)

type FullTextIndex

type FullTextIndex struct {
	// contains filtered or unexported fields
}

FullTextIndex is a full text index. https://dev.mysql.com/doc/refman/8.0/en/innodb-fulltext-index.html Implement the `FullTextIndexes` method to define the full-text indexes.

func (*User) FullTextIndexes() []*myddlmaker.FullTextIndex {
	return []*myddlmaker.FullTextIndex{
		// FULLTEXT INDEX `idx_name` (`name`)
		myddlmaker.NewFullTextIndex("idx_name", "name"),
	}
}

func NewFullTextIndex

func NewFullTextIndex(name string, column string) *FullTextIndex

NewFullTextIndex returns a new full text index.

func (*FullTextIndex) Comment

func (idx *FullTextIndex) Comment(comment string) *FullTextIndex

Comment returns a copy of idx with the comment.

func (*FullTextIndex) Invisible

func (idx *FullTextIndex) Invisible() *FullTextIndex

Invisible returns a copy of idx, but it is invisible from MySQL planner.

func (*FullTextIndex) WithParser

func (idx *FullTextIndex) WithParser(parser string) *FullTextIndex

WithParser returns a copy of idx with the full-text plugin.

type Index

type Index struct {
	// contains filtered or unexported fields
}

Index is an index of a table. Implement the Indexes method to define the indexes.

func (*User) Indexes() []*myddlmaker.Index {
    return []*myddlmaker.Index{
        // INDEX `idx_name` (`name`)
        myddlmaker.NewIndex("idx_name", "name"),
    }
}

func NewIndex

func NewIndex(name string, col ...string) *Index

NewIndex returns a new index.

func (*Index) Comment

func (idx *Index) Comment(comment string) *Index

Comment returns a copy of idx with the comment.

func (*Index) Invisible

func (idx *Index) Invisible() *Index

Invisible returns a copy of idx, but it is invisible from MySQL planner.

type JSON added in v0.0.5

type JSON[T any] [1]T

JSON[T] represents a MySQL JSON type tied to Go types. It is EXPERIMENTAL feature.

func (JSON[T]) Get added in v0.0.5

func (v JSON[T]) Get() T

Get returns the value of v.

func (*JSON[T]) Scan added in v0.0.5

func (v *JSON[T]) Scan(src any) error

Scan implements database/sql.Scanner interface.

func (*JSON[T]) Set added in v0.0.5

func (v *JSON[T]) Set(u T)

Set sets v = u.

func (JSON[T]) Value added in v0.0.5

func (v JSON[T]) Value() (driver.Value, error)

Value implements database/sql/driver.Valuer interface.

type Maker

type Maker struct {
	// contains filtered or unexported fields
}

func New

func New(config *Config) (*Maker, error)

func (*Maker) AddStructs

func (m *Maker) AddStructs(structs ...any)

func (*Maker) Generate

func (m *Maker) Generate(w io.Writer) error

func (*Maker) GenerateFile

func (m *Maker) GenerateFile() error

GenerateFile opens

func (*Maker) GenerateGo

func (m *Maker) GenerateGo(w io.Writer) error

func (*Maker) GenerateGoFile

func (m *Maker) GenerateGoFile() error

type PrimaryKey

type PrimaryKey struct {
	// contains filtered or unexported fields
}

func NewPrimaryKey

func NewPrimaryKey(field ...string) *PrimaryKey

type SpatialIndex

type SpatialIndex struct {
	// contains filtered or unexported fields
}

SpatialIndex is a spatial index. Implement the SpatialIndexes method to define the spatial indexes.

func (*User) SpatialIndexes() []*myddlmaker.SpatialIndex {
	return []*myddlmaker.SpatialIndex{
		// SPATIAL INDEX `idx_name` (`name`)
		myddlmaker.NewSpatialIndex("idx_name", "name"),
	}
}

func NewSpatialIndex

func NewSpatialIndex(name string, column string) *SpatialIndex

NewSpatialIndex returns a new spatial index.

func (*SpatialIndex) Comment

func (idx *SpatialIndex) Comment(comment string) *SpatialIndex

Comment returns a copy of idx with the comment.

func (*SpatialIndex) Invisible

func (idx *SpatialIndex) Invisible() *SpatialIndex

Invisible returns a copy of idx, but it is invisible from MySQL planner.

type Table

type Table interface {
	Table() string
}

Table is used for customizing the table name. It is an optional interface that may be implemented by a table.

// it generates CREATE TABLE `users` ...
func (*User) Table() string {
    return "users"
}

type TableComment added in v0.0.8

type TableComment interface {
	TableComment() string
}

TableComment is used for customizing the table comment. It is an optional interface that may be implemented by a table.

// it generates CREATE TABLE `user` ( ... ) COMMENT='some comments'
func (*User) TableComment() string {
    return "some comments"
}

type UniqueIndex

type UniqueIndex struct {
	// contains filtered or unexported fields
}

UniqueIndex is a unique index of a table. Implement the UniqueIndexes method to define the unique indexes.

func (*User) UniqueIndexes() []*myddlmaker.Index {
	return []*myddlmaker.Index{
		// UNIQUE INDEX `idx_name` (`name`)
		myddlmaker.NewUniqueIndex("idx_name", "name"),
	}
}

func NewUniqueIndex

func NewUniqueIndex(name string, col ...string) *UniqueIndex

NewUniqueIndex returns a new unique index.

func (*UniqueIndex) Comment

func (idx *UniqueIndex) Comment(comment string) *UniqueIndex

Comment returns a copy of idx with the comment.

func (*UniqueIndex) Invisible

func (idx *UniqueIndex) Invisible() *UniqueIndex

Invisible returns a copy of idx, but it is invisible from MySQL planner.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL