diff --git a/contrib/drivers/pgsql/pgsql_do_insert.go b/contrib/drivers/pgsql/pgsql_do_insert.go
index 0e0ac31de9a..163076ed11b 100644
--- a/contrib/drivers/pgsql/pgsql_do_insert.go
+++ b/contrib/drivers/pgsql/pgsql_do_insert.go
@@ -9,10 +9,11 @@ package pgsql
import (
"context"
"database/sql"
-
+ "fmt"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
+ "strings"
)
// DoInsert inserts or updates data for given table.
@@ -25,10 +26,7 @@ func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list
)
case gdb.InsertOptionIgnore:
- return nil, gerror.NewCode(
- gcode.CodeNotSupported,
- `Insert ignore operation is not supported by pgsql driver`,
- )
+ return d.doInsertIgnore(ctx, link, table, list)
case gdb.InsertOptionDefault:
tableFields, err := d.GetCore().GetDB().TableFields(ctx, table)
@@ -44,3 +42,39 @@ func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list
}
return d.Core.DoInsert(ctx, link, table, list, option)
}
+
+// doInsertIgnore inserts a list of records into the specified table in pgsql.
+// INSERT INTO
() VALUES () ON CONFLICT DO NOTHING
+func (d *Driver) doInsertIgnore(ctx context.Context, link gdb.Link, table string, list gdb.List) (result sql.Result, err error) {
+ if len(list) == 0 {
+ return nil, gerror.NewCode(gcode.CodeInvalidRequest, `Insert operation list is empty for pgsql driver`)
+ }
+
+ var (
+ one = list[0]
+ charL, charR = d.GetChars()
+ insertKeys = make([]string, 0, len(one))
+ insertValues = make([]string, 0, len(one))
+ queryValues = make([]interface{}, 0, len(one))
+ )
+
+ for key, value := range one {
+ insertKeys = append(insertKeys, charL+key+charR)
+ insertValues = append(insertValues, "?")
+ queryValues = append(queryValues, value)
+ }
+
+ sqlStr := fmt.Sprintf(
+ `INSERT INTO %s (%s) VALUES (%s) ON CONFLICT DO NOTHING`,
+ table,
+ strings.Join(insertKeys, ","),
+ strings.Join(insertValues, ","),
+ )
+
+ result, err = d.DoExec(ctx, link, sqlStr, queryValues...)
+ if err != nil {
+ return result, err
+ }
+
+ return result, nil
+}
diff --git a/contrib/drivers/pgsql/pgsql_z_unit_db_test.go b/contrib/drivers/pgsql/pgsql_z_unit_db_test.go
index 9cadd86d142..8216df44e07 100644
--- a/contrib/drivers/pgsql/pgsql_z_unit_db_test.go
+++ b/contrib/drivers/pgsql/pgsql_z_unit_db_test.go
@@ -383,3 +383,66 @@ int_col INT);`
})
}
+
+func Test_DB_InsertIgnore(t *testing.T) {
+ table := createTable()
+ defer dropTable(table)
+
+ // Insert test record
+ gtest.C(t, func(t *gtest.T) {
+ _, err := db.Insert(ctx, table, g.Map{
+ "id": 1,
+ "passport": "t1",
+ "password": "25d55ad283aa400af464c76d713c07ad",
+ "nickname": "T1",
+ "create_time": gtime.Now().String(),
+ })
+ t.AssertNil(err)
+
+ answer, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
+ t.AssertNil(err)
+ t.Assert(len(answer), 1)
+ t.Assert(answer[0]["passport"], "t1")
+ t.Assert(answer[0]["password"], "25d55ad283aa400af464c76d713c07ad")
+ t.Assert(answer[0]["nickname"], "T1")
+
+ // Ignore Duplicate record
+ result, err := db.InsertIgnore(ctx, table, g.Map{
+ "id": 1,
+ "passport": "t1_duplicate",
+ "password": "duplicate_password",
+ "nickname": "Duplicate",
+ "create_time": gtime.Now().String(),
+ })
+ t.AssertNil(err)
+
+ n, _ := result.RowsAffected()
+ t.Assert(n, 0)
+
+ answer, err = db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
+ t.AssertNil(err)
+ t.Assert(len(answer), 1)
+ t.Assert(answer[0]["passport"], "t1")
+ t.Assert(answer[0]["password"], "25d55ad283aa400af464c76d713c07ad")
+ t.Assert(answer[0]["nickname"], "T1")
+
+ // Insert Correct Record
+ result, err = db.Insert(ctx, table, g.Map{
+ "id": 2,
+ "passport": "t2",
+ "password": "25d55ad283aa400af464c76d713c07ad",
+ "nickname": "name_2",
+ "create_time": gtime.Now().String(),
+ })
+ t.AssertNil(err)
+ n, _ = result.RowsAffected()
+ t.Assert(n, 1)
+
+ answer, err = db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 2)
+ t.AssertNil(err)
+ t.Assert(len(answer), 1)
+ t.Assert(answer[0]["passport"], "t2")
+ t.Assert(answer[0]["password"], "25d55ad283aa400af464c76d713c07ad")
+ t.Assert(answer[0]["nickname"], "name_2")
+ })
+}
diff --git a/util/gmeta/gmeta.go b/util/gmeta/gmeta.go
index 936a22d4f20..22a616ccb83 100644
--- a/util/gmeta/gmeta.go
+++ b/util/gmeta/gmeta.go
@@ -8,6 +8,8 @@
package gmeta
import (
+ "sync"
+
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/os/gstructs"
)
@@ -20,18 +22,33 @@ const (
metaTypeName = "gmeta.Meta" // metaTypeName is for type string comparison.
)
+// cachedMetadata stores the parsed metadata for struct types.
+var cachedMetadata = sync.Map{}
+
// Data retrieves and returns all metadata from `object`.
func Data(object interface{}) map[string]string {
reflectType, err := gstructs.StructType(object)
if err != nil {
return nil
}
+
+ if cachedData, ok := cachedMetadata.Load(reflectType.String()); ok {
+ return cachedData.(map[string]string)
+ }
+
+ var metadata map[string]string
if field, ok := reflectType.FieldByName(metaAttributeName); ok {
if field.Type.String() == metaTypeName {
- return gstructs.ParseTag(string(field.Tag))
+ metadata = gstructs.ParseTag(string(field.Tag))
}
}
- return map[string]string{}
+
+ if metadata == nil {
+ metadata = map[string]string{}
+ }
+
+ cachedMetadata.Store(reflectType.String(), metadata)
+ return metadata
}
// Get retrieves and returns specified metadata by `key` from `object`.