Initial commit
This commit is contained in:
commit
33bc3cc53f
6 changed files with 803 additions and 0 deletions
280
db.gotmpl
Normal file
280
db.gotmpl
Normal file
|
@ -0,0 +1,280 @@
|
|||
// Code generated by gnorm, DO NOT EDIT!
|
||||
|
||||
package {{.Params.RootPkg}}
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DB is the common interface for database operations.
|
||||
//
|
||||
// This should work with database/sql.DB and database/sql.Tx.
|
||||
type DB interface {
|
||||
Exec(string, ...interface{}) (sql.Result, error)
|
||||
Query(string, ...interface{}) (*sql.Rows, error)
|
||||
QueryRow(string, ...interface{}) *sql.Row
|
||||
}
|
||||
|
||||
// Bytes is an wrapper for []byte for storing bytes in postgres
|
||||
type Bytes []byte
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (b Bytes) Value() (driver.Value, error) {
|
||||
return []byte(b), nil
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (b *Bytes) Scan(value interface{}) error {
|
||||
bytes, ok := value.([]byte)
|
||||
if !ok {
|
||||
return errors.New("Type assertion .([]byte) failed")
|
||||
}
|
||||
|
||||
*b = Bytes(bytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NullBytes is an wrapper for []byte for storing bytes in postgres
|
||||
type NullBytes struct {
|
||||
Bytes []byte
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (nb NullBytes) Value() (driver.Value, error) {
|
||||
if !nb.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return nb.Bytes, nil
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (nb *NullBytes) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
nb.Bytes, nb.Valid = []byte(""), false
|
||||
return nil
|
||||
}
|
||||
nb.Valid = true
|
||||
|
||||
var ok bool
|
||||
nb.Bytes, ok = value.([]byte)
|
||||
if !ok {
|
||||
return errors.New("Type assertion .([]byte) failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Jsonb is a wrapper for map[string]interface{} for storing json into postgres
|
||||
type Jsonb map[string]interface{}
|
||||
|
||||
// Value marshals the json into the database
|
||||
func (j Jsonb) Value() (driver.Value, error) {
|
||||
return json.Marshal(j)
|
||||
}
|
||||
|
||||
// Scan Unmarshalls the bytes[] back into a Jsonb object
|
||||
func (j *Jsonb) Scan(src interface{}) error {
|
||||
source, ok := src.([]byte)
|
||||
if !ok {
|
||||
return errors.New("Type assertion .([]byte) failed")
|
||||
}
|
||||
|
||||
var i interface{}
|
||||
err := json.Unmarshal(source, &i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
*j, ok = i.(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("reading from DB into Jsonb, failed to convert to map[string]interface{}")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnOrdered is a convenience value to make it clear you're not sorting a query.
|
||||
var UnOrdered = OrderBy{}
|
||||
|
||||
// OrderByDesc returns a sort order descending by the given field.
|
||||
func OrderByDesc(field string) OrderBy {
|
||||
return OrderBy{
|
||||
Field: field,
|
||||
Order: OrderDesc,
|
||||
}
|
||||
}
|
||||
|
||||
// OrderByAsc returns a sort order ascending by the given field.
|
||||
func OrderByAsc(field string) OrderBy {
|
||||
return OrderBy{
|
||||
Field: field,
|
||||
Order: OrderAsc,
|
||||
}
|
||||
}
|
||||
|
||||
// OrderBy indicates how rows should be sorted.
|
||||
type OrderBy struct {
|
||||
Field string
|
||||
Order SortOrder
|
||||
}
|
||||
|
||||
func (o OrderBy) String() string {
|
||||
if o.Order == OrderNone {
|
||||
return ""
|
||||
}
|
||||
return " ORDER BY " + o.Field + " " + o.Order.String() + " "
|
||||
}
|
||||
|
||||
// SortOrder defines how to order rows returned.
|
||||
type SortOrder int
|
||||
|
||||
// Defined sort orders for not sorted, descending and ascending.
|
||||
const (
|
||||
OrderNone SortOrder = iota
|
||||
OrderDesc
|
||||
OrderAsc
|
||||
)
|
||||
|
||||
// String returns the sql string representation of this sort order.
|
||||
func (s SortOrder) String() string {
|
||||
switch s {
|
||||
case OrderDesc:
|
||||
return "DESC"
|
||||
case OrderAsc:
|
||||
return "ASC"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// WhereClause has a String function should return a properly formatted where
|
||||
// clause (not including the WHERE) for positional arguments starting at idx.
|
||||
type WhereClause interface {
|
||||
String(idx *int) string
|
||||
Values() []interface{}
|
||||
}
|
||||
|
||||
// Comparison is used by WhereClauses to create valid sql.
|
||||
type Comparison string
|
||||
|
||||
// Comparison types.
|
||||
const (
|
||||
CompEqual Comparison = " = "
|
||||
CompGreater Comparison = " > "
|
||||
CompLess Comparison = " < "
|
||||
CompGTE Comparison = " >= "
|
||||
CompLTE Comparison = " <= "
|
||||
CompNE Comparison = " <> "
|
||||
)
|
||||
|
||||
type Where struct {
|
||||
Field string
|
||||
Comp Comparison
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (w Where) String(idx *int) string {
|
||||
ret := w.Field + string(w.Comp) + "$" + strconv.Itoa(*idx)
|
||||
(*idx)++
|
||||
return ret
|
||||
}
|
||||
|
||||
func (w Where) Values() []interface{} {
|
||||
return []interface{}{w.Value}
|
||||
}
|
||||
|
||||
// NullClause is a clause that checks for a column being null or not.
|
||||
type NullClause struct {
|
||||
Field string
|
||||
Null bool
|
||||
}
|
||||
|
||||
func (n NullClause) String(idx *int) string {
|
||||
if n.Null {
|
||||
return n.Field + " IS NULL "
|
||||
}
|
||||
return n.Field + " IS NOT NULL "
|
||||
}
|
||||
|
||||
func (n NullClause) Values() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
// AndClause returns a WhereClause that serializes to the AND
|
||||
// of all the given where clauses.
|
||||
func AndClause(wheres ...WhereClause) WhereClause {
|
||||
return andClause(wheres)
|
||||
}
|
||||
|
||||
type andClause []WhereClause
|
||||
|
||||
func (a andClause) String(idx *int) string {
|
||||
wheres := make([]string, len(a))
|
||||
for x := 0; x < len(a); x++ {
|
||||
wheres[x] = a[x].String(idx)
|
||||
}
|
||||
return strings.Join(wheres, " AND ")
|
||||
}
|
||||
|
||||
func (a andClause) Values() []interface{} {
|
||||
vals := make([]interface{}, 0, len(a))
|
||||
for x := 0; x < len(a); x++ {
|
||||
vals = append(vals, a[x].Values()...)
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// OrClause returns a WhereClause that serializes to the OR
|
||||
// of all the given where clauses.
|
||||
func OrClause(wheres ...WhereClause) WhereClause {
|
||||
return orClause(wheres)
|
||||
}
|
||||
|
||||
type orClause []WhereClause
|
||||
|
||||
func (o orClause) String(idx *int) string {
|
||||
wheres := make([]string, len(o))
|
||||
for x := 0; x < len(wheres); x++ {
|
||||
wheres[x] = o[x].String(idx)
|
||||
}
|
||||
return strings.Join(wheres, " OR ")
|
||||
}
|
||||
|
||||
func (o orClause) Values() []interface{} {
|
||||
vals := make([]interface{}, len(o))
|
||||
for x := 0; x < len(o); x++ {
|
||||
vals = append(vals, o[x].Values()...)
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
// InClause takes a slice of values that it matches against.
|
||||
type InClause struct {
|
||||
Field string
|
||||
Vals []interface{}
|
||||
}
|
||||
|
||||
func (in InClause) String(idx *int) string {
|
||||
ret := in.Field + " in ("
|
||||
for x := range in.Vals {
|
||||
if x != 0 {
|
||||
ret += ", "
|
||||
}
|
||||
ret += "$" + strconv.Itoa(*idx)
|
||||
(*idx)++
|
||||
}
|
||||
ret += ")"
|
||||
return ret
|
||||
}
|
||||
|
||||
func (in InClause) Values() []interface{} {
|
||||
return in.Vals
|
||||
}
|
21
doc.go
Normal file
21
doc.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Package postgres contains autogenerated code for postgres databases.
|
||||
// They are a couple of special paths in this directory:
|
||||
// * /_templates contains the gnorm templates for generations
|
||||
// * /gnorm.toml holds the gnorm configuration
|
||||
// * /migrations contains the logic to migrate the database
|
||||
// * /doc.go (this file) contains some instructions about this package
|
||||
//
|
||||
// All query and row definitions are autogenerated from the database, and
|
||||
// converted into static code. This eliminates the need for an ORM, makes
|
||||
// requests very fast and eliminates the possibilities of typos, syntax
|
||||
// errors, SQL injections and therefore runtime problems with the database.
|
||||
//
|
||||
// If you need to update the database logic however, this can be a little
|
||||
// tricky. As this approach takes the DB as the source of truth, start with
|
||||
// writing the up- and down migration (migrations can be found in the
|
||||
// assets/migrations/ dir).
|
||||
// apply the migrations and run `gnorm gen` in this package, this will
|
||||
// update all database related code. If there are compile time errors, the
|
||||
// changes were incompatible with the codebase and you need to adjust the
|
||||
// code a little (usually this is just a matter of minutes).
|
||||
package postgres
|
146
enum.gotmpl
Normal file
146
enum.gotmpl
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Code generated by gnorm, DO NOT EDIT!
|
||||
|
||||
package enum
|
||||
|
||||
import "{{.Params.RootImport}}"
|
||||
|
||||
{{$rootPkg := .Params.RootPkg}}
|
||||
|
||||
{{- $type := .Enum.Name -}}
|
||||
|
||||
// {{ $type }} is the '{{ .Enum.DBName }}' enum type from schema '{{ .Enum.Schema.Name }}'.
|
||||
type {{ $type }} uint16
|
||||
|
||||
const (
|
||||
// Unknown{{$type}} defines an invalid {{$type}}.
|
||||
Unknown{{$type}} {{$type}} = 0
|
||||
{{- range .Enum.Values }}
|
||||
{{ .Name }}{{ $type }} {{$type}} = {{ .Value }}
|
||||
{{ end -}}
|
||||
)
|
||||
|
||||
// String returns the string value of the {{ $type }}.
|
||||
func (e {{ $type }}) String() string {
|
||||
switch e {
|
||||
{{- range .Enum.Values }}
|
||||
case {{ .Name }}{{ $type }}:
|
||||
return "{{ .DBName }}"
|
||||
{{- end }}
|
||||
default:
|
||||
return "Unknown{{$type}}"
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalText marshals {{ $type }} into text.
|
||||
func (e {{ $type }}) MarshalText() ([]byte, error) {
|
||||
return []byte(e.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText unmarshals {{ $type }} from text.
|
||||
func (e *{{ $type }}) UnmarshalText(text []byte) error {
|
||||
val, err := Parse{{$type}}(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = val
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse{{$type}} converts s into a {{$type}} if it is a valid
|
||||
// stringified value of {{$type}}.
|
||||
func Parse{{$type}}(s string) ({{$type}}, error) {
|
||||
switch s {
|
||||
{{- range .Enum.Values }}
|
||||
case "{{ .DBName }}":
|
||||
return {{ .Name }}{{ $type }}, nil
|
||||
{{- end }}
|
||||
default:
|
||||
return Unknown{{$type}}, errors.New("invalid {{ $type }}")
|
||||
}
|
||||
}
|
||||
|
||||
// Value satisfies the sql/driver.Valuer interface for {{ $type }}.
|
||||
func (e {{ $type }}) Value() (driver.Value, error) {
|
||||
return e.String(), nil
|
||||
}
|
||||
|
||||
// Scan satisfies the database/sql.Scanner interface for {{ $type }}.
|
||||
func (e *{{ $type }}) Scan(src interface{}) error {
|
||||
buf, ok := src.([]byte)
|
||||
if !ok {
|
||||
return errors.New("invalid {{ $type }}")
|
||||
}
|
||||
|
||||
return e.UnmarshalText(buf)
|
||||
}
|
||||
|
||||
// {{$type}}Field is a component that returns a {{$rootPkg}}.WhereClause that contains a
|
||||
// comparison based on its field and a strongly typed value.
|
||||
type {{$type}}Field string
|
||||
|
||||
// Equals returns a {{$rootPkg}}.WhereClause for this field.
|
||||
func (f {{$type}}Field) Equals(v {{$type}}) {{$rootPkg}}.WhereClause {
|
||||
return {{$rootPkg}}.Where{
|
||||
Field: string(f),
|
||||
Comp: {{$rootPkg}}.CompEqual,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// GreaterThan returns a {{$rootPkg}}.WhereClause for this field.
|
||||
func (f {{$type}}Field) GreaterThan(v {{$type}}) {{$rootPkg}}.WhereClause {
|
||||
return {{$rootPkg}}.Where{
|
||||
Field: string(f),
|
||||
Comp: {{$rootPkg}}.CompGreater,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// LessThan returns a {{$rootPkg}}.WhereClause for this field.
|
||||
func (f {{$type}}Field) LessThan(v {{$type}}) {{$rootPkg}}.WhereClause {
|
||||
return {{$rootPkg}}.Where{
|
||||
Field: string(f),
|
||||
Comp: {{$rootPkg}}.CompEqual,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// GreaterOrEqual returns a {{$rootPkg}}.WhereClause for this field.
|
||||
func (f {{$type}}Field) GreaterOrEqual(v {{$type}}) {{$rootPkg}}.WhereClause {
|
||||
return {{$rootPkg}}.Where{
|
||||
Field: string(f),
|
||||
Comp: {{$rootPkg}}.CompGTE,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// LessOrEqual returns a {{$rootPkg}}.WhereClause for this field.
|
||||
func (f {{$type}}Field) LessOrEqual(v {{$type}}) {{$rootPkg}}.WhereClause {
|
||||
return {{$rootPkg}}.Where{
|
||||
Field: string(f),
|
||||
Comp: {{$rootPkg}}.CompLTE,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// NotEqual returns a {{$rootPkg}}.WhereClause for this field.
|
||||
func (f {{$type}}Field) NotEqual(v {{$type}}) {{$rootPkg}}.WhereClause {
|
||||
return {{$rootPkg}}.Where{
|
||||
Field: string(f),
|
||||
Comp: {{$rootPkg}}.CompNE,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// In returns a {{$rootPkg}}.WhereClause for this field.
|
||||
func (f {{$type}}Field) In(vals []{{$type}}) {{$rootPkg}}.WhereClause {
|
||||
values := make([]interface{}, len(vals))
|
||||
for x := range vals {
|
||||
values[x] = vals[x]
|
||||
}
|
||||
return {{$rootPkg}}.InClause{
|
||||
Field: string(f),
|
||||
Vals: values,
|
||||
}
|
||||
}
|
103
fields.gotmpl
Normal file
103
fields.gotmpl
Normal file
|
@ -0,0 +1,103 @@
|
|||
// Code generated by gnorm, DO NOT EDIT!
|
||||
|
||||
package {{.Params.RootPkg}}
|
||||
|
||||
import (
|
||||
"github.com/lib/pq"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
{{ range (makeSlice "Bytes" "Jsonb" "int" "string" "sql.NullString" "int64" "sql.NullInt64" "float64" "sql.NullFloat64" "bool" "sql.NullBool" "time.Time" "pq.NullTime" "uint32" "uuid.UUID" "uuid.NullUUID" "hstore.Hstore") }}
|
||||
{{ $fieldName := title (replace . "." "" 1) }}
|
||||
// {{$fieldName}}Field is a component that returns a WhereClause that contains a
|
||||
// comparison based on its field and a strongly typed value.
|
||||
type {{$fieldName}}Field string
|
||||
|
||||
// Equals returns a WhereClause for this field.
|
||||
func (f {{$fieldName}}Field) Equals(v {{.}}) WhereClause {
|
||||
return Where{
|
||||
Field: string(f),
|
||||
Comp: CompEqual,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// GreaterThan returns a WhereClause for this field.
|
||||
func (f {{$fieldName}}Field) GreaterThan(v {{.}}) WhereClause {
|
||||
return Where{
|
||||
Field: string(f),
|
||||
Comp: CompGreater,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// LessThan returns a WhereClause for this field.
|
||||
func (f {{$fieldName}}Field) LessThan(v {{.}}) WhereClause {
|
||||
return Where{
|
||||
Field: string(f),
|
||||
Comp: CompLess,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// GreaterOrEqual returns a WhereClause for this field.
|
||||
func (f {{$fieldName}}Field) GreaterOrEqual(v {{.}}) WhereClause {
|
||||
return Where{
|
||||
Field: string(f),
|
||||
Comp: CompGTE,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// LessOrEqual returns a WhereClause for this field.
|
||||
func (f {{$fieldName}}Field) LessOrEqual(v {{.}}) WhereClause {
|
||||
return Where{
|
||||
Field: string(f),
|
||||
Comp: CompLTE,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// NotEqual returns a WhereClause for this field.
|
||||
func (f {{$fieldName}}Field) NotEqual(v {{.}}) WhereClause {
|
||||
return Where{
|
||||
Field: string(f),
|
||||
Comp: CompNE,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
|
||||
// In returns a WhereClause for this field.
|
||||
func (f {{$fieldName}}Field) In(vals []{{.}}) WhereClause {
|
||||
values := make([]interface{}, len(vals))
|
||||
for x := range vals {
|
||||
values[x] = vals[x]
|
||||
}
|
||||
return InClause{
|
||||
Field: string(f),
|
||||
Vals: values,
|
||||
}
|
||||
}
|
||||
|
||||
{{end}}
|
||||
|
||||
{{ range (makeSlice "Jsonb" "sql.NullString" "sql.NullInt64" "sql.NullFloat64" "sql.NullBool" "pq.NullTime" "uuid.NullUUID") }}
|
||||
{{ $fieldName := title (replace . "." "" 1) }}
|
||||
// IsNull returns a WhereClause that matches when this field is NULL.
|
||||
func (f {{$fieldName}}Field) IsNull() WhereClause {
|
||||
return NullClause{
|
||||
Field: string(f),
|
||||
Null: true,
|
||||
}
|
||||
}
|
||||
|
||||
// IsNotNull returns a WhereClause that matches when this field is not NULL.
|
||||
func (f {{$fieldName}}Field) IsNotNull() WhereClause {
|
||||
return NullClause{
|
||||
Field: string(f),
|
||||
Null: false,
|
||||
}
|
||||
}
|
||||
|
||||
{{end}}
|
||||
|
83
gnorm.toml
Normal file
83
gnorm.toml
Normal file
|
@ -0,0 +1,83 @@
|
|||
DBType = "postgres"
|
||||
|
||||
# ConnStr is the connection string for the database where the schema is
|
||||
# generated from
|
||||
ConnStr = "dbname=postgres host=127.0.0.1 sslmode=disable user=postgres password=postgres"
|
||||
|
||||
Schemas = ["public"]
|
||||
|
||||
ExcludeTables = ["schema_migrations"]
|
||||
|
||||
# Run this command after generation, to lint the generated files
|
||||
PostRun = ["goimports", "-w", "$GNORMFILE"]
|
||||
|
||||
# PascalCase should be used for our go database
|
||||
NameConversion = "{{pascal .}}"
|
||||
|
||||
# Generate in the current directory. If this is changed, the RootPkg
|
||||
# below should match the folder name.
|
||||
OutputDir = "."
|
||||
|
||||
[Params]
|
||||
# RootPkg is the package declaration for the output dir. It should match the
|
||||
# directory name above.
|
||||
RootPkg = "postgres"
|
||||
|
||||
# RootImport is the import path for the output directory.
|
||||
RootImport = "git.klink.asia/paul/kregistry/database/postgres"
|
||||
|
||||
[SchemaPaths]
|
||||
"fields.go" = "_templates/fields.gotmpl"
|
||||
"db.go" = "_templates/db.gotmpl"
|
||||
|
||||
[TablePaths]
|
||||
"{{toLower .Table}}/{{toLower .Table}}.go" = "_templates/table.gotmpl"
|
||||
|
||||
[EnumPaths]
|
||||
"enum/{{toLower .Enum}}.go" = "_templates/enum.gotmpl"
|
||||
|
||||
[TypeMap]
|
||||
"timestamp with time zone" = "time.Time"
|
||||
"timestamp without time zone" = "time.Time"
|
||||
"timestamptz" = "time.Time"
|
||||
"timestamp" = "time.Time"
|
||||
"varchar" = "string"
|
||||
"text" = "string"
|
||||
"citext" = "string"
|
||||
"boolean" = "bool"
|
||||
"uuid" = "uuid.UUID" # from "github.com/satori/go.uuid"
|
||||
"character varying" = "string"
|
||||
"character" = "string"
|
||||
"bigserial" = "int64"
|
||||
"bigint" = "int64"
|
||||
"integer" = "int"
|
||||
"int4" = "int32"
|
||||
"numeric" = "float64"
|
||||
"real" = "float64"
|
||||
"hstore" = "hstore.Hstore" # from "github.com/lib/pq/hstore"
|
||||
"jsonb" = "postgres.Jsonb" # package name here has to be kept in sync with RootPkg.
|
||||
"bytea" = "postgres.Bytes"
|
||||
|
||||
# needs to be kept in sync with the enum template's package name
|
||||
"rank" = "enum.Rank"
|
||||
|
||||
|
||||
[NullableTypeMap]
|
||||
"timestamp with time zone" = "pq.NullTime"
|
||||
"timestamptz" = "pq.NullTime"
|
||||
"timestamp" = "pq.NullTime"
|
||||
"text" = "sql.NullString"
|
||||
"citext" = "sql.NullString"
|
||||
"varchar" = "sql.NullString"
|
||||
"public.citext" = "sql.NullString"
|
||||
"boolean" = "sql.NullBool"
|
||||
"uuid" = "uuid.NullUUID"
|
||||
"character varying" = "sql.NullString"
|
||||
"character" = "sql.NullString"
|
||||
"integer" = "sql.NullInt64"
|
||||
"bigint" = "sql.NullInt64"
|
||||
"numeric" = "sql.NullFloat64"
|
||||
"real" = "sql.NullFloat64"
|
||||
"hstore" = "hstore.Hstore"
|
||||
"jsonb" = "postgres.Jsonb" # package name here has to be kept in sync with RootPkg.
|
||||
"bytea" = "postgres.NullBytes"
|
170
table.gotmpl
Normal file
170
table.gotmpl
Normal file
|
@ -0,0 +1,170 @@
|
|||
// Code generated by gnorm, DO NOT EDIT!
|
||||
|
||||
package {{toLower .Table.Name}}
|
||||
|
||||
import (
|
||||
"{{.Params.RootImport}}"
|
||||
"{{.Params.RootImport}}/enum"
|
||||
)
|
||||
|
||||
{{$rootPkg := .Params.RootPkg -}}
|
||||
{{$table := .Table.DBName -}}
|
||||
{{$schema := .Table.Schema.DBName -}}
|
||||
// Row represents a row from '{{ $table }}'.
|
||||
type Row struct {
|
||||
{{- range .Table.Columns }}
|
||||
{{ .Name }} {{ .Type }} // {{ .DBName }}{{if .IsPrimaryKey}} (PK){{end}}
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
|
||||
// Field values for every column in {{.Table.Name}}.
|
||||
var (
|
||||
{{- range .Table.Columns }}
|
||||
{{- if or (hasPrefix .Type (printf "%s." $rootPkg)) (hasPrefix .Type "enum.")}}
|
||||
{{.Name}}Col {{ .Type }}Field = "{{ .DBName }}"
|
||||
{{- else}}
|
||||
{{.Name}}Col {{$rootPkg}}.{{ title (replace .Type "." "" 1) }}Field = "{{ .DBName }}"
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
// Query retrieves rows from '{{ $table }}' as a slice of Row.
|
||||
func Query(db {{$rootPkg}}.DB, where {{$rootPkg}}.WhereClause) ([]*Row, error) {
|
||||
const origsqlstr = `SELECT
|
||||
{{ join .Table.Columns.DBNames ", " }}
|
||||
FROM {{$schema}}.{{ $table }} WHERE (`
|
||||
|
||||
idx := 1
|
||||
sqlstr := origsqlstr + where.String(&idx) + ") "
|
||||
|
||||
var vals []*Row
|
||||
q, err := db.Query(sqlstr, where.Values()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for q.Next() {
|
||||
r := Row{}
|
||||
err := q.Scan( {{ join (.Table.Columns.Names.Sprintf "&r.%s") ", " }} )
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vals = append(vals, &r)
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// One retrieve one row from '{{ $table }}'.
|
||||
func One(db {{$rootPkg}}.DB, where {{$rootPkg}}.WhereClause) (*Row, error) {
|
||||
const origsqlstr = `SELECT
|
||||
{{ join .Table.Columns.DBNames ", " }}
|
||||
FROM {{$schema}}.{{ $table }} WHERE (`
|
||||
|
||||
idx := 1
|
||||
sqlstr := origsqlstr + where.String(&idx) + ") "
|
||||
|
||||
row := db.QueryRow(sqlstr, where.Values()...)
|
||||
|
||||
r := Row{}
|
||||
err := row.Scan({{ join (.Table.Columns.Names.Sprintf "&r.%s") ", " }} )
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
{{- define "values" -}}
|
||||
{{$nums := numbers 1 . -}}
|
||||
{{$indices := $nums.Sprintf "$%s" -}}
|
||||
{{join $indices ", " -}}
|
||||
{{end}}
|
||||
|
||||
// Insert inserts the row into the database, returning the autogenerated
|
||||
// primary keys. If the primary keys cannot be autogenerated, please use
|
||||
// InsertExact instead.
|
||||
func Insert(db {{$rootPkg}}.DB, r *Row) error {
|
||||
const sqlstr = `INSERT INTO {{ $table }} (
|
||||
{{ join (.Table.Columns.DBNames.Except .Table.PrimaryKeys.DBNames) ", " }}
|
||||
) VALUES (
|
||||
{{template "values" (len (.Table.Columns.Names.Except .Table.PrimaryKeys.Names)) }}
|
||||
) RETURNING {{join (.Table.PrimaryKeys.DBNames.Sprintf "%s") ", "}}`
|
||||
err := db.QueryRow(sqlstr, {{join ((.Table.Columns.Names.Except .Table.PrimaryKeys.Names).Sprintf "r.%s") ", "}}).Scan({{join (.Table.PrimaryKeys.Names.Sprintf "&r.%s") ", "}})
|
||||
return errors.Wrap(err, "insert {{.Table.Name}}")
|
||||
}
|
||||
|
||||
// InsertExact works like Insert, but lets you specify the primary keys.
|
||||
// For most cases you should consider using Insert.
|
||||
func InsertExact(db {{$rootPkg}}.DB, r *Row) error {
|
||||
const sqlstr = `INSERT INTO {{ $table }} (
|
||||
{{ join .Table.Columns.DBNames ", " }}
|
||||
) VALUES (
|
||||
{{template "values" (len .Table.Columns) }}
|
||||
)`
|
||||
_, err := db.Exec(sqlstr, {{join (.Table.Columns.Names.Sprintf "r.%s") ", "}})
|
||||
return errors.Wrap(err, "insert {{.Table.Name}}")
|
||||
}
|
||||
|
||||
{{- if .Table.HasPrimaryKey }}
|
||||
{{- if gt (len .Table.Columns) (len .Table.PrimaryKeys) }}
|
||||
{{- $nonPKFields := join ((.Table.Columns.Names.Except .Table.PrimaryKeys.Names).Sprintf "r.%s") ", "}}
|
||||
{{- $PKFields := join (.Table.PrimaryKeys.Names.Sprintf "r.%s") ", "}}
|
||||
{{- $nonPKNames := .Table.Columns.DBNames.Except .Table.PrimaryKeys.DBNames}}
|
||||
{{- $numNonPKs := sub (len .Table.Columns) (len .Table.PrimaryKeys)}}
|
||||
{{- $updateCols := join (.Table.Columns.DBNames.Except .Table.PrimaryKeys.DBNames) ", " }}
|
||||
|
||||
|
||||
// Update updates the Row in the database.
|
||||
func Update(db {{$rootPkg}}.DB, r *Row) error {
|
||||
const sqlstr = `UPDATE {{ $table }} SET (
|
||||
{{$updateCols}}
|
||||
) = (
|
||||
{{ template "values" $numNonPKs }}
|
||||
) WHERE
|
||||
{{- $PKnums := numbers (inc $numNonPKs) (len .Table.Columns)}}
|
||||
{{join .Table.PrimaryKeys.DBNames ", "}} = {{ join ($PKnums.Sprintf "$%s") ", " }}
|
||||
`
|
||||
_, err := db.Exec(sqlstr, {{$nonPKFields}}, {{$PKFields}})
|
||||
return errors.Wrap(err, "update {{.Table.Name}}:")
|
||||
}
|
||||
|
||||
// Upsert performs an upsert for {{ .Table.Name }}.
|
||||
//
|
||||
// NOTE: PostgreSQL 9.5+ only
|
||||
func Upsert(db {{$rootPkg}}.DB, r *Row) error {
|
||||
const sqlstr = `INSERT INTO {{ $table }} (
|
||||
{{$updateCols}}, {{join .Table.PrimaryKeys.DBNames ", "}}
|
||||
) VALUES (
|
||||
{{template "values" (len .Table.Columns) }}
|
||||
) ON CONFLICT ({{join .Table.PrimaryKeys.DBNames ", " }}) DO UPDATE SET (
|
||||
{{$updateCols}}
|
||||
) = (
|
||||
{{ template "values" $numNonPKs }}
|
||||
)`
|
||||
|
||||
_, err := db.Exec(sqlstr, {{$nonPKFields}}, {{$PKFields}})
|
||||
return errors.Wrap(err, "upsert {{.Table.Name}}")
|
||||
}
|
||||
{{ else }}
|
||||
// Update statements omitted due to lack of primary key or lack of updateable fields
|
||||
{{ end }}
|
||||
|
||||
|
||||
// Delete deletes the Row from the database.
|
||||
func Delete(
|
||||
db {{$rootPkg}}.DB,
|
||||
{{- range .Table.PrimaryKeys}}
|
||||
{{camel .DBName}} {{.Type}},
|
||||
{{end -}}
|
||||
) error {
|
||||
const sqlstr = `DELETE FROM {{ $table }} WHERE {{join .Table.PrimaryKeys.DBNames ", "}} = {{template "values" (len .Table.PrimaryKeys)}}`
|
||||
|
||||
_, err := db.Exec(
|
||||
sqlstr,
|
||||
{{- range .Table.PrimaryKeys}}
|
||||
{{camel .DBName}},
|
||||
{{end -}}
|
||||
)
|
||||
return errors.Wrap(err, "delete {{.Table.Name}}")
|
||||
}
|
||||
{{- end }}
|
Loading…
Reference in a new issue