agola/internal/sqlg/object.go

215 lines
4.3 KiB
Go

package sqlg
import (
"fmt"
"time"
"github.com/gofrs/uuid"
"github.com/huandu/xstrings"
"github.com/sorintlab/errors"
"agola.io/agola/internal/sqlg/sql"
)
type Object interface {
GetID() string
}
type Initer interface {
Init() error
}
type PreJSONSetupper interface {
PreJSON() error
}
type ExportMeta struct {
Kind string `json:"kind"`
}
type ObjectMeta struct {
// ID is the unique ID of the object.
ID string `json:"id"`
// CreationTime represents the time when this object has been created.
CreationTime time.Time `json:"creationTime"`
// UpdateTime represents the time when this object has been created/updated.
UpdateTime time.Time `json:"updateTime"`
// Revision is the object revision, it's not saved in the object but
// populated by the fetch from the database
Revision uint64 `json:"-"`
// TxID is the current transaction id, used internally and must not be saved in the object
TxID string `json:"-"`
}
func NewObjectMeta(tx *sql.Tx) ObjectMeta {
return ObjectMeta{
ID: uuid.Must(uuid.NewV4()).String(),
TxID: tx.ID(),
}
}
type Sequence struct {
Name string
Table string
Column string
}
func (m *ObjectMeta) GetID() string {
return m.ID
}
var ErrConcurrent = errors.New("concurrent update")
type MigrateFunc func(tx *sql.Tx) error
type ObjectField struct {
Name string
ColName string
Type string
BaseType string
SQLType string
Nullable bool
Unique bool
Sequence bool
JSON bool
}
type ObjectInfo struct {
Name string
Table string
Fields []ObjectField
// TODO(sgotti) instead of a pure ddl, use data to generate it
Constraints []string
// TODO(sgotti) instead of a pure ddl, use data to generate it
Indexes []string
}
func ObjectNames(objectInfos []ObjectInfo) []string {
names := []string{}
for _, objectInfo := range objectInfos {
names = append(names, objectInfo.Name)
}
return names
}
func (oi ObjectInfo) PopulatePostgres() ObjectInfo {
for i, field := range oi.Fields {
if field.JSON {
field.SQLType = "jsonb"
}
if field.Sequence {
field.SQLType = "bigint generated by default as identity"
field.Unique = true
}
if field.SQLType == "" {
switch field.BaseType {
case "string":
field.SQLType = "varchar"
case "bool":
field.SQLType = "boolean"
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "byte", "rune":
field.SQLType = "bigint"
case "float32":
field.SQLType = "real"
case "float64":
field.SQLType = "double precision"
case "time.Time":
field.SQLType = "timestamptz"
case "time.Duration":
field.SQLType = "bigint"
case "[]byte":
field.SQLType = "bytea"
default:
panic(fmt.Errorf("unknown field type: %q", field.BaseType))
}
}
oi.Fields[i] = field
}
return oi
}
func (oi ObjectInfo) PopulateSqlite3() ObjectInfo {
for i, field := range oi.Fields {
if field.JSON {
field.SQLType = "text"
}
if field.Sequence {
field.SQLType = "integer"
field.Unique = true
}
if field.SQLType == "" {
switch field.BaseType {
case "string":
field.SQLType = "varchar"
case "bool":
field.SQLType = "integer"
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "byte", "rune":
field.SQLType = "bigint"
case "float32":
field.SQLType = "real"
case "float64":
field.SQLType = "double precision"
case "time.Time":
// timestamp, datatime and date are declared column types supported by the go-sqlite3 driver
field.SQLType = "timestamp"
case "time.Duration":
field.SQLType = "bigint"
case "[]byte":
field.SQLType = "blob"
default:
panic(fmt.Errorf("unknown field type: %q", field.BaseType))
}
}
if field.SQLType == "serial" {
field.Nullable = false
}
oi.Fields[i] = field
}
return oi
}
func PopulateObjectsInfo(inObjectsInfo []ObjectInfo, dbType sql.Type) []ObjectInfo {
objectsInfo := make([]ObjectInfo, len(inObjectsInfo))
for i, oi := range inObjectsInfo {
for i, field := range oi.Fields {
if field.ColName == "" {
field.ColName = xstrings.ToSnakeCase(field.Name)
}
if field.BaseType == "" {
field.BaseType = field.Type
}
oi.Fields[i] = field
}
switch dbType {
case sql.Postgres:
oi = oi.PopulatePostgres()
case sql.Sqlite3:
oi = oi.PopulateSqlite3()
default:
}
objectsInfo[i] = oi
}
return objectsInfo
}