init - add project files
This commit is contained in:
31
tools/jet-2.12.0/generator/metadata/column_meta_data.go
Normal file
31
tools/jet-2.12.0/generator/metadata/column_meta_data.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package metadata
|
||||
|
||||
// Column struct
|
||||
type Column struct {
|
||||
Name string `sql:"primary_key"`
|
||||
IsPrimaryKey bool
|
||||
IsNullable bool
|
||||
IsGenerated bool
|
||||
HasDefault bool
|
||||
DataType DataType
|
||||
Comment string
|
||||
}
|
||||
|
||||
// DataTypeKind is database type kind(base, enum, user-defined, array)
|
||||
type DataTypeKind string
|
||||
|
||||
// DataTypeKind possible values
|
||||
const (
|
||||
BaseType DataTypeKind = "base"
|
||||
EnumType DataTypeKind = "enum"
|
||||
UserDefinedType DataTypeKind = "user-defined"
|
||||
ArrayType DataTypeKind = "array"
|
||||
RangeType DataTypeKind = "range"
|
||||
)
|
||||
|
||||
// DataType contains information about column data type
|
||||
type DataType struct {
|
||||
Name string
|
||||
Kind DataTypeKind
|
||||
IsUnsigned bool
|
||||
}
|
||||
51
tools/jet-2.12.0/generator/metadata/dialect_query_set.go
Normal file
51
tools/jet-2.12.0/generator/metadata/dialect_query_set.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// TableType is type of database table(view or base)
|
||||
type TableType string
|
||||
|
||||
// SQL table types
|
||||
const (
|
||||
BaseTable TableType = "BASE TABLE"
|
||||
ViewTable TableType = "VIEW"
|
||||
)
|
||||
|
||||
// DialectQuerySet is set of methods necessary to retrieve dialect metadata information
|
||||
type DialectQuerySet interface {
|
||||
GetTablesMetaData(db *sql.DB, schemaName string, tableType TableType) ([]Table, error)
|
||||
GetEnumsMetaData(db *sql.DB, schemaName string) ([]Enum, error)
|
||||
}
|
||||
|
||||
// GetSchema retrieves Schema information from database
|
||||
func GetSchema(db *sql.DB, querySet DialectQuerySet, schemaName string) (Schema, error) {
|
||||
tablesMetaData, err := querySet.GetTablesMetaData(db, schemaName, BaseTable)
|
||||
if err != nil {
|
||||
return Schema{}, fmt.Errorf("failed to get %s tables metadata: %w", schemaName, err)
|
||||
}
|
||||
|
||||
viewMetaData, err := querySet.GetTablesMetaData(db, schemaName, ViewTable)
|
||||
if err != nil {
|
||||
return Schema{}, fmt.Errorf("failed to get %s view metadata: %w", schemaName, err)
|
||||
}
|
||||
|
||||
enumsMetaData, err := querySet.GetEnumsMetaData(db, schemaName)
|
||||
if err != nil {
|
||||
return Schema{}, fmt.Errorf("failed to get %s enum metadata: %w", schemaName, err)
|
||||
}
|
||||
|
||||
ret := Schema{
|
||||
Name: schemaName,
|
||||
TablesMetaData: tablesMetaData,
|
||||
ViewsMetaData: viewMetaData,
|
||||
EnumsMetaData: enumsMetaData,
|
||||
}
|
||||
|
||||
fmt.Println(" FOUND", len(ret.TablesMetaData), "table(s),", len(ret.ViewsMetaData), "view(s),",
|
||||
len(ret.EnumsMetaData), "enum(s)")
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
8
tools/jet-2.12.0/generator/metadata/enum_meta_data.go
Normal file
8
tools/jet-2.12.0/generator/metadata/enum_meta_data.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package metadata
|
||||
|
||||
// Enum metadata struct
|
||||
type Enum struct {
|
||||
Name string `sql:"primary_key"`
|
||||
Comment string
|
||||
Values []string
|
||||
}
|
||||
14
tools/jet-2.12.0/generator/metadata/schema_meta_data.go
Normal file
14
tools/jet-2.12.0/generator/metadata/schema_meta_data.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package metadata
|
||||
|
||||
// Schema struct
|
||||
type Schema struct {
|
||||
Name string
|
||||
TablesMetaData []Table
|
||||
ViewsMetaData []Table
|
||||
EnumsMetaData []Enum
|
||||
}
|
||||
|
||||
// IsEmpty returns true if schema info does not contain any table, views or enums metadata
|
||||
func (s Schema) IsEmpty() bool {
|
||||
return len(s.TablesMetaData) == 0 && len(s.ViewsMetaData) == 0 && len(s.EnumsMetaData) == 0
|
||||
}
|
||||
23
tools/jet-2.12.0/generator/metadata/table_meta_data.go
Normal file
23
tools/jet-2.12.0/generator/metadata/table_meta_data.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package metadata
|
||||
|
||||
// Table metadata struct
|
||||
type Table struct {
|
||||
Name string `sql:"primary_key"`
|
||||
Comment string
|
||||
Columns []Column
|
||||
}
|
||||
|
||||
// MutableColumns returns list of mutable columns for table
|
||||
func (t Table) MutableColumns() []Column {
|
||||
var ret []Column
|
||||
|
||||
for _, column := range t.Columns {
|
||||
if column.IsPrimaryKey || column.IsGenerated {
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, column)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
118
tools/jet-2.12.0/generator/mysql/mysql_generator.go
Normal file
118
tools/jet-2.12.0/generator/mysql/mysql_generator.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/generator/template"
|
||||
"github.com/go-jet/jet/v2/mysql"
|
||||
mysqldr "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
const mysqlMaxConns = 10
|
||||
|
||||
// DBConnection contains MySQL connection details
|
||||
type DBConnection struct {
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Password string
|
||||
Params string
|
||||
|
||||
DBName string
|
||||
}
|
||||
|
||||
// Generate generates jet files at destination dir from database connection details
|
||||
func Generate(destDir string, dbConn DBConnection, generatorTemplate ...template.Template) error {
|
||||
connectionString := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", dbConn.User, dbConn.Password, dbConn.Host, dbConn.Port, dbConn.DBName)
|
||||
if dbConn.Params != "" {
|
||||
connectionString += "?" + dbConn.Params
|
||||
}
|
||||
|
||||
db, err := openConnection(connectionString)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open db connection: %w", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = generate(db, dbConn.DBName, destDir, generatorTemplate...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateDSN opens connection via DSN string and does everything what Generate does.
|
||||
func GenerateDSN(dsn, destDir string, templates ...template.Template) error {
|
||||
// Special case for go mysql driver. It does not understand schema,
|
||||
// so we need to trim it before passing to generator
|
||||
// https://github.com/go-sql-driver/mysql#dsn-data-source-name
|
||||
idx := strings.Index(dsn, "://")
|
||||
if idx != -1 {
|
||||
dsn = dsn[idx+len("://"):]
|
||||
}
|
||||
|
||||
cfg, err := mysqldr.ParseDSN(dsn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse DSN: %w", err)
|
||||
}
|
||||
if cfg.DBName == "" {
|
||||
return errors.New("database name is required")
|
||||
}
|
||||
|
||||
db, err := openConnection(dsn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open db connection: %w", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = generate(db, cfg.DBName, destDir, templates...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func openConnection(connectionString string) (*sql.DB, error) {
|
||||
fmt.Println("Connecting to MySQL database...")
|
||||
db, err := sql.Open("mysql", connectionString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open mysql connection: %w", err)
|
||||
}
|
||||
|
||||
db.SetMaxOpenConns(mysqlMaxConns)
|
||||
db.SetMaxIdleConns(mysqlMaxConns)
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to ping database: %w", err)
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func generate(db *sql.DB, dbName, destDir string, templates ...template.Template) error {
|
||||
fmt.Println("Retrieving database information...")
|
||||
// No schemas in MySQL
|
||||
schemaMetaData, err := metadata.GetSchema(db, &mySqlQuerySet{}, dbName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get '%s' database metadata: %w", dbName, err)
|
||||
}
|
||||
|
||||
genTemplate := template.Default(mysql.Dialect)
|
||||
if len(templates) > 0 {
|
||||
genTemplate = templates[0]
|
||||
}
|
||||
|
||||
err = template.ProcessSchema(destDir, schemaMetaData, genTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process '%s' database: %w", schemaMetaData.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
91
tools/jet-2.12.0/generator/mysql/query_set.go
Normal file
91
tools/jet-2.12.0/generator/mysql/query_set.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/qrm"
|
||||
)
|
||||
|
||||
// mySqlQuerySet is dialect query set for MySQL
|
||||
type mySqlQuerySet struct{}
|
||||
|
||||
func (m mySqlQuerySet) GetTablesMetaData(db *sql.DB, schemaName string, tableType metadata.TableType) ([]metadata.Table, error) {
|
||||
query := `
|
||||
SELECT
|
||||
t.table_name as "table.name",
|
||||
col.COLUMN_NAME AS "column.Name",
|
||||
col.COLUMN_DEFAULT IS NOT NULL as "column.HasDefault",
|
||||
col.IS_NULLABLE = "YES" AS "column.IsNullable",
|
||||
col.COLUMN_COMMENT AS "column.Comment",
|
||||
COALESCE(pk.IsPrimaryKey, 0) AS "column.IsPrimaryKey",
|
||||
IF (col.COLUMN_TYPE = 'tinyint(1)',
|
||||
'boolean',
|
||||
IF (col.DATA_TYPE = 'enum',
|
||||
CONCAT(col.TABLE_NAME, '_', col.COLUMN_NAME),
|
||||
col.DATA_TYPE)
|
||||
) AS "dataType.Name",
|
||||
IF (col.DATA_TYPE = 'enum', 'enum', 'base') AS "dataType.Kind",
|
||||
col.COLUMN_TYPE LIKE '%unsigned%' AS "dataType.IsUnsigned"
|
||||
FROM INFORMATION_SCHEMA.tables AS t
|
||||
INNER JOIN
|
||||
information_schema.columns AS col
|
||||
ON t.table_schema = col.table_schema AND t.table_name = col.table_name
|
||||
LEFT JOIN (
|
||||
SELECT k.column_name, 1 AS IsPrimaryKey, k.table_name
|
||||
FROM information_schema.table_constraints t
|
||||
JOIN information_schema.key_column_usage k USING(constraint_name, table_schema, table_name)
|
||||
WHERE t.table_schema = ?
|
||||
AND t.constraint_type = 'PRIMARY KEY'
|
||||
) AS pk ON col.COLUMN_NAME = pk.column_name AND col.table_name = pk.table_name
|
||||
WHERE t.table_schema = ?
|
||||
AND t.table_type = ?
|
||||
ORDER BY
|
||||
t.table_name,
|
||||
col.ordinal_position;
|
||||
`
|
||||
|
||||
var tables []metadata.Table
|
||||
|
||||
_, err := qrm.Query(context.Background(), db, query, []interface{}{schemaName, schemaName, tableType}, &tables)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query column meta data: %w", err)
|
||||
}
|
||||
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
func (m mySqlQuerySet) GetEnumsMetaData(db *sql.DB, schemaName string) ([]metadata.Enum, error) {
|
||||
query := `
|
||||
SELECT (CASE c.DATA_TYPE WHEN 'enum' then CONCAT(c.TABLE_NAME, '_', c.COLUMN_NAME) ELSE '' END ) as "name",
|
||||
SUBSTRING(c.COLUMN_TYPE,5) as "values"
|
||||
FROM information_schema.columns as c
|
||||
INNER JOIN information_schema.tables as t on (t.table_schema = c.table_schema AND t.table_name = c.table_name)
|
||||
WHERE c.table_schema = ? AND DATA_TYPE = 'enum';
|
||||
`
|
||||
var queryResult []struct {
|
||||
Name string
|
||||
Values string
|
||||
}
|
||||
|
||||
_, err := qrm.Query(context.Background(), db, query, []interface{}{schemaName}, &queryResult)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query enums meta data: %w", err)
|
||||
}
|
||||
|
||||
var ret []metadata.Enum
|
||||
|
||||
for _, result := range queryResult {
|
||||
enumValues := strings.Replace(result.Values[1:len(result.Values)-1], "'", "", -1)
|
||||
|
||||
ret = append(ret, metadata.Enum{
|
||||
Name: result.Name,
|
||||
Values: strings.Split(enumValues, ","),
|
||||
})
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
93
tools/jet-2.12.0/generator/postgres/postgres_generator.go
Normal file
93
tools/jet-2.12.0/generator/postgres/postgres_generator.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/generator/template"
|
||||
"github.com/go-jet/jet/v2/postgres"
|
||||
"github.com/jackc/pgconn"
|
||||
)
|
||||
|
||||
// DBConnection contains postgres connection details
|
||||
type DBConnection struct {
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Password string
|
||||
SslMode string
|
||||
Params string
|
||||
|
||||
DBName string
|
||||
SchemaName string
|
||||
}
|
||||
|
||||
// Generate generates jet files at destination dir from database connection details
|
||||
func Generate(destDir string, dbConn DBConnection, genTemplate ...template.Template) (err error) {
|
||||
dsn := fmt.Sprintf("postgresql://%s:%s@%s:%s/%s?sslmode=%s",
|
||||
url.PathEscape(dbConn.User),
|
||||
url.PathEscape(dbConn.Password),
|
||||
dbConn.Host,
|
||||
strconv.Itoa(dbConn.Port),
|
||||
url.PathEscape(dbConn.DBName),
|
||||
dbConn.SslMode,
|
||||
)
|
||||
|
||||
return GenerateDSN(dsn, dbConn.SchemaName, destDir, genTemplate...)
|
||||
}
|
||||
|
||||
// GenerateDSN generates jet files using dsn connection string
|
||||
func GenerateDSN(dsn, schema, destDir string, templates ...template.Template) error {
|
||||
cfg, err := pgconn.ParseConfig(dsn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse config: %w", err)
|
||||
}
|
||||
if cfg.Database == "" {
|
||||
return fmt.Errorf("database name is required")
|
||||
}
|
||||
db, err := openConnection(dsn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open db connection: %w", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
fmt.Println("Retrieving schema information...")
|
||||
generatorTemplate := template.Default(postgres.Dialect)
|
||||
if len(templates) > 0 {
|
||||
generatorTemplate = templates[0]
|
||||
}
|
||||
|
||||
schemaMetadata, err := metadata.GetSchema(db, &postgresQuerySet{}, schema)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get '%s' schema metadata: %w", schema, err)
|
||||
}
|
||||
|
||||
dirPath := path.Join(destDir, cfg.Database)
|
||||
|
||||
err = template.ProcessSchema(dirPath, schemaMetadata, generatorTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate schema %s: %d", schemaMetadata.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func openConnection(dsn string) (*sql.DB, error) {
|
||||
fmt.Println("Connecting to postgres database...")
|
||||
|
||||
db, err := sql.Open("postgres", dsn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open db connection: %w", err)
|
||||
}
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to ping database: %w", err)
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
121
tools/jet-2.12.0/generator/postgres/query_set.go
Normal file
121
tools/jet-2.12.0/generator/postgres/query_set.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/qrm"
|
||||
)
|
||||
|
||||
// postgresQuerySet is dialect query set for PostgreSQL
|
||||
type postgresQuerySet struct{}
|
||||
|
||||
func (p postgresQuerySet) GetTablesMetaData(db *sql.DB, schemaName string, tableType metadata.TableType) ([]metadata.Table, error) {
|
||||
query := `
|
||||
SELECT table_name as "table.name", obj_description((quote_ident(table_schema)||'.'||quote_ident(table_name))::regclass) as "table.comment"
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = $1 and table_type = $2
|
||||
ORDER BY table_name;
|
||||
`
|
||||
var tables []metadata.Table
|
||||
|
||||
_, err := qrm.Query(context.Background(), db, query, []interface{}{schemaName, tableType}, &tables)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query %s metadata: %w", tableType, err)
|
||||
}
|
||||
|
||||
// add materialized views separately, because materialized views are not part of standard information schema
|
||||
if tableType == metadata.ViewTable {
|
||||
matViewQuery := `
|
||||
select matviewname as "table.name"
|
||||
from pg_matviews
|
||||
where schemaname = $1;
|
||||
`
|
||||
var matViews []metadata.Table
|
||||
|
||||
_, err := qrm.Query(context.Background(), db, matViewQuery, []interface{}{schemaName}, &matViews)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query materialized view metadata: %w", err)
|
||||
}
|
||||
|
||||
tables = append(tables, matViews...)
|
||||
}
|
||||
|
||||
for i := range tables {
|
||||
tables[i].Columns, err = getColumnsMetaData(db, schemaName, tables[i].Name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query %s columns metadata: %w", tableType, err)
|
||||
}
|
||||
}
|
||||
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
func getColumnsMetaData(db *sql.DB, schemaName string, tableName string) ([]metadata.Column, error) {
|
||||
query := `
|
||||
select
|
||||
attr.attname as "column.Name",
|
||||
col_description(attr.attrelid, attr.attnum) as "column.Comment",
|
||||
exists(
|
||||
select 1
|
||||
from pg_catalog.pg_index indx
|
||||
where attr.attrelid = indx.indrelid and attr.attnum = any(indx.indkey) and indx.indisprimary
|
||||
) as "column.IsPrimaryKey",
|
||||
not attr.attnotnull as "column.isNullable",
|
||||
attr.attgenerated = 's' as "column.isGenerated",
|
||||
attr.atthasdef as "column.hasDefault",
|
||||
(case
|
||||
when tp.typtype = 'b' AND tp.typcategory <> 'A' then 'base'
|
||||
when tp.typtype = 'b' AND tp.typcategory = 'A' then 'array'
|
||||
when tp.typtype = 'd' then 'base'
|
||||
when tp.typtype = 'e' then 'enum'
|
||||
when tp.typtype = 'r' then 'range'
|
||||
end) as "dataType.Kind",
|
||||
(case when tp.typtype = 'd' then (select pg_type.typname from pg_catalog.pg_type where pg_type.oid = tp.typbasetype)
|
||||
when tp.typcategory = 'A' then pg_catalog.format_type(attr.atttypid, attr.atttypmod)
|
||||
else tp.typname
|
||||
end) as "dataType.Name",
|
||||
false as "dataType.isUnsigned"
|
||||
from pg_catalog.pg_attribute as attr
|
||||
join pg_catalog.pg_class as cls on cls.oid = attr.attrelid
|
||||
join pg_catalog.pg_namespace as ns on ns.oid = cls.relnamespace
|
||||
join pg_catalog.pg_type as tp on tp.oid = attr.atttypid
|
||||
where
|
||||
ns.nspname = $1 and
|
||||
cls.relname = $2 and
|
||||
not attr.attisdropped and
|
||||
attr.attnum > 0
|
||||
order by
|
||||
attr.attnum;
|
||||
`
|
||||
var columns []metadata.Column
|
||||
_, err := qrm.Query(context.Background(), db, query, []interface{}{schemaName, tableName}, &columns)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query '%s' columns metadata: %w", tableName, err)
|
||||
}
|
||||
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func (p postgresQuerySet) GetEnumsMetaData(db *sql.DB, schemaName string) ([]metadata.Enum, error) {
|
||||
query := `
|
||||
SELECT t.typname as "enum.name",
|
||||
obj_description(t.oid) as "enum.comment",
|
||||
e.enumlabel as "values"
|
||||
FROM pg_catalog.pg_type t
|
||||
JOIN pg_catalog.pg_enum e on t.oid = e.enumtypid
|
||||
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
||||
WHERE n.nspname = $1
|
||||
ORDER BY n.nspname, t.typname, e.enumsortorder;`
|
||||
|
||||
var result []metadata.Enum
|
||||
|
||||
_, err := qrm.Query(context.Background(), db, query, []interface{}{schemaName}, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query enums metadata for schema '%s': %w", schemaName, err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
122
tools/jet-2.12.0/generator/sqlite/query_set.go
Normal file
122
tools/jet-2.12.0/generator/sqlite/query_set.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/internal/utils/semantic"
|
||||
"github.com/go-jet/jet/v2/qrm"
|
||||
)
|
||||
|
||||
// sqliteQuerySet is dialect query set for SQLite
|
||||
type sqliteQuerySet struct{}
|
||||
|
||||
func (p sqliteQuerySet) GetTablesMetaData(db *sql.DB, schemaName string, tableType metadata.TableType) ([]metadata.Table, error) {
|
||||
query := `
|
||||
SELECT name as "table.name"
|
||||
FROM sqlite_master
|
||||
WHERE type=? AND name != 'sqlite_sequence'
|
||||
ORDER BY name;
|
||||
`
|
||||
sqlTableType := "table"
|
||||
|
||||
if tableType == metadata.ViewTable {
|
||||
sqlTableType = "view"
|
||||
}
|
||||
|
||||
var tables []metadata.Table
|
||||
|
||||
_, err := qrm.Query(context.Background(), db, query, []interface{}{sqlTableType}, &tables)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query %s metadata: %w", schemaName, err)
|
||||
}
|
||||
|
||||
for i := range tables {
|
||||
tables[i].Columns, err = p.GetTableColumnsMetaData(db, schemaName, tables[i].Name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query column metadata: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
func getTableInfoQuery(db *sql.DB) (string, error) {
|
||||
var version string
|
||||
err := db.QueryRow("select sqlite_version();").Scan(&version)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get sqlite version: %w", err)
|
||||
}
|
||||
|
||||
sqliteVersion, err := semantic.VersionFromString(version)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't parse sqlite version: %w", err)
|
||||
}
|
||||
|
||||
// generated columns were added in version 3.26.0
|
||||
if sqliteVersion.Lt(semantic.Version{Major: 3, Minor: 26, Patch: 0}) {
|
||||
return `select * from pragma_table_info(?);`, nil
|
||||
}
|
||||
|
||||
return `select * from pragma_table_xinfo(?);`, nil
|
||||
}
|
||||
|
||||
func (p sqliteQuerySet) GetTableColumnsMetaData(db *sql.DB, schemaName string, tableName string) ([]metadata.Column, error) {
|
||||
|
||||
tableInfoQuery, err := getTableInfoQuery(db)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var columnInfos []struct {
|
||||
Name string
|
||||
Type string
|
||||
NotNull int32
|
||||
DfltValue string
|
||||
Pk int32
|
||||
Hidden int32
|
||||
}
|
||||
|
||||
_, err = qrm.Query(context.Background(), db, tableInfoQuery, []interface{}{tableName}, &columnInfos)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query '%s' column metadata: %w", tableName, err)
|
||||
}
|
||||
|
||||
var columns []metadata.Column
|
||||
|
||||
for _, columnInfo := range columnInfos {
|
||||
columnType := strings.TrimSuffix(getColumnType(columnInfo.Type), " GENERATED ALWAYS")
|
||||
isGenerated := columnInfo.Hidden == 2 || columnInfo.Hidden == 3 // stored or virtual column
|
||||
hasDefault := columnInfo.DfltValue != ""
|
||||
|
||||
columns = append(columns, metadata.Column{
|
||||
Name: columnInfo.Name,
|
||||
IsPrimaryKey: columnInfo.Pk != 0,
|
||||
IsNullable: columnInfo.NotNull != 1,
|
||||
IsGenerated: isGenerated,
|
||||
HasDefault: hasDefault,
|
||||
DataType: metadata.DataType{
|
||||
Name: columnType,
|
||||
Kind: metadata.BaseType,
|
||||
IsUnsigned: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
// will convert VARCHAR(10) -> VARCHAR, etc...
|
||||
func getColumnType(columnType string) string {
|
||||
return strings.TrimSpace(strings.Split(columnType, "(")[0])
|
||||
}
|
||||
|
||||
func (p sqliteQuerySet) GetEnumsMetaData(db *sql.DB, schemaName string) ([]metadata.Enum, error) {
|
||||
return nil, nil
|
||||
}
|
||||
37
tools/jet-2.12.0/generator/sqlite/sqlite_generator.go
Normal file
37
tools/jet-2.12.0/generator/sqlite/sqlite_generator.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/generator/template"
|
||||
"github.com/go-jet/jet/v2/sqlite"
|
||||
)
|
||||
|
||||
// GenerateDSN generates jet files using dsn connection string
|
||||
func GenerateDSN(dsn, destDir string, templates ...template.Template) error {
|
||||
db, err := sql.Open("sqlite3", dsn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open sqlite connection: %w", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
fmt.Println("Retrieving schema information...")
|
||||
|
||||
generatorTemplate := template.Default(sqlite.Dialect)
|
||||
if len(templates) > 0 {
|
||||
generatorTemplate = templates[0]
|
||||
}
|
||||
|
||||
schemaMetadata, err := metadata.GetSchema(db, &sqliteQuerySet{}, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to query database metadata: %w", err)
|
||||
}
|
||||
|
||||
err = template.ProcessSchema(destDir, schemaMetadata, generatorTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process database %s: %w", schemaMetadata.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
196
tools/jet-2.12.0/generator/template/file_templates.go
Normal file
196
tools/jet-2.12.0/generator/template/file_templates.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package template
|
||||
|
||||
var autoGenWarningTemplate = `
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
`
|
||||
|
||||
var tableSQLBuilderTemplate = `
|
||||
{{define "column-list" -}}
|
||||
{{- range $i, $c := . }}
|
||||
{{- $field := columnField $c}}
|
||||
{{- if gt $i 0 }}, {{end}}{{$field.Name}}Column
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
||||
package {{package}}
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/{{dialect.PackageName}}"
|
||||
)
|
||||
|
||||
var {{tableTemplate.InstanceName}} = new{{tableTemplate.TypeName}}("{{schemaName}}", "{{.Name}}", "{{tableTemplate.DefaultAlias}}")
|
||||
|
||||
{{golangComment .Comment}}
|
||||
type {{structImplName}} struct {
|
||||
{{dialect.PackageName}}.Table
|
||||
|
||||
// Columns
|
||||
{{- range $i, $c := .Columns}}
|
||||
{{- $field := columnField $c}}
|
||||
{{$field.Name}} {{dialect.PackageName}}.Column{{$field.Type}} {{golangComment .Comment}}
|
||||
{{- end}}
|
||||
|
||||
AllColumns {{dialect.PackageName}}.ColumnList
|
||||
MutableColumns {{dialect.PackageName}}.ColumnList
|
||||
}
|
||||
|
||||
type {{tableTemplate.TypeName}} struct {
|
||||
{{structImplName}}
|
||||
|
||||
{{toUpper insertedRowAlias}} {{structImplName}}
|
||||
}
|
||||
|
||||
// AS creates new {{tableTemplate.TypeName}} with assigned alias
|
||||
func (a {{tableTemplate.TypeName}}) AS(alias string) *{{tableTemplate.TypeName}} {
|
||||
return new{{tableTemplate.TypeName}}(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new {{tableTemplate.TypeName}} with assigned schema name
|
||||
func (a {{tableTemplate.TypeName}}) FromSchema(schemaName string) *{{tableTemplate.TypeName}} {
|
||||
return new{{tableTemplate.TypeName}}(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
// WithPrefix creates new {{tableTemplate.TypeName}} with assigned table prefix
|
||||
func (a {{tableTemplate.TypeName}}) WithPrefix(prefix string) *{{tableTemplate.TypeName}} {
|
||||
return new{{tableTemplate.TypeName}}(a.SchemaName(), prefix+a.TableName(), a.TableName())
|
||||
}
|
||||
|
||||
// WithSuffix creates new {{tableTemplate.TypeName}} with assigned table suffix
|
||||
func (a {{tableTemplate.TypeName}}) WithSuffix(suffix string) *{{tableTemplate.TypeName}} {
|
||||
return new{{tableTemplate.TypeName}}(a.SchemaName(), a.TableName()+suffix, a.TableName())
|
||||
}
|
||||
|
||||
func new{{tableTemplate.TypeName}}(schemaName, tableName, alias string) *{{tableTemplate.TypeName}} {
|
||||
return &{{tableTemplate.TypeName}}{
|
||||
{{structImplName}}: new{{tableTemplate.TypeName}}Impl(schemaName, tableName, alias),
|
||||
{{toUpper insertedRowAlias}}: new{{tableTemplate.TypeName}}Impl("", "{{insertedRowAlias}}", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func new{{tableTemplate.TypeName}}Impl(schemaName, tableName, alias string) {{structImplName}} {
|
||||
var (
|
||||
{{- range $i, $c := .Columns}}
|
||||
{{- $field := columnField $c}}
|
||||
{{$field.Name}}Column = {{dialect.PackageName}}.{{$field.Type}}Column("{{$c.Name}}")
|
||||
{{- end}}
|
||||
allColumns = {{dialect.PackageName}}.ColumnList{ {{template "column-list" .Columns}} }
|
||||
mutableColumns = {{dialect.PackageName}}.ColumnList{ {{template "column-list" .MutableColumns}} }
|
||||
)
|
||||
|
||||
return {{structImplName}}{
|
||||
Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
{{- range $i, $c := .Columns}}
|
||||
{{- $field := columnField $c}}
|
||||
{{$field.Name}}: {{$field.Name}}Column,
|
||||
{{- end}}
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var tableSqlBuilderSetSchemaTemplate = `package {{package}}
|
||||
|
||||
// UseSchema sets a new schema name for all generated {{type}} SQL builder types. It is recommended to invoke
|
||||
// this method only once at the beginning of the program.
|
||||
func UseSchema(schema string) {
|
||||
{{- range .}}
|
||||
{{ .InstanceName }} = {{ .InstanceName }}.FromSchema(schema)
|
||||
{{- end}}
|
||||
}
|
||||
`
|
||||
|
||||
var tableModelFileTemplate = `package {{package}}
|
||||
|
||||
{{ with modelImports }}
|
||||
import (
|
||||
{{- range .}}
|
||||
"{{.}}"
|
||||
{{- end}}
|
||||
)
|
||||
{{end}}
|
||||
|
||||
{{$modelTableTemplate := tableTemplate}}
|
||||
{{golangComment .Comment}}
|
||||
type {{$modelTableTemplate.TypeName}} struct {
|
||||
{{- range .Columns}}
|
||||
{{- $field := structField .}}
|
||||
{{$field.Name}} {{$field.Type.Name}} ` + "{{$field.TagsString}}" + ` {{golangComment .Comment}}
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
var enumSQLBuilderTemplate = `package {{package}}
|
||||
|
||||
import "github.com/go-jet/jet/v2/{{dialect.PackageName}}"
|
||||
|
||||
{{golangComment .Comment}}
|
||||
var {{enumTemplate.InstanceName}} = &struct {
|
||||
{{- range $index, $value := .Values}}
|
||||
{{enumValueName $value}} {{dialect.PackageName}}.StringExpression
|
||||
{{- end}}
|
||||
} {
|
||||
{{- range $index, $value := .Values}}
|
||||
{{enumValueName $value}}: {{dialect.PackageName}}.NewEnumValue("{{$value}}"),
|
||||
{{- end}}
|
||||
}
|
||||
`
|
||||
|
||||
var enumModelTemplate = `package {{package}}
|
||||
{{- $enumTemplate := enumTemplate}}
|
||||
|
||||
import "errors"
|
||||
|
||||
{{golangComment .Comment}}
|
||||
type {{$enumTemplate.TypeName}} string
|
||||
|
||||
const (
|
||||
{{- range $_, $value := .Values}}
|
||||
{{valueName $value}} {{$enumTemplate.TypeName}} = "{{$value}}"
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
var {{$enumTemplate.TypeName}}AllValues = []{{$enumTemplate.TypeName}} {
|
||||
{{- range $_, $value := .Values}}
|
||||
{{valueName $value}},
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
func (e *{{$enumTemplate.TypeName}}) Scan(value interface{}) error {
|
||||
var enumValue string
|
||||
switch val := value.(type) {
|
||||
case string:
|
||||
enumValue = val
|
||||
case []byte:
|
||||
enumValue = string(val)
|
||||
default:
|
||||
return errors.New("jet: Invalid scan value for AllTypesEnum enum. Enum value has to be of type string or []byte")
|
||||
}
|
||||
|
||||
switch enumValue {
|
||||
{{- range $_, $value := .Values}}
|
||||
case "{{$value}}":
|
||||
*e = {{valueName $value}}
|
||||
{{- end}}
|
||||
default:
|
||||
return errors.New("jet: Invalid scan value '" + enumValue + "' for {{$enumTemplate.TypeName}} enum")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e {{$enumTemplate.TypeName}}) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
`
|
||||
13
tools/jet-2.12.0/generator/template/format.go
Normal file
13
tools/jet-2.12.0/generator/template/format.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package template
|
||||
|
||||
import "regexp"
|
||||
|
||||
// Returns the provided string as golang comment without ascii control characters
|
||||
func formatGolangComment(comment string) string {
|
||||
if len(comment) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Format as colang comment and remove ascii control characters from string
|
||||
return "// " + regexp.MustCompile(`[[:cntrl:]]+`).ReplaceAllString(comment, "")
|
||||
}
|
||||
27
tools/jet-2.12.0/generator/template/format_test.go
Normal file
27
tools/jet-2.12.0/generator/template/format_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package template
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_formatGolangComment(t *testing.T) {
|
||||
type args struct {
|
||||
comment string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{name: "Empty string", args: args{comment: ""}, want: ""},
|
||||
{name: "Non-empty string", args: args{comment: "This is a comment"}, want: "// This is a comment"},
|
||||
{name: "String with control characters", args: args{comment: "This is a comment with control characters \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f and text after"}, want: "// This is a comment with control characters and text after"},
|
||||
{name: "String with escape characters", args: args{comment: "This is a comment with escape characters \n\r\t and text after"}, want: "// This is a comment with escape characters and text after"},
|
||||
{name: "String with unicode characters", args: args{comment: "This is a comment with unicode characters ₲鬼佬℧⇄↻ and text after"}, want: "// This is a comment with unicode characters ₲鬼佬℧⇄↻ and text after"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := formatGolangComment(tt.args.comment); got != tt.want {
|
||||
t.Errorf("formatGoLangComment() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
60
tools/jet-2.12.0/generator/template/generator_template.go
Normal file
60
tools/jet-2.12.0/generator/template/generator_template.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
)
|
||||
|
||||
// Template is generator template used for file generation
|
||||
type Template struct {
|
||||
Dialect jet.Dialect
|
||||
Schema func(schemaMetaData metadata.Schema) Schema
|
||||
}
|
||||
|
||||
// Default is default generator template implementation
|
||||
func Default(dialect jet.Dialect) Template {
|
||||
return Template{
|
||||
Dialect: dialect,
|
||||
Schema: DefaultSchema,
|
||||
}
|
||||
}
|
||||
|
||||
// UseSchema replaces current schema generate function with a new implementation and returns new generator template
|
||||
func (t Template) UseSchema(schemaFunc func(schemaMetaData metadata.Schema) Schema) Template {
|
||||
t.Schema = schemaFunc
|
||||
return t
|
||||
}
|
||||
|
||||
// Schema is schema generator template used to generate schema(model and sql builder) files
|
||||
type Schema struct {
|
||||
Path string
|
||||
Model Model
|
||||
SQLBuilder SQLBuilder
|
||||
}
|
||||
|
||||
// UsePath replaces path and returns new schema template
|
||||
func (s Schema) UsePath(path string) Schema {
|
||||
s.Path = path
|
||||
return s
|
||||
}
|
||||
|
||||
// UseModel returns new schema template with replaced template for model files generation
|
||||
func (s Schema) UseModel(model Model) Schema {
|
||||
s.Model = model
|
||||
return s
|
||||
}
|
||||
|
||||
// UseSQLBuilder returns new schema with replaced template for sql builder files generation
|
||||
func (s Schema) UseSQLBuilder(sqlBuilder SQLBuilder) Schema {
|
||||
s.SQLBuilder = sqlBuilder
|
||||
return s
|
||||
}
|
||||
|
||||
// DefaultSchema returns default schema template implementation
|
||||
func DefaultSchema(schemaMetaData metadata.Schema) Schema {
|
||||
return Schema{
|
||||
Path: schemaMetaData.Name,
|
||||
Model: DefaultModel(),
|
||||
SQLBuilder: DefaultSQLBuilder(),
|
||||
}
|
||||
}
|
||||
340
tools/jet-2.12.0/generator/template/model_template.go
Normal file
340
tools/jet-2.12.0/generator/template/model_template.go
Normal file
@@ -0,0 +1,340 @@
|
||||
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 ""
|
||||
}
|
||||
}
|
||||
45
tools/jet-2.12.0/generator/template/model_template_test.go
Normal file
45
tools/jet-2.12.0/generator/template/model_template_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_TableModelField(t *testing.T) {
|
||||
require.Equal(t, DefaultTableModelField(metadata.Column{
|
||||
Name: "col_name",
|
||||
IsPrimaryKey: true,
|
||||
IsNullable: true,
|
||||
DataType: metadata.DataType{
|
||||
Name: "smallint",
|
||||
Kind: "base",
|
||||
IsUnsigned: true,
|
||||
},
|
||||
}), TableModelField{
|
||||
Name: "ColName",
|
||||
Type: Type{
|
||||
ImportPath: "",
|
||||
Name: "*uint16",
|
||||
},
|
||||
Tags: []string{"sql:\"primary_key\""},
|
||||
})
|
||||
|
||||
require.Equal(t, DefaultTableModelField(metadata.Column{
|
||||
Name: "time_column_1",
|
||||
IsPrimaryKey: false,
|
||||
IsNullable: true,
|
||||
DataType: metadata.DataType{
|
||||
Name: "timestamp with time zone",
|
||||
Kind: "base",
|
||||
IsUnsigned: false,
|
||||
},
|
||||
}), TableModelField{
|
||||
Name: "TimeColumn1",
|
||||
Type: Type{
|
||||
ImportPath: "time",
|
||||
Name: "*time.Time",
|
||||
},
|
||||
Tags: nil,
|
||||
})
|
||||
}
|
||||
382
tools/jet-2.12.0/generator/template/process.go
Normal file
382
tools/jet-2.12.0/generator/template/process.go
Normal file
@@ -0,0 +1,382 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/internal/utils/filesys"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
)
|
||||
|
||||
// ProcessSchema will process schema metadata and constructs go files using generator Template
|
||||
func ProcessSchema(dirPath string, schemaMetaData metadata.Schema, generatorTemplate Template) error {
|
||||
if schemaMetaData.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
schemaTemplate := generatorTemplate.Schema(schemaMetaData)
|
||||
schemaPath := path.Join(dirPath, schemaTemplate.Path)
|
||||
|
||||
fmt.Println("Destination directory:", schemaPath)
|
||||
fmt.Println("Cleaning up destination directory...")
|
||||
err := filesys.RemoveDir(schemaPath)
|
||||
if err != nil {
|
||||
return errors.New("failed to cleanup generated files")
|
||||
}
|
||||
|
||||
err = processModel(schemaPath, schemaMetaData, schemaTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate model types: %w", err)
|
||||
}
|
||||
|
||||
err = processSQLBuilder(schemaPath, generatorTemplate.Dialect, schemaMetaData, schemaTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate sql builder types: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processModel(dirPath string, schemaMetaData metadata.Schema, schemaTemplate Schema) error {
|
||||
modelTemplate := schemaTemplate.Model
|
||||
|
||||
if modelTemplate.Skip {
|
||||
fmt.Println("Skipping the generation of model types.")
|
||||
return nil
|
||||
}
|
||||
|
||||
modelDirPath := path.Join(dirPath, modelTemplate.Path)
|
||||
|
||||
err := filesys.EnsureDirPathExist(modelDirPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("destination dir path does not exist: %w", err)
|
||||
}
|
||||
|
||||
err = processTableModels("table", modelDirPath, schemaMetaData.TablesMetaData, modelTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate table model types: %w", err)
|
||||
}
|
||||
|
||||
err = processTableModels("view", modelDirPath, schemaMetaData.ViewsMetaData, modelTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate view model types: %w", err)
|
||||
}
|
||||
|
||||
err = processEnumModels(modelDirPath, schemaMetaData.EnumsMetaData, modelTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process enum types: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processSQLBuilder(dirPath string, dialect jet.Dialect, schemaMetaData metadata.Schema, schemaTemplate Schema) error {
|
||||
sqlBuilderTemplate := schemaTemplate.SQLBuilder
|
||||
|
||||
if sqlBuilderTemplate.Skip {
|
||||
fmt.Println("Skipping the generation of SQL Builder types.")
|
||||
return nil
|
||||
}
|
||||
|
||||
sqlBuilderPath := path.Join(dirPath, sqlBuilderTemplate.Path)
|
||||
|
||||
err := processTableSQLBuilder("table", sqlBuilderPath, dialect, schemaMetaData, schemaMetaData.TablesMetaData, sqlBuilderTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process table sql builder types: %w", err)
|
||||
}
|
||||
|
||||
err = processTableSQLBuilder("view", sqlBuilderPath, dialect, schemaMetaData, schemaMetaData.ViewsMetaData, sqlBuilderTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process view sql builder types: %w", err)
|
||||
}
|
||||
|
||||
err = processEnumSQLBuilder(sqlBuilderPath, dialect, schemaMetaData.EnumsMetaData, sqlBuilderTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process enum types: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processEnumSQLBuilder(dirPath string, dialect jet.Dialect, enumsMetaData []metadata.Enum, sqlBuilder SQLBuilder) error {
|
||||
if len(enumsMetaData) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Generating enum sql builder files\n")
|
||||
|
||||
for _, enumMetaData := range enumsMetaData {
|
||||
enumTemplate := sqlBuilder.Enum(enumMetaData)
|
||||
|
||||
if enumTemplate.Skip {
|
||||
continue
|
||||
}
|
||||
|
||||
enumSQLBuilderPath := path.Join(dirPath, enumTemplate.Path)
|
||||
|
||||
err := filesys.EnsureDirPathExist(enumSQLBuilderPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create enum sql builder directory - %s: %w", enumSQLBuilderPath, err)
|
||||
}
|
||||
|
||||
text, err := generateTemplate(
|
||||
autoGenWarningTemplate+enumSQLBuilderTemplate,
|
||||
enumMetaData,
|
||||
template.FuncMap{
|
||||
"package": func() string {
|
||||
return enumTemplate.PackageName()
|
||||
},
|
||||
"dialect": func() jet.Dialect {
|
||||
return dialect
|
||||
},
|
||||
"enumTemplate": func() EnumSQLBuilder {
|
||||
return enumTemplate
|
||||
},
|
||||
"enumValueName": func(enumValue string) string {
|
||||
return enumTemplate.ValueName(enumValue)
|
||||
},
|
||||
"golangComment": formatGolangComment,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generete enum type %s: %w", enumTemplate.FileName, err)
|
||||
}
|
||||
|
||||
err = filesys.FormatAndSaveGoFile(enumSQLBuilderPath, enumTemplate.FileName, text)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to format and save '%s' enum type : %w", enumTemplate.FileName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processTableSQLBuilder(fileTypes, dirPath string,
|
||||
dialect jet.Dialect,
|
||||
schemaMetaData metadata.Schema,
|
||||
tablesMetaData []metadata.Table,
|
||||
sqlBuilderTemplate SQLBuilder) error {
|
||||
|
||||
if len(tablesMetaData) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Generating %s sql builder files\n", fileTypes)
|
||||
|
||||
var generatedBuilders []TableSQLBuilder
|
||||
|
||||
for _, tableMetaData := range tablesMetaData {
|
||||
var tableSQLBuilder TableSQLBuilder
|
||||
|
||||
if fileTypes == "view" {
|
||||
tableSQLBuilder = sqlBuilderTemplate.View(tableMetaData)
|
||||
} else {
|
||||
tableSQLBuilder = sqlBuilderTemplate.Table(tableMetaData)
|
||||
}
|
||||
|
||||
if tableSQLBuilder.Skip {
|
||||
continue
|
||||
}
|
||||
|
||||
tableSQLBuilderPath := path.Join(dirPath, tableSQLBuilder.Path)
|
||||
|
||||
err := filesys.EnsureDirPathExist(tableSQLBuilderPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create table sql builder directory - %s: %w", tableSQLBuilderPath, err)
|
||||
}
|
||||
|
||||
text, err := generateTemplate(
|
||||
autoGenWarningTemplate+tableSQLBuilderTemplate,
|
||||
tableMetaData,
|
||||
template.FuncMap{
|
||||
"package": func() string {
|
||||
return tableSQLBuilder.PackageName()
|
||||
},
|
||||
"dialect": func() jet.Dialect {
|
||||
return dialect
|
||||
},
|
||||
"schemaName": func() string {
|
||||
return schemaMetaData.Name
|
||||
},
|
||||
"tableTemplate": func() TableSQLBuilder {
|
||||
return tableSQLBuilder
|
||||
},
|
||||
"structImplName": func() string { // postgres only
|
||||
structName := tableSQLBuilder.TypeName
|
||||
return string(strings.ToLower(structName)[0]) + structName[1:]
|
||||
},
|
||||
"columnField": func(columnMetaData metadata.Column) TableSQLBuilderColumn {
|
||||
return tableSQLBuilder.Column(columnMetaData)
|
||||
},
|
||||
"toUpper": strings.ToUpper,
|
||||
"insertedRowAlias": func() string {
|
||||
return insertedRowAlias(dialect)
|
||||
},
|
||||
"golangComment": formatGolangComment,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate table sql builder type %s: %w", tableSQLBuilder.TypeName, err)
|
||||
}
|
||||
|
||||
err = filesys.FormatAndSaveGoFile(tableSQLBuilderPath, tableSQLBuilder.FileName, text)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to format and save generated sql builder type '%s': %w", tableSQLBuilder.FileName, err)
|
||||
}
|
||||
|
||||
generatedBuilders = append(generatedBuilders, tableSQLBuilder)
|
||||
}
|
||||
|
||||
err := generateUseSchemaFunc(dirPath, fileTypes, generatedBuilders)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate UseSchema function")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateUseSchemaFunc(dirPath, fileTypes string, builders []TableSQLBuilder) error {
|
||||
if len(builders) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
text, err := generateTemplate(
|
||||
autoGenWarningTemplate+tableSqlBuilderSetSchemaTemplate,
|
||||
builders,
|
||||
template.FuncMap{
|
||||
"package": func() string { return builders[0].PackageName() },
|
||||
"type": func() string { return fileTypes },
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate use schema template: %w", err)
|
||||
}
|
||||
|
||||
basePath := path.Join(dirPath, builders[0].Path)
|
||||
fileName := fileTypes + "_use_schema"
|
||||
|
||||
err = filesys.FormatAndSaveGoFile(basePath, fileName, text)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save %s file: %w", fileName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func insertedRowAlias(dialect jet.Dialect) string {
|
||||
if dialect.Name() == "MySQL" {
|
||||
return "new"
|
||||
}
|
||||
|
||||
return "excluded"
|
||||
}
|
||||
|
||||
func processTableModels(fileTypes, modelDirPath string, tablesMetaData []metadata.Table, modelTemplate Model) error {
|
||||
if len(tablesMetaData) == 0 {
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("Generating %s model files...\n", fileTypes)
|
||||
|
||||
for _, tableMetaData := range tablesMetaData {
|
||||
var tableTemplate TableModel
|
||||
|
||||
if fileTypes == "table" {
|
||||
tableTemplate = modelTemplate.Table(tableMetaData)
|
||||
} else {
|
||||
tableTemplate = modelTemplate.View(tableMetaData)
|
||||
}
|
||||
|
||||
if tableTemplate.Skip {
|
||||
continue
|
||||
}
|
||||
|
||||
text, err := generateTemplate(
|
||||
autoGenWarningTemplate+tableModelFileTemplate,
|
||||
tableMetaData,
|
||||
template.FuncMap{
|
||||
"package": func() string {
|
||||
return modelTemplate.PackageName()
|
||||
},
|
||||
"modelImports": func() []string {
|
||||
return getTableModelImports(tableTemplate, tableMetaData)
|
||||
},
|
||||
"tableTemplate": func() TableModel {
|
||||
return tableTemplate
|
||||
},
|
||||
"structField": func(columnMetaData metadata.Column) TableModelField {
|
||||
return tableTemplate.Field(columnMetaData)
|
||||
},
|
||||
"golangComment": formatGolangComment,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate model type '%s': %w", tableMetaData.Name, err)
|
||||
}
|
||||
|
||||
err = filesys.FormatAndSaveGoFile(modelDirPath, tableTemplate.FileName, text)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save '%s' model type: %w", tableTemplate.FileName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processEnumModels(modelDir string, enumsMetaData []metadata.Enum, modelTemplate Model) error {
|
||||
if len(enumsMetaData) == 0 {
|
||||
return nil
|
||||
}
|
||||
fmt.Print("Generating enum model files...\n")
|
||||
|
||||
for _, enumMetaData := range enumsMetaData {
|
||||
enumTemplate := modelTemplate.Enum(enumMetaData)
|
||||
|
||||
if enumTemplate.Skip {
|
||||
continue
|
||||
}
|
||||
|
||||
text, err := generateTemplate(
|
||||
autoGenWarningTemplate+enumModelTemplate,
|
||||
enumMetaData,
|
||||
template.FuncMap{
|
||||
"package": func() string {
|
||||
return modelTemplate.PackageName()
|
||||
},
|
||||
"enumTemplate": func() EnumModel {
|
||||
return enumTemplate
|
||||
},
|
||||
"valueName": func(value string) string {
|
||||
return enumTemplate.ValueName(value)
|
||||
},
|
||||
"golangComment": formatGolangComment,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate enum type '%s': %w", enumMetaData.Name, err)
|
||||
}
|
||||
|
||||
err = filesys.FormatAndSaveGoFile(modelDir, enumTemplate.FileName, text)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save '%s' enum type: %w", enumTemplate.FileName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateTemplate(templateText string, templateData interface{}, funcMap template.FuncMap) ([]byte, error) {
|
||||
t, err := template.New("sqlBuilderTableTemplate").Funcs(funcMap).Parse(templateText)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse template: %w", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := t.Execute(&buf, templateData); err != nil {
|
||||
return nil, fmt.Errorf("failed to generate template: %w", err)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
259
tools/jet-2.12.0/generator/template/sql_builder_template.go
Normal file
259
tools/jet-2.12.0/generator/template/sql_builder_template.go
Normal file
@@ -0,0 +1,259 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/internal/utils/dbidentifier"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// TableSQLBuilder is template for generating table SQLBuilder files
|
||||
type TableSQLBuilder struct {
|
||||
Skip bool
|
||||
Path string
|
||||
FileName string
|
||||
InstanceName string
|
||||
TypeName string
|
||||
DefaultAlias string
|
||||
Column func(columnMetaData metadata.Column) TableSQLBuilderColumn
|
||||
}
|
||||
|
||||
// ViewSQLBuilder is template for generating view SQLBuilder files
|
||||
type ViewSQLBuilder = TableSQLBuilder
|
||||
|
||||
// DefaultTableSQLBuilder returns default implementation for TableSQLBuilder
|
||||
func DefaultTableSQLBuilder(tableMetaData metadata.Table) TableSQLBuilder {
|
||||
tableNameGoIdentifier := dbidentifier.ToGoIdentifier(tableMetaData.Name)
|
||||
|
||||
return TableSQLBuilder{
|
||||
Path: "/table",
|
||||
FileName: dbidentifier.ToGoFileName(tableMetaData.Name),
|
||||
InstanceName: tableNameGoIdentifier,
|
||||
TypeName: tableNameGoIdentifier + "Table",
|
||||
DefaultAlias: "",
|
||||
Column: DefaultTableSQLBuilderColumn,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return path.Base(tb.Path)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// UseDefaultAlias returns new TableSQLBuilder with new default alias set
|
||||
func (tb TableSQLBuilder) UseDefaultAlias(defaultAlias string) TableSQLBuilder {
|
||||
tb.DefaultAlias = defaultAlias
|
||||
return tb
|
||||
}
|
||||
|
||||
// UseColumn returns new TableSQLBuilder with new column template function set
|
||||
func (tb TableSQLBuilder) UseColumn(columnsFunc func(column metadata.Column) TableSQLBuilderColumn) TableSQLBuilder {
|
||||
tb.Column = columnsFunc
|
||||
return tb
|
||||
}
|
||||
|
||||
// TableSQLBuilderColumn is template for table sql builder column
|
||||
type TableSQLBuilderColumn struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
var reservedKeywords = []string{"TableName", "Table", "SchemaName", "Alias", "AllColumns", "MutableColumns"}
|
||||
|
||||
func renameIfReserved(name string) string {
|
||||
if slices.Contains(reservedKeywords, name) {
|
||||
return name + "_"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// DefaultTableSQLBuilderColumn returns default implementation of TableSQLBuilderColumn
|
||||
func DefaultTableSQLBuilderColumn(columnMetaData metadata.Column) TableSQLBuilderColumn {
|
||||
return TableSQLBuilderColumn{
|
||||
Name: renameIfReserved(dbidentifier.ToGoIdentifier(columnMetaData.Name)),
|
||||
Type: getSqlBuilderColumnType(columnMetaData),
|
||||
}
|
||||
}
|
||||
|
||||
// getSqlBuilderColumnType returns type of jet sql builder column
|
||||
func getSqlBuilderColumnType(columnMetaData metadata.Column) string {
|
||||
if columnMetaData.DataType.Kind != metadata.BaseType &&
|
||||
columnMetaData.DataType.Kind != metadata.RangeType {
|
||||
return "String"
|
||||
}
|
||||
|
||||
switch strings.ToLower(columnMetaData.DataType.Name) {
|
||||
case "boolean", "bool":
|
||||
return "Bool"
|
||||
case "smallint", "integer", "bigint", "int2", "int4", "int8",
|
||||
"tinyint", "mediumint", "int", "year": //MySQL
|
||||
return "Integer"
|
||||
case "date":
|
||||
return "Date"
|
||||
case "timestamp without time zone",
|
||||
"timestamp", "datetime": //MySQL:
|
||||
return "Timestamp"
|
||||
case "timestamp with time zone", "timestamptz":
|
||||
return "Timestampz"
|
||||
case "time without time zone",
|
||||
"time": //MySQL
|
||||
return "Time"
|
||||
case "time with time zone", "timetz":
|
||||
return "Timez"
|
||||
case "interval":
|
||||
return "Interval"
|
||||
case "user-defined", "enum", "text", "character", "character varying", "bytea", "uuid",
|
||||
"tsvector", "bit", "bit varying", "money", "json", "jsonb", "xml", "point", "line", "ARRAY",
|
||||
"char", "varchar", "nvarchar", "binary", "varbinary", "bpchar", "varbit",
|
||||
"tinyblob", "blob", "mediumblob", "longblob", "tinytext", "mediumtext", "longtext": // MySQL
|
||||
return "String"
|
||||
case "real", "numeric", "decimal", "double precision", "float", "float4", "float8",
|
||||
"double": // MySQL
|
||||
return "Float"
|
||||
case "daterange":
|
||||
return "DateRange"
|
||||
case "tsrange":
|
||||
return "TimestampRange"
|
||||
case "tstzrange":
|
||||
return "TimestampzRange"
|
||||
case "int4range":
|
||||
return "Int4Range"
|
||||
case "int8range":
|
||||
return "Int8Range"
|
||||
case "numrange":
|
||||
return "NumericRange"
|
||||
default:
|
||||
fmt.Println("- [SQL Builder] Unsupported sql column '" + columnMetaData.Name + " " + columnMetaData.DataType.Name + "', using StringColumn instead.")
|
||||
return "String"
|
||||
}
|
||||
}
|
||||
|
||||
// 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",
|
||||
FileName: dbidentifier.ToGoFileName(enumMetaData.Name),
|
||||
InstanceName: dbidentifier.ToGoIdentifier(enumMetaData.Name),
|
||||
ValueName: func(enumValue string) string {
|
||||
return defaultEnumValueName(enumMetaData.Name, enumValue)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// PackageName returns enum sql builder package name
|
||||
func (e EnumSQLBuilder) PackageName() string {
|
||||
return path.Base(e.Path)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
enumValueName := dbidentifier.ToGoIdentifier(enumValue)
|
||||
if !unicode.IsLetter([]rune(enumValueName)[0]) {
|
||||
return dbidentifier.ToGoIdentifier(enumName) + enumValueName
|
||||
}
|
||||
|
||||
return enumValueName
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestToGoEnumValueIdentifier(t *testing.T) {
|
||||
require.Equal(t, defaultEnumValueName("enum_name", "enum_value"), "EnumValue")
|
||||
require.Equal(t, defaultEnumValueName("NumEnum", "100"), "NumEnum100")
|
||||
}
|
||||
|
||||
func TestColumnRenameReserved(t *testing.T) {
|
||||
tests := []struct {
|
||||
col string
|
||||
want string
|
||||
}{
|
||||
{col: "TableName", want: "TableName_"},
|
||||
{col: "Table", want: "Table_"},
|
||||
{col: "SchemaName", want: "SchemaName_"},
|
||||
{col: "Alias", want: "Alias_"},
|
||||
{col: "AllColumns", want: "AllColumns_"},
|
||||
{col: "MutableColumns", want: "MutableColumns_"},
|
||||
{col: "OtherColumn", want: "OtherColumn"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.col, func(t *testing.T) {
|
||||
builder := DefaultTableSQLBuilderColumn(metadata.Column{
|
||||
Name: tt.col,
|
||||
})
|
||||
require.Equal(t, builder.Name, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user