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
go get github.com/donghquinn/gqbdπ 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
GQBD follows the same patterns as gdct but without database connections. Just build queries and use them with your preferred driver.
// 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...)PostgreSQL uses $N parameter placeholders and double quotes for identifiers.
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]
}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()
}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]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 uses ? parameter placeholders and backticks for identifiers.
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]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 uses ? parameter placeholders and double quotes for identifiers, with support for RETURNING clause.
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]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]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]GQBD includes built-in support for generating database connection strings for use with sql.Open().
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:"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)
}
}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]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]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(*) > $1GQBD works with any Go database driver. Here are the recommended drivers for each database type:
import _ "github.com/lib/pq" // Most common
// OR
import _ "github.com/jackc/pgx/v5/stdlib" // High performanceimport _ "github.com/go-sql-driver/mysql"import _ "github.com/mattn/go-sqlite3"// 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()
}| 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+) |
GQBD is designed with the same performance principles as gdct:
// 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
}
}π― 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
The codebase is organized for maintainability and database-specific optimizations:
gqbd.go- Main API, shared logic, and database selectionpostgres.go- PostgreSQL-specific query building methodsmariadb.go- MySQL/MariaDB-specific query building methodssqlite.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
Feel free to open issues or submit pull requests. All major SQL databases are now supported!