Skip to content

Commit 7321170

Browse files
LanceAddCopilotgqcngithub-actions[bot]
authored
refactor(container): add default nil checker, rename RegisterNilChecker to SetNilChecker, migrate instance containers to type-safe generics (gogf#4630)
## 变更说明 本 PR 主要对代码库进行了重构,以提升类型安全性和优化连接管理实现。 ### 详细变更 #### 1. 数据库连接管理优化 - 修改 `RegisterNilChecker`方法返回实例以支持链式调用,涉及 `KVMap`、`ListKVMap`、`TSet`、`AVLKVTree`、`BKVTree`、`RedBlackKVTree` 等多个容器类型 - 更新 `Core`结构体中 `links`字段类型为类型安全的 `KVMap[ConfigNode, *sql.DB]` - 添加专门的链接检查器函数用于连接池管理 - 使用泛型 `KVMap`替代原始 map 类型提升类型安全性 - 简化连接关闭逻辑并移除不必要的类型断言 - 优化统计功能中的迭代器实现提高性能 #### 2. 数据库驱动类型安全增强 - 将 dm、gaussdb、mssql、oracle 驱动中的 `conflictKeySet` 从 `gset.New`修改为 `gset.NewStrSet` - 统一使用字符串集合类型以提高类型安全性 #### 3. 配置文件适配器类型安全改进 - 将 `jsonMap`从 `StrAnyMap` 类型更改为泛型 `KVMap[string, *gjson.Json]` 类型 - 添加 `jsonMapChecker` 函数用于 JSON 对象验证 - 使用 `NewKVMapWithChecker` 替代 `NewStrAnyMap` 提高类型安全性 - 简化数据库链接关闭日志中的键值转换逻辑 ## 影响范围 - 数据库连接管理模块 - 多个数据库驱动实现 - 配置文件管理系统 --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: John Guo <[email protected]> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 609f44c commit 7321170

21 files changed

+164
-180
lines changed

container/garray/garray_normal_t.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,9 @@ func (a *TArray[T]) String() string {
749749
}
750750

751751
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
752-
// Note that do not use pointer as its receiver here.
752+
// DO NOT change this receiver to pointer type, as the TArray can be used as a var defined variable, like:
753+
// var a TArray[int]
754+
// Please refer to corresponding tests for more details.
753755
func (a TArray[T]) MarshalJSON() ([]byte, error) {
754756
a.mu.RLock()
755757
defer a.mu.RUnlock()

container/garray/garray_sorted_t.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func NewSortedTArray[T comparable](comparator func(a, b T) int, safe ...bool) *S
4646
return NewSortedTArraySize(0, comparator, safe...)
4747
}
4848

49-
// NewSortedTArraySize create and returns an sorted array with given size and cap.
49+
// NewSortedTArraySize create and returns a sorted array with given size and cap.
5050
// The parameter `safe` is used to specify whether using array in concurrent-safety,
5151
// which is false in default.
5252
func NewSortedTArraySize[T comparable](cap int, comparator func(a, b T) int, safe ...bool) *SortedTArray[T] {
@@ -718,7 +718,9 @@ func (a *SortedTArray[T]) String() string {
718718
}
719719

720720
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
721-
// Note that do not use pointer as its receiver here.
721+
// DO NOT change this receiver to pointer type, as the TArray can be used as a var defined variable, like:
722+
// var a SortedTArray[int]
723+
// Please refer to corresponding tests for more details.
722724
func (a SortedTArray[T]) MarshalJSON() ([]byte, error) {
723725
a.mu.RLock()
724726
defer a.mu.RUnlock()

container/gmap/gmap_hash_k_v_map.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ type NilChecker[V any] func(V) bool
2222

2323
// KVMap wraps map type `map[K]V` and provides more map features.
2424
type KVMap[K comparable, V any] struct {
25-
mu rwmutex.RWMutex
26-
data map[K]V
25+
mu rwmutex.RWMutex
26+
data map[K]V
27+
28+
// nilChecker is the custom nil checker function.
29+
// It uses empty.IsNil if it's nil.
2730
nilChecker NilChecker[V]
2831
}
2932

@@ -58,28 +61,28 @@ func NewKVMapFrom[K comparable, V any](data map[K]V, safe ...bool) *KVMap[K, V]
5861
// The parameter `safe` is used to specify whether to use the map in concurrent-safety mode, which is false by default.
5962
func NewKVMapWithCheckerFrom[K comparable, V any](data map[K]V, checker NilChecker[V], safe ...bool) *KVMap[K, V] {
6063
m := NewKVMapFrom[K, V](data, safe...)
61-
m.RegisterNilChecker(checker)
64+
m.SetNilChecker(checker)
6265
return m
6366
}
6467

65-
// RegisterNilChecker registers a custom nil checker function for the map values.
68+
// SetNilChecker registers a custom nil checker function for the map values.
6669
// This function is used to determine if a value should be considered as nil.
6770
// The nil checker function takes a value of type V and returns a boolean indicating
6871
// whether the value should be treated as nil.
69-
func (m *KVMap[K, V]) RegisterNilChecker(nilChecker NilChecker[V]) {
72+
func (m *KVMap[K, V]) SetNilChecker(nilChecker NilChecker[V]) {
7073
m.mu.Lock()
7174
defer m.mu.Unlock()
7275
m.nilChecker = nilChecker
7376
}
7477

7578
// isNil checks whether the given value is nil.
7679
// It first checks if a custom nil checker function is registered and uses it if available,
77-
// otherwise it performs a standard nil check using any(v) == nil.
80+
// otherwise it falls back to the default empty.IsNil function.
7881
func (m *KVMap[K, V]) isNil(v V) bool {
7982
if m.nilChecker != nil {
8083
return m.nilChecker(v)
8184
}
82-
return any(v) == nil
85+
return empty.IsNil(v)
8386
}
8487

8588
// Iterator iterates the hash map readonly with custom callback function `f`.
@@ -242,11 +245,12 @@ func (m *KVMap[K, V]) Pops(size int) map[K]V {
242245
return newMap
243246
}
244247

245-
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
246-
// if not exists, set value to the map with given `key`,
247-
// or else just return the existing value.
248+
// doSetWithLockCheck sets value with given `value` if it does not exist,
249+
// and then returns this value and whether it exists.
250+
//
251+
// It is a helper function for GetOrSet* functions.
248252
//
249-
// It returns value with given `key`.
253+
// Note that, it does not add the value to the map if the given `value` is nil.
250254
func (m *KVMap[K, V]) doSetWithLockCheck(key K, value V) (val V, ok bool) {
251255
m.mu.Lock()
252256
defer m.mu.Unlock()
@@ -274,6 +278,8 @@ func (m *KVMap[K, V]) GetOrSet(key K, value V) V {
274278
// GetOrSetFunc returns the value by key,
275279
// or sets value with returned value of callback function `f` if it does not exist
276280
// and then returns this value.
281+
//
282+
// Note that, it does not add the value to the map if the returned value of `f` is nil.
277283
func (m *KVMap[K, V]) GetOrSetFunc(key K, f func() V) V {
278284
v, _ := m.doSetWithLockCheck(key, f())
279285
return v
@@ -285,6 +291,8 @@ func (m *KVMap[K, V]) GetOrSetFunc(key K, f func() V) V {
285291
//
286292
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function `f`
287293
// with mutex.Lock of the hash map.
294+
//
295+
// Note that, it does not add the value to the map if the returned value of `f` is nil.
288296
func (m *KVMap[K, V]) GetOrSetFuncLock(key K, f func() V) V {
289297
m.mu.Lock()
290298
defer m.mu.Unlock()
@@ -524,6 +532,9 @@ func (m *KVMap[K, V]) String() string {
524532
}
525533

526534
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
535+
// DO NOT change this receiver to pointer type, as the KVMap can be used as a var defined variable, like:
536+
// var m gmap.KVMap[int, string]
537+
// Please refer to corresponding tests for more details.
527538
func (m KVMap[K, V]) MarshalJSON() ([]byte, error) {
528539
return json.Marshal(gconv.Map(m.Map()))
529540
}

container/gmap/gmap_list_k_v_map.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func NewListKVMap[K comparable, V any](safe ...bool) *ListKVMap[K, V] {
5656
// which is false by default.
5757
func NewListKVMapWithChecker[K comparable, V any](checker NilChecker[V], safe ...bool) *ListKVMap[K, V] {
5858
m := NewListKVMap[K, V](safe...)
59-
m.RegisterNilChecker(checker)
59+
m.SetNilChecker(checker)
6060
return m
6161
}
6262

@@ -81,24 +81,24 @@ func NewListKVMapWithCheckerFrom[K comparable, V any](data map[K]V, nilChecker N
8181
return m
8282
}
8383

84-
// RegisterNilChecker registers a custom nil checker function for the map values.
84+
// SetNilChecker registers a custom nil checker function for the map values.
8585
// This function is used to determine if a value should be considered as nil.
8686
// The nil checker function takes a value of type V and returns a boolean indicating
8787
// whether the value should be treated as nil.
88-
func (m *ListKVMap[K, V]) RegisterNilChecker(nilChecker NilChecker[V]) {
88+
func (m *ListKVMap[K, V]) SetNilChecker(nilChecker NilChecker[V]) {
8989
m.mu.Lock()
9090
defer m.mu.Unlock()
9191
m.nilChecker = nilChecker
9292
}
9393

9494
// isNil checks whether the given value is nil.
9595
// It first checks if a custom nil checker function is registered and uses it if available,
96-
// otherwise it performs a standard nil check using any(v) == nil.
96+
// otherwise it falls back to the default empty.IsNil function.
9797
func (m *ListKVMap[K, V]) isNil(v V) bool {
9898
if m.nilChecker != nil {
9999
return m.nilChecker(v)
100100
}
101-
return any(v) == nil
101+
return empty.IsNil(v)
102102
}
103103

104104
// Iterator is alias of IteratorAsc.
@@ -402,6 +402,8 @@ func (m *ListKVMap[K, V]) GetVarOrSetFuncLock(key K, f func() V) *gvar.Var {
402402

403403
// SetIfNotExist sets `value` to the map if the `key` does not exist, and then returns true.
404404
// It returns false if `key` exists, and `value` would be ignored.
405+
//
406+
// Note that it does not add the value to the map if `value` is nil.
405407
func (m *ListKVMap[K, V]) SetIfNotExist(key K, value V) bool {
406408
m.mu.Lock()
407409
defer m.mu.Unlock()
@@ -421,6 +423,8 @@ func (m *ListKVMap[K, V]) SetIfNotExist(key K, value V) bool {
421423

422424
// SetIfNotExistFunc sets value with return value of callback function `f`, and then returns true.
423425
// It returns false if `key` exists, and `value` would be ignored.
426+
//
427+
// Note that, it does not add the value to the map if the returned value of `f` is nil.
424428
func (m *ListKVMap[K, V]) SetIfNotExistFunc(key K, f func() V) bool {
425429
m.mu.Lock()
426430
defer m.mu.Unlock()
@@ -444,6 +448,8 @@ func (m *ListKVMap[K, V]) SetIfNotExistFunc(key K, f func() V) bool {
444448
//
445449
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
446450
// it executes function `f` with mutex.Lock of the map.
451+
//
452+
// Note that, it does not add the value to the map if the returned value of `f` is nil.
447453
func (m *ListKVMap[K, V]) SetIfNotExistFuncLock(key K, f func() V) bool {
448454
m.mu.Lock()
449455
defer m.mu.Unlock()
@@ -609,6 +615,9 @@ func (m *ListKVMap[K, V]) String() string {
609615
}
610616

611617
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
618+
// DO NOT change this receiver to pointer type, as the ListKVMap can be used as a var defined variable, like:
619+
// var m gmap.ListKVMap[string]string
620+
// Please refer to corresponding tests for more details.
612621
func (m ListKVMap[K, V]) MarshalJSON() (jsonBytes []byte, err error) {
613622
if m.data == nil {
614623
return []byte("{}"), nil

container/gmap/gmap_z_unit_k_v_map_test.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,13 @@ func Test_KVMap_MarshalJSON(t *testing.T) {
774774
t.Assert(data["a"], 1)
775775
t.Assert(data["b"], 2)
776776
})
777+
gtest.C(t, func(t *gtest.T) {
778+
var m gmap.KVMap[int, int]
779+
m.Set(1, 10)
780+
b, err := json.Marshal(m)
781+
t.AssertNil(err)
782+
t.Assert(string(b), `{"1":10}`)
783+
})
777784
}
778785

779786
func Test_KVMap_UnmarshalJSON(t *testing.T) {
@@ -1647,9 +1654,10 @@ func Test_KVMap_TypedNil(t *testing.T) {
16471654
return nil
16481655
})
16491656
}
1650-
t.Assert(m1.Size(), 10)
1657+
t.Assert(m1.Size(), 5)
1658+
16511659
m2 := gmap.NewKVMap[int, *Student](true)
1652-
m2.RegisterNilChecker(func(student *Student) bool {
1660+
m2.SetNilChecker(func(student *Student) bool {
16531661
return student == nil
16541662
})
16551663
for i := 0; i < 10; i++ {
@@ -1679,7 +1687,8 @@ func Test_NewKVMapWithChecker_TypedNil(t *testing.T) {
16791687
return nil
16801688
})
16811689
}
1682-
t.Assert(m1.Size(), 10)
1690+
t.Assert(m1.Size(), 5)
1691+
16831692
m2 := gmap.NewKVMapWithChecker[int, *Student](func(student *Student) bool {
16841693
return student == nil
16851694
}, true)

container/gmap/gmap_z_unit_list_k_v_map_race_test.go

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -183,77 +183,6 @@ func Test_ListKVMap_SetIfNotExistFuncLock_MultipleKeys(t *testing.T) {
183183
})
184184
}
185185

186-
// Test_ListKVMap_GetOrSetFuncLock_NilValue tests that nil values are handled correctly.
187-
func Test_ListKVMap_GetOrSetFuncLock_NilValue(t *testing.T) {
188-
gtest.C(t, func(t *gtest.T) {
189-
m := gmap.NewListKVMap[string, *int](true)
190-
key := "nilKey"
191-
callCount := int32(0)
192-
193-
var wg sync.WaitGroup
194-
goroutines := 50
195-
wg.Add(goroutines)
196-
197-
for i := 0; i < goroutines; i++ {
198-
go func() {
199-
defer wg.Done()
200-
m.GetOrSetFuncLock(key, func() *int {
201-
atomic.AddInt32(&callCount, 1)
202-
return nil
203-
})
204-
}()
205-
}
206-
207-
wg.Wait()
208-
209-
// Callback should be called once
210-
t.Assert(atomic.LoadInt32(&callCount), 1)
211-
// Typed nil pointer (*int)(nil) is stored because any(value) != nil for typed nil
212-
// This is a Go language feature: typed nil is not the same as interface nil
213-
t.Assert(m.Contains(key), true)
214-
t.Assert(m.Get(key), (*int)(nil))
215-
t.Assert(m.Size(), 1)
216-
})
217-
}
218-
219-
// Test_ListKVMap_SetIfNotExistFuncLock_NilValue tests that nil values are handled correctly.
220-
func Test_ListKVMap_SetIfNotExistFuncLock_NilValue(t *testing.T) {
221-
gtest.C(t, func(t *gtest.T) {
222-
m := gmap.NewListKVMap[string, *string](true)
223-
key := "nilKey"
224-
callCount := int32(0)
225-
successCount := int32(0)
226-
227-
var wg sync.WaitGroup
228-
goroutines := 50
229-
wg.Add(goroutines)
230-
231-
for i := 0; i < goroutines; i++ {
232-
go func() {
233-
defer wg.Done()
234-
success := m.SetIfNotExistFuncLock(key, func() *string {
235-
atomic.AddInt32(&callCount, 1)
236-
return nil
237-
})
238-
if success {
239-
atomic.AddInt32(&successCount, 1)
240-
}
241-
}()
242-
}
243-
244-
wg.Wait()
245-
246-
// Callback should be called once
247-
t.Assert(atomic.LoadInt32(&callCount), 1)
248-
// Should report success once
249-
t.Assert(atomic.LoadInt32(&successCount), 1)
250-
// Typed nil pointer (*string)(nil) is stored because any(value) != nil for typed nil
251-
t.Assert(m.Contains(key), true)
252-
t.Assert(m.Get(key), (*string)(nil))
253-
t.Assert(m.Size(), 1)
254-
})
255-
}
256-
257186
// Test_ListKVMap_GetOrSetFuncLock_ExistingKey tests behavior when key already exists.
258187
func Test_ListKVMap_GetOrSetFuncLock_ExistingKey(t *testing.T) {
259188
gtest.C(t, func(t *gtest.T) {

container/gmap/gmap_z_unit_list_k_v_map_test.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,13 @@ func Test_ListKVMap_MarshalJSON_Error(t *testing.T) {
11591159
t.AssertNil(err)
11601160
t.Assert(string(b), `{"a":"1"}`)
11611161
})
1162+
gtest.C(t, func(t *gtest.T) {
1163+
var m gmap.ListKVMap[int, int]
1164+
m.Set(1, 10)
1165+
b, err := json.Marshal(m)
1166+
t.AssertNil(err)
1167+
t.Assert(string(b), `{"1":10}`)
1168+
})
11621169
}
11631170

11641171
// Test empty map operations
@@ -1358,9 +1365,10 @@ func Test_ListKVMap_TypedNil(t *testing.T) {
13581365
return nil
13591366
})
13601367
}
1361-
t.Assert(m1.Size(), 10)
1368+
t.Assert(m1.Size(), 5)
1369+
13621370
m2 := gmap.NewListKVMap[int, *Student](true)
1363-
m2.RegisterNilChecker(func(student *Student) bool {
1371+
m2.SetNilChecker(func(student *Student) bool {
13641372
return student == nil
13651373
})
13661374
for i := 0; i < 10; i++ {
@@ -1390,7 +1398,8 @@ func Test_NewListKVMapWithChecker_TypedNil(t *testing.T) {
13901398
return nil
13911399
})
13921400
}
1393-
t.Assert(m1.Size(), 10)
1401+
t.Assert(m1.Size(), 5)
1402+
13941403
m2 := gmap.NewListKVMapWithChecker[int, *Student](func(student *Student) bool {
13951404
return student == nil
13961405
}, true)

0 commit comments

Comments
 (0)