diff --git a/lib/sql/meta.go b/lib/sql/meta.go new file mode 100644 index 00000000..7ce51f67 --- /dev/null +++ b/lib/sql/meta.go @@ -0,0 +1,84 @@ +// Copyright 2024, Shulhan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sql + +import ( + "fmt" + "strings" +) + +// Meta contains the DML meta data, including driver name, list of column +// names, list of column holders, and list of values. +type Meta struct { + driver string + + // ListName contains list of column name. + ListName []string + + // ListHolder contains list of column holder, as in "?" or "$x", + // depends on driver. + ListHolder []string + + // ListValue contains list of column values, either for insert or + // select. + ListValue []any + + // ListWhereValue contains list of values for where condition. + ListWhereValue []any +} + +// NewMeta create new Meta using specific driver name. +// The driver affect the ListHolder value. +func NewMeta(driverName string) (meta *Meta) { + meta = &Meta{ + driver: driverName, + } + return meta +} + +// Add column name and variable. +func (meta *Meta) Add(colName string, val any) { + meta.ListName = append(meta.ListName, colName) + + if meta.driver == DriverNamePostgres { + meta.ListHolder = append(meta.ListHolder, fmt.Sprintf(`$%d`, len(meta.ListName))) + } else { + meta.ListHolder = append(meta.ListHolder, DefaultPlaceHolder) + } + + meta.ListValue = append(meta.ListValue, val) +} + +// AddWhere add value for where condition. +// It return the length of ListWhereValue in list after addition. +func (meta *Meta) AddWhere(val any) int { + meta.ListWhereValue = append(meta.ListWhereValue, val) + return len(meta.ListWhereValue) +} + +// Names generate string of column names, for example "col1, col2, ...". +func (meta *Meta) Names() string { + return strings.Join(meta.ListName, `,`) +} + +// Holders generate string of holder, for example "$1, $2, ...". +func (meta *Meta) Holders() string { + return strings.Join(meta.ListHolder, `,`) +} + +// UpdateFields generate string of "col1=, col2=, ...". +func (meta *Meta) UpdateFields() string { + var ( + sb strings.Builder + x int + ) + for ; x < len(meta.ListName); x++ { + if x > 0 { + sb.WriteByte(',') + } + fmt.Fprintf(&sb, `%s=%s`, meta.ListName[x], meta.ListHolder[x]) + } + return sb.String() +} diff --git a/lib/sql/meta_example_test.go b/lib/sql/meta_example_test.go new file mode 100644 index 00000000..8f85b671 --- /dev/null +++ b/lib/sql/meta_example_test.go @@ -0,0 +1,77 @@ +// Copyright 2024, Shulhan . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sql_test + +import ( + "fmt" + + "github.com/shuLhan/share/lib/sql" +) + +func ExampleMeta_Add_mysql() { + type Table struct { + Name string + ID int + } + + var ( + meta = sql.NewMeta(sql.DriverNameMysql) + t = Table{} + ) + + meta.Add(`id`, &t.ID) + meta.Add(`name`, &t.Name) + + fmt.Println(meta.Names()) + fmt.Println(meta.Holders()) + fmt.Println(meta.UpdateFields()) + // Output: + // id,name + // ?,? + // id=?,name=? +} + +func ExampleMeta_Add_postgres() { + type Table struct { + Name string + ID int + } + + var ( + meta = sql.NewMeta(sql.DriverNamePostgres) + t = Table{} + ) + + meta.Add(`id`, &t.ID) + meta.Add(`name`, &t.Name) + + fmt.Println(meta.Names()) + fmt.Println(meta.Holders()) + fmt.Println(meta.UpdateFields()) + // Output: + // id,name + // $1,$2 + // id=$1,name=$2 +} + +func ExampleMeta_AddWhere() { + var ( + meta = sql.NewMeta(sql.DriverNamePostgres) + vals = []any{ + int(1000), + string(`name`), + } + idx int + ) + + idx = meta.AddWhere(vals[0]) + fmt.Println(idx, meta.ListWhereValue) + + idx = meta.AddWhere(vals[1]) + fmt.Println(idx, meta.ListWhereValue) + // Output: + // 1 [1000] + // 2 [1000 name] +} diff --git a/lib/sql/row.go b/lib/sql/row.go index b992403f..1fdbfb7b 100644 --- a/lib/sql/row.go +++ b/lib/sql/row.go @@ -13,8 +13,40 @@ import ( // The map's key is the column name in database and the map's value is // the column's value. // This type can be used to create dynamic insert-update fields. +// +// DEPRECATED: use [Meta] instead. type Row map[string]interface{} +// Meta convert the Row into Meta. +func (row Row) Meta(driverName string) (meta *Meta) { + meta = &Meta{} + + if len(row) == 0 { + return meta + } + + meta.ListName = make([]string, 0, len(row)) + meta.ListHolder = make([]string, 0, len(row)) + meta.ListValue = make([]interface{}, 0, len(row)) + + var colName string + for colName = range row { + meta.ListName = append(meta.ListName, colName) + } + sort.Strings(meta.ListName) + + var x int + for x, colName = range meta.ListName { + if driverName == DriverNamePostgres { + meta.ListHolder = append(meta.ListHolder, fmt.Sprintf(`$%d`, x+1)) + } else { + meta.ListHolder = append(meta.ListHolder, DefaultPlaceHolder) + } + meta.ListValue = append(meta.ListValue, row[colName]) + } + return meta +} + // ExtractSQLFields extract the column's name, column place holder, and column // values as slices. // @@ -30,23 +62,7 @@ func (row Row) ExtractSQLFields(driverName string) (names, holders []string, val return nil, nil, nil } - names = make([]string, 0, len(row)) - holders = make([]string, 0, len(row)) - values = make([]interface{}, 0, len(row)) - - for k := range row { - names = append(names, k) - } - sort.Strings(names) - - for x, k := range names { - if driverName == DriverNamePostgres { - holders = append(holders, fmt.Sprintf("$%d", x+1)) - } else { - holders = append(holders, DefaultPlaceHolder) - } - values = append(values, row[k]) - } + var meta = row.Meta(driverName) - return names, holders, values + return meta.ListName, meta.ListHolder, meta.ListValue }