Skip to content
Merged
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
241 changes: 241 additions & 0 deletions challenge-13/submissions/Kosench/solution-template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
package main

import (
"database/sql"
"errors"
"fmt"

_ "github.com/mattn/go-sqlite3"
)

// Product represents a product in the inventory system
type Product struct {
ID int64
Name string
Price float64
Quantity int
Category string
}

// ProductStore manages product operations
type ProductStore struct {
db *sql.DB
}

// NewProductStore creates a new ProductStore with the given database connection
func NewProductStore(db *sql.DB) *ProductStore {
return &ProductStore{db: db}
}

// InitDB sets up a new SQLite database and creates the products table
func InitDB(dbPath string) (*sql.DB, error) {
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
return nil, fmt.Errorf("failed to open database: %w", err)
}

if err := db.Ping(); err != nil {
db.Close()
return nil, fmt.Errorf("failed to ping database: %w", err)
}

query := `
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY,
name TEXT,
price REAL NOT NULL CHECK(price >= 0),
quantity INTEGER NOT NULL DEFAULT 0 CHECK(quantity >= 0),
category TEXT
)`

_, err = db.Exec(query)
if err != nil {
db.Close()
return nil, fmt.Errorf("failed to create table: %w", err)
}

// The table should have columns: id, name, price, quantity, category
return db, nil
}

// CreateProduct adds a new product to the database
func (ps *ProductStore) CreateProduct(product *Product) error {
query := `
INSERT INTO products (name, price, quantity, category)
VALUES (?, ?, ?, ?)
`

result, err := ps.db.Exec(query, product.Name, product.Price, product.Quantity, product.Category)
if err != nil {
return fmt.Errorf("failed to create product: %w", err)
}

id, err := result.LastInsertId()
if err != nil {
return fmt.Errorf("failed to get last insert id: %w", err)
}

product.ID = id

return nil
}

// GetProduct retrieves a product by ID
func (ps *ProductStore) GetProduct(id int64) (*Product, error) {
query := `
SELECT id, name, price, quantity, category
FROM products
WHERE id = ?
`

row := ps.db.QueryRow(query, id)

p := &Product{}
err := row.Scan(&p.ID, &p.Name, &p.Price, &p.Quantity, &p.Category)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, fmt.Errorf("product with ID %d not found", id)
}
return nil, err
}

return p, nil
}

// UpdateProduct updates an existing product
func (ps *ProductStore) UpdateProduct(product *Product) error {
query := `
UPDATE products
SET name = ?, price = ?, quantity = ?, category = ?
WHERE id = ?
`

result, err := ps.db.Exec(query,
product.Name,
product.Price,
product.Quantity,
product.Category,
product.ID,
)
if err != nil {
return fmt.Errorf("failed to update product: %w", err)
}

rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get rows affected: %w", err)
}

if rowsAffected == 0 {
return fmt.Errorf("product with ID %d not found", product.ID)
}

return nil
}

// DeleteProduct removes a product by ID
func (ps *ProductStore) DeleteProduct(id int64) error {
query := `DELETE FROM products WHERE id = ?`

result, err := ps.db.Exec(query, id)
if err != nil {
return fmt.Errorf("failed to delete product: %w", err)
}

rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get rows affected: %w", err)
}

if rowsAffected == 0 {
return fmt.Errorf("product with ID %d not found", id)
}

return nil
}

// ListProducts returns all products with optional filtering by category
func (ps *ProductStore) ListProducts(category string) ([]*Product, error) {
var query string
var rows *sql.Rows
var err error

if category == "" {
query = `
SELECT id, name, price, quantity, category
FROM products
`
rows, err = ps.db.Query(query)
} else {
query = `
SELECT id, name, price, quantity, category
FROM products
WHERE category = ?
`
rows, err = ps.db.Query(query, category)
}

if err != nil {
return nil, fmt.Errorf("failed to query products: %w", err)
}
defer rows.Close()

var products []*Product
for rows.Next() {
p := &Product{}
err := rows.Scan(&p.ID, &p.Name, &p.Price, &p.Quantity, &p.Category)
if err != nil {
return nil, fmt.Errorf("failed to scan product: %w", err)
}
products = append(products, p)
}

if err = rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating products: %w", err)
}
return products, nil
}

// BatchUpdateInventory updates the quantity of multiple products in a single transaction
func (ps *ProductStore) BatchUpdateInventory(updates map[int64]int) error {
tx, err := ps.db.Begin()
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}

stmt, err := tx.Prepare("UPDATE products SET quantity = ? WHERE id = ?")
if err != nil {
tx.Rollback()
return fmt.Errorf("failed to prepare statement: %w", err)
}
defer stmt.Close()

for id, quantity := range updates {
result, err := stmt.Exec(quantity, id)
if err != nil {
tx.Rollback()
return fmt.Errorf("failed to update product %d: %w", id, err)
}

rowsAffected, err := result.RowsAffected()
if err != nil {
tx.Rollback()
return fmt.Errorf("failed to get rows affected: %w", err)
}

if rowsAffected == 0 {
tx.Rollback()
return fmt.Errorf("product with ID %d not found", id)
}
}

if err := tx.Commit(); err != nil {
return fmt.Errorf("failed to commit transaction: %w", err)
}

return nil
}

func main() {
// Optional: you can write code here to test your implementation
}
Loading