done with wuditing with AI
This commit is contained in:
49
README.md
49
README.md
@@ -102,7 +102,7 @@ pgm -version
|
||||
|
||||
### 1. Create Your Schema
|
||||
|
||||
Create a SQL schema file `schema.sql`:
|
||||
Create a SQL schema file `schema.sql` or use the one created by [dbmate](https://github.com/amacneil/dbmate):
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
@@ -174,16 +174,49 @@ func main() {
|
||||
|
||||
## Important: Query Builder Lifecycle
|
||||
|
||||
⚠️ **Query builders are single-use and should not be reused:**
|
||||
### ✅ Conditional Building (CORRECT)
|
||||
|
||||
Query builders are **mutable by design** to support conditional query building:
|
||||
|
||||
```go
|
||||
// ❌ WRONG - Don't reuse query builders
|
||||
baseQuery := users.User.Select(users.ID, users.Email)
|
||||
baseQuery.Where(users.ID.Eq(1)).First(ctx, &id1, &email1)
|
||||
baseQuery.Where(users.Status.Eq(2)).First(ctx, &id2, &email2)
|
||||
// Second query has BOTH WHERE clauses - incorrect behavior!
|
||||
// ✅ CORRECT - Conditional building pattern
|
||||
query := users.User.Select(users.ID, users.Email, users.Name)
|
||||
|
||||
// ✅ CORRECT - Create new query each time
|
||||
// Add conditions based on filters
|
||||
if nameFilter != "" {
|
||||
query = query.Where(users.Name.Like("%" + nameFilter + "%"))
|
||||
}
|
||||
|
||||
if statusFilter > 0 {
|
||||
query = query.Where(users.Status.Eq(statusFilter))
|
||||
}
|
||||
|
||||
if sortByName {
|
||||
query = query.OrderBy(users.Name.Asc())
|
||||
}
|
||||
|
||||
// Execute the final query with all accumulated conditions
|
||||
err := query.First(ctx, &id, &email, &name)
|
||||
```
|
||||
|
||||
**This is the intended use!** The builder accumulates your conditions, which is powerful and flexible.
|
||||
|
||||
### ❌ Unintentional Reuse (INCORRECT)
|
||||
|
||||
Don't try to create a "base query" and reuse it for **multiple different queries**:
|
||||
|
||||
```go
|
||||
// ❌ WRONG - Trying to reuse for multiple separate queries
|
||||
baseQuery := users.User.Select(users.ID, users.Email)
|
||||
|
||||
// First query - adds ID condition
|
||||
baseQuery.Where(users.ID.Eq(1)).First(ctx, &id1, &email1)
|
||||
|
||||
// Second query - ALSO has ID=1 from above PLUS Status=2!
|
||||
baseQuery.Where(users.Status.Eq(2)).First(ctx, &id2, &email2)
|
||||
// This executes: WHERE users.id = 1 AND users.status = 2 (WRONG!)
|
||||
|
||||
// ✅ CORRECT - Each separate query gets its own builder
|
||||
users.User.Select(users.ID, users.Email).Where(users.ID.Eq(1)).First(ctx, &id1, &email1)
|
||||
users.User.Select(users.ID, users.Email).Where(users.Status.Eq(2)).First(ctx, &id2, &email2)
|
||||
```
|
||||
|
||||
36
pgm_test.go
36
pgm_test.go
@@ -383,42 +383,6 @@ func TestConditionActionTypes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestQueryBuilderReuse documents query builder reuse behavior
|
||||
// This test verifies that query builders accumulate state and should NOT be reused
|
||||
func TestQueryBuilderReuse(t *testing.T) {
|
||||
t.Skip("Query builders are not designed for reuse - this test documents the limitation")
|
||||
|
||||
// This test would require actual database tables to demonstrate the issue
|
||||
// The behavior is:
|
||||
// 1. Creating a base query builder
|
||||
// 2. Adding a WHERE clause
|
||||
// 3. Reusing the same builder with another WHERE clause
|
||||
// 4. The second query would have BOTH WHERE clauses
|
||||
//
|
||||
// Example:
|
||||
// baseQuery := users.User.Select(user.ID, user.Email)
|
||||
// baseQuery.Where(user.ID.Eq(1)) // First query
|
||||
// baseQuery.Where(user.Status.Eq(2)) // Second query has BOTH conditions!
|
||||
//
|
||||
// This is by design - query builders are mutable and single-use.
|
||||
// Each query should create a new builder instance.
|
||||
}
|
||||
|
||||
// TestQueryBuilderThreadSafety documents that query builders are not thread-safe
|
||||
func TestQueryBuilderThreadSafety(t *testing.T) {
|
||||
t.Skip("Query builders are not thread-safe by design - this test documents the limitation")
|
||||
|
||||
// Query builders accumulate state in their internal fields (where, args, etc.)
|
||||
// and do not use any synchronization primitives.
|
||||
//
|
||||
// Sharing a query builder across goroutines will cause race conditions.
|
||||
//
|
||||
// CORRECT usage: Create a new query builder in each goroutine
|
||||
// INCORRECT usage: Share a query builder variable across goroutines
|
||||
//
|
||||
// The connection pool itself IS thread-safe and can be used concurrently.
|
||||
}
|
||||
|
||||
// TestSelectQueryBuilderBasics tests basic query building
|
||||
func TestSelectQueryBuilderBasics(t *testing.T) {
|
||||
// Create a test table
|
||||
|
||||
Reference in New Issue
Block a user