relica

package module
v0.4.0-beta Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2025 License: MIT Imports: 3 Imported by: 1

README

Relica

CI Go Version Go Report Card License Release Go Reference

Relica is a lightweight, type-safe database query builder for Go with zero production dependencies.

✨ Features

  • 🚀 Zero Production Dependencies - Uses only Go standard library
  • High Performance - LRU statement cache, batch operations (3.3x faster)
  • 🎯 Type-Safe - Reflection-based struct scanning with compile-time checks
  • 🔒 Transaction Support - Full ACID with all isolation levels
  • 📦 Batch Operations - Efficient multi-row INSERT and UPDATE
  • 🔗 JOIN Operations - INNER, LEFT, RIGHT, FULL, CROSS JOIN support (v0.2.0+)
  • 📊 Sorting & Pagination - ORDER BY, LIMIT, OFFSET (v0.2.0+)
  • 🔢 Aggregate Functions - COUNT, SUM, AVG, MIN, MAX, GROUP BY, HAVING (v0.2.0+)
  • 🔍 Subqueries - IN, EXISTS, FROM subqueries, scalar subqueries (v0.3.0+)
  • 🔀 Set Operations - UNION, UNION ALL, INTERSECT, EXCEPT (v0.3.0+)
  • 🌳 Common Table Expressions - WITH clause, recursive CTEs (v0.3.0+)
  • 🌐 Multi-Database - PostgreSQL, MySQL 8.0+, SQLite 3.25+ support
  • 🧪 Well-Tested - 310+ tests, 92.9% coverage
  • 📝 Clean API - Fluent builder pattern with context support

🎉 What's New in v0.4.0-beta

Better Documentation & API Stability - We've migrated from type aliases to wrapper types:

  • All methods now visible on pkg.go.dev - Complete API documentation with examples
  • Zero performance overhead - Wrapper calls are inlined by the compiler (0ns)
  • 95% of code unchanged - Your existing code continues working
  • Industry best practices - Follows patterns from sqlx, pgx, GORM
  • 🔧 Unwrap() methods - Access internal types when needed for advanced use cases

Migration: See docs/MIGRATION_GUIDE.md for v0.3.0 → v0.4.0 upgrade guide.

🚀 Quick Start

Installation
go get github.com/coregx/relica

Note: Always import only the main relica package. Internal packages are protected and not part of the public API.

Basic Usage
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/coregx/relica"
    _ "github.com/lib/pq" // PostgreSQL driver
)

type User struct {
    ID    int    `db:"id"`
    Name  string `db:"name"`
    Email string `db:"email"`
}

func main() {
    // Connect to database
    db, err := relica.Open("postgres", "postgres://user:pass@localhost/db")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    ctx := context.Background()

    // SELECT - Query single row
    var user User
    err = db.Builder().
        Select().
        From("users").
        Where("id = ?", 1).
        WithContext(ctx).
        One(&user)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("User: %+v\n", user)

    // SELECT - Query multiple rows
    var users []User
    err = db.Builder().
        Select().
        From("users").
        Where("age > ?", 18).
        All(&users)

    // INSERT
    result, err := db.Builder().
        Insert("users", map[string]interface{}{
            "name":  "Alice",
            "email": "alice@example.com",
        }).
        Execute()

    // UPDATE
    result, err = db.Builder().
        Update("users").
        Set(map[string]interface{}{
            "name": "Alice Updated",
        }).
        Where("id = ?", 1).
        Execute()

    // DELETE
    result, err = db.Builder().
        Delete("users").
        Where("id = ?", 1).
        Execute()
}

📚 Core Features

CRUD Operations
// SELECT
var user User
db.Builder().Select().From("users").Where("id = ?", 1).One(&user)

// SELECT with multiple conditions
var users []User
db.Builder().
    Select("id", "name", "email").
    From("users").
    Where("age > ?", 18).
    Where("status = ?", "active").
    All(&users)

// INSERT
db.Builder().Insert("users", map[string]interface{}{
    "name": "Bob",
    "email": "bob@example.com",
}).Execute()

// UPDATE
db.Builder().
    Update("users").
    Set(map[string]interface{}{"status": "inactive"}).
    Where("last_login < ?", time.Now().AddDate(0, -6, 0)).
    Execute()

// DELETE
db.Builder().Delete("users").Where("id = ?", 123).Execute()

// UPSERT (INSERT ON CONFLICT)
db.Builder().
    Upsert("users", map[string]interface{}{
        "id":    1,
        "name":  "Alice",
        "email": "alice@example.com",
    }).
    OnConflict("id").
    DoUpdate("name", "email").
    Execute()
Expression API (v0.1.2+)

Relica supports fluent expression builders for type-safe, complex WHERE clauses:

HashExp - Simple Conditions
// Simple equality
db.Builder().Select().From("users").
    Where(relica.HashExp{"status": 1}).
    All(&users)

// Multiple conditions (AND)
db.Builder().Select().From("users").
    Where(relica.HashExp{
        "status": 1,
        "age":    30,
    }).
    All(&users)

// IN clause (slice values)
db.Builder().Select().From("users").
    Where(relica.HashExp{
        "status": []interface{}{1, 2, 3},
    }).
    All(&users)

// NULL handling
db.Builder().Select().From("users").
    Where(relica.HashExp{
        "deleted_at": nil,  // IS NULL
    }).
    All(&users)

// Combined: IN + NULL + equality
db.Builder().Select().From("users").
    Where(relica.HashExp{
        "status":     []interface{}{1, 2},
        "deleted_at": nil,
        "role":       "admin",
    }).
    All(&users)
Comparison Operators
// Greater than
db.Builder().Select().From("users").
    Where(relica.GreaterThan("age", 18)).
    All(&users)

// Less than or equal
db.Builder().Select().From("users").
    Where(relica.LessOrEqual("price", 100.0)).
    All(&products)

// Available: Eq, NotEq, GreaterThan, LessThan, GreaterOrEqual, LessOrEqual
IN and BETWEEN
// IN
db.Builder().Select().From("users").
    Where(relica.In("role", "admin", "moderator")).
    All(&users)

// NOT IN
db.Builder().Select().From("users").
    Where(relica.NotIn("status", 0, 99)).
    All(&users)

// BETWEEN
db.Builder().Select().From("orders").
    Where(relica.Between("created_at", startDate, endDate)).
    All(&orders)
LIKE with Automatic Escaping
// Default: %value% (partial match)
db.Builder().Select().From("users").
    Where(relica.Like("name", "john")).  // name LIKE '%john%'
    All(&users)

// Multiple values (AND)
db.Builder().Select().From("articles").
    Where(relica.Like("title", "go", "database")).  // title LIKE '%go%' AND title LIKE '%database%'
    All(&articles)

// Custom matching (prefix/suffix)
db.Builder().Select().From("files").
    Where(relica.Like("filename", ".txt").Match(false, true)).  // filename LIKE '%.txt'
    All(&files)

// OR logic
db.Builder().Select().From("users").
    Where(relica.OrLike("email", "gmail", "yahoo")).  // email LIKE '%gmail%' OR email LIKE '%yahoo%'
    All(&users)
Logical Combinators
// AND
db.Builder().Select().From("users").
    Where(relica.And(
        relica.Eq("status", 1),
        relica.GreaterThan("age", 18),
    )).
    All(&users)

// OR
db.Builder().Select().From("users").
    Where(relica.Or(
        relica.Eq("role", "admin"),
        relica.Eq("role", "moderator"),
    )).
    All(&users)

// NOT
db.Builder().Select().From("users").
    Where(relica.Not(
        relica.In("status", 0, 99),
    )).
    All(&users)

// Nested combinations
db.Builder().Select().From("users").
    Where(relica.And(
        relica.Eq("status", 1),
        relica.Or(
            relica.Eq("role", "admin"),
            relica.GreaterThan("age", 30),
        ),
    )).
    All(&users)
Backward Compatibility

String-based WHERE still works:

// Old style (still supported)
db.Builder().Select().From("users").
    Where("status = ? AND age > ?", 1, 18).
    All(&users)

// Can mix both styles
db.Builder().Select().From("users").
    Where("status = ?", 1).
    Where(relica.GreaterThan("age", 18)).
    All(&users)
JOIN Operations (v0.2.0+)

Solve N+1 query problems with JOIN support - reduces 101 queries to 1 query (100x improvement).

// Simple INNER JOIN
var results []struct {
    UserID   int    `db:"user_id"`
    UserName string `db:"user_name"`
    PostID   int    `db:"post_id"`
    Title    string `db:"title"`
}

db.Builder().
    Select("u.id as user_id", "u.name as user_name", "p.id as post_id", "p.title").
    From("users u").
    InnerJoin("posts p", "p.user_id = u.id").
    All(&results)

// Multiple JOINs with aggregates
db.Builder().
    Select("messages.*", "users.name", "COUNT(attachments.id) as attachment_count").
    From("messages m").
    InnerJoin("users u", "m.user_id = u.id").
    LeftJoin("attachments a", "m.id = a.message_id").
    Where("m.status = ?", 1).
    GroupBy("messages.id").
    All(&results)

// All JOIN types supported
db.Builder().InnerJoin(table, on)  // INNER JOIN
db.Builder().LeftJoin(table, on)   // LEFT OUTER JOIN
db.Builder().RightJoin(table, on)  // RIGHT OUTER JOIN
db.Builder().FullJoin(table, on)   // FULL OUTER JOIN (PostgreSQL, SQLite)
db.Builder().CrossJoin(table)      // CROSS JOIN (no ON condition)

// JOIN with Expression API
db.Builder().
    Select().
    From("messages m").
    InnerJoin("users u", relica.And(
        relica.Raw("m.user_id = u.id"),
        relica.GreaterThan("u.status", 0),
    )).
    All(&results)

Performance: 100x query reduction (N+1 problem solved), 6-25x faster depending on database.

See JOIN Guide for comprehensive examples and best practices.

Sorting and Pagination (v0.2.0+)

Database-side sorting and pagination for efficient data retrieval - 100x memory reduction.

// ORDER BY with multiple columns
db.Builder().
    Select().
    From("messages").
    OrderBy("created_at DESC", "id ASC").
    All(&messages)

// Pagination with LIMIT and OFFSET
const pageSize = 100
const pageNumber = 2 // Third page (0-indexed)

db.Builder().
    Select().
    From("users").
    OrderBy("age DESC").
    Limit(pageSize).
    Offset(pageNumber * pageSize).
    All(&users)

// Table column references
db.Builder().
    Select().
    From("messages m").
    InnerJoin("users u", "m.user_id = u.id").
    OrderBy("m.created_at DESC", "u.name ASC").
    Limit(50).
    All(&results)

Performance: 100x memory reduction (fetch only what you need vs all rows), 6x faster.

Aggregate Functions (v0.2.0+)

Database-side aggregations for COUNT, SUM, AVG, MIN, MAX - 2,500,000x memory reduction.

// Simple COUNT
var count struct{ Total int `db:"total"` }
db.Builder().
    Select("COUNT(*) as total").
    From("messages").
    One(&count)

// Multiple aggregates
type Stats struct {
    Count int     `db:"count"`
    Sum   int64   `db:"sum"`
    Avg   float64 `db:"avg"`
    Min   int     `db:"min"`
    Max   int     `db:"max"`
}

var stats Stats
db.Builder().
    Select("COUNT(*) as count", "SUM(size) as sum", "AVG(size) as avg", "MIN(size) as min", "MAX(size) as max").
    From("messages").
    One(&stats)

// GROUP BY with HAVING
type UserStats struct {
    UserID       int `db:"user_id"`
    MessageCount int `db:"message_count"`
}

var userStats []UserStats
db.Builder().
    Select("user_id", "COUNT(*) as message_count").
    From("messages").
    GroupBy("user_id").
    Having("COUNT(*) > ?", 100).
    OrderBy("message_count DESC").
    All(&userStats)

Performance: 2,500,000x memory reduction (database aggregation vs fetching all rows), 20x faster.

See Aggregates Guide for comprehensive examples and patterns.

Advanced SQL Features (v0.3.0+)

Relica v0.3.0 adds powerful SQL features for complex queries.

Subqueries

IN/EXISTS Subqueries:

// Find users who have placed orders
sub := db.Builder().Select("user_id").From("orders").Where("status = ?", "completed")
db.Builder().Select("*").From("users").Where(relica.In("id", sub)).All(&users)

// Find users with at least one order (EXISTS is often faster)
orderCheck := db.Builder().Select("1").From("orders").Where("orders.user_id = users.id")
db.Builder().Select("*").From("users").Where(relica.Exists(orderCheck)).All(&users)

FROM Subqueries:

// Calculate aggregates, then filter
stats := db.Builder().
    Select("user_id", "COUNT(*) as order_count", "SUM(total) as total_spent").
    From("orders").
    GroupBy("user_id")

db.Builder().
    FromSelect(stats, "order_stats").
    Select("user_id", "order_count", "total_spent").
    Where("order_count > ? AND total_spent > ?", 10, 5000).
    All(&topCustomers)

See Subquery Guide for complete examples and performance tips.

Set Operations

UNION/UNION ALL:

// Combine active and archived users (UNION removes duplicates)
active := db.Builder().Select("name").From("users").Where("status = ?", 1)
archived := db.Builder().Select("name").From("archived_users").Where("status = ?", 1)
active.Union(archived).All(&allNames)

// UNION ALL is 2-3x faster (keeps duplicates)
active.UnionAll(archived).All(&allNames)

INTERSECT/EXCEPT (PostgreSQL, MySQL 8.0.31+, SQLite):

// Find users who have placed orders (INTERSECT)
allUsers := db.Builder().Select("id").From("users")
orderUsers := db.Builder().Select("user_id").From("orders")
allUsers.Intersect(orderUsers).All(&activeUsers)

// Find users without orders (EXCEPT)
allUsers.Except(orderUsers).All(&inactiveUsers)

See Set Operations Guide for database compatibility and workarounds.

Common Table Expressions (CTEs)

Basic CTEs:

// Define reusable query
orderTotals := db.Builder().
    Select("user_id", "SUM(total) as total").
    From("orders").
    GroupBy("user_id")

// Use CTE in main query
db.Builder().
    With("order_totals", orderTotals).
    Select("*").
    From("order_totals").
    Where("total > ?", 1000).
    All(&premiumUsers)

Recursive CTEs (organizational hierarchies, trees):

// Anchor: top-level employees
anchor := db.Builder().
    Select("id", "name", "manager_id", "1 as level").
    From("employees").
    Where("manager_id IS NULL")

// Recursive: children
recursive := db.Builder().
    Select("e.id", "e.name", "e.manager_id", "h.level + 1").
    From("employees e").
    InnerJoin("hierarchy h", "e.manager_id = h.id")

// Build hierarchy
db.Builder().
    WithRecursive("hierarchy", anchor.UnionAll(recursive)).
    Select("*").
    From("hierarchy").
    OrderBy("level", "name").
    All(&orgChart)

See CTE Guide for hierarchical data examples (org charts, bill of materials, category trees).

Window Functions

Relica supports window functions via SelectExpr() for advanced analytics:

// Rank users by order total within each country
db.Builder().
    SelectExpr("user_id", "country", "total",
        "RANK() OVER (PARTITION BY country ORDER BY total DESC) as rank").
    From("orders").
    All(&rankedOrders)

// Running totals with frame specification
db.Builder().
    SelectExpr("date", "amount",
        "SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as running_total").
    From("transactions").
    OrderBy("date").
    All(&runningTotals)

See Window Functions Guide for complete reference with RANK(), ROW_NUMBER(), LAG(), LEAD(), and frame specifications.

Transactions
// Start transaction
tx, err := db.BeginTx(ctx, &relica.TxOptions{
    Isolation: sql.LevelSerializable,
})
if err != nil {
    return err
}
defer tx.Rollback() // Rollback if not committed

// Execute queries within transaction
_, err = tx.Builder().Insert("users", userData).Execute()
if err != nil {
    return err
}

_, err = tx.Builder().
    Update("accounts").
    Set(map[string]interface{}{"balance": newBalance}).
    Where("user_id = ?", userID).
    Execute()
if err != nil {
    return err
}

// Commit transaction
return tx.Commit()
Batch Operations

Batch INSERT (3.3x faster than individual inserts):

result, err := db.Builder().
    BatchInsert("users", []string{"name", "email"}).
    Values("Alice", "alice@example.com").
    Values("Bob", "bob@example.com").
    Values("Charlie", "charlie@example.com").
    Execute()

// Or from a slice
users := []User{
    {Name: "Alice", Email: "alice@example.com"},
    {Name: "Bob", Email: "bob@example.com"},
}

batch := db.Builder().BatchInsert("users", []string{"name", "email"})
for _, user := range users {
    batch.Values(user.Name, user.Email)
}
result, err := batch.Execute()

Batch UPDATE (updates multiple rows with different values):

result, err := db.Builder().
    BatchUpdate("users", "id").
    Set(1, map[string]interface{}{"name": "Alice Updated", "status": "active"}).
    Set(2, map[string]interface{}{"name": "Bob Updated", "status": "active"}).
    Set(3, map[string]interface{}{"age": 30}).
    Execute()
Context Support
// Query with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

var users []User
err := db.Builder().
    WithContext(ctx).
    Select().
    From("users").
    All(&users)

// Context on query level
err = db.Builder().
    Select().
    From("users").
    WithContext(ctx).
    One(&user)

// Transaction context auto-propagates
tx, _ := db.BeginTx(ctx, nil)
tx.Builder().Select().From("users").One(&user) // Uses ctx automatically

🏗️ Database Support

Database Status Placeholders Identifiers UPSERT
PostgreSQL ✅ Full $1, $2, $3 "users" ON CONFLICT
MySQL ✅ Full ?, ?, ? `users` ON DUPLICATE KEY
SQLite ✅ Full ?, ?, ? "users" ON CONFLICT

⚡ Performance

Statement Cache
  • Default capacity: 1000 prepared statements
  • Hit latency: <60ns
  • Thread-safe: Concurrent access optimized
  • Metrics: Hit rate, evictions, cache size
// Configure cache capacity
db, err := relica.Open("postgres", dsn,
    relica.WithStmtCacheCapacity(2000),
    relica.WithMaxOpenConns(25),
    relica.WithMaxIdleConns(5),
)

// Check cache statistics
stats := db.stmtCache.Stats()
fmt.Printf("Cache hit rate: %.2f%%\n", stats.HitRate*100)
Batch Operations Performance
Operation Rows Time vs Single Memory
Batch INSERT 100 327ms 3.3x faster -15%
Single INSERT 100 1094ms Baseline Baseline
Batch UPDATE 100 1370ms 2.5x faster -55% allocs

🔧 Configuration

db, err := relica.Open("postgres", dsn,
    // Connection pool
    relica.WithMaxOpenConns(25),
    relica.WithMaxIdleConns(5),

    // Statement cache
    relica.WithStmtCacheCapacity(1000),
)
Connection Management
Standard Connection
// Create new connection with Relica managing the pool
db, err := relica.Open("postgres", dsn)
defer db.Close()
Wrap Existing Connection (v0.3.0+)

Use WrapDB() when you need to integrate Relica with an existing *sql.DB connection:

import (
    "database/sql"
    "time"

    "github.com/coregx/relica"
    _ "github.com/lib/pq"
)

// Create and configure external connection pool
sqlDB, err := sql.Open("postgres", dsn)
if err != nil {
    log.Fatal(err)
}

// Apply custom pool settings
sqlDB.SetMaxOpenConns(100)
sqlDB.SetMaxIdleConns(50)
sqlDB.SetConnMaxLifetime(time.Hour)
sqlDB.SetConnMaxIdleTime(10 * time.Minute)

// Wrap with Relica query builder
db := relica.WrapDB(sqlDB, "postgres")

// Use Relica's fluent API
var users []User
err = db.Builder().
    Select().
    From("users").
    Where("status = ?", 1).
    All(&users)

// Caller is responsible for closing the connection
defer sqlDB.Close()  // NOT db.Close()

Use Cases for WrapDB:

  • Existing Codebase Integration: Add Relica to projects with established *sql.DB connections
  • Custom Pool Configuration: Apply advanced connection pool settings before wrapping
  • Shared Connections: Multiple parts of your application can share the same pool
  • Testing: Wrap test database connections without managing lifecycle

Important Notes:

  • Each WrapDB() call creates a new Relica instance with its own statement cache
  • The caller is responsible for closing the underlying *sql.DB connection
  • Multiple wraps of the same connection are isolated (separate caches)

📖 Documentation

User Guides (v0.3.0+)
Additional Resources

🧪 Testing

# Run unit tests
go test ./...

# Run with coverage
go test -cover ./...

# Run integration tests (requires Docker)
go test -tags=integration ./test/...

# Run benchmarks
go test -bench=. -benchmem ./benchmark/...

🎯 Design Philosophy

  1. Zero Dependencies - Production code uses only Go standard library
  2. Type Safety - Compile-time checks, runtime safety
  3. Performance - Statement caching, batch operations, zero allocations in hot paths
  4. Simplicity - Clean API, easy to learn, hard to misuse
  5. Correctness - ACID transactions, proper error handling
  6. Observability - Built-in metrics, context support for tracing

📊 Project Status

  • Version: v0.2.0-beta
  • Go Version: 1.25+
  • Production Ready: Yes (beta)
  • Test Coverage: 88.9%
  • Dependencies: 0 (production), 2 (tests only)
  • API: Stable public API, internal packages protected

🤝 Contributing

Contributions are welcome! Please read our Contributing Guide first.

📝 License

Relica is released under the MIT License.

🙏 Acknowledgments

  • Inspired by ozzo-dbx
  • Built with Go 1.25+ features
  • Zero-dependency philosophy inspired by Go standard library

📞 Support

✨ Special Thanks

Professor Ancha Baranova - This project would not have been possible without her invaluable help and support. Her assistance was crucial in bringing Relica to life.


Made with ❤️ by COREGX Team

Relica - Lightweight, Fast, Zero-Dependency Database Query Builder for Go

Documentation

Overview

Package relica provides a lightweight, type-safe database query builder for Go.

Relica offers a fluent API for building SQL queries with support for:

  • Multiple databases (PostgreSQL, MySQL, SQLite)
  • Zero production dependencies
  • Prepared statement caching
  • Transaction management
  • Advanced SQL features (JOINs, aggregates, subqueries, CTEs)

Quick Start

Install:

go get github.com/coregx/relica

Basic usage:

db, err := relica.Open("postgres", "user=postgres dbname=myapp")
if err != nil {
    log.Fatal(err)
}
defer db.Close()

var users []User
err = db.Builder().Select("*").From("users").All(&users)

Features

CRUD Operations:

// SELECT
db.Builder().Select("*").From("users").Where("id = ?", 123).One(&user)

// INSERT
db.Builder().Insert("users", map[string]interface{}{
    "name": "Alice",
    "email": "alice@example.com",
}).Execute()

// UPDATE
db.Builder().Update("users").
    Set(map[string]interface{}{"status": "active"}).
    Where("id = ?", 123).
    Execute()

// DELETE
db.Builder().Delete("users").Where("id = ?", 123).Execute()

Index

Constants

This section is empty.

Variables

View Source
var And = core.And

And combines expressions with AND.

View Source
var Between = core.Between

Between creates a BETWEEN expression (column BETWEEN low AND high).

View Source
var Eq = core.Eq

Eq creates an equality expression (column = value).

View Source
var Exists = core.Exists

Exists creates an EXISTS subquery expression.

View Source
var GreaterOrEqual = core.GreaterOrEqual

GreaterOrEqual creates a greater-or-equal expression (column >= value).

View Source
var GreaterThan = core.GreaterThan

GreaterThan creates a greater-than expression (column > value).

View Source
var In = core.In

In creates an IN expression (column IN (values...)).

View Source
var LessOrEqual = core.LessOrEqual

LessOrEqual creates a less-or-equal expression (column <= value).

View Source
var LessThan = core.LessThan

LessThan creates a less-than expression (column < value).

View Source
var Like = core.Like

Like creates a LIKE expression with automatic escaping.

View Source
var NewExp = core.NewExp

NewExp creates a new raw SQL expression.

View Source
var Not = core.Not

Not negates an expression.

View Source
var NotBetween = core.NotBetween

NotBetween creates a NOT BETWEEN expression.

View Source
var NotEq = core.NotEq

NotEq creates a not-equal expression (column != value).

View Source
var NotExists = core.NotExists

NotExists creates a NOT EXISTS subquery expression.

View Source
var NotIn = core.NotIn

NotIn creates a NOT IN expression (column NOT IN (values...)).

View Source
var NotLike = core.NotLike

NotLike creates a NOT LIKE expression.

View Source
var Or = core.Or

Or combines expressions with OR.

View Source
var OrLike = core.OrLike

OrLike creates a LIKE expression combined with OR.

View Source
var OrNotLike = core.OrNotLike

OrNotLike creates a NOT LIKE expression combined with OR.

View Source
var WithMaxIdleConns = core.WithMaxIdleConns

WithMaxIdleConns sets the maximum number of idle connections.

View Source
var WithMaxOpenConns = core.WithMaxOpenConns

WithMaxOpenConns sets the maximum number of open connections.

View Source
var WithStmtCacheCapacity = core.WithStmtCacheCapacity

WithStmtCacheCapacity sets the prepared statement cache capacity.

Functions

This section is empty.

Types

type BatchInsertQuery

type BatchInsertQuery struct {
	// contains filtered or unexported fields
}

BatchInsertQuery represents a batch INSERT query being built.

func (*BatchInsertQuery) Build

func (biq *BatchInsertQuery) Build() *Query

Build constructs the Query object.

func (*BatchInsertQuery) Execute

func (biq *BatchInsertQuery) Execute() (sql.Result, error)

Execute executes the batch INSERT query.

func (*BatchInsertQuery) Values

func (biq *BatchInsertQuery) Values(values ...interface{}) *BatchInsertQuery

Values adds a row of values to the batch insert.

Example:

BatchInsert("users", []string{"name", "email"}).
    Values("Alice", "alice@example.com").
    Values("Bob", "bob@example.com")

func (*BatchInsertQuery) ValuesMap

func (biq *BatchInsertQuery) ValuesMap(values map[string]interface{}) *BatchInsertQuery

ValuesMap adds a row from a map.

Example:

BatchInsert("users", []string{"name", "email"}).
    ValuesMap(map[string]interface{}{"name": "Alice", "email": "alice@example.com"})

func (*BatchInsertQuery) WithContext

func (biq *BatchInsertQuery) WithContext(ctx context.Context) *BatchInsertQuery

WithContext sets the context for this batch INSERT query.

type BatchUpdateQuery

type BatchUpdateQuery struct {
	// contains filtered or unexported fields
}

BatchUpdateQuery represents a batch UPDATE query being built.

func (*BatchUpdateQuery) Build

func (buq *BatchUpdateQuery) Build() *Query

Build constructs the Query object.

func (*BatchUpdateQuery) Execute

func (buq *BatchUpdateQuery) Execute() (sql.Result, error)

Execute executes the batch UPDATE query.

func (*BatchUpdateQuery) Set

func (buq *BatchUpdateQuery) Set(keyValue interface{}, values map[string]interface{}) *BatchUpdateQuery

Set adds a row update to the batch.

Example:

BatchUpdate("users", "id").
    Set(1, map[string]interface{}{"status": 2}).
    Set(2, map[string]interface{}{"status": 3})

func (*BatchUpdateQuery) WithContext

func (buq *BatchUpdateQuery) WithContext(ctx context.Context) *BatchUpdateQuery

WithContext sets the context for this batch UPDATE query.

type DB

type DB struct {
	// contains filtered or unexported fields
}

DB represents a database connection with query building capabilities.

DB provides a fluent API for constructing and executing SQL queries in a type-safe manner. It wraps the underlying database/sql connection and adds features like:

  • Prepared statement caching (LRU eviction, <60ns hit latency)
  • Query builder with method chaining
  • Transaction management (all isolation levels)
  • Multi-database support (PostgreSQL, MySQL, SQLite)

Example:

db, err := relica.Open("postgres", "user=postgres dbname=myapp")
if err != nil {
    log.Fatal(err)
}
defer db.Close()

var users []User
err = db.Builder().
    Select("id", "name", "email").
    From("users").
    Where("active = ?", true).
    OrderBy("name").
    All(&users)

func NewDB

func NewDB(driverName, dsn string) (*DB, error)

NewDB creates a database connection (deprecated: use Open).

This function exists for backward compatibility. New code should use Open.

Example:

db, err := relica.NewDB("postgres", dsn)

func Open

func Open(driverName, dsn string, opts ...Option) (*DB, error)

Open creates a new database connection with optional configuration.

The driverName parameter specifies the database driver:

  • "postgres" - PostgreSQL
  • "mysql" - MySQL
  • "sqlite3" - SQLite

The dsn parameter is the database-specific connection string.

Example:

db, err := relica.Open("postgres", "user=postgres dbname=myapp",
    relica.WithMaxOpenConns(100),
    relica.WithMaxIdleConns(50))
if err != nil {
    log.Fatal(err)
}
defer db.Close()

func WrapDB

func WrapDB(sqlDB *sql.DB, driverName string) *DB

WrapDB wraps an existing *sql.DB connection with Relica's query builder.

The caller is responsible for managing the connection lifecycle (including Close()). This is useful when you need to:

  • Use Relica with an externally managed connection pool
  • Integrate with existing code that already has a *sql.DB instance
  • Apply custom connection pool settings before wrapping

Example:

sqlDB, _ := sql.Open("postgres", dsn)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
db := relica.WrapDB(sqlDB, "postgres")
defer sqlDB.Close() // Caller's responsibility

func (*DB) Begin

func (d *DB) Begin(ctx context.Context) (*Tx, error)

Begin starts a transaction with default options.

The transaction must be committed or rolled back to release resources. It's safe to call Rollback() even after Commit().

Example:

tx, err := db.Begin(ctx)
if err != nil {
    return err
}
defer tx.Rollback() // Safe even after Commit

// Use transaction
_, err = tx.Builder().Insert("users", data).Execute()
if err != nil {
    return err
}

return tx.Commit()

func (*DB) BeginTx

func (d *DB) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error)

BeginTx starts a transaction with specified options.

Options can specify isolation level and read-only mode:

  • Isolation: sql.LevelReadUncommitted, sql.LevelReadCommitted, sql.LevelRepeatableRead, sql.LevelSerializable
  • ReadOnly: true for read-only transactions (some databases optimize these)

Example:

opts := &relica.TxOptions{
    Isolation: sql.LevelSerializable,
    ReadOnly:  false,
}
tx, err := db.BeginTx(ctx, opts)

func (*DB) Builder

func (d *DB) Builder() *QueryBuilder

Builder returns a new QueryBuilder for constructing queries.

The query builder provides a fluent interface for building SELECT, INSERT, UPDATE, DELETE, and UPSERT queries.

Example:

db.Builder().
    Select("*").
    From("users").
    Where("id = ?", 123).
    One(&user)

func (*DB) Close

func (d *DB) Close() error

Close releases all database resources including the connection pool and statement cache.

After calling Close, the DB instance should not be used.

Example:

db, _ := relica.Open("postgres", dsn)
defer db.Close()

func (*DB) ExecContext

func (d *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)

ExecContext executes a raw SQL query (INSERT/UPDATE/DELETE).

This bypasses the query builder and executes SQL directly. Use this for queries that aren't supported by the query builder or when you need maximum control.

Example:

result, err := db.ExecContext(ctx,
    "UPDATE users SET status = ? WHERE id = ?",
    1, 123)
if err != nil {
    return err
}
rowsAffected, _ := result.RowsAffected()

func (*DB) GenerateParamName

func (d *DB) GenerateParamName() string

GenerateParamName generates a unique parameter placeholder name.

This is useful when building dynamic SQL queries.

Example:

ph := db.GenerateParamName()
// Returns: p1, p2, p3, etc.

func (*DB) QueryContext

func (d *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)

QueryContext executes a raw SQL query and returns rows.

This bypasses the query builder and executes SQL directly. You are responsible for closing the returned rows.

Example:

rows, err := db.QueryContext(ctx,
    "SELECT * FROM users WHERE status = ?", 1)
if err != nil {
    return err
}
defer rows.Close()

for rows.Next() {
    // Process rows
}

func (*DB) QueryRowContext

func (d *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row

QueryRowContext executes a raw SQL query expected to return at most one row.

This bypasses the query builder and executes SQL directly.

Example:

var count int
err := db.QueryRowContext(ctx,
    "SELECT COUNT(*) FROM users").Scan(&count)

func (*DB) QuoteColumnName

func (d *DB) QuoteColumnName(column string) string

QuoteColumnName quotes a column name using the database's identifier quoting style.

This is useful when building dynamic SQL queries.

Example:

quoted := db.QuoteColumnName("user_id")
// PostgreSQL: "user_id"
// MySQL: `user_id`

func (*DB) QuoteTableName

func (d *DB) QuoteTableName(table string) string

QuoteTableName quotes a table name using the database's identifier quoting style.

This is useful when building dynamic SQL queries.

Example:

quoted := db.QuoteTableName("users")
// PostgreSQL: "users"
// MySQL: `users`

func (*DB) Unwrap

func (d *DB) Unwrap() *core.DB

Unwrap returns the underlying core.DB for advanced use cases.

This method is provided for edge cases where direct access to internal types is needed. Most users should not need this.

Example:

coreDB := db.Unwrap()
// Use coreDB for advanced operations

func (*DB) WithContext

func (d *DB) WithContext(ctx context.Context) *DB

WithContext returns a new DB with the given context.

The context will be used for all subsequent query operations unless overridden at the query level.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
db := db.WithContext(ctx)
db.Builder().Select("*").From("users").All(&users)

type DeleteQuery

type DeleteQuery struct {
	// contains filtered or unexported fields
}

DeleteQuery represents a DELETE query being built.

func (*DeleteQuery) Build

func (dq *DeleteQuery) Build() *Query

Build constructs the Query object.

func (*DeleteQuery) Execute

func (dq *DeleteQuery) Execute() (sql.Result, error)

Execute executes the DELETE query.

func (*DeleteQuery) Where

func (dq *DeleteQuery) Where(condition interface{}, params ...interface{}) *DeleteQuery

Where adds a WHERE condition to the DELETE query.

Example:

Delete("users").Where("id = ?", 123)

func (*DeleteQuery) WithContext

func (dq *DeleteQuery) WithContext(ctx context.Context) *DeleteQuery

WithContext sets the context for this DELETE query.

type Expression

type Expression = core.Expression

Expression represents a database expression for building complex WHERE clauses.

Expressions provide a type-safe way to construct SQL conditions without writing raw SQL strings. They support nesting and composition.

Example:

expr := relica.And(
    relica.Eq("status", 1),
    relica.Or(
        relica.GreaterThan("age", 18),
        relica.Eq("verified", true),
    ),
)
db.Builder().Select("*").From("users").Where(expr).All(&users)

type HashExp

type HashExp = core.HashExp

HashExp represents a hash-based expression using column-value pairs.

HashExp provides a convenient map syntax for simple equality conditions. Special values are handled automatically:

  • nil → "column IS NULL"
  • []interface{} → "column IN (...)"

Example:

db.Builder().Select("*").From("users").Where(relica.HashExp{
    "status": 1,
    "role": []string{"admin", "moderator"},
    "deleted_at": nil,
}).All(&users)

type LikeExp

type LikeExp = core.LikeExp

LikeExp represents a LIKE expression with automatic escaping.

LikeExp provides pattern matching with automatic escaping of SQL wildcard characters (%, _).

Example:

db.Builder().Select("*").From("users").Where(
    relica.Like("name", "john%"),
).All(&users)

type Option

type Option = core.Option

Option is a functional option for configuring DB.

Example:

db, err := relica.Open("postgres", dsn,
    relica.WithMaxOpenConns(100),
    relica.WithMaxIdleConns(50))

type Query

type Query struct {
	// contains filtered or unexported fields
}

Query represents a built query ready for execution.

Query encapsulates the SQL string, parameters, and execution context. It provides methods for executing the query and scanning results.

Example:

q := db.Builder().Select("*").From("users").Where("id = ?", 123).Build()
var user User
err := q.One(&user)

func (*Query) All

func (q *Query) All(dest interface{}) error

All fetches all rows into dest slice.

func (*Query) Execute

func (q *Query) Execute() (sql.Result, error)

Execute runs the query and returns results.

func (*Query) One

func (q *Query) One(dest interface{}) error

One fetches a single row into dest.

type QueryBuilder

type QueryBuilder struct {
	// contains filtered or unexported fields
}

QueryBuilder constructs type-safe queries.

The query builder provides a fluent interface for building SELECT, INSERT, UPDATE, DELETE, UPSERT, and batch operations. All queries are cached and executed with prepared statements.

Example:

qb := db.Builder()
qb.Select("*").From("users").Where("status = ?", 1).All(&users)

func (*QueryBuilder) BatchInsert

func (qb *QueryBuilder) BatchInsert(table string, columns []string) *BatchInsertQuery

BatchInsert creates a batch INSERT query for multiple rows.

This is 3.3x faster than individual INSERTs for 100 rows. Use Values() or ValuesMap() to add rows.

Example:

db.Builder().BatchInsert("users", []string{"name", "email"}).
    Values("Alice", "alice@example.com").
    Values("Bob", "bob@example.com").
    Execute()

func (*QueryBuilder) BatchUpdate

func (qb *QueryBuilder) BatchUpdate(table, keyColumn string) *BatchUpdateQuery

BatchUpdate creates a batch UPDATE query for multiple rows.

This is 2.5x faster than individual UPDATEs for 100 rows. Uses CASE-WHEN logic to update multiple rows with different values.

Example:

db.Builder().BatchUpdate("users", "id").
    Set(1, map[string]interface{}{"status": 2}).
    Set(2, map[string]interface{}{"status": 3}).
    Execute()

func (*QueryBuilder) Delete

func (qb *QueryBuilder) Delete(table string) *DeleteQuery

Delete creates a DELETE query for the specified table.

Use Where() to filter rows to delete.

Example:

db.Builder().Delete("users").
    Where("id = ?", 123).
    Execute()

func (*QueryBuilder) Insert

func (qb *QueryBuilder) Insert(table string, values map[string]interface{}) *Query

Insert builds an INSERT query for a single row.

The values parameter is a map of column names to values. Column order is deterministic (alphabetically sorted) for cache efficiency.

Example:

result, err := db.Builder().Insert("users", map[string]interface{}{
    "name": "Alice",
    "email": "alice@example.com",
    "status": 1,
}).Execute()

func (*QueryBuilder) Select

func (qb *QueryBuilder) Select(cols ...string) *SelectQuery

Select starts a SELECT query with the specified columns.

If no columns are provided, defaults to "*" (all columns).

Example:

db.Builder().Select("id", "name", "email").From("users").All(&users)

func (*QueryBuilder) Unwrap

func (qb *QueryBuilder) Unwrap() *core.QueryBuilder

Unwrap returns the underlying core.QueryBuilder for advanced use cases.

This method is provided for edge cases where direct access to internal types is needed. Most users should not need this.

func (*QueryBuilder) Update

func (qb *QueryBuilder) Update(table string) *UpdateQuery

Update creates an UPDATE query for the specified table.

Use Set() to specify column values and Where() to filter rows.

Example:

db.Builder().Update("users").
    Set(map[string]interface{}{"status": 2}).
    Where("id = ?", 123).
    Execute()

func (*QueryBuilder) Upsert

func (qb *QueryBuilder) Upsert(table string, values map[string]interface{}) *UpsertQuery

Upsert creates an UPSERT query (INSERT with conflict resolution).

Supported strategies:

  • PostgreSQL/SQLite: ON CONFLICT ... DO UPDATE
  • MySQL: ON DUPLICATE KEY UPDATE

Example:

db.Builder().Upsert("users", map[string]interface{}{
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com",
}).OnConflict("id").DoUpdate("name", "email").Execute()

func (*QueryBuilder) WithContext

func (qb *QueryBuilder) WithContext(ctx context.Context) *QueryBuilder

WithContext sets the context for all queries built by this builder.

The context will be used for all subsequent query operations unless overridden at the query level.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
qb := db.Builder().WithContext(ctx)
qb.Select("*").From("users").All(&users)

type SelectQuery

type SelectQuery struct {
	// contains filtered or unexported fields
}

SelectQuery represents a SELECT query being built.

SelectQuery supports a wide range of SQL features including:

  • JOINs (INNER, LEFT, RIGHT, FULL, CROSS)
  • Aggregates (COUNT, SUM, AVG, MIN, MAX)
  • GROUP BY and HAVING
  • ORDER BY, LIMIT, OFFSET
  • Set operations (UNION, INTERSECT, EXCEPT)
  • Common Table Expressions (WITH, WITH RECURSIVE)
  • Subqueries (in FROM, WHERE, SELECT clauses)

Example:

sq := db.Builder().
    Select("u.name", "COUNT(*) as order_count").
    From("users u").
    InnerJoin("orders o", "o.user_id = u.id").
    GroupBy("u.id", "u.name").
    Having("COUNT(*) > ?", 10).
    OrderBy("order_count DESC")
sq.All(&results)

func (*SelectQuery) All

func (sq *SelectQuery) All(dest interface{}) error

All scans all rows into dest slice.

Example:

var users []User
err := db.Builder().Select("*").From("users").All(&users)

func (*SelectQuery) AsExpression

func (sq *SelectQuery) AsExpression() Expression

AsExpression converts a SelectQuery to an Expression for subquery use.

Example:

sub := db.Builder().Select("user_id").From("orders").Where("total > ?", 100)
db.Builder().Select("*").From("users").
    Where(relica.In("id", sub.AsExpression())).All(&users)

func (*SelectQuery) Build

func (sq *SelectQuery) Build() *Query

Build constructs the Query object from SelectQuery.

Example:

q := db.Builder().Select("*").From("users").Where("id = ?", 123).Build()
sql, params := q.SQL(), q.Params()

func (*SelectQuery) CrossJoin

func (sq *SelectQuery) CrossJoin(table string) *SelectQuery

CrossJoin adds a CROSS JOIN clause (Cartesian product).

Example:

db.Builder().Select("*").
    From("colors").
    CrossJoin("sizes").
    All(&results)

func (*SelectQuery) Except

func (sq *SelectQuery) Except(other *SelectQuery) *SelectQuery

Except combines queries using EXCEPT (rows in first but not second).

Database support: PostgreSQL 9.1+, MySQL 8.0.31+, SQLite 3.25+

Example:

q1 := db.Builder().Select("id").From("all_users")
q2 := db.Builder().Select("user_id").From("banned_users")
q1.Except(q2).All(&activeUsers)

func (*SelectQuery) From

func (sq *SelectQuery) From(table string) *SelectQuery

From specifies the table to select from.

Supports table aliases: From("users u")

Example:

db.Builder().Select("*").From("users").All(&users)

func (*SelectQuery) FromSelect

func (sq *SelectQuery) FromSelect(subquery *SelectQuery, alias string) *SelectQuery

FromSelect specifies a subquery as the FROM source.

The alias parameter is required for the subquery.

Example:

sub := db.Builder().Select("user_id", "COUNT(*) as cnt").
    From("orders").GroupBy("user_id")
db.Builder().Select("*").FromSelect(sub, "order_counts").
    Where("cnt > ?", 10).All(&results)

func (*SelectQuery) FullJoin

func (sq *SelectQuery) FullJoin(table string, on interface{}) *SelectQuery

FullJoin adds a FULL OUTER JOIN clause.

Note: Not supported by MySQL.

Example:

db.Builder().Select("u.name", "o.total").
    From("users u").
    FullJoin("orders o", "o.user_id = u.id").
    All(&results)

func (*SelectQuery) GroupBy

func (sq *SelectQuery) GroupBy(columns ...string) *SelectQuery

GroupBy adds GROUP BY clause.

Multiple columns supported. Multiple GroupBy() calls are additive.

Example:

GroupBy("user_id", "status")

func (*SelectQuery) Having

func (sq *SelectQuery) Having(condition interface{}, args ...interface{}) *SelectQuery

Having adds HAVING clause (WHERE for aggregates).

Accepts string or Expression. Multiple calls are combined with AND.

Example:

Having("COUNT(*) > ?", 100)

func (*SelectQuery) InnerJoin

func (sq *SelectQuery) InnerJoin(table string, on interface{}) *SelectQuery

InnerJoin adds an INNER JOIN clause.

Example:

db.Builder().Select("u.name", "o.total").
    From("users u").
    InnerJoin("orders o", "o.user_id = u.id").
    All(&results)

func (*SelectQuery) Intersect

func (sq *SelectQuery) Intersect(other *SelectQuery) *SelectQuery

Intersect combines queries using INTERSECT (rows in both).

Database support: PostgreSQL 9.1+, MySQL 8.0.31+, SQLite 3.25+

Example:

q1 := db.Builder().Select("id").From("users")
q2 := db.Builder().Select("user_id").From("orders")
q1.Intersect(q2).All(&ids)  // Users who have placed orders

func (*SelectQuery) LeftJoin

func (sq *SelectQuery) LeftJoin(table string, on interface{}) *SelectQuery

LeftJoin adds a LEFT JOIN clause.

Example:

db.Builder().Select("u.name", "o.total").
    From("users u").
    LeftJoin("orders o", "o.user_id = u.id").
    All(&results)

func (*SelectQuery) Limit

func (sq *SelectQuery) Limit(limit int64) *SelectQuery

Limit sets the LIMIT clause.

Example:

Limit(100)  // Return at most 100 rows

func (*SelectQuery) Offset

func (sq *SelectQuery) Offset(offset int64) *SelectQuery

Offset sets the OFFSET clause.

Example:

Offset(200)  // Skip first 200 rows

func (*SelectQuery) One

func (sq *SelectQuery) One(dest interface{}) error

One scans a single row into dest.

Returns sql.ErrNoRows if no row is found.

Example:

var user User
err := db.Builder().Select("*").From("users").
    Where("id = ?", 123).One(&user)

func (*SelectQuery) OrderBy

func (sq *SelectQuery) OrderBy(columns ...string) *SelectQuery

OrderBy adds ORDER BY clause with optional direction (ASC/DESC).

Supports multiple columns. Multiple OrderBy() calls are additive.

Example:

OrderBy("age DESC", "name ASC")

func (*SelectQuery) RightJoin

func (sq *SelectQuery) RightJoin(table string, on interface{}) *SelectQuery

RightJoin adds a RIGHT JOIN clause.

Example:

db.Builder().Select("u.name", "o.total").
    From("users u").
    RightJoin("orders o", "o.user_id = u.id").
    All(&results)

func (*SelectQuery) SelectExpr

func (sq *SelectQuery) SelectExpr(expr string, args ...interface{}) *SelectQuery

SelectExpr adds a raw SQL expression to the SELECT clause.

Useful for scalar subqueries, window functions, or complex expressions.

Example:

db.Builder().Select("id", "name").
    SelectExpr("(SELECT COUNT(*) FROM orders WHERE orders.user_id = users.id)", "order_count").
    From("users").All(&results)

func (*SelectQuery) Union

func (sq *SelectQuery) Union(other *SelectQuery) *SelectQuery

Union combines this query with another using UNION (removes duplicates).

Example:

q1 := db.Builder().Select("name").From("users")
q2 := db.Builder().Select("name").From("archived_users")
q1.Union(q2).All(&names)

func (*SelectQuery) UnionAll

func (sq *SelectQuery) UnionAll(other *SelectQuery) *SelectQuery

UnionAll combines this query with another using UNION ALL (keeps duplicates).

Example:

q1 := db.Builder().Select("id").From("orders_2023")
q2 := db.Builder().Select("id").From("orders_2024")
q1.UnionAll(q2).All(&orderIDs)

func (*SelectQuery) Unwrap

func (sq *SelectQuery) Unwrap() *core.SelectQuery

Unwrap returns the underlying core.SelectQuery for advanced use cases.

This method is provided for edge cases where direct access to internal types is needed. Most users should not need this.

func (*SelectQuery) Where

func (sq *SelectQuery) Where(condition interface{}, params ...interface{}) *SelectQuery

Where adds a WHERE condition.

Accepts either a string with placeholders or an Expression. Multiple Where() calls are combined with AND.

String example:

Where("status = ? AND age > ?", 1, 18)

Expression example:

Where(relica.And(
    relica.Eq("status", 1),
    relica.GreaterThan("age", 18),
))

func (*SelectQuery) With

func (sq *SelectQuery) With(name string, query *SelectQuery) *SelectQuery

With adds a Common Table Expression (CTE).

Example:

cte := db.Builder().Select("user_id", "SUM(total) as total").
    From("orders").GroupBy("user_id")
db.Builder().Select("*").With("order_totals", cte).
    From("order_totals").Where("total > ?", 1000).All(&users)

func (*SelectQuery) WithContext

func (sq *SelectQuery) WithContext(ctx context.Context) *SelectQuery

WithContext sets the context for this SELECT query.

This overrides any context set on the QueryBuilder.

Example:

sq.WithContext(ctx).All(&users)

func (*SelectQuery) WithRecursive

func (sq *SelectQuery) WithRecursive(name string, query *SelectQuery) *SelectQuery

WithRecursive adds a recursive Common Table Expression.

The query MUST use UNION or UNION ALL. Database support: PostgreSQL (all), MySQL 8.0+, SQLite 3.25+

Example:

anchor := db.Builder().Select("id", "name", "manager_id", "1 as level").
    From("employees").Where("manager_id IS NULL")
recursive := db.Builder().Select("e.id", "e.name", "e.manager_id", "h.level + 1").
    From("employees e").InnerJoin("hierarchy h", "e.manager_id = h.id")
cte := anchor.UnionAll(recursive)
db.Builder().Select("*").WithRecursive("hierarchy", cte).
    From("hierarchy").OrderBy("level", "name").All(&employees)

type Tx

type Tx struct {
	// contains filtered or unexported fields
}

Tx represents a database transaction.

Transactions provide ACID guarantees and support all standard isolation levels. All queries executed through a transaction's builder automatically participate in that transaction.

Example:

tx, err := db.Begin(ctx)
if err != nil {
    return err
}
defer tx.Rollback() // Safe to call even after Commit

_, err = tx.Builder().Insert("users", data).Execute()
if err != nil {
    return err
}

return tx.Commit()

func (*Tx) Builder

func (t *Tx) Builder() *QueryBuilder

Builder returns the query builder for this transaction.

All queries built using this builder will execute within the transaction. The builder automatically inherits the transaction's context.

Example:

tx.Builder().Insert("users", data).Execute()

func (*Tx) Commit

func (t *Tx) Commit() error

Commit commits the transaction.

After calling Commit, the transaction cannot be used for further queries.

Example:

if err := tx.Commit(); err != nil {
    return err
}

func (*Tx) Rollback

func (t *Tx) Rollback() error

Rollback rolls back the transaction.

After calling Rollback, the transaction cannot be used for further queries. It's safe to call Rollback even after Commit (it will be a no-op).

Example:

defer tx.Rollback() // Safe even after Commit

func (*Tx) Unwrap

func (t *Tx) Unwrap() *core.Tx

Unwrap returns the underlying core.Tx for advanced use cases.

This method is provided for edge cases where direct access to internal types is needed. Most users should not need this.

type TxOptions

type TxOptions = core.TxOptions

TxOptions represents transaction options including isolation level.

Example:

opts := &relica.TxOptions{
    Isolation: sql.LevelSerializable,
    ReadOnly:  true,
}
tx, err := db.BeginTx(ctx, opts)

type UpdateQuery

type UpdateQuery struct {
	// contains filtered or unexported fields
}

UpdateQuery represents an UPDATE query being built.

func (*UpdateQuery) Build

func (uq *UpdateQuery) Build() *Query

Build constructs the Query object.

func (*UpdateQuery) Execute

func (uq *UpdateQuery) Execute() (sql.Result, error)

Execute executes the UPDATE query.

func (*UpdateQuery) Set

func (uq *UpdateQuery) Set(values map[string]interface{}) *UpdateQuery

Set specifies the columns and values to update.

Example:

Update("users").Set(map[string]interface{}{"status": 2})

func (*UpdateQuery) Where

func (uq *UpdateQuery) Where(condition interface{}, params ...interface{}) *UpdateQuery

Where adds a WHERE condition to the UPDATE query.

Example:

Update("users").Set(...).Where("id = ?", 123)

func (*UpdateQuery) WithContext

func (uq *UpdateQuery) WithContext(ctx context.Context) *UpdateQuery

WithContext sets the context for this UPDATE query.

type UpsertQuery

type UpsertQuery struct {
	// contains filtered or unexported fields
}

UpsertQuery represents an UPSERT query being built.

func (*UpsertQuery) Build

func (uq *UpsertQuery) Build() *Query

Build constructs the Query object.

func (*UpsertQuery) DoNothing

func (uq *UpsertQuery) DoNothing() *UpsertQuery

DoNothing ignores conflicts (no update).

Example:

Upsert(...).OnConflict("id").DoNothing()

func (*UpsertQuery) DoUpdate

func (uq *UpsertQuery) DoUpdate(columns ...string) *UpsertQuery

DoUpdate specifies which columns to update on conflict.

Example:

Upsert(...).OnConflict("id").DoUpdate("name", "email")

func (*UpsertQuery) Execute

func (uq *UpsertQuery) Execute() (sql.Result, error)

Execute executes the UPSERT query.

func (*UpsertQuery) OnConflict

func (uq *UpsertQuery) OnConflict(columns ...string) *UpsertQuery

OnConflict specifies the columns that determine a conflict.

Example:

Upsert(...).OnConflict("id", "email")

func (*UpsertQuery) WithContext

func (uq *UpsertQuery) WithContext(ctx context.Context) *UpsertQuery

WithContext sets the context for this UPSERT query.

Directories

Path Synopsis
examples module
internal
cache
Package cache provides caching utilities for database prepared statements.
Package cache provides caching utilities for database prepared statements.
core
Package core provides the core database functionality including connection management, query building, statement caching, and result scanning for Relica.
Package core provides the core database functionality including connection management, query building, statement caching, and result scanning for Relica.
dialects
Package dialects provides database-specific SQL dialect implementations for PostgreSQL, MySQL, and SQLite, handling identifier quoting, placeholders, and UPSERT operations.
Package dialects provides database-specific SQL dialect implementations for PostgreSQL, MySQL, and SQLite, handling identifier quoting, placeholders, and UPSERT operations.
util
Package util provides utility functions for context handling, string sanitization, and reflection helpers used throughout the Relica library.
Package util provides utility functions for context handling, string sanitization, and reflection helpers used throughout the Relica library.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL