return t
}
-func (t *SqlTx[T]) DoPlaceHolder(sqlf SqlFunc[T], ptr any) *SqlTx[T] {
+func (t *SqlTx[T]) DoPlaceHolder(sqlf SqlFunc[T], ptr any, replaceF ...func(index int, holder string) (replaceTo string)) *SqlTx[T] {
dataR := reflect.ValueOf(ptr).Elem()
+ index := 0
for i := 0; i < dataR.NumField(); i++ {
field := dataR.Field(i)
if field.IsValid() && field.CanSet() {
replaceS := "{" + dataR.Type().Field(i).Name + "}"
if strings.Contains(sqlf.Query, replaceS) {
- sqlf.Query = strings.ReplaceAll(sqlf.Query, replaceS, "?")
+ if len(replaceF) == 0 {
+ sqlf.Query = strings.ReplaceAll(sqlf.Query, replaceS, "?")
+ } else {
+ sqlf.Query = strings.ReplaceAll(sqlf.Query, replaceS, replaceF[0](index, replaceS))
+ index += 1
+ }
sqlf.Args = append(sqlf.Args, field.Interface())
}
}
return nil, fmt.Errorf("BeginTx; [] >> fin")
}
+ var hasErr bool
+
tx, err := t.canTx.BeginTx(t.ctx, t.opts)
if err != nil {
e = fmt.Errorf("BeginTx; [] >> %s", err)
+ hasErr = true
} else {
for i := 0; i < len(t.sqlFuncs); i++ {
sqlf := t.sqlFuncs[i]
if sqlf.beforeF != nil {
if datap, err := sqlf.beforeF(t.dataP, sqlf, e); err != nil {
e = errors.Join(e, fmt.Errorf("%s; >> %s", sqlf.Query, err))
+ hasErr = true
} else {
t.dataP = datap
}
switch sqlf.Ty {
case Execf:
if res, err := tx.ExecContext(sqlf.Ctx, sqlf.Query, sqlf.Args...); err != nil {
+ hasErr = true
if !sqlf.SkipSqlErr {
e = errors.Join(e, fmt.Errorf("%s; %s >> %s", sqlf.Query, sqlf.Args, err))
}
} else if sqlf.afterEF != nil {
if datap, err := sqlf.afterEF(t.dataP, res, e); err != nil {
+ hasErr = true
e = errors.Join(e, fmt.Errorf("%s; %s >> %s", sqlf.Query, sqlf.Args, err))
} else {
t.dataP = datap
}
case Queryf:
if res, err := tx.QueryContext(sqlf.Ctx, sqlf.Query, sqlf.Args...); err != nil {
+ hasErr = true
if !sqlf.SkipSqlErr {
e = errors.Join(e, fmt.Errorf("%s; %s >> %s", sqlf.Query, sqlf.Args, err))
}
} else if sqlf.afterQF != nil {
if datap, err := sqlf.afterQF(t.dataP, res, e); err != nil {
+ hasErr = true
e = errors.Join(e, fmt.Errorf("%s; %s >> %s", sqlf.Query, sqlf.Args, err))
} else {
t.dataP = datap
}
}
}
- if e != nil {
+ if hasErr {
if tx != nil {
if err := tx.Rollback(); err != nil {
e = errors.Join(e, fmt.Errorf("Rollback; [] >> %s", err))
return t == nil || t.fin
}
-func DealRows[T any](rows *sql.Rows, createF func() T) (*[]T, error) {
+func DealRows[T any](rows *sql.Rows, newT func() T) (*[]T, error) {
rowNames, err := rows.Columns()
if err != nil {
return nil, err
return nil, err
}
- var stu = createF()
+ var (
+ stu = newT()
+ refV = reflect.ValueOf(&stu).Elem()
+ refT = reflect.TypeOf(&stu).Elem()
+ FieldMap = make(map[string]*reflect.Value)
+ )
+
+ for NumField := refV.NumField() - 1; NumField >= 0; NumField-- {
+ field := refV.Field(NumField)
+ fieldT := refT.Field(NumField)
+ fieldTName := fieldT.Name
+ if value, ok := fieldT.Tag.Lookup("sql"); ok {
+ fieldTName = value
+ }
+ if !field.IsValid() {
+ continue
+ }
+ if !field.CanSet() {
+ FieldMap[strings.ToUpper(fieldTName)] = nil
+ continue
+ }
+ FieldMap[strings.ToUpper(fieldTName)] = &field
+ }
+
for i := 0; i < len(rowNames); i++ {
- v := reflect.ValueOf(&stu).Elem().FieldByName(rowNames[i])
- if v.IsValid() {
- refT := reflect.TypeOf(&stu).Elem()
- if v.CanSet() {
- val := reflect.ValueOf(*rowP[i].(*any))
- if reflect.TypeOf(*rowP[i].(*any)).ConvertibleTo(v.Type()) {
- v.Set(val)
- } else {
- return nil, fmt.Errorf("DealRows:KindNotMatch:[sql] %v !> [%s.%s] %v", val.Kind(), refT.Name(), rowNames[i], v.Type())
- }
+ if field, ok := FieldMap[strings.ToUpper(rowNames[i])]; ok {
+ if field == nil {
+ return nil, fmt.Errorf("DealRows:%s.%s CanSet:false", refT.Name(), rowNames[i])
+ }
+ val := reflect.ValueOf(*rowP[i].(*any))
+ if reflect.TypeOf(*rowP[i].(*any)).ConvertibleTo(field.Type()) {
+ field.Set(val)
} else {
- return nil, fmt.Errorf("DealRows:%s.%s CanSet:%v", refT.Name(), rowNames[i], v.CanSet())
+ return nil, fmt.Errorf("DealRows:KindNotMatch:[sql] %v !> [%s.%s] %v", val.Kind(), refT.Name(), rowNames[i], field.Type())
}
}
}
res = append(res, stu)
-
}
return &res, nil
}
+// for mysql,oracle not postgresql
func SimpleQ[T any](canTx CanTx, query string, ptr *T) (*[]T, error) {
tx := BeginTx[[]T](canTx, context.Background())
tx.DoPlaceHolder(SqlFunc[[]T]{Query: query}, ptr)
"context"
"database/sql"
"errors"
+ "fmt"
"sync"
"testing"
"time"
+ _ "github.com/lib/pq"
file "github.com/qydysky/part/file"
_ "modernc.org/sqlite"
)
t.Fatal(err)
}
defer db.Close()
- defer file.New("test.sqlite3", 0, true).Delete()
+ defer func() {
+ _ = file.New("test.sqlite3", 0, true).Delete()
+ }()
{
tx := BeginTx[any](db, context.Background())
t.Fatal(err)
}
defer db.Close()
- defer file.New("test.sqlite3", 0, true).Delete()
+ defer func() {
+ _ = file.New("test.sqlite3", 0, true).Delete()
+ }()
conn, _ := db.Conn(context.Background())
if _, e := BeginTx[any](conn, context.Background(), &sql.TxOptions{}).Do(SqlFunc[any]{
t.Fatal()
}
}
+
+func Local_TestPostgresql(t *testing.T) {
+ // connect
+ db, err := sql.Open("postgres", "postgres://postgres:qydysky@192.168.31.103:5432/postgres?sslmode=disable")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer db.Close()
+
+ type test1 struct {
+ Created string `sql:"sss"`
+ }
+
+ if _, e := BeginTx[any](db, context.Background(), &sql.TxOptions{}).Do(SqlFunc[any]{
+ Query: "create table test (created varchar(20))",
+ SkipSqlErr: true,
+ }).Fin(); e != nil {
+ t.Fatal(e)
+ }
+
+ if _, e := BeginTx[any](db, context.Background(), &sql.TxOptions{}).DoPlaceHolder(SqlFunc[any]{
+ Query: "insert into test (created) values ({Created})",
+ }, &test1{"1"}, func(index int, holder string) (replaceTo string) {
+ return fmt.Sprintf("$%d", index+1)
+ }).Fin(); e != nil {
+ t.Fatal(e)
+ }
+
+ if _, e := BeginTx[any](db, context.Background(), &sql.TxOptions{}).Do(SqlFunc[any]{
+ Query: "select created as sss from test",
+ afterQF: func(_ *any, rows *sql.Rows, txE error) (dataPR *any, stopErr error) {
+ if rowsP, e := DealRows[test1](rows, func() test1 { return test1{} }); e != nil {
+ return nil, e
+ } else {
+ if len(*rowsP) != 1 {
+ return nil, errors.New("no match")
+ }
+ if (*rowsP)[0].Created != "1" {
+ return nil, errors.New("no match")
+ }
+ }
+ return nil, nil
+ },
+ }).Fin(); e != nil {
+ t.Fatal(e)
+ }
+}