sqlc

package
v0.0.37-pre1 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2025 License: MIT Imports: 7 Imported by: 9

README

Overview

SQLC is a great tool to use raw sql query with golang. But currently sqlc lacks of dynamic query support.

It's currently only support static query that compiled to golang code, but sometimes we need dynamic query for condition/order/limit/etc. So i write this code as wrapper for sqlc for dynamic query.

Usage

Below is usage example for this library to write dynamic query filters. See full example here

package main

import (
	"context"
	"fmt"
	"os"
	"strconv"

	"github.com/irdaislakhuafa/go-sdk/db"
	"github.com/irdaislakhuafa/go-sdk/querybuilder/sqlc"
	"github.com/irdaislakhuafa/go-sdk/querybuilder/sqlc/example/gen"
)

type (
	ListUserParams struct {
		IDs       []int64
		Search    string
		IsDeleted int
		Ages      []int64
		MinAge    int64
		MaxAge    int64
		Limit     int
		Page      int
		OrderBy   string
		OrderType string
	}
)

func main() {
	ctx := context.Background()

	// connect to db
	db, err := db.Init(db.Config{
		Driver:  db.DriverSQLite,
		User:    "example",
		Options: db.Options{InMemory: true},
	})
	if err != nil {
		panic(err)
	}

	// execute schema query [only for this example, don't write it here in production]
	schema, err := os.ReadFile("./schema.sql")
	if err != nil {
		panic(err)
	}
	_, err = db.ExecContext(ctx, string(schema))
	if err != nil {
		panic(err)
	}

	// wrap sqlc generated code with query builder. query builder won't work without this
	queries := gen.New(sqlc.Wrap(db, sqlc.WrappedOpts{
		ShowQuery: true, // disable in production
		ShowArgs:  true, // disable in production
	}))

	// create data
	for i := 1; i <= 5; i++ {
		_, err := queries.CreateUser(ctx, gen.CreateUserParams{
			Name:  "John doe " + strconv.Itoa(i),
			Email: fmt.Sprintf("jhondoe[%d]@gmail.com", i),
			Age:   int64(20 + i),
		})
		if err != nil {
			panic(err)
		}
		fmt.Println()
	}

	// querying data with dynamic filter
	params := ListUserParams{
		IDs:       []int64{},
		Search:    "doe 4",
		IsDeleted: 0,
		Ages:      []int64{},
		MinAge:    20,
		MaxAge:    25,
		Limit:     1,
		Page:      0,
		OrderBy:   "id",
		OrderType: "desc",
	}
	users, err := queries.ListUser(sqlc.Build(ctx, func(b *sqlc.Builder) {
		// if params.IDs is not empty then filter by params.IDs
		if len(params.IDs) > 0 {
			_, args := sqlc.GenQueryArgs(ctx, params.IDs...)
			b.In("id", args...)
		}

		// AND if params.Ages is not empty then filter by params.Ages
		if len(params.Ages) > 0 {
			_, args := sqlc.GenQueryArgs(ctx, params.Ages...)
			b.In("age", args...)
		}

		// AND filter by `age` that greater than or equal params.MinAge
		b.And("age >= ?", params.MinAge)

		// AND if params.MaxAge is greater than 0 then filter by `age` that lower than or equal params.MaxAge
		if params.MaxAge > 0 {
			b.And("age <= ?", params.MaxAge)
		}

		// AND filter user deleted/undeleted
		b.And("is_deleted = ?", params.IsDeleted)

		// AND if params.Search is not empty then search by name/email
		if params.Search != "" {
			params.Search = "%" + params.Search + "%"
			b.And("(name LIKE ? OR email LIKE ?)", params.Search, params.Search)
		}

		b.Limit(params.Limit)
		b.Offset(params.Page)
		b.Order(params.OrderBy + " " + params.OrderType)
	}))
	if err != nil {
		panic(err)
	}
	fmt.Println()

	fmt.Printf("users: %+v\n", users) // expected: [{ID:4 Name:John doe 4 Email:jhondoe[4]@gmail.com Age:24}]
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Build

func Build(ctx context.Context, fn func(b *Builder)) context.Context

Build create new builder or clone builder from context

func GenQueryArgs added in v0.0.36

func GenQueryArgs[T any](ctx context.Context, params ...T) (exprs string, args []any)

func WithBuilder

func WithBuilder(ctx context.Context, b *Builder) context.Context

Types

type Builder

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

func GetBuilder

func GetBuilder(ctx context.Context) (*Builder, bool)

func (*Builder) And

func (b *Builder) And(column string, args ...any) BuilderInterface

Like method Where() but will add "AND" logic to your expression

Then your query will look like "AND {{ your expression here }}"

Example:

And("name LIKE ?", "%me%")

func (*Builder) Build

func (b *Builder) Build(query string, args ...any) (string, []any)

Build or compile your queries

func (*Builder) In

func (b *Builder) In(column string, args ...any) BuilderInterface

Is equal with Where("id IN (?,?,?,...)", args...)

Example:

In("id", 1,2,3)

In("OR id", 1,2,3)

func (*Builder) Limit

func (b *Builder) Limit(limit int) BuilderInterface

Limit for rows that returned on SELECT query.

Example:

Limit(10)

func (*Builder) Offset

func (b *Builder) Offset(offset int) BuilderInterface

Offset for rows that returned on SELECT query.

Example:

Offset(10)

func (*Builder) Or

func (b *Builder) Or(column string, args ...any) BuilderInterface

Like method Where() but will add "OR" logic to your expression

Then your query will look like "OR {{ your expression here }}"

Example:

Or("name LIKE ?", "%me%")

func (*Builder) Order

func (b *Builder) Order(cols string, args ...any) BuilderInterface

Order columns on SELECT query. Your query will like "SELECT x FROM x WHERE x ORDER BY {{ cols }}"

Example:

Order("id DESC")

Order("id, age DESC")

func (*Builder) Where

func (b *Builder) Where(expression string, args ...any) BuilderInterface

Determine condition on "SELECT x FROM x WHERE {{ this is your expression }}".

Where("id = ?", 1) - If no logic is set, then will use "AND" by default

Where("OR email = ?", "x@gmail.com") - If logic has set, then will use it instead of "AND" (default)

Where("is_deleted = $1", 0)

Where("AND id IN (SELECT user_id FROM user_roles WHERE role_id = $1)", 2)

type BuilderInterface

type BuilderInterface interface {
	Where(expression string, args ...any) BuilderInterface
	In(column string, args ...any) BuilderInterface
	Or(column string, args ...any) BuilderInterface
	And(column string, args ...any) BuilderInterface
	Order(cols string, args ...any) BuilderInterface
	Limit(limit int) BuilderInterface
	Offset(offset int) BuilderInterface
	Build(query string, args ...any) (string, []any)
}

type DBTX

type DBTX interface {
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}

func Wrap

func Wrap(db DBTX, opts WrappedOpts) DBTX

type WrappedOpts added in v0.0.34

type WrappedOpts struct {
	ShowQuery bool
	ShowArgs  bool
}

Directories

Path Synopsis
gen

Jump to

Keyboard shortcuts

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