Skip to content

Commit

Permalink
Merge pull request #25 from kanmu/michiomochi/replace-to-withstack
Browse files Browse the repository at this point in the history
Replace errors.Wrap to errors.WithStack
  • Loading branch information
michiomochi authored Feb 27, 2024
2 parents 7d2e598 + 71537b9 commit 54c3d1b
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 42 deletions.
15 changes: 6 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,19 @@

`dgw` generates Golang struct, and simple Table/Row Data Gateway functions from PostgreSQL table metadata. Heavily inspired by [xo](https://github.com/knq/xo).


## Why created

Personally, I prefer Table/Row Data Gateway over ORM/Query Builder approach when using Go with RDBMS. However, it is very time consuming, tedious, and error-prone to write a lot of columns, query place holders, and struct fields that all have to be exactly in order even for a simple select/insert statement. `dgw` generate Go struct, and simple functions to get/create row from PostgreSQL table definitions to avoid manually writing simple but tedious SQL.

- `dgw` can properly detect autogenerated column (e.g. serial, bigserial), and composit primary key to create appropriate SQL.
- `dgw` has ability to easily customize PostgreSQL column type <-> Go type mapping using toml config file.


## Installation

```
brew install kanmu/tools/dgw
```


## How to use

```
Expand Down Expand Up @@ -108,7 +105,7 @@ func (r *T1) Create(db Queryer) error {
`INSERT INTO t1 (i, str, num_float, nullable_str, t_with_tz, t_without_tz, nullable_tz, json_data, xml_data) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id`,
&r.I, &r.Str, &r.NumFloat, &r.NullableStr, &r.TWithTz, &r.TWithoutTz, &r.NullableTz, &r.JSONData, &r.XMLData).Scan(&r.ID)
if err != nil {
return errors.Wrap(err, "failed to insert t1")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -120,7 +117,7 @@ func GetT1ByPk(db Queryer, pk0 int64) (*T1, error) {
`SELECT id, i, str, num_float, nullable_str, t_with_tz, t_without_tz, nullable_tz, json_data, xml_data FROM t1 WHERE id = $1`,
pk0).Scan(&r.ID, &r.I, &r.Str, &r.NumFloat, &r.NullableStr, &r.TWithTz, &r.TWithoutTz, &r.NullableTz, &r.JSONData, &r.XMLData)
if err != nil {
return nil, errors.Wrap(err, "failed to select t1")
return nil, errors.WithStack(err)
}
return &r, nil
}
Expand All @@ -140,7 +137,7 @@ func (r *T2) Create(db Queryer) error {
`INSERT INTO t2 (str, t_with_tz, t_without_tz) VALUES ($1, $2, $3) RETURNING id, i`,
&r.Str, &r.TWithTz, &r.TWithoutTz).Scan(&r.ID, &r.I)
if err != nil {
return errors.Wrap(err, "failed to insert t2")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -152,7 +149,7 @@ func GetT2ByPk(db Queryer, pk0 int64, pk1 int) (*T2, error) {
`SELECT id, i, str, t_with_tz, t_without_tz FROM t2 WHERE id = $1 AND i = $2`,
pk0, pk1).Scan(&r.ID, &r.I, &r.Str, &r.TWithTz, &r.TWithoutTz)
if err != nil {
return nil, errors.Wrap(err, "failed to select t2")
return nil, errors.WithStack(err)
}
return &r, nil
}
Expand All @@ -169,7 +166,7 @@ func (r *T3) Create(db Queryer) error {
`INSERT INTO t3 (id, i) VALUES ($1, $2)`,
&r.ID, &r.I)
if err != nil {
return errors.Wrap(err, "failed to insert t3")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -181,7 +178,7 @@ func GetT3ByPk(db Queryer, pk0 int, pk1 int) (*T3, error) {
`SELECT id, i FROM t3 WHERE id = $1 AND i = $2`,
pk0, pk1).Scan(&r.ID, &r.I)
if err != nil {
return nil, errors.Wrap(err, "failed to select t3")
return nil, errors.WithStack(err)
}
return &r, nil
}
Expand Down
32 changes: 16 additions & 16 deletions dgw.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type Queryer interface {
func OpenDB(connStr string) (*sql.DB, error) {
conn, err := sql.Open("postgres", connStr)
if err != nil {
return nil, errors.Wrap(err, "failed to connect to database")
return nil, errors.WithStack(err)
}
return conn, nil
}
Expand Down Expand Up @@ -180,7 +180,7 @@ type StructField struct {
func PgLoadTypeMapFromFile(filePath string) (*PgTypeMapConfig, error) {
var conf PgTypeMapConfig
if _, err := toml.DecodeFile(filePath, &conf); err != nil {
return nil, errors.Wrap(err, "faild to parse config file")
return nil, errors.WithStack(err)
}
return &conf, nil
}
Expand All @@ -189,7 +189,7 @@ func PgLoadTypeMapFromFile(filePath string) (*PgTypeMapConfig, error) {
func PgLoadColumnDef(db Queryer, schema string, table string) ([]*PgColumn, error) {
colDefs, err := db.Query(pgLoadColumnDef, schema, table)
if err != nil {
return nil, errors.Wrap(err, "failed to load table def")
return nil, errors.WithStack(err)
}

cols := []*PgColumn{}
Expand All @@ -205,7 +205,7 @@ func PgLoadColumnDef(db Queryer, schema string, table string) ([]*PgColumn, erro
&c.DDLType,
)
if err != nil {
return nil, errors.Wrap(err, "failed to scan")
return nil, errors.WithStack(err)
}

// Some data types have an extra part e.g, "character varying(16)" and
Expand All @@ -223,7 +223,7 @@ func PgLoadColumnDef(db Queryer, schema string, table string) ([]*PgColumn, erro
func PgLoadTableDef(db Queryer, schema string) ([]*PgTable, error) {
tbDefs, err := db.Query(pgLoadTableDef, schema)
if err != nil {
return nil, errors.Wrap(err, "failed to load table def")
return nil, errors.WithStack(err)
}
tbs := []*PgTable{}
for tbDefs.Next() {
Expand All @@ -233,7 +233,7 @@ func PgLoadTableDef(db Queryer, schema string) ([]*PgTable, error) {
&t.Name,
)
if err != nil {
return nil, errors.Wrap(err, "failed to scan")
return nil, errors.WithStack(err)
}
cols, err := PgLoadColumnDef(db, schema, t.Name)
if err != nil {
Expand Down Expand Up @@ -291,7 +291,7 @@ func PgTableToStruct(t *PgTable, typeCfg *PgTypeMapConfig, keyConfig *AutoKeyMap
for _, c := range t.Columns {
f, err := PgColToField(c, typeCfg)
if err != nil {
return nil, errors.Wrap(err, "faield to convert col to field")
return nil, errors.WithStack(err)
}
fs = append(fs, f)
}
Expand All @@ -304,11 +304,11 @@ func PgExecuteDefaultTmpl(st *StructTmpl, path string) ([]byte, error) {
var src []byte
d, err := Asset(path)
if err != nil {
return src, errors.Wrap(err, "failed to load asset")
return src, errors.WithStack(err)
}
tpl, err := template.New("struct").Funcs(tmplFuncMap).Parse(string(d))
if err != nil {
return src, errors.Wrap(err, "failed to parse template")
return src, errors.WithStack(err)
}
buf := new(bytes.Buffer)
if err := tpl.Execute(buf, st); err != nil {
Expand All @@ -326,7 +326,7 @@ func PgExecuteCustomTmpl(st *StructTmpl, customTmpl string) ([]byte, error) {
var src []byte
tpl, err := template.New("struct").Funcs(tmplFuncMap).Parse(customTmpl)
if err != nil {
return src, errors.Wrap(err, "failed to parse template")
return src, errors.WithStack(err)
}
buf := new(bytes.Buffer)
if err := tpl.Execute(buf, st); err != nil {
Expand All @@ -348,12 +348,12 @@ func PgCreateStruct(

tbls, err := PgLoadTableDef(db, schema)
if err != nil {
return src, errors.Wrap(err, "faield to load table definitions")
return src, errors.WithStack(err)
}
cfg := &PgTypeMapConfig{}
if typeMapPath == "" {
if _, err := toml.Decode(typeMap, cfg); err != nil {
return src, errors.Wrap(err, "faield to read type map")
return src, errors.WithStack(err)
}
} else {
if _, err := toml.DecodeFile(typeMapPath, cfg); err != nil {
Expand All @@ -366,7 +366,7 @@ func PgCreateStruct(
}
st, err := PgTableToStruct(tbl, cfg, autoGenKeyCfg)
if err != nil {
return src, errors.Wrap(err, "faield to convert table definition to struct")
return src, errors.WithStack(err)
}
if customTmpl != "" {
tmpl, err := ioutil.ReadFile(customTmpl)
Expand All @@ -375,17 +375,17 @@ func PgCreateStruct(
}
s, err := PgExecuteCustomTmpl(&StructTmpl{Struct: st}, string(tmpl))
if err != nil {
return nil, errors.Wrap(err, "PgExecuteCustomTmpl failed")
return nil, errors.WithStack(err)
}
src = append(src, s...)
} else {
s, err := PgExecuteDefaultTmpl(&StructTmpl{Struct: st}, "template/struct.tmpl")
if err != nil {
return src, errors.Wrap(err, "faield to execute template")
return src, errors.WithStack(err)
}
m, err := PgExecuteDefaultTmpl(&StructTmpl{Struct: st}, "template/method.tmpl")
if err != nil {
return src, errors.Wrap(err, "faield to execute template")
return src, errors.WithStack(err)
}
src = append(src, s...)
src = append(src, m...)
Expand Down
2 changes: 1 addition & 1 deletion example/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
func OpenDB(connStr string) (*sql.DB, error) {
conn, err := sql.Open("postgres", connStr)
if err != nil {
return nil, errors.Wrap(err, "failed to connect to database")
return nil, errors.WithStack(err)
}
return conn, nil
}
28 changes: 14 additions & 14 deletions example/defaultstruct.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (r *T1) Create(db Queryer) error {
`INSERT INTO t1 (i, str, num_float, nullable_str, t_with_tz, t_without_tz, nullable_tz, json_data, xml_data) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id`,
&r.I, &r.Str, &r.NumFloat, &r.NullableStr, &r.TWithTz, &r.TWithoutTz, &r.NullableTz, &r.JSONData, &r.XMLData).Scan(&r.ID)
if err != nil {
return errors.Wrap(err, "failed to insert t1")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -39,7 +39,7 @@ func GetT1ByPk(db Queryer, pk0 int64) (*T1, error) {
`SELECT id, i, str, num_float, nullable_str, t_with_tz, t_without_tz, nullable_tz, json_data, xml_data FROM t1 WHERE id = $1`,
pk0).Scan(&r.ID, &r.I, &r.Str, &r.NumFloat, &r.NullableStr, &r.TWithTz, &r.TWithoutTz, &r.NullableTz, &r.JSONData, &r.XMLData)
if err != nil {
return nil, errors.Wrap(err, "failed to select t1")
return nil, errors.WithStack(err)
}
return &r, nil
}
Expand All @@ -59,7 +59,7 @@ func (r *T2) Create(db Queryer) error {
`INSERT INTO t2 (str, t_with_tz, t_without_tz) VALUES ($1, $2, $3) RETURNING id, i`,
&r.Str, &r.TWithTz, &r.TWithoutTz).Scan(&r.ID, &r.I)
if err != nil {
return errors.Wrap(err, "failed to insert t2")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -71,7 +71,7 @@ func GetT2ByPk(db Queryer, pk0 int64, pk1 int) (*T2, error) {
`SELECT id, i, str, t_with_tz, t_without_tz FROM t2 WHERE id = $1 AND i = $2`,
pk0, pk1).Scan(&r.ID, &r.I, &r.Str, &r.TWithTz, &r.TWithoutTz)
if err != nil {
return nil, errors.Wrap(err, "failed to select t2")
return nil, errors.WithStack(err)
}
return &r, nil
}
Expand All @@ -88,7 +88,7 @@ func (r *T3) Create(db Queryer) error {
`INSERT INTO t3 (id, i) VALUES ($1, $2)`,
&r.ID, &r.I)
if err != nil {
return errors.Wrap(err, "failed to insert t3")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -100,7 +100,7 @@ func GetT3ByPk(db Queryer, pk0 int, pk1 int) (*T3, error) {
`SELECT id, i FROM t3 WHERE id = $1 AND i = $2`,
pk0, pk1).Scan(&r.ID, &r.I)
if err != nil {
return nil, errors.Wrap(err, "failed to select t3")
return nil, errors.WithStack(err)
}
return &r, nil
}
Expand All @@ -117,7 +117,7 @@ func (r *T4) Create(db Queryer) error {
`INSERT INTO t4 (id, i) VALUES ($1, $2)`,
&r.ID, &r.I)
if err != nil {
return errors.Wrap(err, "failed to insert t4")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -129,7 +129,7 @@ func GetT4ByPk(db Queryer, pk0 int, pk1 int) (*T4, error) {
`SELECT id, i FROM t4 WHERE id = $1 AND i = $2`,
pk0, pk1).Scan(&r.ID, &r.I)
if err != nil {
return nil, errors.Wrap(err, "failed to select t4")
return nil, errors.WithStack(err)
}
return &r, nil
}
Expand All @@ -148,7 +148,7 @@ func (r *UserAccount) Create(db Queryer) error {
`INSERT INTO user_account (email, last_name, first_name) VALUES ($1, $2, $3) RETURNING id`,
&r.Email, &r.LastName, &r.FirstName).Scan(&r.ID)
if err != nil {
return errors.Wrap(err, "failed to insert user_account")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -160,7 +160,7 @@ func GetUserAccountByPk(db Queryer, pk0 int64) (*UserAccount, error) {
`SELECT id, email, last_name, first_name FROM user_account WHERE id = $1`,
pk0).Scan(&r.ID, &r.Email, &r.LastName, &r.FirstName)
if err != nil {
return nil, errors.Wrap(err, "failed to select user_account")
return nil, errors.WithStack(err)
}
return &r, nil
}
Expand All @@ -179,7 +179,7 @@ func (r *UserAccountCompositePk) Create(db Queryer) error {
`INSERT INTO user_account_composite_pk (id, email, last_name, first_name) VALUES ($1, $2, $3, $4)`,
&r.ID, &r.Email, &r.LastName, &r.FirstName)
if err != nil {
return errors.Wrap(err, "failed to insert user_account_composite_pk")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -191,7 +191,7 @@ func GetUserAccountCompositePkByPk(db Queryer, pk0 int64, pk1 string) (*UserAcco
`SELECT id, email, last_name, first_name FROM user_account_composite_pk WHERE id = $1 AND email = $2`,
pk0, pk1).Scan(&r.ID, &r.Email, &r.LastName, &r.FirstName)
if err != nil {
return nil, errors.Wrap(err, "failed to select user_account_composite_pk")
return nil, errors.WithStack(err)
}
return &r, nil
}
Expand All @@ -210,7 +210,7 @@ func (r *UserAccountUUID) Create(db Queryer) error {
`INSERT INTO user_account_uuid (email, last_name, first_name) VALUES ($1, $2, $3) RETURNING uuid`,
&r.Email, &r.LastName, &r.FirstName).Scan(&r.UUID)
if err != nil {
return errors.Wrap(err, "failed to insert user_account_uuid")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -222,7 +222,7 @@ func GetUserAccountUUIDByPk(db Queryer, pk0 string) (*UserAccountUUID, error) {
`SELECT uuid, email, last_name, first_name FROM user_account_uuid WHERE uuid = $1`,
pk0).Scan(&r.UUID, &r.Email, &r.LastName, &r.FirstName)
if err != nil {
return nil, errors.Wrap(err, "failed to select user_account_uuid")
return nil, errors.WithStack(err)
}
return &r, nil
}
4 changes: 2 additions & 2 deletions template/method.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func (r *{{ .Struct.Name }}) CreateContext(ctx context.Context, db Queryer) erro
{{ createInsertParams .Struct }})
{{- end }}
if err != nil {
return errors.Wrap(err, "failed to insert {{ .Struct.Table.Name }}")
return errors.WithStack(err)
}
return nil
}
Expand All @@ -32,7 +32,7 @@ func Get{{ .Struct.Name }}ByPkContext(ctx context.Context, db Queryer, {{ create
`{{ createSelectByPkSQL .Struct }}`,
{{ createSelectByPkSQLParams .Struct }}).Scan({{ createSelectByPkScan .Struct }})
if err != nil {
return nil, errors.Wrap(err, "failed to select {{ .Struct.Table.Name }}")
return nil, errors.WithStack(err)
}
return &r, nil
}

0 comments on commit 54c3d1b

Please sign in to comment.