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
+54 -6
View File
@@ -5,12 +5,13 @@ import (
gsql "database/sql"
"reflect"
"git.fsdpf.net/go/db/v2/internal/errors"
"git.fsdpf.net/go/db/v2/internal/util"
"git.fsdpf.net/go/db/internal/errors"
"git.fsdpf.net/go/db/internal/util"
)
type (
QueryExecutor struct {
hook func(result interface{}) error
de DbExecutor
err error
query string
@@ -19,6 +20,8 @@ type (
)
var (
errUnsupportedScanMapType = errors.New("type must be a pointer to a map when scanning into a map")
errUnsupportedScanMapsType = errors.New("type must be a pointer to a slice when scanning into a map")
errUnsupportedScanStructType = errors.New("type must be a pointer to a struct when scanning into a struct")
errUnsupportedScanStructsType = errors.New("type must be a pointer to a slice when scanning into structs")
errUnsupportedScanValsType = errors.New("type must be a pointer to a slice when scanning into vals")
@@ -38,22 +41,34 @@ func (q QueryExecutor) Exec() (gsql.Result, error) {
return q.ExecContext(context.Background())
}
func (q QueryExecutor) ExecContext(ctx context.Context) (gsql.Result, error) {
func (q QueryExecutor) ExecContext(ctx context.Context) (result gsql.Result, err error) {
if q.err != nil {
return nil, q.err
}
return q.de.ExecContext(ctx, q.query, q.args...)
result, err = q.de.ExecContext(ctx, q.query, q.args...)
defer func() {
if err == nil && q.hook != nil {
err = q.hook(result)
}
}()
return result, err
}
func (q QueryExecutor) Query() (*gsql.Rows, error) {
return q.QueryContext(context.Background())
}
func (q QueryExecutor) QueryContext(ctx context.Context) (*gsql.Rows, error) {
func (q QueryExecutor) QueryContext(ctx context.Context) (result *gsql.Rows, err error) {
if q.err != nil {
return nil, q.err
}
return q.de.QueryContext(ctx, q.query, q.args...)
result, err = q.de.QueryContext(ctx, q.query, q.args...)
defer func() {
if err == nil && q.hook != nil {
err = q.hook(result)
}
}()
return result, err
}
// This will execute the SQL and append results to the slice
@@ -238,6 +253,35 @@ func (q QueryExecutor) ScanValContext(ctx context.Context, i interface{}) (bool,
return false, scanner.Err()
}
func (q QueryExecutor) GetRecord() (map[string]any, error) {
return q.GetRecordContext(context.Background())
}
func (q QueryExecutor) GetRecordContext(ctx context.Context) (map[string]any, error) {
scanner, err := q.ScannerContext(ctx)
if err != nil {
return nil, err
}
defer func() { _ = scanner.Close() }()
if scanner.Next() {
return scanner.GetRecord()
}
return nil, scanner.Err()
}
func (q QueryExecutor) GetRecords() ([]map[string]any, error) {
return q.GetRecordsContext(context.Background())
}
func (q QueryExecutor) GetRecordsContext(ctx context.Context) ([]map[string]any, error) {
scanner, err := q.ScannerContext(ctx)
if err != nil {
return nil, err
}
defer func() { _ = scanner.Close() }()
return scanner.GetRecords()
}
// Scanner will return a Scanner that can be used for manually scanning rows.
func (q QueryExecutor) Scanner() (Scanner, error) {
return q.ScannerContext(context.Background())
@@ -251,3 +295,7 @@ func (q QueryExecutor) ScannerContext(ctx context.Context) (Scanner, error) {
}
return NewScanner(rows), nil
}
func (q *QueryExecutor) Hook(hook func(dataset interface{}) error) {
q.hook = hook
}