plus and minus infinity keyword tests implemented range table tests added skip cockroach db added select test case added for range fields generator modified to generate correct types generator tests modified to include sample range table model and template generators modified to support range fields returning the T in UPPER and LOWER functions raw ranges implemented bounds set as optional dep modified dependencies modified and issue fixed range expression with templates implemented rangeExpression change to make it more type safe third parameter of constructor function fixed literals removed, functions added tests modified constructor functions used for creating range expressions NumRange converted to a constructor function from literal range_lower and range_upper renamed to lower_bound and upper_bound range literal removed PlusInfinity and MinusInfinity implemented int4 and int8 castings added issues fixed and tests checked number, ts, tstz literal and cast implemented date range literal expression modified and raw function used parent type converted from RangeExpression to Expression range type implemented for postgres range column type, function and literal expression implemented CONTAINS and OVERLAP operations added for range expressions range expressions implemented
340 lines
8.5 KiB
Go
340 lines
8.5 KiB
Go
package template
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/go-jet/jet/v2/generator/metadata"
|
|
"github.com/go-jet/jet/v2/internal/utils/dbidentifier"
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgtype"
|
|
"path"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Model is template for model files generation
|
|
type Model struct {
|
|
Skip bool
|
|
Path string
|
|
Table func(table metadata.Table) TableModel
|
|
View func(table metadata.Table) ViewModel
|
|
Enum func(enum metadata.Enum) EnumModel
|
|
}
|
|
|
|
// PackageName returns package name of model types
|
|
func (m Model) PackageName() string {
|
|
return path.Base(m.Path)
|
|
}
|
|
|
|
// UsePath returns new Model template with replaced file path
|
|
func (m Model) UsePath(path string) Model {
|
|
m.Path = path
|
|
return m
|
|
}
|
|
|
|
// UseTable returns new Model template with replaced template for table model files generation
|
|
func (m Model) UseTable(tableModelFunc func(table metadata.Table) TableModel) Model {
|
|
m.Table = tableModelFunc
|
|
return m
|
|
}
|
|
|
|
// UseView returns new Model template with replaced template for view model files generation
|
|
func (m Model) UseView(tableModelFunc func(table metadata.Table) TableModel) Model {
|
|
m.View = tableModelFunc
|
|
return m
|
|
}
|
|
|
|
// UseEnum returns new Model template with replaced template for enum model files generation
|
|
func (m Model) UseEnum(enumFunc func(enumMetaData metadata.Enum) EnumModel) Model {
|
|
m.Enum = enumFunc
|
|
return m
|
|
}
|
|
|
|
// DefaultModel returns default Model template implementation
|
|
func DefaultModel() Model {
|
|
return Model{
|
|
Skip: false,
|
|
Path: "/model",
|
|
Table: DefaultTableModel,
|
|
View: DefaultViewModel,
|
|
Enum: DefaultEnumModel,
|
|
}
|
|
}
|
|
|
|
// TableModel is template for table model files generation
|
|
type TableModel struct {
|
|
Skip bool
|
|
FileName string
|
|
TypeName string
|
|
Field func(columnMetaData metadata.Column) TableModelField
|
|
}
|
|
|
|
// ViewModel is template for view model files generation
|
|
type ViewModel = TableModel
|
|
|
|
// DefaultViewModel is default view template implementation
|
|
var DefaultViewModel = DefaultTableModel
|
|
|
|
// DefaultTableModel is default table template implementation
|
|
func DefaultTableModel(tableMetaData metadata.Table) TableModel {
|
|
return TableModel{
|
|
FileName: dbidentifier.ToGoFileName(tableMetaData.Name),
|
|
TypeName: dbidentifier.ToGoIdentifier(tableMetaData.Name),
|
|
Field: DefaultTableModelField,
|
|
}
|
|
}
|
|
|
|
// UseFileName returns new TableModel with new file name set
|
|
func (t TableModel) UseFileName(fileName string) TableModel {
|
|
t.FileName = fileName
|
|
return t
|
|
}
|
|
|
|
// UseTypeName returns new TableModel with new type name set
|
|
func (t TableModel) UseTypeName(typeName string) TableModel {
|
|
t.TypeName = typeName
|
|
return t
|
|
}
|
|
|
|
// UseField returns new TableModel with new TableModelField template function
|
|
func (t TableModel) UseField(structFieldFunc func(columnMetaData metadata.Column) TableModelField) TableModel {
|
|
t.Field = structFieldFunc
|
|
return t
|
|
}
|
|
|
|
func getTableModelImports(modelType TableModel, tableMetaData metadata.Table) []string {
|
|
importPaths := map[string]bool{}
|
|
for _, columnMetaData := range tableMetaData.Columns {
|
|
field := modelType.Field(columnMetaData)
|
|
importPath := field.Type.ImportPath
|
|
|
|
if importPath != "" {
|
|
importPaths[importPath] = true
|
|
}
|
|
}
|
|
|
|
var ret []string
|
|
for importPath := range importPaths {
|
|
ret = append(ret, importPath)
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
// EnumModel is template for enum model files generation
|
|
type EnumModel struct {
|
|
Skip bool
|
|
FileName string
|
|
TypeName string
|
|
ValueName func(value string) string
|
|
}
|
|
|
|
// UseFileName returns new EnumModel with new file name set
|
|
func (em EnumModel) UseFileName(fileName string) EnumModel {
|
|
em.FileName = fileName
|
|
return em
|
|
}
|
|
|
|
// UseTypeName returns new EnumModel with new type name set
|
|
func (em EnumModel) UseTypeName(typeName string) EnumModel {
|
|
em.TypeName = typeName
|
|
return em
|
|
}
|
|
|
|
// DefaultEnumModel returns default implementation for EnumModel
|
|
func DefaultEnumModel(enumMetaData metadata.Enum) EnumModel {
|
|
typeName := dbidentifier.ToGoIdentifier(enumMetaData.Name)
|
|
|
|
return EnumModel{
|
|
FileName: dbidentifier.ToGoFileName(enumMetaData.Name),
|
|
TypeName: typeName,
|
|
ValueName: func(value string) string {
|
|
return typeName + "_" + dbidentifier.ToGoIdentifier(value)
|
|
},
|
|
}
|
|
}
|
|
|
|
// TableModelField is template for table model field generation
|
|
type TableModelField struct {
|
|
Name string
|
|
Type Type
|
|
Tags []string
|
|
}
|
|
|
|
// DefaultTableModelField returns default TableModelField implementation
|
|
func DefaultTableModelField(columnMetaData metadata.Column) TableModelField {
|
|
var tags []string
|
|
|
|
if columnMetaData.IsPrimaryKey {
|
|
tags = append(tags, `sql:"primary_key"`)
|
|
}
|
|
|
|
return TableModelField{
|
|
Name: dbidentifier.ToGoIdentifier(columnMetaData.Name),
|
|
Type: getType(columnMetaData),
|
|
Tags: tags,
|
|
}
|
|
}
|
|
|
|
// UseType returns new TypeModelField with a new field type set
|
|
func (f TableModelField) UseType(t Type) TableModelField {
|
|
f.Type = t
|
|
return f
|
|
}
|
|
|
|
// UseName returns new TableModelField implementation with new field name set
|
|
func (f TableModelField) UseName(name string) TableModelField {
|
|
f.Name = name
|
|
return f
|
|
}
|
|
|
|
// UseTags returns new TableModelField implementation with additional tags added.
|
|
func (f TableModelField) UseTags(tags ...string) TableModelField {
|
|
f.Tags = append(f.Tags, tags...)
|
|
return f
|
|
}
|
|
|
|
// TagsString returns tags string representation
|
|
func (f TableModelField) TagsString() string {
|
|
if len(f.Tags) == 0 {
|
|
return ""
|
|
}
|
|
|
|
return fmt.Sprintf("`%s`", strings.Join(f.Tags, " "))
|
|
}
|
|
|
|
// Type represents type of the struct field
|
|
type Type struct {
|
|
ImportPath string
|
|
Name string
|
|
}
|
|
|
|
// NewType creates new type for dummy object
|
|
func NewType(dummyObject interface{}) Type {
|
|
return Type{
|
|
ImportPath: getImportPath(dummyObject),
|
|
Name: getTypeName(dummyObject),
|
|
}
|
|
}
|
|
|
|
func getTypeName(t interface{}) string {
|
|
typeStr := reflect.TypeOf(t).String()
|
|
typeStr = strings.Replace(typeStr, "[]uint8", "[]byte", -1)
|
|
|
|
return typeStr
|
|
}
|
|
|
|
func getImportPath(dummyData interface{}) string {
|
|
dataType := reflect.TypeOf(dummyData)
|
|
if dataType.Kind() == reflect.Ptr {
|
|
return dataType.Elem().PkgPath()
|
|
}
|
|
return dataType.PkgPath()
|
|
}
|
|
|
|
func getType(columnMetadata metadata.Column) Type {
|
|
userDefinedType := getUserDefinedType(columnMetadata)
|
|
|
|
if userDefinedType != "" {
|
|
if columnMetadata.IsNullable {
|
|
return Type{Name: "*" + userDefinedType}
|
|
}
|
|
return Type{Name: userDefinedType}
|
|
}
|
|
|
|
return NewType(getGoType(columnMetadata))
|
|
}
|
|
|
|
func getUserDefinedType(column metadata.Column) string {
|
|
switch column.DataType.Kind {
|
|
case metadata.EnumType:
|
|
return dbidentifier.ToGoIdentifier(column.DataType.Name)
|
|
case metadata.UserDefinedType, metadata.ArrayType:
|
|
return "string"
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func getGoType(column metadata.Column) interface{} {
|
|
defaultGoType := toGoType(column)
|
|
|
|
if column.IsNullable {
|
|
return reflect.New(reflect.TypeOf(defaultGoType)).Interface()
|
|
}
|
|
|
|
return defaultGoType
|
|
}
|
|
|
|
// toGoType returns model type for column info.
|
|
func toGoType(column metadata.Column) interface{} {
|
|
switch strings.ToLower(column.DataType.Name) {
|
|
case "user-defined", "enum":
|
|
return ""
|
|
case "boolean", "bool":
|
|
return false
|
|
case "tinyint":
|
|
if column.DataType.IsUnsigned {
|
|
return uint8(0)
|
|
}
|
|
return int8(0)
|
|
case "smallint", "int2",
|
|
"year":
|
|
if column.DataType.IsUnsigned {
|
|
return uint16(0)
|
|
}
|
|
return int16(0)
|
|
case "integer", "int4",
|
|
"mediumint", "int": //MySQL
|
|
if column.DataType.IsUnsigned {
|
|
return uint32(0)
|
|
}
|
|
return int32(0)
|
|
case "bigint", "int8":
|
|
if column.DataType.IsUnsigned {
|
|
return uint64(0)
|
|
}
|
|
return int64(0)
|
|
case "date",
|
|
"timestamp without time zone", "timestamp",
|
|
"timestamp with time zone", "timestamptz",
|
|
"time without time zone", "time",
|
|
"time with time zone", "timetz",
|
|
"datetime": // MySQL
|
|
return time.Time{}
|
|
case "bytea",
|
|
"binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob": //MySQL
|
|
return []byte("")
|
|
case "text",
|
|
"character", "bpchar",
|
|
"character varying", "varchar", "nvarchar",
|
|
"tsvector", "bit", "bit varying", "varbit",
|
|
"money", "json", "jsonb",
|
|
"xml", "point", "interval", "line", "array",
|
|
"char", "tinytext", "mediumtext", "longtext": // MySQL
|
|
return ""
|
|
case "real", "float4":
|
|
return float32(0.0)
|
|
case "numeric", "decimal",
|
|
"double precision", "float8", "float",
|
|
"double": // MySQL
|
|
return float64(0.0)
|
|
case "uuid":
|
|
return uuid.UUID{}
|
|
case "daterange":
|
|
return pgtype.Daterange{}
|
|
case "tsrange":
|
|
return pgtype.Tsrange{}
|
|
case "tstzrange":
|
|
return pgtype.Tstzrange{}
|
|
case "int4range":
|
|
return pgtype.Int4range{}
|
|
case "int8range":
|
|
return pgtype.Int8range{}
|
|
case "numrange":
|
|
return pgtype.Numrange{}
|
|
default:
|
|
fmt.Println("- [Model ] Unsupported sql column '" + column.Name + " " + column.DataType.Name + "', using string instead.")
|
|
return ""
|
|
}
|
|
}
|