https://github.com/donghquinn/gqbd
go-query-builder
https://github.com/donghquinn/gqbd
builder go query querybuilder
Last synced: about 2 months ago
JSON representation
go-query-builder
- Host: GitHub
- URL: https://github.com/donghquinn/gqbd
- Owner: donghquinn
- License: mit
- Created: 2025-03-10T01:21:42.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2026-01-01T11:48:49.000Z (5 months ago)
- Last Synced: 2026-01-06T09:21:50.698Z (5 months ago)
- Topics: builder, go, query, querybuilder
- Language: Go
- Homepage:
- Size: 90.8 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# GQBD - High-Performance Go Query Builder
## Introduction
**GQBD** is a high-performance, zero-allocation SQL query builder for Go, inspired by gdct design principles.
✨ **Key Features:**
- 🚀 **Zero allocations** in critical query building paths
- 🔒 **SQL injection safe** with automatic parameter binding and identifier escaping
- 🎯 **Multi-database support**: PostgreSQL, MySQL, MariaDB, SQLite
- ⚡ **Performance optimized** - designed for high-throughput applications
- 📦 **Zero dependencies** - pure Go implementation
- 🔧 **Fluent API** - chainable method design for readable code
## Installation
```bash
go get github.com/donghquinn/gqbd
```
## Performance & Features
🏆 **Performance Benefits:**
- Zero allocations in query building (similar to gdct)
- Optimized string building and parameter handling
- Minimal overhead compared to other query builders
- Built for high-throughput, low-latency applications
📋 **Full Feature Set:**
- ✅ **SELECT**: Complex queries with joins, conditions, grouping, ordering
- ✅ **INSERT**: Bulk inserts with optional RETURNING clause (PostgreSQL)
- ✅ **UPDATE**: Conditional updates with proper parameter binding
- ✅ **DELETE**: Safe deletion with WHERE conditions
- ✅ **Advanced Clauses**: IN, BETWEEN, aggregate functions, DISTINCT
- ✅ **Security**: Automatic SQL injection prevention
- ✅ **Cross-Database**: PostgreSQL ($N), MySQL/MariaDB/SQLite (?) placeholder formats
- ✅ **Connection Strings**: Built-in database connection string generation
## Quick Start
GQBD follows the same patterns as gdct but without database connections. Just build queries and use them with your preferred driver.
```go
// Simple example - matches gdct API pattern
query, args, err := gqbd.BuildSelect(gqbd.PostgreSQL, "users").
Where("age > ?", 18).
OrderBy("created_at", "DESC", nil).
Limit(10).
Build()
// Use with any database driver
rows, err := db.Query(query, args...)
```
## Detailed Examples
### PostgreSQL Examples
PostgreSQL uses `$N` parameter placeholders and double quotes for identifiers.
#### SELECT Query
```go
package main
import (
"fmt"
"github.com/donghquinn/gqbd"
)
func main() {
// Basic SELECT with joins and conditions
qb := gqbd.BuildSelect(gqbd.PostgreSQL, "example_table e", "e.id", "e.name", "u.username").
LeftJoin("user_table u", "u.user_id = e.id").
Where("e.status = ?", "active").
OrderBy("e.created_at", "DESC", nil).
Limit(10).
Offset(0)
query, args, err := qb.Build()
if err != nil {
panic(err)
}
fmt.Printf("Query: %s\n", query)
fmt.Printf("Args: %v\n", args)
// Output:
// Query: SELECT "e"."id", "e"."name", "u"."username" FROM "example_table" e LEFT JOIN "user_table" u ON u.user_id = e.id WHERE e.status = $1 ORDER BY "e"."created_at" DESC LIMIT $2 OFFSET $3
// Args: [active 10 0]
}
```
#### Dynamic WHERE Conditions
```go
func buildDynamicQuery(userName, title string, status string) (string, []interface{}, error) {
qb := gqbd.BuildSelect(gqbd.PostgreSQL, "posts p", "p.id", "p.title", "u.username").
LeftJoin("users u", "u.id = p.user_id")
// Add conditions only if values are provided
if userName != "" {
qb = qb.Where("u.username ILIKE ?", "%"+userName+"%")
}
if title != "" {
qb = qb.Where("p.title ILIKE ?", "%"+title+"%")
}
// Always add status filter
qb = qb.Where("p.status = ?", status)
return qb.Build()
}
```
#### INSERT with RETURNING
```go
data := map[string]interface{}{
"name": "John Doe",
"email": "john@example.com",
"active": true,
}
qb := gqbd.BuildInsert(gqbd.PostgreSQL, "users").
Values(data).
Returning("id, created_at")
query, args, err := qb.Build()
// Query: INSERT INTO "users" ("name", "email", "active") VALUES ($1, $2, $3) RETURNING id, created_at
// Args: [John Doe john@example.com true]
```
#### UPDATE Query
```go
data := map[string]interface{}{
"name": "Jane Doe",
"updated_at": "NOW()",
}
qb := gqbd.BuildUpdate(gqbd.PostgreSQL, "users").
Set(data).
Where("id = ?", 123)
query, args, err := qb.Build()
// Query: UPDATE "users" SET "name" = $1, "updated_at" = $2 WHERE id = $3
// Args: [Jane Doe NOW() 123]
```
### MySQL/MariaDB Examples
MySQL/MariaDB uses `?` parameter placeholders and backticks for identifiers.
#### SELECT Query
```go
qb := gqbd.BuildSelect(gqbd.MariaDB, "products", "id", "name", "price").
Where("category_id = ?", 10).
Where("price BETWEEN ? AND ?", 100, 500).
OrderBy("price", "ASC", nil).
Limit(20)
query, args, err := qb.Build()
// Query: SELECT `id`, `name`, `price` FROM `products` WHERE category_id = ? AND price BETWEEN ? AND ? ORDER BY `price` ASC LIMIT ?
// Args: [10 100 500 20]
```
#### INSERT Query
```go
data := map[string]interface{}{
"name": "New Product",
"price": 99.99,
"category_id": 5,
}
qb := gqbd.BuildInsert(gqbd.MariaDB, "products").
Values(data)
query, args, err := qb.Build()
// Query: INSERT INTO `products` (`name`, `price`, `category_id`) VALUES (?, ?, ?)
// Args: [New Product 99.99 5]
```
### SQLite Examples
SQLite uses `?` parameter placeholders and double quotes for identifiers, with support for RETURNING clause.
#### SELECT Query
```go
qb := gqbd.BuildSelect(gqbd.SQLite, "users", "id", "name", "email").
Where("age > ?", 18).
Where("status = ?", "active").
OrderBy("created_at", "DESC", nil).
Limit(10).
Offset(5)
query, args, err := qb.Build()
// Query: SELECT "id", "name", "email" FROM "users" WHERE age > ? AND status = ? ORDER BY "created_at" DESC LIMIT ? OFFSET ?
// Args: [18 active 10 5]
```
#### INSERT with RETURNING
```go
data := map[string]interface{}{
"name": "Alice",
"email": "alice@example.com",
"age": 28,
}
qb := gqbd.BuildInsert(gqbd.SQLite, "users").
Values(data).
Returning("id, name")
query, args, err := qb.Build()
// Query: INSERT INTO "users" ("name", "email", "age") VALUES (?, ?, ?) RETURNING id, name
// Args: [Alice alice@example.com 28]
```
#### UPDATE Query
```go
qb := gqbd.BuildUpdate(gqbd.SQLite, "users").
Set(map[string]interface{}{
"name": "Updated Name",
"status": "inactive",
}).
Where("id = ?", 1)
query, args, err := qb.Build()
// Query: UPDATE "users" SET "name" = ?, "status" = ? WHERE id = ?
// Args: [Updated Name inactive 1]
```
## Database Connection Strings
GQBD includes built-in support for generating database connection strings for use with `sql.Open()`.
### Connection String Generation
```go
import "github.com/donghquinn/gqbd"
// PostgreSQL connection string
pgConfig := gqbd.DBConfig{
Host: "localhost",
Port: 5432,
User: "postgres",
Password: "postgres",
DBName: "myapp",
SSLMode: "disable",
}
pgDSN := gqbd.BuildConnectionString(gqbd.PostgreSQL, pgConfig)
// Result: "host=localhost port=5432 user=postgres password=postgres dbname=myapp sslmode=disable"
// MySQL connection string
mysqlConfig := gqbd.DBConfig{
Host: "localhost",
Port: 3306,
User: "root",
Password: "password",
DBName: "myapp",
Charset: "utf8mb4",
}
mysqlDSN := gqbd.BuildConnectionString(gqbd.Mysql, mysqlConfig)
// Result: "root:password@tcp(localhost:3306)/myapp?charset=utf8mb4&parseTime=True&loc=Local"
// SQLite connection strings
sqliteConfig := gqbd.DBConfig{
FilePath: "/path/to/database.db",
}
sqliteDSN := gqbd.BuildConnectionString(gqbd.SQLite, sqliteConfig)
// Result: "/path/to/database.db"
// SQLite in-memory
sqliteMemConfig := gqbd.DBConfig{} // Empty config
sqliteMemDSN := gqbd.BuildConnectionString(gqbd.SQLite, sqliteMemConfig)
// Result: ":memory:"
```
### Complete Database Setup Example
```go
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq" // PostgreSQL driver
_ "github.com/go-sql-driver/mysql" // MySQL driver
_ "github.com/mattn/go-sqlite3" // SQLite driver
"github.com/donghquinn/gqbd"
)
func main() {
// Configure database connection
config := gqbd.DBConfig{
Host: "localhost",
Port: 5432,
User: "postgres",
Password: "password",
DBName: "myapp",
SSLMode: "disable",
}
// Generate connection string
dsn := gqbd.BuildConnectionString(gqbd.PostgreSQL, config)
// Open database connection
db, err := sql.Open("postgres", dsn)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Build and execute query
qb := gqbd.BuildSelect(gqbd.PostgreSQL, "users", "id", "name", "email").
Where("status = ?", "active").
OrderBy("name", "ASC", nil).
Limit(10)
query, args, err := qb.Build()
if err != nil {
log.Fatal(err)
}
rows, err := db.Query(query, args...)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
// Process results...
for rows.Next() {
var id int
var name, email string
if err := rows.Scan(&id, &name, &email); err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %d, Name: %s, Email: %s\n", id, name, email)
}
}
```
### Advanced Features
#### IN Clause
```go
qb := gqbd.BuildSelect(gqbd.PostgreSQL, "users", "id", "name").
WhereIn("status", []interface{}{"active", "pending", "verified"})
query, args, err := qb.Build()
// Query: SELECT "id", "name" FROM "users" WHERE "status" IN ($1, $2, $3)
// Args: [active pending verified]
```
#### BETWEEN Clause
```go
qb := gqbd.BuildSelect(gqbd.PostgreSQL, "orders", "id", "total").
WhereBetween("created_at", "2023-01-01", "2023-12-31")
query, args, err := qb.Build()
// Query: SELECT "id", "total" FROM "orders" WHERE "created_at" BETWEEN $1 AND $2
// Args: [2023-01-01 2023-12-31]
```
#### Aggregate Functions
```go
qb := gqbd.BuildSelect(gqbd.PostgreSQL, "orders", "customer_id").
Aggregate("COUNT", "*").
Aggregate("SUM", "total").
GroupBy("customer_id").
Having("COUNT(*) > ?", 5)
query, args, err := qb.Build()
// Query: SELECT "customer_id", COUNT("*"), SUM("total") FROM "orders" GROUP BY "customer_id" HAVING COUNT(*) > $1
```
## Database Driver Integration
GQBD works with any Go database driver. Here are the recommended drivers for each database type:
### PostgreSQL
```go
import _ "github.com/lib/pq" // Most common
// OR
import _ "github.com/jackc/pgx/v5/stdlib" // High performance
```
### MySQL
```go
import _ "github.com/go-sql-driver/mysql"
```
### SQLite
```go
import _ "github.com/mattn/go-sqlite3"
```
### Example Usage with Drivers
```go
// PostgreSQL example
func queryPostgreSQL() {
config := gqbd.DBConfig{
Host: "localhost", Port: 5432, User: "postgres",
Password: "password", DBName: "myapp", SSLMode: "disable",
}
dsn := gqbd.BuildConnectionString(gqbd.PostgreSQL, config)
db, _ := sql.Open("postgres", dsn)
qb := gqbd.BuildSelect(gqbd.PostgreSQL, "users", "id", "name")
query, args, _ := qb.Where("active = ?", true).Build()
rows, _ := db.Query(query, args...)
defer rows.Close()
}
// MySQL example
func queryMySQL() {
config := gqbd.DBConfig{
Host: "localhost", Port: 3306, User: "root",
Password: "password", DBName: "myapp", Charset: "utf8mb4",
}
dsn := gqbd.BuildConnectionString(gqbd.Mysql, config)
db, _ := sql.Open("mysql", dsn)
qb := gqbd.BuildSelect(gqbd.Mysql, "users", "id", "name")
query, args, _ := qb.Where("active = ?", true).Build()
rows, _ := db.Query(query, args...)
defer rows.Close()
}
// SQLite example
func querySQLite() {
config := gqbd.DBConfig{FilePath: "./app.db"}
dsn := gqbd.BuildConnectionString(gqbd.SQLite, config)
db, _ := sql.Open("sqlite3", dsn)
qb := gqbd.BuildSelect(gqbd.SQLite, "users", "id", "name")
query, args, _ := qb.Where("active = ?", true).Build()
rows, _ := db.Query(query, args...)
defer rows.Close()
}
```
## Supported Database Types
| Database | Constant | Placeholders | Identifiers | RETURNING Support |
|----------|----------|--------------|-------------|-------------------|
| PostgreSQL | `gqbd.PostgreSQL` | `$1, $2, $3...` | `"identifier"` | ✅ Yes |
| MySQL | `gqbd.Mysql` | `?, ?, ?...` | `` `identifier` `` | ❌ No |
| MariaDB | `gqbd.MariaDB` | `?, ?, ?...` | `` `identifier` `` | ❌ No |
| SQLite | `gqbd.SQLite` | `?, ?, ?...` | `"identifier"` | ✅ Yes (3.35.0+) |
## Performance Comparison
GQBD is designed with the same performance principles as gdct:
```go
// Benchmark example - zero allocations in hot path
func BenchmarkQueryBuilder(b *testing.B) {
for i := 0; i < b.N; i++ {
query, args, _ := gqbd.BuildSelect(gqbd.PostgreSQL, "users", "id", "name").
Where("active = ?", true).
Where("age > ?", 21).
OrderBy("created_at", "DESC", nil).
Limit(100).
Build()
_ = query
_ = args
}
}
```
## Why Choose GQBD?
🎯 **Same design philosophy as gdct**:
- Zero allocations in critical paths
- SQL injection prevention by design
- Cross-database compatibility
- Pure query building (no database connections)
🚀 **Performance focused**:
- Minimal overhead
- Optimized for high-throughput applications
- Memory efficient
🔒 **Security first**:
- Automatic parameter binding
- Identifier escaping
- Input validation
📋 **Complete solution**:
- Query building for all major databases
- Connection string generation
- Database-specific optimizations
- Modular, maintainable code structure
## Project Structure
The codebase is organized for maintainability and database-specific optimizations:
- `gqbd.go` - Main API, shared logic, and database selection
- `postgres.go` - PostgreSQL-specific query building methods
- `mariadb.go` - MySQL/MariaDB-specific query building methods
- `sqlite.go` - SQLite-specific query building methods
This separation allows for:
- Database-specific optimizations
- Clean, maintainable code
- Easy addition of new database types
- Consistent API across all databases
## Contributing
Feel free to open issues or submit pull requests. All major SQL databases are now supported!