package sq import ( "errors" "fmt" "gogs.mikescher.com/BlackForestBytes/goext/exerr" "reflect" "strings" ) func BuildUpdateStatement[TData any](q Queryable, tableName string, obj TData, idColumn string) (string, PP, error) { rval := reflect.ValueOf(obj) rtyp := rval.Type() params := PP{} setClauses := make([]string, 0) matchClause := "" for i := 0; i < rtyp.NumField(); i++ { rsfield := rtyp.Field(i) rvfield := rval.Field(i) if !rsfield.IsExported() { continue } columnName := rsfield.Tag.Get("db") if columnName == "" || columnName == "-" { continue } if idColumn == columnName { idValue, err := convertValueToDB(q, rvfield.Interface()) if err != nil { return "", nil, err } matchClause = fmt.Sprintf("(%s = :%s)", columnName, params.Add(idValue)) continue } if rsfield.Type.Kind() == reflect.Ptr && rvfield.IsNil() { setClauses = append(setClauses, fmt.Sprintf("%s = NULL", columnName)) } else { val, err := convertValueToDB(q, rvfield.Interface()) if err != nil { return "", nil, err } setClauses = append(setClauses, fmt.Sprintf("%s = :%s", columnName, params.Add(val))) } } if len(setClauses) == 0 { return "", nil, exerr.New(exerr.TypeSQLBuild, "no updates clauses found in object").Build() } if matchClause == "" { return "", nil, exerr.New(exerr.TypeSQLBuild, "id column not found in object").Build() } //goland:noinspection SqlNoDataSourceInspection return fmt.Sprintf("UPDATE %s SET %s WHERE %s", tableName, strings.Join(setClauses, ", "), matchClause), params, nil } func BuildInsertStatement[TData any](q Queryable, tableName string, obj TData) (string, PP, error) { rval := reflect.ValueOf(obj) rtyp := rval.Type() params := PP{} fields := make([]string, 0) values := make([]string, 0) for i := 0; i < rtyp.NumField(); i++ { rsfield := rtyp.Field(i) rvfield := rval.Field(i) if !rsfield.IsExported() { continue } columnName := rsfield.Tag.Get("db") if columnName == "" || columnName == "-" { continue } if rsfield.Type.Kind() == reflect.Ptr && rvfield.IsNil() { fields = append(fields, columnName) values = append(values, "NULL") } else { val, err := convertValueToDB(q, rvfield.Interface()) if err != nil { return "", nil, err } fields = append(fields, columnName) values = append(values, ":"+params.Add(val)) } } if len(fields) == 0 { return "", nil, exerr.New(exerr.TypeSQLBuild, "no fields found in object").Build() } //goland:noinspection SqlNoDataSourceInspection return fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", tableName, strings.Join(fields, ", "), strings.Join(values, ", ")), params, nil } func BuildInsertMultipleStatement[TData any](q Queryable, tableName string, vArr []TData) (string, PP, error) { if len(vArr) == 0 { return "", nil, errors.New("no data supplied") } rtyp := reflect.ValueOf(vArr[0]).Type() sqlPrefix := "" { columns := make([]string, 0) for i := 0; i < rtyp.NumField(); i++ { rsfield := rtyp.Field(i) if !rsfield.IsExported() { continue } columnName := rsfield.Tag.Get("db") if columnName == "" || columnName == "-" { continue } columns = append(columns, "\""+columnName+"\"") } sqlPrefix = fmt.Sprintf("INSERT"+" INTO \"%s\" (%s) VALUES", tableName, strings.Join(columns, ", ")) } pp := PP{} sqlValuesArr := make([]string, 0) for _, v := range vArr { rval := reflect.ValueOf(v) params := make([]string, 0) for i := 0; i < rtyp.NumField(); i++ { rsfield := rtyp.Field(i) rvfield := rval.Field(i) if !rsfield.IsExported() { continue } columnName := rsfield.Tag.Get("db") if columnName == "" || columnName == "-" { continue } if rsfield.Type.Kind() == reflect.Ptr && rvfield.IsNil() { params = append(params, "NULL") } else { val, err := convertValueToDB(q, rvfield.Interface()) if err != nil { return "", nil, err } params = append(params, ":"+pp.Add(val)) } } sqlValuesArr = append(sqlValuesArr, fmt.Sprintf("(%s)", strings.Join(params, ", "))) } sqlstr := fmt.Sprintf("%s %s", sqlPrefix, strings.Join(sqlValuesArr, ", ")) return sqlstr, pp, nil }