mirror of https://github.com/agola-io/agola
215 lines
4.3 KiB
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
|
|
}
|