71e11b27f3
Change-Id: Id3630c26dbfda36dcbece2849e2353d5ab2882af
486 lines
9.1 KiB
Cheetah
486 lines
9.1 KiB
Cheetah
{{- $options := .Options -}}
|
|
|
|
// AUTOGENERATED BY storj.io/dbx
|
|
// DO NOT EDIT.
|
|
|
|
package {{ .Package }}
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
"sync"
|
|
|
|
{{- range .ExtraImports }}
|
|
{{ . }}
|
|
{{- end }}
|
|
)
|
|
|
|
// Prevent conditional imports from causing build failures.
|
|
var _ = strconv.Itoa
|
|
var _ = strings.LastIndex
|
|
var _ = fmt.Sprint
|
|
var _ sync.Mutex
|
|
|
|
var (
|
|
WrapErr = func(err *Error) error {return err}
|
|
Logger func(format string, args ...interface{})
|
|
ShouldRetry func(driver string, err error) bool
|
|
|
|
errTooManyRows = errors.New("too many rows")
|
|
errUnsupportedDriver = errors.New("unsupported driver")
|
|
errEmptyUpdate = errors.New("empty update")
|
|
)
|
|
|
|
func logError(format string, args ...interface{}) {
|
|
if Logger != nil {
|
|
Logger(format, args...)
|
|
}
|
|
}
|
|
|
|
type ErrorCode int
|
|
|
|
const (
|
|
ErrorCode_Unknown ErrorCode = iota
|
|
ErrorCode_UnsupportedDriver
|
|
ErrorCode_NoRows
|
|
ErrorCode_TxDone
|
|
ErrorCode_TooManyRows
|
|
ErrorCode_ConstraintViolation
|
|
ErrorCode_EmptyUpdate
|
|
)
|
|
|
|
type Error struct {
|
|
Err error
|
|
Code ErrorCode
|
|
Driver string
|
|
Constraint string
|
|
QuerySuffix string
|
|
}
|
|
|
|
func (e *Error) Error() string {
|
|
return e.Err.Error()
|
|
}
|
|
|
|
func wrapErr(e *Error) error {
|
|
if WrapErr == nil {
|
|
return e
|
|
}
|
|
return WrapErr(e)
|
|
}
|
|
|
|
func makeErr(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
e := &Error{Err: err}
|
|
switch err {
|
|
case sql.ErrNoRows:
|
|
e.Code = ErrorCode_NoRows
|
|
case sql.ErrTxDone:
|
|
e.Code = ErrorCode_TxDone
|
|
}
|
|
return wrapErr(e)
|
|
}
|
|
|
|
func shouldRetry(driver string, err error) bool {
|
|
if ShouldRetry == nil {
|
|
return false
|
|
}
|
|
return ShouldRetry(driver, err)
|
|
}
|
|
|
|
func unsupportedDriver(driver string) error {
|
|
return wrapErr(&Error{
|
|
Err: errUnsupportedDriver,
|
|
Code: ErrorCode_UnsupportedDriver,
|
|
Driver: driver,
|
|
})
|
|
}
|
|
|
|
func emptyUpdate() error {
|
|
return wrapErr(&Error{
|
|
Err: errEmptyUpdate,
|
|
Code: ErrorCode_EmptyUpdate,
|
|
})
|
|
}
|
|
|
|
func tooManyRows(query_suffix string) error {
|
|
return wrapErr(&Error{
|
|
Err: errTooManyRows,
|
|
Code: ErrorCode_TooManyRows,
|
|
QuerySuffix: query_suffix,
|
|
})
|
|
}
|
|
|
|
func constraintViolation(err error, constraint string) error {
|
|
return wrapErr(&Error{
|
|
Err: err,
|
|
Code: ErrorCode_ConstraintViolation,
|
|
Constraint: constraint,
|
|
})
|
|
}
|
|
|
|
type driver interface {
|
|
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
|
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
|
|
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
|
|
}
|
|
|
|
var (
|
|
notAPointer = errors.New("destination not a pointer")
|
|
lossyConversion = errors.New("lossy conversion")
|
|
)
|
|
|
|
type DB struct {
|
|
*sql.DB
|
|
dbMethods
|
|
|
|
Hooks struct {
|
|
Now func() time.Time
|
|
}
|
|
|
|
driver string
|
|
}
|
|
|
|
func Open(driver, source string) (db *DB, err error) {
|
|
var sql_db *sql.DB
|
|
switch driver {
|
|
{{- range .Dialects }}
|
|
case {{ .Name | printf "%q" }}:
|
|
sql_db, err = open{{ .Name }}(source)
|
|
{{- end }}
|
|
default:
|
|
return nil, unsupportedDriver(driver)
|
|
}
|
|
if err != nil {
|
|
return nil, makeErr(err)
|
|
}
|
|
defer func(sql_db *sql.DB) {
|
|
if err != nil {
|
|
sql_db.Close()
|
|
}
|
|
}(sql_db)
|
|
|
|
if err := sql_db.Ping(); err != nil {
|
|
return nil, makeErr(err)
|
|
}
|
|
|
|
db = &DB{
|
|
DB: sql_db,
|
|
|
|
driver: driver,
|
|
}
|
|
db.Hooks.Now = time.Now
|
|
|
|
switch driver {
|
|
{{- range .Dialects }}
|
|
case {{ .Name | printf "%q" }}:
|
|
db.dbMethods = new{{ .Name }}(db)
|
|
{{- end }}
|
|
default:
|
|
return nil, unsupportedDriver(driver)
|
|
}
|
|
|
|
return db, nil
|
|
}
|
|
|
|
func (obj *DB) Close() (err error) {
|
|
return obj.makeErr(obj.DB.Close())
|
|
}
|
|
|
|
func (obj *DB) Open(ctx context.Context) (*Tx, error) {
|
|
tx, err := obj.DB.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return nil, obj.makeErr(err)
|
|
}
|
|
|
|
return &Tx{
|
|
Tx: tx,
|
|
txMethods: obj.wrapTx(tx),
|
|
}, nil
|
|
}
|
|
{{ if $options.SupportRx }}
|
|
func (obj *DB) NewRx() *Rx {
|
|
return &Rx{db: obj}
|
|
}
|
|
{{ end }}
|
|
func DeleteAll(ctx context.Context, db *DB) (int64, error) {
|
|
tx, err := db.Open(ctx)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer func() {
|
|
if err == nil {
|
|
err = db.makeErr(tx.Commit())
|
|
return
|
|
}
|
|
|
|
if err_rollback := tx.Rollback(); err_rollback != nil {
|
|
logError("delete-all: rollback failed: %v", db.makeErr(err_rollback))
|
|
}
|
|
}()
|
|
return tx.deleteAll(ctx)
|
|
}
|
|
|
|
type Tx struct {
|
|
Tx *sql.Tx
|
|
txMethods
|
|
}
|
|
|
|
type dialectTx struct {
|
|
tx *sql.Tx
|
|
}
|
|
|
|
func (tx *dialectTx) Commit() (err error) {
|
|
return makeErr(tx.tx.Commit())
|
|
}
|
|
|
|
func (tx *dialectTx) Rollback() (err error) {
|
|
return makeErr(tx.tx.Rollback())
|
|
}
|
|
|
|
{{- range .Dialects }}
|
|
|
|
{{- $dbtype := .Name | printf "%sDB" -}}
|
|
{{- $txtype := .Name | printf "%sTx" -}}
|
|
{{- $impltype := .Name | printf "%sImpl" }}
|
|
|
|
type {{ $impltype }} struct {
|
|
db *DB
|
|
dialect __sqlbundle_{{ .Name }}
|
|
driver driver
|
|
txn bool
|
|
}
|
|
|
|
func (obj *{{ $impltype }}) Rebind(s string) string {
|
|
return obj.dialect.Rebind(s)
|
|
}
|
|
|
|
func (obj *{{ $impltype }}) logStmt(stmt string, args... interface{}) {
|
|
{{ .Name }}LogStmt(stmt, args...)
|
|
}
|
|
|
|
func (obj *{{ $impltype }}) makeErr(err error) error {
|
|
constraint, ok := obj.isConstraintError(err)
|
|
if ok {
|
|
return constraintViolation(err, constraint)
|
|
}
|
|
return makeErr(err)
|
|
}
|
|
|
|
func (obj *{{ $impltype }}) shouldRetry(err error) bool {
|
|
return !obj.txn && shouldRetry(obj.db.driver, err)
|
|
}
|
|
|
|
type {{ $impltype }}_retryingRow struct {
|
|
obj *{{ $impltype }}
|
|
ctx context.Context
|
|
query string
|
|
args []interface{}
|
|
}
|
|
|
|
func (obj *{{ $impltype }}) queryRowContext(ctx context.Context, query string, args ...interface{}) *{{ $impltype }}_retryingRow {
|
|
return &{{ $impltype }}_retryingRow{
|
|
obj: obj,
|
|
ctx: ctx,
|
|
query: query,
|
|
args: args,
|
|
}
|
|
}
|
|
|
|
func (rows *{{ $impltype }}_retryingRow) Scan(dest ...interface{}) error {
|
|
for {
|
|
err := rows.obj.driver.QueryRowContext(rows.ctx, rows.query, rows.args...).Scan(dest...)
|
|
if err != nil {
|
|
if rows.obj.shouldRetry(err) {
|
|
continue
|
|
}
|
|
// caller will wrap this error
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
type {{ $dbtype }} struct {
|
|
db *DB
|
|
*{{ $impltype }}
|
|
}
|
|
|
|
func new{{ .Name }}(db *DB) *{{ $dbtype }} {
|
|
return &{{ $dbtype }}{
|
|
db: db,
|
|
{{ $impltype }}: &{{ $impltype }}{
|
|
db: db,
|
|
driver: db.DB,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (obj *{{ $dbtype }}) Schema() string {
|
|
return `{{ .SchemaSQL }}`
|
|
}
|
|
|
|
func (obj *{{ $dbtype }}) wrapTx(tx *sql.Tx) txMethods {
|
|
return &{{ $txtype }}{
|
|
dialectTx: dialectTx{tx: tx},
|
|
{{ $impltype }}: &{{ $impltype }}{
|
|
db: obj.db,
|
|
driver: tx,
|
|
txn: true,
|
|
},
|
|
}
|
|
}
|
|
|
|
type {{ $txtype }} struct {
|
|
dialectTx
|
|
*{{ $impltype }}
|
|
}
|
|
|
|
func {{ .Name }}LogStmt(stmt string, args ...interface{}) {
|
|
// TODO: render placeholders
|
|
if Logger != nil {
|
|
out := fmt.Sprintf("stmt: %s\nargs: %v\n", stmt, pretty(args))
|
|
Logger(out)
|
|
}
|
|
}
|
|
|
|
{{- end }}
|
|
|
|
type pretty []interface{}
|
|
|
|
func (p pretty) Format(f fmt.State, c rune) {
|
|
fmt.Fprint(f, "[")
|
|
nextval:
|
|
for i, val := range p {
|
|
if i > 0 {
|
|
fmt.Fprint(f, ", ")
|
|
}
|
|
rv := reflect.ValueOf(val)
|
|
if rv.Kind() == reflect.Ptr {
|
|
if rv.IsNil() {
|
|
fmt.Fprint(f, "NULL")
|
|
continue
|
|
}
|
|
val = rv.Elem().Interface()
|
|
}
|
|
switch v := val.(type) {
|
|
case string:
|
|
fmt.Fprintf(f, "%q", v)
|
|
case time.Time:
|
|
fmt.Fprintf(f, "%s", v.Format(time.RFC3339Nano))
|
|
case []byte:
|
|
for _, b := range v {
|
|
if !unicode.IsPrint(rune(b)) {
|
|
fmt.Fprintf(f, "%#x", v)
|
|
continue nextval
|
|
}
|
|
}
|
|
fmt.Fprintf(f, "%q", v)
|
|
default:
|
|
fmt.Fprintf(f, "%v", v)
|
|
}
|
|
}
|
|
fmt.Fprint(f, "]")
|
|
}
|
|
|
|
{{ range .Structs }}
|
|
{{- $struct := .Name -}}
|
|
{{- $createstruct := .CreateStructName -}}
|
|
{{- $updatestruct := .UpdateStructName }}
|
|
|
|
type {{ $struct }} struct {
|
|
{{- range .Fields }}
|
|
{{ .Name }} {{ .Type }}
|
|
{{- end }}
|
|
{{- if $options.SupportUserdata }}
|
|
|
|
userdata_mu sync.Mutex
|
|
userdata interface{}
|
|
{{- end}}
|
|
}
|
|
|
|
func ({{ $struct }}) _Table() string { return "{{ .Table }}" }
|
|
|
|
{{- if .InsertableOptionalFields }}
|
|
|
|
type {{ $createstruct }} struct {
|
|
{{- range .InsertableOptionalFields }}
|
|
{{ .Name }} {{ .StructName }}
|
|
{{- end }}
|
|
}
|
|
|
|
{{- end }}
|
|
|
|
type {{ $updatestruct }} struct {
|
|
{{- range .UpdatableFields }}
|
|
{{ .Name }} {{ .StructName }}
|
|
{{- end }}
|
|
}
|
|
|
|
{{- range .Fields }}
|
|
{{- $fstruct := .StructName }}
|
|
{{- $ctor := printf "%s_%s" .ModelName .Name }}
|
|
|
|
type {{ $fstruct }} struct {
|
|
_set bool
|
|
_null bool
|
|
_value {{ .Type }}
|
|
}
|
|
|
|
func {{ $ctor }}(v {{ .CtorValue }}) {{ $fstruct }} {
|
|
{{- if .MutateFn }}
|
|
v = {{ .MutateFn }}(v)
|
|
{{- end }}
|
|
return {{ $fstruct }}{ _set: true, _value: {{ if .TakeAddr }}&{{ end }}v }
|
|
}
|
|
{{ if .Nullable }}
|
|
func {{ $ctor }}_Raw(v {{ .Type }}) {{ $fstruct }} {
|
|
if v == nil {
|
|
return {{ $ctor }}_Null()
|
|
}
|
|
return {{ $ctor }}({{ if .TakeAddr }}*{{ end }}v)
|
|
}
|
|
|
|
func {{ $ctor }}_Null() {{ $fstruct }} {
|
|
return {{ $fstruct }}{ _set: true, _null: true }
|
|
}
|
|
|
|
func (f {{ $fstruct }}) isnull() bool { return !f._set || f._null || f._value == nil }
|
|
{{ end }}
|
|
|
|
func (f {{ $fstruct }}) value() interface{} { if !f._set || f._null { return nil }; return f._value }
|
|
|
|
func ({{ $fstruct }}) _Column() string { return "{{ .Column }}" }
|
|
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
func toUTC(t time.Time) time.Time {
|
|
return t.UTC()
|
|
}
|
|
|
|
func toDate(t time.Time) time.Time {
|
|
// keep up the minute portion so that translations between timezones will
|
|
// continue to reflect properly.
|
|
return t.Truncate(time.Minute)
|
|
}
|
|
|
|
//
|
|
// runtime support for building sql statements
|
|
//
|
|
|
|
{{ .SQLSupport }}
|
|
|
|
//
|
|
// end runtime support for building sql statements
|
|
//
|