forked from go/pgm
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
|
### 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
|
```sql
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
@@ -174,16 +174,49 @@ func main() {
|
|||||||
|
|
||||||
## Important: Query Builder Lifecycle
|
## 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
|
```go
|
||||||
// ❌ WRONG - Don't reuse query builders
|
// ✅ CORRECT - Conditional building pattern
|
||||||
baseQuery := users.User.Select(users.ID, users.Email)
|
query := users.User.Select(users.ID, users.Email, users.Name)
|
||||||
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 - 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.ID.Eq(1)).First(ctx, &id1, &email1)
|
||||||
users.User.Select(users.ID, users.Email).Where(users.Status.Eq(2)).First(ctx, &id2, &email2)
|
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
|
// TestSelectQueryBuilderBasics tests basic query building
|
||||||
func TestSelectQueryBuilderBasics(t *testing.T) {
|
func TestSelectQueryBuilderBasics(t *testing.T) {
|
||||||
// Create a test table
|
// Create a test table
|
||||||
|
|||||||
Reference in New Issue
Block a user