Skip to content

Slim Connection for v1.0 #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 103 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
b0565e5
wip
ungerik Dec 1, 2024
844985f
remove Connection.IsTransaction
ungerik Dec 13, 2024
c475b4c
ListenerConnection and fix tests
ungerik Dec 13, 2024
0edd2db
TransactionOptions
ungerik Dec 13, 2024
153ee3c
transaction number must not be zero
ungerik Dec 13, 2024
d96a875
added Connection.Query
ungerik Dec 13, 2024
0209603
added db.TableForStruct
ungerik Dec 16, 2024
1b97cb8
StructWithTableName interface
ungerik Dec 16, 2024
23339b6
StructFieldMapper.MapStructField does not return table name
ungerik Dec 17, 2024
b46366e
refactor
ungerik Dec 20, 2024
0db87a5
remove methods from Connection
ungerik Dec 21, 2024
566d698
remove package impl
ungerik Dec 21, 2024
2a76688
added MockConn
ungerik Dec 21, 2024
e74451b
make pqconn a module
ungerik Dec 21, 2024
d5eb2b6
QueryFormatter
ungerik Dec 21, 2024
1555429
added QueryData and NormalizedQueryData
ungerik Dec 21, 2024
d8717f2
write queries
ungerik Dec 21, 2024
ba567f2
move to db package
ungerik Dec 21, 2024
718ba14
move more to db package
ungerik Dec 21, 2024
ff8a434
DefaultStructReflector
ungerik Dec 21, 2024
ba960aa
RecordingMockConn
ungerik Dec 22, 2024
428cf2e
TestInsertStructWithTableName
ungerik Dec 22, 2024
a061869
StdQueryFormatter.PlaceholderPosPrefix
ungerik Dec 22, 2024
db1ef65
postgres docker test
ungerik Dec 22, 2024
cbe3ceb
CreateTablesAndInsertStructs
ungerik Dec 22, 2024
29acb6f
MapStructField returns Column
ungerik Dec 23, 2024
e850d08
fix TaggedStructReflector.MapStructField
ungerik Dec 23, 2024
d7b7091
TypeMapper
ungerik Dec 23, 2024
b1ff162
FormatStringLiteral
ungerik Dec 23, 2024
358b3ee
db.QuerySlice
ungerik Dec 29, 2024
d1a8f85
db.QueryCallback
ungerik Dec 29, 2024
8629ef9
readme todo
ungerik Dec 30, 2024
6a52dfa
migrate package information
ungerik Dec 30, 2024
70e6fe0
prepared statements
ungerik Dec 31, 2024
e9d0a15
InsertStructStmt
ungerik Jan 1, 2025
1371c22
ContextWithStructReflector
ungerik Jan 1, 2025
466f940
printMockConn
ungerik Jan 1, 2025
c2d3d5b
ExampleUpsertStructStmt
ungerik Jan 3, 2025
cec6aa0
db.QueryOption
ungerik Jan 3, 2025
6e62cb3
db.TestInsert
ungerik Jan 7, 2025
4e34fa2
implement NewPrintMockConn with MockConn
ungerik Jan 7, 2025
db37152
NewRecordingMockConn implemented with MockConn
ungerik Jan 7, 2025
609387e
NewMockConn
ungerik Jan 7, 2025
2d7960a
QueryRowStruct does not return pointer
ungerik Jan 8, 2025
93b0833
added db.OptionalTransaction
ungerik Mar 12, 2025
eba23fb
GetRow reflect.TypeFor
ungerik Apr 5, 2025
1853733
rename InsertStruct to InsertRowStruct
ungerik Apr 5, 2025
e14cf26
rename GetRow to GetRowStruct
ungerik Apr 5, 2025
494b0a8
remove unused code
ungerik Apr 7, 2025
6fa2f87
go 1.24
ungerik Apr 10, 2025
b02a099
fix ConnOr
ungerik Apr 10, 2025
d6b8ac8
postgres utils
ungerik Apr 10, 2025
ac38b76
Config.ConnectURL() returns *url.URL
ungerik Apr 11, 2025
2e7a8ca
remove sqldb.Row
ungerik Apr 11, 2025
00c1b5e
QueryRowsAsSlice
ungerik Apr 11, 2025
9dfa8b9
rename db.RowScanner to Row
ungerik Apr 11, 2025
95c51f1
added QueryRowStructStmt
ungerik Apr 12, 2025
80fdc65
update deps
ungerik Apr 12, 2025
62d773d
db.QueryRowValue scans structs or single values
ungerik Apr 14, 2025
aaa6480
fix db.Row.Scan for a struct
ungerik Apr 14, 2025
13544ab
ParseConfigURL
ungerik Apr 15, 2025
51c6ff0
ParseConfig
ungerik Apr 15, 2025
8529b1d
Config.String does not include password
ungerik Apr 15, 2025
3f63156
fix Config.Connect
ungerik Apr 15, 2025
684d879
update deps
ungerik Apr 15, 2025
e31e836
todo user demo
ungerik Apr 22, 2025
36ddbfb
update deps
ungerik Apr 26, 2025
4d5acf2
go.work.sum
ungerik Apr 26, 2025
473ccd8
add db.Close
ungerik May 6, 2025
d569b96
db.ContextWithGlobalConn
ungerik May 26, 2025
880943a
update deps
ungerik May 26, 2025
53dc5ef
go 1.23
ungerik May 27, 2025
dabee52
Add ReadOnly configuration option for database connections
ungerik Jul 9, 2025
f1676e5
added db.ContextWithoutTransactions
ungerik Jul 21, 2025
61fa917
added TransactionResult, IsolatedTransactionResult, OptionalTransacti…
ungerik Jul 23, 2025
e5e38af
added db.ContextWithNonConnectionForTest
ungerik Jul 21, 2025
222539a
update deps
ungerik Jul 23, 2025
179ecab
added CLAUDE.md
ungerik Jul 26, 2025
63bf322
TODO.md
ungerik Jul 26, 2025
f78f9e0
todo
ungerik Jul 26, 2025
2d7dc53
Refactor transaction handling: removed unused error field from Config…
ungerik Jul 26, 2025
8a6b10c
refactor to use QueryBuilder
ungerik Jul 26, 2025
0757fd5
Refactor database query interfaces: renamed Queryer to QueryExec and …
ungerik Jul 26, 2025
d08c935
Refactor database query interfaces: introduced Preparer, Executor, an…
ungerik Jul 26, 2025
50ef6d4
Refactor transaction handling: renamed TransactionInfo to Transaction…
ungerik Jul 27, 2025
9cfa40c
comments
ungerik Jul 27, 2025
35e58e5
introduced QueryBuilderFunc type and updated related methods
ungerik Jul 27, 2025
dffbaef
QueryBuilder returns strings
ungerik Jul 27, 2025
5f6e59a
docu
ungerik Jul 27, 2025
d1f61b7
Refactor QueryBuilder interface: renamed UpdateValues method to Update
ungerik Jul 27, 2025
1b00da7
move db struct reflection to root package
ungerik Jul 28, 2025
3d30aa3
more moving from db to root
ungerik Jul 28, 2025
50441eb
moved db insert impl to root
ungerik Jul 28, 2025
29f8a61
move query funcs from db to root package
ungerik Jul 28, 2025
75400a6
move update funcs from db to root package
ungerik Jul 28, 2025
b38ae5a
move upsert funcs from db to root package
ungerik Jul 28, 2025
b05b9fd
Add query caching for InsertRowStruct and new reflection helper function
ungerik Jul 28, 2025
f686693
Add caching mechanism for QueryBuilder to optimize performance and re…
ungerik Jul 28, 2025
b60c18d
add go-types dependency
ungerik Jul 31, 2025
a910434
Refactor pqconn/arrays.go: remove unused imports and simplify type de…
ungerik Jul 31, 2025
237e1bf
Fix User struct to use sqldb.TableName instead of db.TableName
ungerik Aug 5, 2025
dcc9ef0
go mod tidy
ungerik Aug 5, 2025
00cb60c
Add SQL query to drop all types in the current schema
ungerik Aug 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

pqconn/test/postgres-data
104 changes: 104 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Development Commands

- **Run tests for all modules**: `./test-workspace.sh` - This runs tests across all Go workspace modules (main, mssqlconn, mysqlconn, pqconn)
- **Run tests for specific module**: `go test ./...` in the module directory
- **Build all modules**: `go build ./...`
- **Get dependencies**: `go mod tidy` (run in each module directory as needed)

## Go Workspace Structure

This repository uses Go workspaces with multiple modules:
- Main module: `github.com/domonda/go-sqldb` (root)
- Database drivers: `./mssqlconn`, `./mysqlconn`, `./pqconn`
- Command tools: `./cmd/sqldb-dump`
- Examples: `./examples/user_demo`

## Architecture Overview

### Core Components

- **Connection Interface**: Central abstraction for database connections and transactions (`connection.go`)
- **DB Package**: Context-based connection management pattern (`db/` directory)
- **Database Drivers**: Separate modules for PostgreSQL (`pqconn/`), MySQL (`mysqlconn/`), and SQL Server (`mssqlconn/`)
- **Query Building**: Flexible query construction with struct mapping
- **Transaction Management**: Nested transactions with savepoint support

### Key Design Patterns

1. **Context-Based Connection Storage**: Store connections/transactions in context for seamless function composition
2. **Struct-to-SQL Mapping**: Automatic mapping between Go structs and database rows using reflection
3. **Transaction Callbacks**: Execute transactions in callback functions that can be nested
4. **Flexible Query Interface**: Support for both raw SQL and struct-based operations

### Important Packages

- `sqldb` (root): Core interfaces and types
- `db/`: Context-based connection management and transaction utilities
- `information/`: Database schema introspection
- `_mockconn/`: Mock implementations for testing

## Code Conventions

### SQL Queries
- Write SQL string literals with backticks and prefix with `/*sql*/` comment
- Use numbered parameters (`$1`, `$2`) for PostgreSQL driver

### Error Handling
- Use `github.com/domonda/go-errs` instead of standard `errors` package
- Use `errs.New()` instead of `errors.New()`
- Use `errs.Errorf()` instead of `fmt.Errorf()`

### UUID Types
- Use `github.com/domonda/go-types/uu` package for UUIDs
- Single UUID: `uu.ID`
- UUID slice: `uu.IDSlice` (not `[]uu.ID`)
- Zero values: `uu.IDNil` for `uu.ID`, `uu.IDNull` for `uu.NullableID`, `nil` for `uu.IDSlice`

### General Go Rules
- Use `any` instead of `interface{}`
- In HTTP handlers, use `http.Request.Context()` for context
- Never return actual error strings as HTTP 500 responses - use abstract descriptions

### Struct Field Mapping
- Default tag: `db:"column_name"`
- Primary key: `db:"id,pk"`
- Ignore field: `db:"-"`

## Testing
- Mock connections available in `_mockconn/` package
- PostgreSQL integration tests use Docker (see `pqconn/test/`)
- Use `db.ContextWithNonConnectionForTest()` for testing without real database
- Helper functions in `db/testhelper.go`

## Common Usage Patterns

### Transaction Management
```go
// Simple transaction
err := db.Transaction(ctx, func(ctx context.Context) error {
// All db.Conn(ctx) calls use the transaction
return db.Conn(ctx).Exec(/*sql*/ `INSERT ...`)
})

// Serialized transaction (for high concurrency scenarios)
err := db.SerializedTransaction(ctx, func(ctx context.Context) error { ... })

// Transaction with savepoints (nested transactions)
err := db.TransactionSavepoint(ctx, func(ctx context.Context) error { ... })
```

### Struct Operations
```go
// Insert with struct
err := db.InsertStruct(ctx, "table_name", &structInstance)

// Upsert (uses primary key fields)
err := db.UpsertStruct(ctx, "table_name", &structInstance)

// Query into struct
user, err := db.QueryRowValue[User](ctx, /*sql*/ `SELECT * FROM users WHERE id = $1`, id)
```
78 changes: 78 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# TODO - Unfinished Work in go-sqldb

## Major TODOs from README.md

- [ ] Test all pkg db functions
- [ ] pkg information completion
- [ ] Test pqconn with dockerized Postgres
- [ ] Cache struct types (see commit 090e73d1d9db8534d2950dd7236d7ebe192cd512)
- [ ] Std SQL driver for mocks
- [ ] Smooth out listener for Postgres
- [ ] SQLite integration https://github.com/zombiezen/go-sqlite
- [ ] Bartch insert
```go
func BatchInsert[T any](ctx context.Context, table string, items []T,
batchSize int) error
```

## Code-Level TODOs and Missing Implementations

### Performance Optimizations

- [ ] **db/insert.go:203** - `InsertRowStructs` missing optimized batch insert (currently processes one-by-one in transaction)
- [ ] **db/insert.go:76** - Commented code for RETURNING clause needs error wrapping
- [ ] **pqconn/arrays.go:128** - Array element scanning needs type conversion improvement for different element types

### Function Implementations

- [ ] **db/insert.go:152** - Complete commented out `InsertStructStmt` function with TODO placeholder
- [ ] **mssqlconn/queryformatter.go:11** - Allow spaces and other characters with backtick escaping

### API Design Questions

- [ ] **db/scanresult.go:3** - Consider moving ScanResult to RowScanner interface
- [ ] **db/multirowscanner.go:15,97** - Resolve API design questions about single vs multi-column scanning
- [ ] **db/reflectstruct.go:168** - Clean up Connection implementation detail

## Missing Patterns

### 1. Batch Operations
- Current `InsertRowStructs` processes items individually in a transaction
- Need optimized batch INSERT statements that combine multiple structs
- Consider maxArgs parameter limitations

### 2. RETURNING Clause Support
- Commented implementation exists in insert.go:76
- Need proper error wrapping for query execution
- Should integrate with existing query building patterns

### 3. Error Handling Standardization
- Some query error wrapping is incomplete
- Need consistent pattern across all database operations

### 4. Type Conversion Enhancement
- Array scanning needs improvement for different element types
- String-to-type conversion challenges in pqconn/arrays.go:128

## Key Areas for Completion

### High Priority
1. **Performance Optimization**: Implement batch insert operations
2. **Testing**: Comprehensive test coverage for db package functions
3. **Configuration**: Rethink and improve Config structure

### Medium Priority
4. **Database Support**: Complete SQLite integration
5. **Error Handling**: Standardize query error wrapping patterns
6. **API Consistency**: Resolve design questions in multirowscanner

### Low Priority
7. **Code Organization**: Move ScanResult and clean up implementation details
8. **Features**: Enhanced array type support and RETURNING clause functionality

## Implementation Notes

- The UpsertStruct function (db/upsert.go:14) is marked "TODO" but appears fully implemented
- Mock connection patterns are well established in _mockconn/ package
- Go workspace structure supports multiple database drivers effectively
- Context-based connection management pattern is consistently implemented
File renamed without changes.
136 changes: 136 additions & 0 deletions _mockconn/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package mockconn

/*
import (
"context"
"database/sql"
"errors"
"fmt"
"io"
"time"

"github.com/domonda/go-sqldb"
)

var DefaultArgFmt = "?"

func New(ctx context.Context, queryWriter io.Writer, rowsProvider RowsProvider) sqldb.Connection {
return &connection{
ctx: ctx,
queryWriter: queryWriter,
listening: newBoolMap(),
rowsProvider: rowsProvider,
structFieldNamer: sqldb.DefaultStructFieldMapping,
argFmt: DefaultArgFmt,
}
}

type connection struct {
ctx context.Context
queryWriter io.Writer
listening *boolMap
rowsProvider RowsProvider
structFieldNamer sqldb.StructFieldMapper
argFmt string
}



func (conn *connection) Stats() sql.DBStats {
return sql.DBStats{}
}

func (conn *connection) Config() *sqldb.Config {
return &sqldb.Config{Driver: "mockconn", Host: "localhost", Database: "mock"}
}

func (conn *connection) Ping(time.Duration) error {
return nil
}

func (conn *connection) Exec(query string, args ...any) error {
if conn.queryWriter != nil {
fmt.Fprint(conn.queryWriter, query)
}
return nil
}

func (conn *connection) Query(query string, args ...any) sqldb.Rows {
if err := conn.ctx.Err(); err != nil {
return sqldb.RowsErr(err)
}
return conn.rowsProvider.Query(conn.structFieldNamer, query, args...)
}

// func (conn *connection) QueryRow(query string, args ...any) sqldb.RowScanner {
// if conn.ctx.Err() != nil {
// return sqldb.RowScannerWithError(conn.ctx.Err())
// }
// if conn.queryWriter != nil {
// fmt.Fprint(conn.queryWriter, query)
// }
// if conn.rowsProvider == nil {
// return sqldb.RowScannerWithError(nil)
// }
// return conn.rowsProvider.QueryRow(conn.structFieldNamer, query, args...)
// }

// func (conn *connection) QueryRows(query string, args ...any) sqldb.RowsScanner {
// if conn.ctx.Err() != nil {
// return sqldb.RowsScannerWithError(conn.ctx.Err())
// }
// if conn.queryWriter != nil {
// fmt.Fprint(conn.queryWriter, query)
// }
// if conn.rowsProvider == nil {
// return sqldb.RowsScannerWithError(nil)
// }
// return conn.rowsProvider.QueryRows(conn.structFieldNamer, query, args...)
// }

func (conn *connection) Transaction() (no uint64, opts *sql.TxOptions) {
return 0, nil
}

func (conn *connection) Begin(no uint64, opts *sql.TxOptions) (sqldb.Connection, error) {
if no == 0 {
return nil, errors.New("transaction number must not be zero")
}
if conn.queryWriter != nil {
fmt.Fprint(conn.queryWriter, "BEGIN")
}
return transaction{conn, opts, no}, nil
}

func (conn *connection) Commit() error {
return sqldb.ErrNotWithinTransaction
}

func (conn *connection) Rollback() error {
return sqldb.ErrNotWithinTransaction
}

func (conn *connection) ListenOnChannel(channel string, onNotify sqldb.OnNotifyFunc, onUnlisten sqldb.OnUnlistenFunc) (err error) {
conn.listening.Set(channel, true)
if conn.queryWriter != nil {
fmt.Fprint(conn.queryWriter, "LISTEN "+channel)
}
return nil
}

func (conn *connection) UnlistenChannel(channel string) (err error) {
conn.listening.Set(channel, false)
if conn.queryWriter != nil {
fmt.Fprint(conn.queryWriter, "UNLISTEN "+channel)
}
return nil
}

func (conn *connection) IsListeningOnChannel(channel string) bool {
return conn.listening.Get(channel)
}

func (conn *connection) Close() error {
return nil
}
*/
Loading