2021-07-27 17:39:21 +02:00
package template
import (
"fmt"
2024-11-26 15:40:34 +06:00
"path/filepath"
2024-10-26 22:40:49 -04:00
"slices"
2021-10-21 13:21:01 +02:00
"strings"
2021-07-27 17:39:21 +02:00
"unicode"
2025-04-10 09:02:23 -04:00
2026-05-14 17:04:09 +00:00
"source.gleipnir.technology/Gleipnir/jet/v2/generator/metadata"
"source.gleipnir.technology/Gleipnir/jet/v2/internal/jet"
"source.gleipnir.technology/Gleipnir/jet/v2/internal/utils/dbidentifier"
2021-07-27 17:39:21 +02:00
)
// SQLBuilder is template for generating sql builder files
type SQLBuilder struct {
Skip bool
Path string
Table func ( table metadata . Table ) TableSQLBuilder
View func ( view metadata . Table ) TableSQLBuilder
Enum func ( enum metadata . Enum ) EnumSQLBuilder
}
// DefaultSQLBuilder returns default SQLBuilder implementation
func DefaultSQLBuilder ( ) SQLBuilder {
return SQLBuilder {
Path : "" ,
Table : DefaultTableSQLBuilder ,
View : DefaultViewSQLBuilder ,
Enum : DefaultEnumSQLBuilder ,
}
}
// UsePath returns new SQLBuilder with new relative path set
func ( sb SQLBuilder ) UsePath ( path string ) SQLBuilder {
sb . Path = path
return sb
}
// UseTable returns new SQLBuilder with new TableSQLBuilder template function set
func ( sb SQLBuilder ) UseTable ( tableFunc func ( table metadata . Table ) TableSQLBuilder ) SQLBuilder {
sb . Table = tableFunc
return sb
}
// UseView returns new SQLBuilder with new ViewSQLBuilder template function set
func ( sb SQLBuilder ) UseView ( viewFunc func ( table metadata . Table ) ViewSQLBuilder ) SQLBuilder {
sb . View = viewFunc
return sb
}
// UseEnum returns new SQLBuilder with new EnumSQLBuilder template function set
func ( sb SQLBuilder ) UseEnum ( enumFunc func ( enum metadata . Enum ) EnumSQLBuilder ) SQLBuilder {
sb . Enum = enumFunc
return sb
}
2025-04-10 09:02:23 -04:00
// ShouldSkip returns new SQLBuilder with new skip flag set
func ( sb SQLBuilder ) ShouldSkip ( skip bool ) SQLBuilder {
sb . Skip = skip
return sb
}
2021-07-27 17:39:21 +02:00
// TableSQLBuilder is template for generating table SQLBuilder files
type TableSQLBuilder struct {
2026-05-06 16:26:38 +00:00
Column func ( dialect jet . Dialect , columnMetaData metadata . Column ) TableSQLBuilderColumn
DefaultAlias string
Imports [ ] string
2021-07-27 17:39:21 +02:00
InstanceName string
2026-05-06 16:26:38 +00:00
FileName string
Path string
Skip bool
2021-07-27 17:39:21 +02:00
TypeName string
}
// ViewSQLBuilder is template for generating view SQLBuilder files
type ViewSQLBuilder = TableSQLBuilder
// DefaultTableSQLBuilder returns default implementation for TableSQLBuilder
func DefaultTableSQLBuilder ( tableMetaData metadata . Table ) TableSQLBuilder {
2024-02-17 12:46:00 +01:00
tableNameGoIdentifier := dbidentifier . ToGoIdentifier ( tableMetaData . Name )
2021-07-27 17:39:21 +02:00
return TableSQLBuilder {
2026-05-06 16:26:38 +00:00
Column : DefaultTableSQLBuilderColumn ,
DefaultAlias : "" ,
2023-07-21 14:11:31 +02:00
FileName : dbidentifier . ToGoFileName ( tableMetaData . Name ) ,
2026-05-06 16:26:38 +00:00
Imports : [ ] string { } ,
2024-02-17 12:46:00 +01:00
InstanceName : tableNameGoIdentifier ,
2026-05-06 16:26:38 +00:00
Path : "/table" ,
2024-02-17 12:46:00 +01:00
TypeName : tableNameGoIdentifier + "Table" ,
2021-07-27 17:39:21 +02:00
}
}
// DefaultViewSQLBuilder returns default implementation for ViewSQLBuilder
func DefaultViewSQLBuilder ( viewMetaData metadata . Table ) ViewSQLBuilder {
tableSQLBuilder := DefaultTableSQLBuilder ( viewMetaData )
tableSQLBuilder . Path = "/view"
return tableSQLBuilder
}
// PackageName returns package name of table sql builder types
func ( tb TableSQLBuilder ) PackageName ( ) string {
2024-11-26 15:40:34 +06:00
return filepath . Base ( tb . Path )
2021-07-27 17:39:21 +02:00
}
// UsePath returns new TableSQLBuilder with new relative path set
func ( tb TableSQLBuilder ) UsePath ( path string ) TableSQLBuilder {
tb . Path = path
return tb
}
// UseFileName returns new TableSQLBuilder with new file name set
func ( tb TableSQLBuilder ) UseFileName ( name string ) TableSQLBuilder {
tb . FileName = name
return tb
}
// UseInstanceName returns new TableSQLBuilder with new instance name set
func ( tb TableSQLBuilder ) UseInstanceName ( name string ) TableSQLBuilder {
tb . InstanceName = name
return tb
}
// UseTypeName returns new TableSQLBuilder with new type name set
func ( tb TableSQLBuilder ) UseTypeName ( name string ) TableSQLBuilder {
tb . TypeName = name
return tb
}
2023-08-02 10:03:59 +09:00
// UseDefaultAlias returns new TableSQLBuilder with new default alias set
func ( tb TableSQLBuilder ) UseDefaultAlias ( defaultAlias string ) TableSQLBuilder {
tb . DefaultAlias = defaultAlias
return tb
}
2021-07-27 17:39:21 +02:00
// UseColumn returns new TableSQLBuilder with new column template function set
2026-05-06 16:26:38 +00:00
func ( tb TableSQLBuilder ) UseColumn ( columnsFunc func ( dialect jet . Dialect , column metadata . Column ) TableSQLBuilderColumn ) TableSQLBuilder {
2021-07-27 17:39:21 +02:00
tb . Column = columnsFunc
return tb
}
// TableSQLBuilderColumn is template for table sql builder column
type TableSQLBuilderColumn struct {
2026-05-06 16:26:38 +00:00
Import string
2021-07-27 17:39:21 +02:00
Name string
2026-05-06 16:26:38 +00:00
PackageName string
Skip bool
2021-07-27 17:39:21 +02:00
Type string
2026-05-06 16:26:38 +00:00
TypeFactory string
2021-07-27 17:39:21 +02:00
}
2025-02-06 09:34:22 +01:00
var reservedKeywords = [ ] string { "TableName" , "Table" , "SchemaName" , "Alias" , "AllColumns" , "MutableColumns" , "DefaultColumns" }
2024-10-26 22:40:49 -04:00
func renameIfReserved ( name string ) string {
if slices . Contains ( reservedKeywords , name ) {
return name + "_"
}
return name
}
2021-07-27 17:39:21 +02:00
// DefaultTableSQLBuilderColumn returns default implementation of TableSQLBuilderColumn
2026-05-06 16:26:38 +00:00
func DefaultTableSQLBuilderColumn ( dialect jet . Dialect , columnMetaData metadata . Column ) TableSQLBuilderColumn {
package_name := dialect . PackageName ( )
2021-07-27 17:39:21 +02:00
return TableSQLBuilderColumn {
2026-05-14 17:04:09 +00:00
Import : "source.gleipnir.technology/Gleipnir/jet/v2/" + package_name ,
2024-10-26 22:40:49 -04:00
Name : renameIfReserved ( dbidentifier . ToGoIdentifier ( columnMetaData . Name ) ) ,
2026-05-06 16:26:38 +00:00
PackageName : package_name ,
Type : "Column" + getSqlBuilderColumnType ( columnMetaData ) ,
TypeFactory : getSqlBuilderColumnType ( columnMetaData ) + "Column" ,
2021-07-27 17:39:21 +02:00
}
}
// getSqlBuilderColumnType returns type of jet sql builder column
func getSqlBuilderColumnType ( columnMetaData metadata . Column ) string {
2025-10-16 15:09:07 +02:00
switch columnMetaData . DataType . Kind {
case metadata . EnumType , metadata . UserDefinedType :
if columnMetaData . DataType . IsArray ( ) {
return "StringArray"
}
2021-07-27 17:39:21 +02:00
return "String"
}
2025-10-16 15:09:07 +02:00
columnType := sqlToColumnType ( columnMetaData )
2024-09-03 15:39:36 +02:00
2025-10-16 15:09:07 +02:00
if columnMetaData . DataType . IsArray ( ) {
2024-09-03 15:39:36 +02:00
if columnMetaData . DataType . Dimensions > 1 {
2025-10-16 15:09:07 +02:00
fmt . Println ( "- [SQL Builder] Unsupported sql array with multiple dimensions column '" +
columnMetaData . Name + " " + columnMetaData . DataType . Name + "', using StringColumn instead." )
2024-09-03 15:39:36 +02:00
return "String"
}
2025-10-16 15:09:07 +02:00
columnType = columnType + "Array"
2024-09-03 15:39:36 +02:00
}
return columnType
}
2025-10-16 15:09:07 +02:00
// sqlToColumnType maps the type of a SQL column type to a go jet sql builder column. The second return value returns
// whether the given type is supported.
func sqlToColumnType ( columnMetaData metadata . Column ) string {
switch strings . ToLower ( columnMetaData . DataType . Name ) {
2024-02-01 15:20:49 +01:00
case "boolean" , "bool" :
2021-07-27 17:39:21 +02:00
return "Bool"
2024-02-01 15:20:49 +01:00
case "smallint" , "integer" , "bigint" , "int2" , "int4" , "int8" ,
2021-07-27 17:39:21 +02:00
"tinyint" , "mediumint" , "int" , "year" : //MySQL
return "Integer"
case "date" :
return "Date"
case "timestamp without time zone" ,
"timestamp" , "datetime" : //MySQL:
return "Timestamp"
2024-02-01 15:20:49 +01:00
case "timestamp with time zone" , "timestamptz" :
2021-07-27 17:39:21 +02:00
return "Timestampz"
case "time without time zone" ,
"time" : //MySQL
return "Time"
2024-02-01 15:20:49 +01:00
case "time with time zone" , "timetz" :
2021-07-27 17:39:21 +02:00
return "Timez"
case "interval" :
return "Interval"
2025-02-28 18:23:15 +01:00
case "user-defined" , "enum" , "text" , "character" , "character varying" , "uuid" ,
2021-07-27 17:39:21 +02:00
"tsvector" , "bit" , "bit varying" , "money" , "json" , "jsonb" , "xml" , "point" , "line" , "ARRAY" ,
2025-02-28 18:23:15 +01:00
"char" , "varchar" , "nvarchar" , "bpchar" , "varbit" ,
"tinytext" , "mediumtext" , "longtext" : // MySQL
2021-07-27 17:39:21 +02:00
return "String"
2025-02-28 18:23:15 +01:00
case "bytea" : // postgres
return "Bytea"
case "binary" , "varbinary" , "tinyblob" , "mediumblob" , "longblob" , "blob" : // mysql and sqlite
return "Blob"
2024-02-01 15:20:49 +01:00
case "real" , "numeric" , "decimal" , "double precision" , "float" , "float4" , "float8" ,
2021-07-27 17:39:21 +02:00
"double" : // MySQL
return "Float"
2024-01-31 15:30:09 +01:00
case "daterange" :
return "DateRange"
case "tsrange" :
return "TimestampRange"
case "tstzrange" :
return "TimestampzRange"
case "int4range" :
return "Int4Range"
case "int8range" :
return "Int8Range"
case "numrange" :
return "NumericRange"
2026-05-06 20:36:10 +00:00
case "geometry" :
return "Geometry"
2021-07-27 17:39:21 +02:00
default :
2026-05-06 20:36:10 +00:00
fmt . Println ( "- [SQL Builder] Unsupported sql column '" + columnMetaData . Name + "' with type '" + columnMetaData . DataType . Name + "', using StringColumn instead." )
2025-10-16 15:09:07 +02:00
return "String"
2021-07-27 17:39:21 +02:00
}
}
// EnumSQLBuilder is template for generating enum SQLBuilder files
type EnumSQLBuilder struct {
Skip bool
Path string
FileName string
InstanceName string
ValueName func ( enumValue string ) string
}
// DefaultEnumSQLBuilder returns default implementation of EnumSQLBuilder
func DefaultEnumSQLBuilder ( enumMetaData metadata . Enum ) EnumSQLBuilder {
return EnumSQLBuilder {
Path : "/enum" ,
2023-07-21 14:11:31 +02:00
FileName : dbidentifier . ToGoFileName ( enumMetaData . Name ) ,
InstanceName : dbidentifier . ToGoIdentifier ( enumMetaData . Name ) ,
2021-07-27 17:39:21 +02:00
ValueName : func ( enumValue string ) string {
return defaultEnumValueName ( enumMetaData . Name , enumValue )
} ,
}
}
// PackageName returns enum sql builder package name
func ( e EnumSQLBuilder ) PackageName ( ) string {
2024-11-26 15:40:34 +06:00
return filepath . Base ( e . Path )
2021-07-27 17:39:21 +02:00
}
// UsePath returns new EnumSQLBuilder with new path set
func ( e EnumSQLBuilder ) UsePath ( path string ) EnumSQLBuilder {
e . Path = path
return e
}
// UseFileName returns new EnumSQLBuilder with new file name set
func ( e EnumSQLBuilder ) UseFileName ( name string ) EnumSQLBuilder {
e . FileName = name
return e
}
// UseInstanceName returns new EnumSQLBuilder with instance name set
func ( e EnumSQLBuilder ) UseInstanceName ( name string ) EnumSQLBuilder {
e . InstanceName = name
return e
}
func defaultEnumValueName ( enumName , enumValue string ) string {
2023-07-21 14:11:31 +02:00
enumValueName := dbidentifier . ToGoIdentifier ( enumValue )
2021-07-27 17:39:21 +02:00
if ! unicode . IsLetter ( [ ] rune ( enumValueName ) [ 0 ] ) {
2023-07-21 14:11:31 +02:00
return dbidentifier . ToGoIdentifier ( enumName ) + enumValueName
2021-07-27 17:39:21 +02:00
}
return enumValueName
}