docs: Add CLAUDE.md with codebase guidance

Create comprehensive documentation for future Claude Code instances working in this repository, including:
- Development commands for testing, building, and code quality
- Core architecture overview of the SQL query builder system
- Directory structure and component explanations
- Testing patterns and conventions
- Key dependencies and their purposes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-27 15:47:28 +08:00
co-authored by Claude
parent 764eccfafd
commit 304d553b3c
113 changed files with 914 additions and 432 deletions
+9 -8
View File
@@ -5,7 +5,7 @@ import (
"sort"
"strings"
"git.fsdpf.net/go/db/v2/internal/tag"
"git.fsdpf.net/go/db/internal/tag"
)
type (
@@ -37,13 +37,14 @@ func newColumnMap(t reflect.Type, fieldIndex []int, prefixes []string) ColumnMap
// if PkgPath is empty then it is an exported field
columnName := getColumnName(&f, dbTag)
if !shouldIgnoreField(dbTag) {
if !implementsScanner(f.Type) {
subCm := getStructColumnMap(&f, fieldIndex, []string{columnName}, prefixes)
if len(subCm) != 0 {
subColMaps = append(subColMaps, subCm)
continue
}
}
// 移除原来的关联结构,并 scans 字段出现 table.col
// if !implementsScanner(f.Type) {
// subCm := getStructColumnMap(&f, fieldIndex, []string{columnName}, prefixes)
// if len(subCm) != 0 {
// subColMaps = append(subColMaps, subCm)
// continue
// }
// }
ffTag := tag.New("ff", f.Tag)
columnName = strings.Join(append(prefixes, columnName), ".")
cm[columnName] = newColumnData(&f, columnName, fieldIndex, ffTag)
+39 -11
View File
@@ -2,11 +2,12 @@ package util
import (
"database/sql"
"encoding/json"
"reflect"
"strings"
"sync"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/internal/errors"
)
const (
@@ -56,6 +57,10 @@ func IsStruct(k reflect.Kind) bool {
return k == reflect.Struct
}
func IsMap(k reflect.Kind) bool {
return k == reflect.Map
}
func IsInvalid(k reflect.Kind) bool {
return k == reflect.Invalid
}
@@ -117,6 +122,15 @@ func GetSliceElementType(val reflect.Value) reflect.Type {
return elemType
}
func GetMapElementType(val reflect.Value) reflect.Type {
elemType := val.Type().Elem()
if elemType.Kind() == reflect.Ptr {
elemType = elemType.Elem()
}
return elemType
}
// AppendSliceElement will append val to slice. Handles slice of pointers and
// not pointers. Val needs to be a pointer.
func AppendSliceElement(slice, val reflect.Value) {
@@ -167,16 +181,8 @@ func SafeSetFieldByIndex(v reflect.Value, fieldIndex []int, src interface{}) (re
return v
case 1:
f := v.FieldByIndex(fieldIndex)
srcVal := reflect.ValueOf(src).Elem()
// 处理 converting NULL to string is unsupported
// 前面将 string 和 int 类型转为了 *string 和 *int
// 这里做还原
if srcVal.IsNil() {
f.Set(reflect.Zero(f.Type()))
} else if f.Type().ConvertibleTo(srcVal.Type().Elem()) {
f.Set(srcVal.Elem())
} else {
f.Set(srcVal)
if err := SafeSetVarValue(f, src); err != nil {
panic(err)
}
default:
f := v.Field(fieldIndex[0])
@@ -196,6 +202,28 @@ func SafeSetFieldByIndex(v reflect.Value, fieldIndex []int, src interface{}) (re
return v
}
func SafeSetVarValue(v reflect.Value, src interface{}) error {
f := reflect.Indirect(v)
srcVal := reflect.ValueOf(src).Elem()
// 处理 converting NULL to string is unsupported
// 前面将 string 和 int 类型转为了 *string 和 *int
// 这里做还原
if srcVal.IsNil() {
f.Set(reflect.Zero(f.Type()))
} else if f.Type().ConvertibleTo(srcVal.Type().Elem()) {
f.Set(srcVal.Elem())
} else if f.Type().ConvertibleTo(srcVal.Type()) {
f.Set(srcVal)
} else if u, ok := srcVal.Interface().(*json.RawMessage); ok && len(*u) >= 2 {
if err := json.Unmarshal(*u, f.Addr().Interface()); err != nil {
return err
}
}
return nil
}
type rowData = map[string]interface{}
// AssignStructVals will assign the data from rd to i.
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"testing"
"time"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/internal/util"
"github.com/stretchr/testify/suite"
)