package schema import ( "errors" "reflect" "strings" "git.gdulai.com/gdulai/simpleorm/util" ) type OrderBy struct { Field string Direction string } type Table struct { name string typ reflect.Type columns []Column constraints []Constraint } func (t Table) Name() string { return t.name } func (t Table) Type() reflect.Type { return t.typ } func (t Table) Columns() []Column { return t.columns } func (t Table) Constraints() []Constraint { return t.constraints } func (t Table) GetDDL() (string, error) { var ddl strings.Builder ddl.WriteString("CREATE TABLE IF NOT EXISTS ") ddl.WriteString(t.name) ddl.WriteString(" (") for i, col := range t.columns { if i != 0 { ddl.WriteString(", ") } colDdl, err := col.GetDDL() if err != nil { return "", err } ddl.WriteString(colDdl) } for _, constr := range t.constraints { constrDdl, err := constr.GetDDL() if err != nil { return "", err } ddl.WriteString(", ") ddl.WriteString(constrDdl) } ddl.WriteString(");") return ddl.String(), nil } func (t Table) GetSelectDML() (string, error) { var dml strings.Builder dml.WriteString("SELECT ") for i, col := range t.columns { if i != 0 { dml.WriteString(", ") } colDml, err := col.GetSelectDML() if err != nil { return "", err } dml.WriteString(colDml) } dml.WriteString(" FROM ") dml.WriteString(t.name) return dml.String(), nil } func (t Table) GetCountDML() string { return "SELECT COUNT(*) FROM " + t.name } func (t Table) GetInsertDML(count int) (string, error) { var dml strings.Builder dml.WriteString("INSERT INTO ") dml.WriteString(util.CamelToSnake(t.typ.Name())) dml.WriteString(" (") columnsLen := len(t.columns) effectiveColumnsLen := 0 for i, col := range t.columns { _, ok := col.Modifiers["pk"] if ok && t.IsPkAuto() { continue } colDml, err := col.GetInsertDML() if err != nil { return "", err } dml.WriteString(colDml) if i != columnsLen-1 { dml.WriteString(", ") } effectiveColumnsLen++ } dml.WriteString(") VALUES") for i := range count { if i != 0 { dml.WriteString(", ") } for j := range effectiveColumnsLen { if j == 0 { dml.WriteString("(") } if j != effectiveColumnsLen-1 { dml.WriteString("?, ") } else { dml.WriteString("?)") } } } dml.WriteString(" RETURNING *") return dml.String(), nil } func (t Table) GetUpdateDML() (string, error) { var dml strings.Builder dml.WriteString("UPDATE ") dml.WriteString(util.CamelToSnake(t.typ.Name())) dml.WriteString(" SET ") colLen := len(t.columns) var pkColumns []string // Colum names for i, col := range t.columns { modifiers := col.Modifiers _, ok := modifiers["pk"] if ok { pkColumns = append(pkColumns, col.Name) if t.IsPkAuto() { continue } } colDml, err := col.GetUpdateDML() if err != nil { return "", err } if i != colLen-1 { dml.WriteString(colDml) dml.WriteString(", ") } else { dml.WriteString(colDml) dml.WriteString(" ") } } dml.WriteString("WHERE ") for i, pkCol := range pkColumns { if i != 0 { dml.WriteString(" AND ") } dml.WriteString(pkCol) dml.WriteString(" = ?") } return dml.String(), nil } func (t Table) GetDeleteDML(count int) (string, error) { var dml strings.Builder dml.WriteString("DELETE FROM ") dml.WriteString(t.name) dml.WriteString(" WHERE ") for _, col := range t.columns { modifiers := col.Modifiers _, ok := modifiers["pk"] if ok { if count > 1 { dml.WriteString(col.Name) dml.WriteString(" IN (") } else { dml.WriteString(col.Name) dml.WriteString(" = ") } } } for i := range count { if i != 0 { dml.WriteString(", ") } dml.WriteString("?") } if count > 1 { dml.WriteString(")") } return dml.String(), nil } func (t Table) GetOrderByDML(ordering ...OrderBy) (string, error) { var dml strings.Builder dml.WriteString(" ORDER BY") for _, orderBy := range ordering { col, err := t.getColumnByField(orderBy.Field) if err != nil { return "", err } dml.WriteString(" ") dml.WriteString(col.Name) dml.WriteString(" ") dml.WriteString(orderBy.Direction) } return dml.String(), nil } func (t Table) getColumnByField(fieldName string) (Column, error) { for _, col := range t.columns { if col.FieldName == fieldName { return col, nil } } return Column{}, errors.New("No column for field " + fieldName + " in table " + t.name) } func (t Table) IsPkAuto() bool { for _, constr := range t.constraints { if constr.Type != "pk" { continue } return len(constr.Columns) == 1 } return true }