Initial commit
This commit is contained in:
707
examples/upsert-examples.go
Normal file
707
examples/upsert-examples.go
Normal file
@@ -0,0 +1,707 @@
|
||||
// Package examples demonstrates upsert patterns with cool-mysql
|
||||
package examples
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
mysql "github.com/StirlingMarketingGroup/cool-mysql"
|
||||
)
|
||||
|
||||
// UpsertExamples demonstrates INSERT ... ON DUPLICATE KEY UPDATE patterns
|
||||
func UpsertExamples() {
|
||||
fmt.Println("=== UPSERT EXAMPLES ===")
|
||||
|
||||
// Basic upsert
|
||||
fmt.Println("\n1. Basic Upsert (by unique key)")
|
||||
basicUpsertExample()
|
||||
|
||||
// Upsert with multiple unique columns
|
||||
fmt.Println("\n2. Upsert with Composite Unique Key")
|
||||
compositeKeyUpsertExample()
|
||||
|
||||
// Conditional upsert
|
||||
fmt.Println("\n3. Conditional Upsert with WHERE clause")
|
||||
conditionalUpsertExample()
|
||||
|
||||
// Batch upsert
|
||||
fmt.Println("\n4. Batch Upsert")
|
||||
batchUpsertExample()
|
||||
|
||||
// Selective column updates
|
||||
fmt.Println("\n5. Selective Column Updates")
|
||||
selectiveUpdateExample()
|
||||
|
||||
// Timestamp tracking
|
||||
fmt.Println("\n6. Timestamp Tracking with Upsert")
|
||||
timestampUpsertExample()
|
||||
|
||||
// Increment counter
|
||||
fmt.Println("\n7. Increment Counter Pattern")
|
||||
incrementCounterExample()
|
||||
|
||||
// Upsert from channel
|
||||
fmt.Println("\n8. Upsert from Channel (streaming)")
|
||||
upsertFromChannelExample()
|
||||
|
||||
// Upsert vs Insert Ignore
|
||||
fmt.Println("\n9. Upsert vs Insert Ignore")
|
||||
UpsertOrIgnoreExample()
|
||||
}
|
||||
|
||||
// basicUpsertExample demonstrates simple upsert by email
|
||||
func basicUpsertExample() {
|
||||
db, err := setupDatabase()
|
||||
if err != nil {
|
||||
log.Printf("Setup failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Define user with unique email
|
||||
user := User{
|
||||
Name: "UpsertUser",
|
||||
Email: "upsert@example.com", // Unique key
|
||||
Age: 30,
|
||||
Active: true,
|
||||
}
|
||||
|
||||
// First upsert - INSERT (user doesn't exist)
|
||||
err = db.Upsert(
|
||||
"users", // table
|
||||
[]string{"email"}, // unique columns (conflict detection)
|
||||
[]string{"name", "age", "active"}, // columns to update on conflict
|
||||
"", // no WHERE clause
|
||||
user, // data
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("First upsert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" First upsert: User inserted")
|
||||
|
||||
// Second upsert - UPDATE (user exists)
|
||||
user.Name = "UpsertUser Updated"
|
||||
user.Age = 31
|
||||
|
||||
err = db.Upsert(
|
||||
"users",
|
||||
[]string{"email"},
|
||||
[]string{"name", "age", "active"},
|
||||
"",
|
||||
user,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Second upsert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Second upsert: User updated")
|
||||
|
||||
// Verify result
|
||||
var retrieved User
|
||||
err = db.Select(&retrieved,
|
||||
"SELECT `id`, `name`, `email`, `age`, `active`, `created_at`, `updated_at` FROM `users` WHERE `email` = @@email",
|
||||
0,
|
||||
"upsert@example.com")
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Verification failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Final state: Name='%s', Age=%d\n", retrieved.Name, retrieved.Age)
|
||||
}
|
||||
|
||||
// compositeKeyUpsertExample demonstrates upsert with multiple unique columns
|
||||
func compositeKeyUpsertExample() {
|
||||
db, err := setupDatabase()
|
||||
if err != nil {
|
||||
log.Printf("Setup failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
type ProductInventory struct {
|
||||
StoreID int `mysql:"store_id"`
|
||||
ProductID int `mysql:"product_id"`
|
||||
Quantity int `mysql:"quantity"`
|
||||
Price float64 `mysql:"price"`
|
||||
}
|
||||
|
||||
// Initial inventory
|
||||
inventory := ProductInventory{
|
||||
StoreID: 1,
|
||||
ProductID: 100,
|
||||
Quantity: 50,
|
||||
Price: 19.99,
|
||||
}
|
||||
|
||||
// First upsert - INSERT
|
||||
err = db.Upsert(
|
||||
"inventory",
|
||||
[]string{"store_id", "product_id"}, // Composite unique key
|
||||
[]string{"quantity", "price"}, // Update these on conflict
|
||||
"",
|
||||
inventory,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("First upsert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Inventory created: Store 1, Product 100, Qty=50, Price=$19.99")
|
||||
|
||||
// Second upsert - UPDATE existing inventory
|
||||
inventory.Quantity = 75
|
||||
inventory.Price = 17.99
|
||||
|
||||
err = db.Upsert(
|
||||
"inventory",
|
||||
[]string{"store_id", "product_id"},
|
||||
[]string{"quantity", "price"},
|
||||
"",
|
||||
inventory,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Second upsert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Inventory updated: Qty=75, Price=$17.99")
|
||||
fmt.Println("✓ Composite key upsert successful")
|
||||
}
|
||||
|
||||
// conditionalUpsertExample demonstrates upsert with WHERE clause
|
||||
func conditionalUpsertExample() {
|
||||
db, err := setupDatabase()
|
||||
if err != nil {
|
||||
log.Printf("Setup failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
type Document struct {
|
||||
ID int `mysql:"id"`
|
||||
Title string `mysql:"title"`
|
||||
Content string `mysql:"content"`
|
||||
Version int `mysql:"version"`
|
||||
UpdatedAt time.Time `mysql:"updated_at"`
|
||||
}
|
||||
|
||||
// Initial document
|
||||
doc := Document{
|
||||
ID: 1,
|
||||
Title: "My Document",
|
||||
Content: "Version 1 content",
|
||||
Version: 1,
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// Insert initial version
|
||||
err = db.Insert("documents", doc)
|
||||
if err != nil {
|
||||
log.Printf("Insert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Document v1 created")
|
||||
|
||||
// Upsert with condition: only update if newer
|
||||
doc.Content = "Version 2 content"
|
||||
doc.Version = 2
|
||||
doc.UpdatedAt = time.Now()
|
||||
|
||||
err = db.Upsert(
|
||||
"documents",
|
||||
[]string{"id"},
|
||||
[]string{"title", "content", "version", "updated_at"},
|
||||
"version < VALUES(version)", // Only update if new version is higher
|
||||
doc,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Conditional upsert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Document updated to v2 (condition met)")
|
||||
|
||||
// Try to upsert with older version (should not update)
|
||||
oldDoc := Document{
|
||||
ID: 1,
|
||||
Title: "My Document",
|
||||
Content: "Old content",
|
||||
Version: 1, // Older version
|
||||
UpdatedAt: time.Now().Add(-time.Hour),
|
||||
}
|
||||
|
||||
err = db.Upsert(
|
||||
"documents",
|
||||
[]string{"id"},
|
||||
[]string{"title", "content", "version", "updated_at"},
|
||||
"version < VALUES(version)",
|
||||
oldDoc,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Old version upsert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Old version upsert executed (but condition prevented update)")
|
||||
|
||||
// Verify current version
|
||||
var current Document
|
||||
err = db.Select(¤t,
|
||||
"SELECT `id`, `title`, `content`, `version`, `updated_at` FROM `documents` WHERE `id` = @@id",
|
||||
0,
|
||||
1)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Verification failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Final version: %d (conditional update worked)\n", current.Version)
|
||||
}
|
||||
|
||||
// batchUpsertExample demonstrates upserting multiple records
|
||||
func batchUpsertExample() {
|
||||
db, err := setupDatabase()
|
||||
if err != nil {
|
||||
log.Printf("Setup failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
type Setting struct {
|
||||
Key string `mysql:"key"`
|
||||
Value string `mysql:"value"`
|
||||
}
|
||||
|
||||
// Batch of settings
|
||||
settings := []Setting{
|
||||
{Key: "theme", Value: "dark"},
|
||||
{Key: "language", Value: "en"},
|
||||
{Key: "notifications", Value: "enabled"},
|
||||
{Key: "timezone", Value: "UTC"},
|
||||
}
|
||||
|
||||
// Upsert all settings
|
||||
err = db.Upsert(
|
||||
"settings",
|
||||
[]string{"key"}, // Unique on key
|
||||
[]string{"value"}, // Update value on conflict
|
||||
"",
|
||||
settings,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Batch upsert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" Inserted/updated %d settings\n", len(settings))
|
||||
|
||||
// Update some settings
|
||||
updatedSettings := []Setting{
|
||||
{Key: "theme", Value: "light"}, // Changed
|
||||
{Key: "language", Value: "es"}, // Changed
|
||||
{Key: "notifications", Value: "enabled"}, // Same
|
||||
{Key: "font_size", Value: "14"}, // New
|
||||
}
|
||||
|
||||
err = db.Upsert(
|
||||
"settings",
|
||||
[]string{"key"},
|
||||
[]string{"value"},
|
||||
"",
|
||||
updatedSettings,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Update batch upsert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" Updated batch: 2 changed, 1 same, 1 new\n")
|
||||
|
||||
// Verify results
|
||||
var allSettings []Setting
|
||||
err = db.Select(&allSettings,
|
||||
"SELECT `key`, `value` FROM `settings` ORDER BY key",
|
||||
0)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Verification failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Total settings: %d\n", len(allSettings))
|
||||
for _, s := range allSettings {
|
||||
fmt.Printf(" %s = %s\n", s.Key, s.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// selectiveUpdateExample demonstrates updating only specific columns
|
||||
func selectiveUpdateExample() {
|
||||
db, err := setupDatabase()
|
||||
if err != nil {
|
||||
log.Printf("Setup failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
type UserProfile struct {
|
||||
Email string `mysql:"email"`
|
||||
Name string `mysql:"name"`
|
||||
Bio string `mysql:"bio"`
|
||||
Avatar string `mysql:"avatar"`
|
||||
UpdatedAt time.Time `mysql:"updated_at,defaultzero"`
|
||||
}
|
||||
|
||||
// Initial profile
|
||||
profile := UserProfile{
|
||||
Email: "profile@example.com",
|
||||
Name: "John Doe",
|
||||
Bio: "Software Developer",
|
||||
Avatar: "avatar1.jpg",
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
err = db.Insert("user_profiles", profile)
|
||||
if err != nil {
|
||||
log.Printf("Insert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Profile created")
|
||||
|
||||
// Update only name and bio (not avatar)
|
||||
profile.Name = "John Smith"
|
||||
profile.Bio = "Senior Software Developer"
|
||||
// Avatar unchanged
|
||||
|
||||
err = db.Upsert(
|
||||
"user_profiles",
|
||||
[]string{"email"},
|
||||
[]string{"name", "bio", "updated_at"}, // Don't include avatar
|
||||
"",
|
||||
profile,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Selective update failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Updated name and bio (avatar unchanged)")
|
||||
|
||||
// Later, update only avatar
|
||||
profile.Avatar = "avatar2.jpg"
|
||||
|
||||
err = db.Upsert(
|
||||
"user_profiles",
|
||||
[]string{"email"},
|
||||
[]string{"avatar", "updated_at"}, // Only avatar
|
||||
"",
|
||||
profile,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Avatar update failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Updated avatar only")
|
||||
|
||||
// Verify
|
||||
var final UserProfile
|
||||
err = db.Select(&final,
|
||||
"SELECT `email`, `name`, `bio`, `avatar`, `updated_at` FROM `user_profiles` WHERE `email` = @@email",
|
||||
0,
|
||||
"profile@example.com")
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Verification failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Final: Name='%s', Bio='%s', Avatar='%s'\n",
|
||||
final.Name, final.Bio, final.Avatar)
|
||||
}
|
||||
|
||||
// timestampUpsertExample demonstrates tracking created_at and updated_at
|
||||
func timestampUpsertExample() {
|
||||
db, err := setupDatabase()
|
||||
if err != nil {
|
||||
log.Printf("Setup failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
type Article struct {
|
||||
Slug string `mysql:"slug"`
|
||||
Title string `mysql:"title"`
|
||||
Content string `mysql:"content"`
|
||||
Views int `mysql:"views"`
|
||||
CreatedAt time.Time `mysql:"created_at,defaultzero"`
|
||||
UpdatedAt time.Time `mysql:"updated_at,defaultzero"`
|
||||
}
|
||||
|
||||
// Initial article
|
||||
article := Article{
|
||||
Slug: "my-article",
|
||||
Title: "My Article",
|
||||
Content: "Initial content",
|
||||
Views: 0,
|
||||
}
|
||||
|
||||
// First upsert - INSERT
|
||||
// CreatedAt and UpdatedAt will use database defaults
|
||||
err = db.Upsert(
|
||||
"articles",
|
||||
[]string{"slug"},
|
||||
[]string{"title", "content", "views", "updated_at"},
|
||||
"",
|
||||
article,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Insert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Article created (created_at set by DB)")
|
||||
|
||||
// Get the article to see timestamps
|
||||
var inserted Article
|
||||
err = db.Select(&inserted,
|
||||
"SELECT `slug`, `title`, `content`, `views`, `created_at`, `updated_at` FROM `articles` WHERE slug = @@slug",
|
||||
0,
|
||||
"my-article")
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Select failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" Created at: %s\n", inserted.CreatedAt.Format(time.RFC3339))
|
||||
|
||||
time.Sleep(2 * time.Second) // Wait to see time difference
|
||||
|
||||
// Update article
|
||||
article.Title = "My Updated Article"
|
||||
article.Content = "Updated content"
|
||||
article.Views = inserted.Views + 10
|
||||
|
||||
err = db.Upsert(
|
||||
"articles",
|
||||
[]string{"slug"},
|
||||
[]string{"title", "content", "views", "updated_at"},
|
||||
"", // Don't update created_at
|
||||
article,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Update failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(" Article updated (updated_at changed, created_at preserved)")
|
||||
|
||||
// Verify timestamps
|
||||
var updated Article
|
||||
err = db.Select(&updated,
|
||||
"SELECT `slug`, `title`, `content`, `views`, `created_at`, `updated_at` FROM `articles` WHERE slug = @@slug",
|
||||
0,
|
||||
"my-article")
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Verification failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" Created at: %s (unchanged)\n", updated.CreatedAt.Format(time.RFC3339))
|
||||
fmt.Printf(" Updated at: %s (newer)\n", updated.UpdatedAt.Format(time.RFC3339))
|
||||
fmt.Printf("✓ Timestamps tracked correctly\n")
|
||||
}
|
||||
|
||||
// incrementCounterExample demonstrates atomic counter updates
|
||||
func incrementCounterExample() {
|
||||
db, err := setupDatabase()
|
||||
if err != nil {
|
||||
log.Printf("Setup failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("\nIncrement Counter Example")
|
||||
|
||||
type PageView struct {
|
||||
Page string `mysql:"page"`
|
||||
Views int `mysql:"views"`
|
||||
}
|
||||
|
||||
page := "homepage"
|
||||
|
||||
// Increment views (insert with 1 if not exists, increment if exists)
|
||||
// This uses MySQL's VALUES() function to reference the insert value
|
||||
viewRecord := PageView{
|
||||
Page: page,
|
||||
Views: 1, // Initial value for insert
|
||||
}
|
||||
|
||||
// Custom upsert to increment on duplicate
|
||||
err = db.Exec(
|
||||
"INSERT INTO `page_views` (`page`, `views`)"+
|
||||
" VALUES (@@page, @@views)"+
|
||||
" ON DUPLICATE KEY UPDATE `views` = `views` + @@views",
|
||||
mysql.Params{
|
||||
"page": viewRecord.Page,
|
||||
"views": viewRecord.Views,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Increment failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" View count incremented for %s\n", page)
|
||||
|
||||
// Increment again
|
||||
err = db.Exec(
|
||||
"INSERT INTO `page_views` (`page`, `views`)"+
|
||||
" VALUES (@@page, @@views)"+
|
||||
" ON DUPLICATE KEY UPDATE `views` = `views` + @@views",
|
||||
mysql.Params{
|
||||
"page": page,
|
||||
"views": 1,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Second increment failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Check total views
|
||||
var totalViews int
|
||||
err = db.Select(&totalViews,
|
||||
"SELECT views FROM `page_views` WHERE page = @@page",
|
||||
0,
|
||||
page)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Select views failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Total views for %s: %d\n", page, totalViews)
|
||||
}
|
||||
|
||||
// upsertFromChannelExample demonstrates streaming upserts
|
||||
func upsertFromChannelExample() {
|
||||
db, err := setupDatabase()
|
||||
if err != nil {
|
||||
log.Printf("Setup failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("\nUpsert from Channel Example")
|
||||
|
||||
type Metric struct {
|
||||
MetricName string `mysql:"metric_name"`
|
||||
Value float64 `mysql:"value"`
|
||||
Timestamp time.Time `mysql:"timestamp,defaultzero"`
|
||||
}
|
||||
|
||||
// Create channel of metrics
|
||||
metricCh := make(chan Metric, 100)
|
||||
|
||||
// Producer: generate metrics
|
||||
go func() {
|
||||
defer close(metricCh)
|
||||
metrics := []string{"cpu", "memory", "disk", "network"}
|
||||
for i := 0; i < 100; i++ {
|
||||
metricCh <- Metric{
|
||||
MetricName: metrics[i%len(metrics)],
|
||||
Value: float64(i),
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Upsert from channel
|
||||
err = db.Upsert(
|
||||
"metrics",
|
||||
[]string{"metric_name"},
|
||||
[]string{"value", "timestamp"},
|
||||
"",
|
||||
metricCh,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Channel upsert failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("✓ Streamed 100 metric upserts")
|
||||
|
||||
// Verify
|
||||
var count int
|
||||
count, err = db.Count("SELECT COUNT(*) FROM `metrics`", 0)
|
||||
if err != nil {
|
||||
log.Printf("Count failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" Total unique metrics: %d\n", count)
|
||||
}
|
||||
|
||||
// UpsertOrIgnoreExample demonstrates choosing between update and ignore
|
||||
func UpsertOrIgnoreExample() {
|
||||
db, err := setupDatabase()
|
||||
if err != nil {
|
||||
log.Printf("Setup failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("\nUpsert vs Insert Ignore Example")
|
||||
|
||||
type UniqueEmail struct {
|
||||
Email string `mysql:"email"`
|
||||
Count int `mysql:"count"`
|
||||
}
|
||||
|
||||
// With UPSERT - updates on duplicate
|
||||
email1 := UniqueEmail{Email: "test1@example.com", Count: 1}
|
||||
err = db.Upsert(
|
||||
"email_counts",
|
||||
[]string{"email"},
|
||||
[]string{"count"},
|
||||
"",
|
||||
email1,
|
||||
)
|
||||
fmt.Println(" Upsert: Inserts or updates")
|
||||
|
||||
// With INSERT IGNORE - silently ignores duplicate
|
||||
err = db.Exec(
|
||||
"INSERT IGNORE INTO `email_counts` (email, count) VALUES (@@email, @@count)",
|
||||
mysql.Params{"email": "test1@example.com", "count": 999})
|
||||
|
||||
fmt.Println(" Insert Ignore: Keeps original value on duplicate")
|
||||
|
||||
// Verify - count should still be 1 (ignore worked)
|
||||
var result UniqueEmail
|
||||
err = db.Select(&result,
|
||||
"SELECT `email`, `count` FROM `email_counts` WHERE `email` = @@email",
|
||||
0,
|
||||
"test1@example.com")
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Verification failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Count=%d (INSERT IGNORE kept original)\n", result.Count)
|
||||
}
|
||||
Reference in New Issue
Block a user