Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions generator/templates/client.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ func (q *Queries) copyHooksFrom(other *Queries) {
{{- range $model := .Schema.Models }}
q.{{ $model.Name }}.beforeCreate = other.{{ $model.Name }}.beforeCreate
q.{{ $model.Name }}.afterCreate = other.{{ $model.Name }}.afterCreate
q.{{ $model.Name }}.afterCreateMany = other.{{ $model.Name }}.afterCreateMany
{{- end }}
}

Expand Down
13 changes: 12 additions & 1 deletion generator/templates/model_create.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ func (d *{{ .Model.Name }}Delegate) CreateManyAndReturn(records ...RecordInput)

func (q *Queries) execute{{ .Model.Name }}CreateMany(ctx context.Context, records []RecordInput) (int64, error) {
rowMaps := make([]map[string]any, len(records))
inputs := make([]{{ .Model.Name }}Create, len(records))
for i, rec := range records {
if err := validate{{ .Model.Name }}Create(rec.Assignments); err != nil {
return 0, fmt.Errorf("validation failed at index %d: %w", i, err)
Expand All @@ -353,8 +354,18 @@ func (q *Queries) execute{{ .Model.Name }}CreateMany(ctx context.Context, record
}
}
rowMaps[i] = input.ToRowMap()
inputs[i] = input
}
return executeCreateMany(ctx, q, rowMaps, "{{ .Model.EffectiveTableName }}", {{ .Model.Name }}ColOrder)
count, err := executeCreateMany(ctx, q, rowMaps, "{{ .Model.EffectiveTableName }}", {{ .Model.Name }}ColOrder)
if err != nil {
return 0, err
}
if q.{{ .Model.Name }}.afterCreateMany != nil {
if err := q.{{ .Model.Name }}.afterCreateMany(ctx, inputs, count); err != nil {
return 0, err
}
}
return count, nil
}

func (q *Queries) execute{{ .Model.Name }}CreateManyAndReturn(ctx context.Context, records []RecordInput, selects *{{ .Model.Name }}Select, omits *{{ .Model.Name }}Omit) ([]*{{ .Model.Name }}, error) {
Expand Down
11 changes: 8 additions & 3 deletions generator/templates/model_structs.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ type {{ .Model.Name }}Omit struct {
}

type {{ .Model.Name }}Delegate struct {
client *Queries
beforeCreate func(context.Context, *{{ .Model.Name }}Create) error
afterCreate func(context.Context, *{{ .Model.Name }}) error
client *Queries
beforeCreate func(context.Context, *{{ .Model.Name }}Create) error
afterCreate func(context.Context, *{{ .Model.Name }}) error
afterCreateMany func(context.Context, []{{ .Model.Name }}Create, int64) error
}

func (d *{{ .Model.Name }}Delegate) BeforeCreate(hook func(context.Context, *{{ .Model.Name }}Create) error) {
Expand All @@ -49,6 +50,10 @@ func (d *{{ .Model.Name }}Delegate) AfterCreate(hook func(context.Context, *{{ .
d.afterCreate = hook
}

func (d *{{ .Model.Name }}Delegate) AfterCreateMany(hook func(context.Context, []{{ .Model.Name }}Create, int64) error) {
d.afterCreateMany = hook
}

func (m *{{ .Model.Name }}) ScanFields(cols []string) []any {
targets := make([]any, len(cols))
for i, col := range cols {
Expand Down
75 changes: 75 additions & 0 deletions integration/create_many_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,81 @@ func TestCreateMany_Hooks(t *testing.T) {
t.Fatalf("expected %d rows (insert still committed), got %d", prevCount+1, count)
}
})

t.Run("AfterCreateMany hook receives structs and count", func(t *testing.T) {
client, cleanup := setupTestDB(t)
defer cleanup()

var gotUsers []valk.UserCreate
var gotCount int64
client.User.AfterCreateMany(func(ctx context.Context, users []valk.UserCreate, count int64) error {
gotUsers = users
gotCount = count
return nil
})

count, err := client.User.CreateMany(
user.Record(
user.Email.Set("bulk1@example.com"),
user.PhoneNum.Set("+700000001"),
),
user.Record(
user.Email.Set("bulk2@example.com"),
user.PhoneNum.Set("+700000002"),
),
).Exec(ctx)

if err != nil {
t.Fatalf("CreateMany failed: %v", err)
}
if count != 2 {
t.Fatalf("expected count 2, got %d", count)
}
if gotCount != 2 {
t.Fatalf("expected hook count 2, got %d", gotCount)
}
if len(gotUsers) != 2 {
t.Fatalf("expected 2 users in hook, got %d", len(gotUsers))
}
if gotUsers[0].Email != "bulk1@example.com" {
t.Errorf("expected email 'bulk1@example.com', got %q", gotUsers[0].Email)
}
if gotUsers[1].Email != "bulk2@example.com" {
t.Errorf("expected email 'bulk2@example.com', got %q", gotUsers[1].Email)
}
})

t.Run("AfterCreateMany hook error does not rollback inserts", func(t *testing.T) {
client, cleanup := setupTestDB(t)
defer cleanup()

client.User.AfterCreateMany(func(ctx context.Context, users []valk.UserCreate, count int64) error {
return fmt.Errorf("after create many rejected")
})

_, err := client.User.CreateMany(
user.Record(
user.Email.Set("ghost@example.com"),
user.PhoneNum.Set("+800000000"),
),
).Exec(ctx)

if err == nil {
t.Fatal("expected error from AfterCreateMany rejection, got nil")
}
if !strings.Contains(err.Error(), "after create many rejected") {
t.Errorf("expected 'after create many rejected' in error, got %v", err)
}

var count int
client.Raw().QueryRowContext(ctx, query(
`SELECT count(*) FROM "User" WHERE email = 'ghost@example.com'`,
`SELECT count(*) FROM "User" WHERE email = 'ghost@example.com'`,
)).Scan(&count)
if count != 1 {
t.Fatalf("expected row to still exist (insert committed before hook), got %d", count)
}
})
}

func TestCreateMany(t *testing.T) {
Expand Down
27 changes: 17 additions & 10 deletions integration/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,23 @@ type SeedData struct {
}

func seed(db *valk.DB, ctx context.Context) *SeedData {
db.User.BeforeCreate(func(ctx context.Context, user *valk.UserCreate) error {

db.User.BeforeCreate(func(ctx context.Context, user *valk.UserCreate) error {
user.Role = new(valk.UserRole.Admin)
return nil
})

db.User.AfterCreateMany(func(ctx context.Context, users []valk.UserCreate, count int64) error {
var emails []string
for _, u := range users {
emails = append(emails, u.Email)
}
fmt.Printf("AfterCreateMany: %d users created (emails: %v)\n", count, emails)
return nil
})

db.User.AfterCreate(func(ctx context.Context, user *valk.User) error {
fmt.Println("CREATED USER WITH ID: ", user.Id)
fmt.Println("USER ROLE: ", user.Role)
fmt.Printf("AfterCreate: user %s (role=%s)\n", user.Email, user.Role)
return nil
})

Expand All @@ -53,22 +60,22 @@ func seed(db *valk.DB, ctx context.Context) *SeedData {
if err != nil {
log.Fatalf("failed to create users: %v", err)
}
fmt.Printf("CreateManyAndReturn: %d users returned with auto-generated IDs\n", len(users))

for _, u := range users {

fmt.Println("USER ROLE: ", u.Role)
}

_, err = db.User.CreateMany(
if _, err := db.User.CreateMany(
user.Record(
user.Email.Set("test"),
user.PhoneNum.Set("555-test"),
user.Password.Set("passwd"),
),
user.Record(
user.Email.Set("again"),
user.PhoneNum.Set("555-again"),
user.Password.Set("123456"),
),
).Exec(ctx)
).Exec(ctx); err != nil {
log.Fatalf("failed to CreateMany: %v", err)
}
referrer, err := db.User.Create(
user.Email.Set("referrer@example.com"),
user.PhoneNum.Set("555-0001"),
Expand Down
4 changes: 2 additions & 2 deletions integration/makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
.PHONY: build run test

build:
go build -o bin/sandbox
go build -o bin/integ

run:
go build -o bin/sandbox && ./bin/sandbox
go run main.go

test:
go test -v ./...
24 changes: 20 additions & 4 deletions integration/valk/category.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ type CategoryOmit struct {
}

type CategoryDelegate struct {
client *Queries
beforeCreate func(context.Context, *CategoryCreate) error
afterCreate func(context.Context, *Category) error
client *Queries
beforeCreate func(context.Context, *CategoryCreate) error
afterCreate func(context.Context, *Category) error
afterCreateMany func(context.Context, []CategoryCreate, int64) error
}

func (d *CategoryDelegate) BeforeCreate(hook func(context.Context, *CategoryCreate) error) {
Expand All @@ -49,6 +50,10 @@ func (d *CategoryDelegate) AfterCreate(hook func(context.Context, *Category) err
d.afterCreate = hook
}

func (d *CategoryDelegate) AfterCreateMany(hook func(context.Context, []CategoryCreate, int64) error) {
d.afterCreateMany = hook
}

func (m *Category) ScanFields(cols []string) []any {
targets := make([]any, len(cols))
for i, col := range cols {
Expand Down Expand Up @@ -245,6 +250,7 @@ func (d *CategoryDelegate) CreateManyAndReturn(records ...RecordInput) *CreateMa

func (q *Queries) executeCategoryCreateMany(ctx context.Context, records []RecordInput) (int64, error) {
rowMaps := make([]map[string]any, len(records))
inputs := make([]CategoryCreate, len(records))
for i, rec := range records {
if err := validateCategoryCreate(rec.Assignments); err != nil {
return 0, fmt.Errorf("validation failed at index %d: %w", i, err)
Expand All @@ -256,8 +262,18 @@ func (q *Queries) executeCategoryCreateMany(ctx context.Context, records []Recor
}
}
rowMaps[i] = input.ToRowMap()
inputs[i] = input
}
count, err := executeCreateMany(ctx, q, rowMaps, "Category", CategoryColOrder)
if err != nil {
return 0, err
}
if q.Category.afterCreateMany != nil {
if err := q.Category.afterCreateMany(ctx, inputs, count); err != nil {
return 0, err
}
}
return executeCreateMany(ctx, q, rowMaps, "Category", CategoryColOrder)
return count, nil
}

func (q *Queries) executeCategoryCreateManyAndReturn(ctx context.Context, records []RecordInput, selects *CategorySelect, omits *CategoryOmit) ([]*Category, error) {
Expand Down
24 changes: 20 additions & 4 deletions integration/valk/categoryToPost.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ type CategoryToPostOmit struct {
}

type CategoryToPostDelegate struct {
client *Queries
beforeCreate func(context.Context, *CategoryToPostCreate) error
afterCreate func(context.Context, *CategoryToPost) error
client *Queries
beforeCreate func(context.Context, *CategoryToPostCreate) error
afterCreate func(context.Context, *CategoryToPost) error
afterCreateMany func(context.Context, []CategoryToPostCreate, int64) error
}

func (d *CategoryToPostDelegate) BeforeCreate(hook func(context.Context, *CategoryToPostCreate) error) {
Expand All @@ -52,6 +53,10 @@ func (d *CategoryToPostDelegate) AfterCreate(hook func(context.Context, *Categor
d.afterCreate = hook
}

func (d *CategoryToPostDelegate) AfterCreateMany(hook func(context.Context, []CategoryToPostCreate, int64) error) {
d.afterCreateMany = hook
}

func (m *CategoryToPost) ScanFields(cols []string) []any {
targets := make([]any, len(cols))
for i, col := range cols {
Expand Down Expand Up @@ -244,6 +249,7 @@ func (d *CategoryToPostDelegate) CreateManyAndReturn(records ...RecordInput) *Cr

func (q *Queries) executeCategoryToPostCreateMany(ctx context.Context, records []RecordInput) (int64, error) {
rowMaps := make([]map[string]any, len(records))
inputs := make([]CategoryToPostCreate, len(records))
for i, rec := range records {
if err := validateCategoryToPostCreate(rec.Assignments); err != nil {
return 0, fmt.Errorf("validation failed at index %d: %w", i, err)
Expand All @@ -255,8 +261,18 @@ func (q *Queries) executeCategoryToPostCreateMany(ctx context.Context, records [
}
}
rowMaps[i] = input.ToRowMap()
inputs[i] = input
}
count, err := executeCreateMany(ctx, q, rowMaps, "CategoryToPost", CategoryToPostColOrder)
if err != nil {
return 0, err
}
if q.CategoryToPost.afterCreateMany != nil {
if err := q.CategoryToPost.afterCreateMany(ctx, inputs, count); err != nil {
return 0, err
}
}
return executeCreateMany(ctx, q, rowMaps, "CategoryToPost", CategoryToPostColOrder)
return count, nil
}

func (q *Queries) executeCategoryToPostCreateManyAndReturn(ctx context.Context, records []RecordInput, selects *CategoryToPostSelect, omits *CategoryToPostOmit) ([]*CategoryToPost, error) {
Expand Down
6 changes: 6 additions & 0 deletions integration/valk/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,16 +240,22 @@ func (q *Queries) initDelegates() {
func (q *Queries) copyHooksFrom(other *Queries) {
q.User.beforeCreate = other.User.beforeCreate
q.User.afterCreate = other.User.afterCreate
q.User.afterCreateMany = other.User.afterCreateMany
q.Profile.beforeCreate = other.Profile.beforeCreate
q.Profile.afterCreate = other.Profile.afterCreate
q.Profile.afterCreateMany = other.Profile.afterCreateMany
q.Post.beforeCreate = other.Post.beforeCreate
q.Post.afterCreate = other.Post.afterCreate
q.Post.afterCreateMany = other.Post.afterCreateMany
q.Comment.beforeCreate = other.Comment.beforeCreate
q.Comment.afterCreate = other.Comment.afterCreate
q.Comment.afterCreateMany = other.Comment.afterCreateMany
q.Category.beforeCreate = other.Category.beforeCreate
q.Category.afterCreate = other.Category.afterCreate
q.Category.afterCreateMany = other.Category.afterCreateMany
q.CategoryToPost.beforeCreate = other.CategoryToPost.beforeCreate
q.CategoryToPost.afterCreate = other.CategoryToPost.afterCreate
q.CategoryToPost.afterCreateMany = other.CategoryToPost.afterCreateMany
}

// Close closes the database connection.
Expand Down
24 changes: 20 additions & 4 deletions integration/valk/comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ type CommentOmit struct {
}

type CommentDelegate struct {
client *Queries
beforeCreate func(context.Context, *CommentCreate) error
afterCreate func(context.Context, *Comment) error
client *Queries
beforeCreate func(context.Context, *CommentCreate) error
afterCreate func(context.Context, *Comment) error
afterCreateMany func(context.Context, []CommentCreate, int64) error
}

func (d *CommentDelegate) BeforeCreate(hook func(context.Context, *CommentCreate) error) {
Expand All @@ -77,6 +78,10 @@ func (d *CommentDelegate) AfterCreate(hook func(context.Context, *Comment) error
d.afterCreate = hook
}

func (d *CommentDelegate) AfterCreateMany(hook func(context.Context, []CommentCreate, int64) error) {
d.afterCreateMany = hook
}

func (m *Comment) ScanFields(cols []string) []any {
targets := make([]any, len(cols))
for i, col := range cols {
Expand Down Expand Up @@ -410,6 +415,7 @@ func (d *CommentDelegate) CreateManyAndReturn(records ...RecordInput) *CreateMan

func (q *Queries) executeCommentCreateMany(ctx context.Context, records []RecordInput) (int64, error) {
rowMaps := make([]map[string]any, len(records))
inputs := make([]CommentCreate, len(records))
for i, rec := range records {
if err := validateCommentCreate(rec.Assignments); err != nil {
return 0, fmt.Errorf("validation failed at index %d: %w", i, err)
Expand All @@ -421,8 +427,18 @@ func (q *Queries) executeCommentCreateMany(ctx context.Context, records []Record
}
}
rowMaps[i] = input.ToRowMap()
inputs[i] = input
}
count, err := executeCreateMany(ctx, q, rowMaps, "Comment", CommentColOrder)
if err != nil {
return 0, err
}
if q.Comment.afterCreateMany != nil {
if err := q.Comment.afterCreateMany(ctx, inputs, count); err != nil {
return 0, err
}
}
return executeCreateMany(ctx, q, rowMaps, "Comment", CommentColOrder)
return count, nil
}

func (q *Queries) executeCommentCreateManyAndReturn(ctx context.Context, records []RecordInput, selects *CommentSelect, omits *CommentOmit) ([]*Comment, error) {
Expand Down
Loading
Loading