sql_query

package
v0.0.0-...-ef69736 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2025 License: MIT Imports: 17 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	InsertCache = make(map[string]*InsertTemplate)

	FieldMapCache sync.Map
)

Functions

func ArrayIncludes

func ArrayIncludes[T comparable](slice []T, value T) bool

func BuildInsertManyQuery

func BuildInsertManyQuery(table string, dataSlice interface{}) (query string, err error)

BuildInsertManyQuery generates an INSERT SQL query for a slice of structs.

Parameters: - table: the name of the SQL table - dataSlice: a slice of structs with fields tagged with `json`

Returns: - SQL query string with multiple value sets - list of arguments in correct order - error if input is invalid

Example input:

type MeasurementInput struct {
	Name string `json:"name"`
	Unit string `json:"unit"`
}

inputs := []MeasurementInput{
	{Name: "Height", Unit: "cm"},
	{Name: "Weight", Unit: "kg"},
	{Name: "Temp", Unit: "C"},
}

func BuildInsertOneQuery

func BuildInsertOneQuery(table string, data interface{}) (query string, err error)

BuildInsertOneQuery generates an INSERT SQL query for a single struct.

Parameters: - table: the name of the SQL table - data: a struct with fields tagged with `json`

Returns: - SQL query string - list of arguments in correct order - error if input is invalid

Example input:

type MeasurementInput struct {
	Name string `json:"name"`
	Unit string `json:"unit"`
}

input := MeasurementInput{
	Name: "Height",
	Unit: "cm",
}

func CachedScanRowObject

func CachedScanRowObject(v any, row pgx.Rows) error

func CachedScanRowsArray

func CachedScanRowsArray(v any, rows pgx.Rows) error

func CamelToSnake

func CamelToSnake(str string) string

func ClearCache

func ClearCache()

func ExtractJSONTags

func ExtractJSONTags[T any](optType ...reflect.Type) []string

ExtractJSONTags returns columns with near-zero cost on warm cache. To avoid complexity in usage, it's better to ignore params and just passing dto.SomeStruct to generic

func FormatPaginationResult

func FormatPaginationResult[T any](result []dto.PaginationResult[T]) dto.PaginationResult[T]

func GenerateCTEOption

func GenerateCTEOption(
	sourceBuilder *SelectBuilder,
	mainBuilder *SelectBuilder,
	aliasName string,
	refTable string,
	optionsQuery bool,
	labelKey string,
	labelValue string,
	customSort ...Sort,
)

func GetFieldMap

func GetFieldMap(elemType reflect.Type, fds []pgconn.FieldDescription) []int

func MakeMapJSONTags

func MakeMapJSONTags[T any]() map[string]string

func MakeMapJSONTagsFromType

func MakeMapJSONTagsFromType(fieldMeta []FieldMeta) map[string]string

func MakeMapJSONTagsFromValue

func MakeMapJSONTagsFromValue(dto any) map[string]string

func PaginationQuery

func PaginationQuery(withQuery, mainQuery, filteredDataQuery, paginatedDataQuery, paginatedCountQuery string) string

func PaginationQuery_old

func PaginationQuery_old(dataQuery, totalQuery string) string

func PascalToCamelCase

func PascalToCamelCase(s string) string

func ScanRowObject

func ScanRowObject(v any, row pgx.Rows) error

func ScanRowsArray

func ScanRowsArray(v any, rows pgx.Rows) error

func ToCamelCase

func ToCamelCase(s string) string

Types

type ArrayAggConfig

type ArrayAggConfig struct {
	Expr      string
	SortBy    string
	SortOrder int
}

type CacheKey

type CacheKey struct {
	Typ    reflect.Type
	Colsig string
}

type DeleteBuilder

type DeleteBuilder struct {
	*SQLEloquentQuery
}

<---To wrap builder to its respective interface, used as pointer type in method of each builder--->

func (*DeleteBuilder) Delete

func (s *DeleteBuilder) Delete(returningColumns ...string) SQLDeleteChainBuilder

func (*DeleteBuilder) Using

func (s *DeleteBuilder) Using(tables []string) SQLDeleteChainBuilder

func (*DeleteBuilder) Where

func (*DeleteBuilder) WhereOr

func (s *DeleteBuilder) WhereOr(filters ...map[string]SQLCondition) SQLDeleteChainBuilder

type FieldMeta

type FieldMeta struct {
	Name         string
	Type         reflect.Type
	JSONTag      string
	ColumnTag    string
	SQLExpr      string
	FieldIndex   []int
	NestedFields []FieldMeta

	IsStruct    bool
	IsSlice     bool
	IsTime      bool
	IsGenerated bool
}

func ExtractFromType

func ExtractFromType(typ reflect.Type) []FieldMeta

type InsertBuilder

type InsertBuilder struct {
	*SQLEloquentQuery
}

func (*InsertBuilder) Conflict

func (s *InsertBuilder) Conflict(constraint, do string) SQLInsertChainBuilder

func (*InsertBuilder) ExcludeEmpty

func (s *InsertBuilder) ExcludeEmpty() SQLInsertChainBuilder

func (*InsertBuilder) Insert

func (s *InsertBuilder) Insert(
	values interface{},
	returningColumns ...string,
) SQLInsertChainBuilder

type InsertTemplate

type InsertTemplate struct {
	InsertColumn []string
	FieldIndexes [][]int

	UseID  []bool
	UseNow []bool
	// contains filtered or unexported fields
}

func BuildInsertTemplate

func BuildInsertTemplate(t reflect.Type) *InsertTemplate

type MultiFilterCondition

type MultiFilterCondition struct {
	And map[string]SQLCondition
	Or  []map[string]SQLCondition
}

type Pagination

type Pagination struct {
	Page        int    `json:"page"      transform:"int"`
	Limit       int    `json:"limit"     transform:"int"`
	SortBy      string `json:"sortBy"    transform:"string"`
	SortOrder   int    `json:"sortOrder" transform:"int"`
	MultiSort   []Sort `json:"multiSort"`
	DefaultSort []Sort `json:"defaultSort"`
}

type QueryBuilder

type QueryBuilder interface {
	// contains filtered or unexported methods
}

type SQLCondition

type SQLCondition struct {
	Operator      SQLOperators  // e.g., '=', '>', '<=', 'LIKE', 'IN', 'IS NULL'
	Key           string        // used for array of object json pointing to the key of the object. This option should only be used with IsArray
	Value         interface{}   // could be a single value, slice, or nil.
	IsRef         bool          // to determine whether WHERE is targeting literal value or reference, e.g. `"column_a" = value` vs `"column_a" = $2` based on given boolean.
	SourceIsValue bool          // to determine whether WHERE is sourcing from literal value or reference, e.g. `"column_a" = value` vs `$2 = value` based on given boolean.
	IsSubQuery    bool          // to determine whether WHERE is sourcing from a query e.g. WHERE category_id IN (SELECT id FROM category_tree).
	IsEpochTime   bool          // assign this to true if value contains epoch/unix time in milliseconds.
	IsArray       bool          // to determine whether WHERE is targeting an array of object json. This option should only be used with Key
	ExtraArgs     []interface{} // for Operator `SQLOperatorRaw`
}

func GenerateArrayFilter

func GenerateArrayFilter[T comparable](filter *[]T) SQLCondition

type SQLDeleteChainBuilder

type SQLDeleteChainBuilder interface {
	// Where implements SQLDeleteChainBuilder. (Accumulates previous value if called again)
	Where(filters map[string]SQLCondition) SQLDeleteChainBuilder
	// WhereOr implements SQLDeleteChainBuilder. (Accumulates previous value if called again)
	WhereOr(filters ...map[string]SQLCondition) SQLDeleteChainBuilder

	// Using implements SQLDeleteChainBuilder. (Overrides previous value if called again)
	// Using adds a USING clause to the DELETE statement.
	// It is useful for multi-table DELETE with a join-like behavior.
	//
	// Example:
	//
	//	builder.Using([]string{"other_table"})
	//
	// Generates:
	//
	//	DELETE FROM table_name USING other_table
	Using(tables []string) SQLDeleteChainBuilder

	// buildDeleteQuery finalizes the DELETE query into a full SQL string + args.
	// It adds USING, WHERE, and RETURNING clauses if provided.
	// Prevents execution if CustomQuery is empty.
	//
	// Example Output:
	//
	//	DELETE FROM users USING roles WHERE users.role_id = roles.id RETURNING id
	Build() (string, []interface{}, error)
}

To ensure method .Delete() has its own chaining methods e.g. .Delete(...).Using(...).Where(...).Build()

type SQLDeleteInitBuilder

type SQLDeleteInitBuilder interface {
	// Delete implements SQLDeleteChainBuilder. (Only able to be called once)
	// Delete initializes a DELETE statement for the current table.
	// By default, it returns the deleted "id", but you can pass custom RETURNING columns.
	//
	// Example:
	//
	//	builder.Delete()                     // RETURNING id
	//	builder.Delete("id", "deleted_at")   // RETURNING id, deleted_at
	//
	// Generates:
	//
	//	DELETE FROM table_name RETURNING id
	Delete(returningColumns ...string) SQLDeleteChainBuilder
}

To ensure SQLDeleteBuilder has its own initial methods e.g. DeleteBuilder(...).Delete()...Rest

func NewSQLDeleteBuilder

func NewSQLDeleteBuilder(tableName string, alias ...string) SQLDeleteInitBuilder

NewSQLDeleteBuilder creates a new delete builder for a given table.

Example:

builder := NewSQLDeleteBuilder("users","u")

type SQLEloquentQuery

type SQLEloquentQuery struct {
	NestedAggregation []string
	WrapAggregation   bool

	ConflictClause  string
	WithClauses     []string
	Table           string
	Filters         []string
	OtherTables     []string
	UnionAllQueries []string
	Columns         []string
	DistinctBy      []string
	DistinctAlias   string
	Offset          int
	Limit           int
	SortBy          []string
	Grouping        []string
	HavingClauses   []string

	CustomQuery       string
	UpdateCaseClauses map[string][]UpdateCaseParam

	Args          []interface{}
	UsePagination bool
	Mode          SQLMode
	LastError     error
	// contains filtered or unexported fields
}

Base struct that contains params used by methods

func (*SQLEloquentQuery) Build

func (s *SQLEloquentQuery) Build() (string, []interface{}, error)

Run respective build method based on given mode

type SQLFilter

type SQLFilter map[string]SQLCondition

type SQLInsertChainBuilder

type SQLInsertChainBuilder interface {
	// By default, the Insert builder includes all fields, even if their values are zero or nil.
	// Calling this method tells the builder to skip fields with zero values when generating the SET clause.
	//
	// Note: This option only affects single-row Insert operations.
	// It has no effect on bulk INSERT, because all rows in those operations must have the same set of columns.
	ExcludeEmpty() SQLInsertChainBuilder
	// Insert implements SQLInsertChainBuilder. (Only able to be called once, will override previous call)
	// Conflict adds an ON CONFLICT clause to the insert statement.
	// Example:
	//
	//	.Conflict("(id)", "NOTHING")
	//	-> INSERT ... ON CONFLICT (id) DO NOTHING
	Conflict(constraint, do string) SQLInsertChainBuilder
	// buildInsertQuery finalizes the insert query into SQL string + args.
	// It prevents unsafe cases (like adding filters, joins, or pagination)
	// and appends RETURNING and ON CONFLICT if defined.
	Build() (string, []interface{}, error)
}

To ensure method .Insert() has its own chaining methods e.g. .Insert(...).Build()

type SQLInsertInitBuilder

type SQLInsertInitBuilder interface {
	// Insert implements SQLInsertChainBuilder. (Only able to be called once)
	// Insert builds an INSERT query from either a single struct or a slice of structs.
	// - For a single struct → generates one row.
	// - For a slice → generates multiple rows.
	//
	// By default, it will RETURN the id::text column,
	// but you can pass custom returning columns:
	//
	//	Insert(user, "id", "name")
	//	Insert([]User{u1, u2})
	Insert(values interface{}, returningColumns ...string) SQLInsertChainBuilder
	// contains filtered or unexported methods
}

To ensure SQLInsertBuilder has its own initial methods e.g. InsertBuilder(...).Insert()...Rest

func NewSQLInsertBuilder

func NewSQLInsertBuilder(tableName string, alias ...string) SQLInsertInitBuilder

NewSQLInsertBuilder creates a new insert builder for a given table. Example:

builder := NewSQLInsertBuilder("users","u")

type SQLMode

type SQLMode string
const (
	SQLDelete SQLMode = "delete"
	SQLInsert SQLMode = "insert"
	SQLSelect SQLMode = "select"
	SQLUpdate SQLMode = "update"
)

type SQLOperators

type SQLOperators string
const (

	// Usage: {"age": {Operator: SQLOperatorEqual, Value: 30}}  →  "age" = $1
	SQLOperatorEqual SQLOperators = "="
	// Usage: {"status": {Operator: SQLOperatorNotEqual, Value: "active"}}  →  "status" != $1
	SQLOperatorNotEqual SQLOperators = "!=" // or "<>"
	// Usage: {"score": {Operator: SQLOperatorGreaterThan, Value: 90}}  →  "score" > $1
	SQLOperatorGreaterThan SQLOperators = ">"
	// Usage: {"price": {Operator: SQLOperatorLessThan, Value: 100}}  →  "price" < $1
	SQLOperatorLessThan SQLOperators = "<"
	// Usage: {"age": {Operator: SQLOperatorGTE, Value: 18}}  →  "age" >= $1
	SQLOperatorGTE SQLOperators = ">="
	// Usage: {"age": {Operator: SQLOperatorLTE, Value: 65}}  →  "age" <= $1
	SQLOperatorLTE SQLOperators = "<="

	// Usage: {"name": {Operator: SQLOperatorRegexCaseSensitive, Value: "^A"}}  →  "name" ~ $1
	SQLOperatorRegexCaseSensitive SQLOperators = "~"
	// Usage: {"email": {Operator: SQLOperatorRegexCaseInsensitive, Value: "@gmail"}}  →  "email" ~* $1
	SQLOperatorRegexCaseInsensitive SQLOperators = "~*"
	// Usage: {"title": {Operator: SQLOperatorNotRegexCaseSensitive, Value: "test"}}  →  "title" !~ $1
	SQLOperatorNotRegexCaseSensitive SQLOperators = "!~"
	// Usage: {"username": {Operator: SQLOperatorNotRegexCaseInsensitive, Value: "admin"}}  →  "username" !~* $1
	SQLOperatorNotRegexCaseInsensitive SQLOperators = "!~*"

	// Usage: {"id": {Operator: SQLOperatorIn, Value: []int{1,2,3}}}  →  "id" IN ($1, $2, $3)
	SQLOperatorIn SQLOperators = "IN"
	// Usage: {"id": {Operator: SQLOperatorNotIn, Value: []int{1,2,3}}}  →  "id" NOT IN ($1, $2, $3)
	SQLOperatorNotIn SQLOperators = "NOT IN"

	// Usage: {"tags": {Operator: SQLOperatorAny, Value: pq.Array([]string{"a","b"})}}
	// →  "tags" = ANY($1)
	SQLOperatorAny SQLOperators = "ANY"

	// Usage: {"title": {Operator: SQLOperatorLike, Value: "%hello%"}}  →  "title" LIKE $1
	SQLOperatorLike SQLOperators = "LIKE"
	// Usage: {"title": {Operator: SQLOperatorNotLike, Value: "%test%"}}  →  "title" NOT LIKE $1
	SQLOperatorNotLike SQLOperators = "NOT LIKE"
	// Usage: {"name": {Operator: SQLOperatorILike, Value: "john%"}}  →  "name" ILIKE $1 (case-insensitive, PostgreSQL only)
	SQLOperatorILike SQLOperators = "ILIKE"
	// Usage: {"name": {Operator: SQLOperatorNotILike, Value: "doe%"}}  →  "name" NOT ILIKE $1 (case-insensitive, PostgreSQL only)
	SQLOperatorNotILike SQLOperators = "NOT ILIKE"

	// Usage: {"deleted_at": {Operator: SQLOperatorIsNull}}  →  "deleted_at" IS NULL
	SQLOperatorIsNull SQLOperators = "IS NULL"
	// Usage: {"deleted_at": {Operator: SQLOperatorIsNotNull}}  →  "deleted_at" IS NOT NULL
	SQLOperatorIsNotNull SQLOperators = "IS NOT NULL"

	// Usage: {"created_at": {Operator: SQLOperatorBetween, Value: []int64{1672531200000, 1675209599000}, IsEpochTime: true}}
	// →  "created_at" BETWEEN to_timestamp($1) AND to_timestamp($2)
	SQLOperatorBetween SQLOperators = "BETWEEN"
	// Usage: {"price": {Operator: SQLOperatorNotBetween, Value: []int{10, 50}}}
	// →  "price" NOT BETWEEN $1 AND $2
	SQLOperatorNotBetween SQLOperators = "NOT BETWEEN"

	// Usage: {"": {Operator: SQLOperatorExist, Value: "(SELECT 1 FROM users WHERE active = TRUE)", IsSubQuery: true}}
	// →  EXISTS (SELECT 1 FROM users WHERE active = TRUE)
	SQLOperatorExist SQLOperators = "EXISTS"
	// Usage: {"": {Operator: SQLOperatorNotExist, Value: "(SELECT 1 FROM users WHERE active = TRUE)", IsSubQuery: true}}
	// →  NOT EXISTS (SELECT 1 FROM users WHERE active = TRUE)
	SQLOperatorNotExist SQLOperators = "NOT EXISTS"

	// Usage: {"": {Operator: SQLOperatorRaw, Value: `"age" > ? OR "status" = 'active'`, ExtraArgs: []any{30}}}
	// →  age > ? OR status = 'active'
	SQLOperatorRaw SQLOperators = "__RAW__"
)

type SQLSelectChainBuilder

type SQLSelectChainBuilder interface {
	// GetCurrentArgIndex returns the current number of arguments in the query.
	// Useful for calculating placeholder positions when building parameterized SQL.
	GetCurrentArgIndex() int
	// AddArgs appends one or more arguments to the query's argument list.
	// Can be used to manually manage parameter placeholders.
	AddArgs(arg ...interface{}) SQLSelectChainBuilder
	// StartPlaceholderFrom set starting point of next generated placeholders.
	// Useful for creating sub query from this builder for another main query.
	// Placeholders for this sub query will be started from this given value.
	StartPlaceholderFrom(index int) SQLSelectChainBuilder

	// Distinct implements SQLSelectChainBuilder.
	// Distinct defines one or more columns for the DISTINCT ON(...) statement.
	//
	// Example:
	//
	//	builder.Distinct("distinct", "u.id", "u.name")
	Distinct(alias string, columns ...string) SQLSelectChainBuilder

	// Select implements SQLSelectChainBuilder.
	// Select defines one or more columns for the SELECT statement.
	// If a column alias already exists, its expression will be overwritten with the new one.
	//
	// Example:
	//
	//	builder.Select("u.id AS user_id", "u.name")
	Select(columns ...string) SQLSelectChainBuilder

	// USE WITH CAUTION
	// Reset all previous appended selects
	// Useful for this part in GenerateCTEOption
	// cteBuilder := sourceBuilder.
	//		ClearSelects(). -- without this the previous selects inside the source builder will get included
	// 		Distinct(
	// 			fmt.Sprintf(`%s AS "value"`, labelValue),
	// 			labelValue,
	// 		).
	// 		Select(
	// 			fmt.Sprintf(`%s AS "key"`, labelKey),
	// 			fmt.Sprintf(`"%s"."id" AS "id"`, refTable),
	// 		)
	//
	// Example usage:
	//
	//	builder.ClearSelect()
	ClearSelects() SQLSelectChainBuilder

	// SelectCaseWhen adds a CASE WHEN expression as a column.
	//
	// Example:
	//
	//	builder.SelectCaseWhen("'Yes'", "'No'", "is_admin", "role = 'admin'")
	//
	// Generates:
	//
	//	CASE WHEN role = 'admin' THEN 'Yes' ELSE 'No' END AS is_admin
	SelectCaseWhen(thenExpr, elseExpr, alias string, whenClause string, whenArgs ...interface{}) SQLSelectChainBuilder
	// SelectBoolAnd adds a bool_or aggregate column with an alias.
	//
	// Example:
	//
	//	builder.SelectBoolAnd("is_active", "any_active")
	//
	// Generates:
	//
	//	bool_and(is_active) AS any_active
	SelectBoolAnd(expr, alias string, args ...interface{}) SQLSelectChainBuilder
	// SelectBoolOr adds a bool_or aggregate column with an alias.
	//
	// Example:
	//
	//	builder.SelectBoolOr("is_active", "any_active")
	//
	// Generates:
	//
	//	bool_or(is_active) AS any_active
	SelectBoolOr(expr, alias string, args ...interface{}) SQLSelectChainBuilder

	SelectArrayAggregation(alias string, source string, config ArrayAggConfig) SQLSelectChainBuilder

	// SelectJSONArrayElements selects elements from a Go slice of maps
	// and expands them as rows using jsonb_array_elements().
	//
	// Each element in the slice is marshaled to JSON and passed as a query argument.
	//
	// Example:
	//
	//	arr := []map[string]string{{"id": "1"}, {"id": "2"}}
	//	builder.SelectJSONArrayElements("items", arr)
	//
	// Generates:
	//
	//	jsonb_array_elements($1::jsonb) AS items
	SelectJSONArrayElements(alias string, arrayElements []map[string]string) SQLSelectChainBuilder
	// SelectJSONAggregate builds a JSON object or JSON array aggregation using jsonb_build_object
	// or jsonb_agg(jsonb_build_object(...)). It supports optional filtering and ordering.
	//
	// The `dto` parameter can be either:
	//   - a struct (fields are mapped from JSON tags), or
	//   - a map[string]string (keys = JSON keys, values = SQL expressions).
	//
	// If asArrayAggregation is true, the result is wrapped with jsonb_agg().
	//
	// Example (array aggregation):
	//
	//	builder.SelectJSONAggregate(
	//	    "order_items",
	//	    map[string]string{"id": "items.id", "name": "items.name"},
	//	    "items.is_active = TRUE",
	//	    true,
	//	    "items.created_at",
	//	)
	//
	// or with a struct:
	//
	//	builder.SelectJSONAggregate(
	//	    "order_items",
	//	    dto.OrderItem{},
	//	    "items.is_active = TRUE",
	//	    true,
	//	    "items.created_at",
	//	)
	//
	// Generates:
	//
	//	jsonb_agg(jsonb_build_object('id', items.id, 'name', items.name) ORDER BY items.created_at)
	//	  FILTER (WHERE items.is_active = TRUE) AS order_items
	SelectJSONAggregate(alias string, dto any, condition string, asArrayAggregation bool, orderByClauses ...string) SQLSelectChainBuilder
	// Read documentation for SelectJSONAggregate since the function is similar but with additional COALESCE
	// Generates:
	//
	//	COALESCE(jsonb_agg(DISTINCT jsonb_build_object('id', items.id, 'name', items.name) ORDER BY items.created_at)
	//	  FILTER (WHERE items.is_active = TRUE) AS order_items, ${coalesce})
	SelectJSONAggregateCoalesce(alias string, dto any, condition string, asArrayAggregation bool, coalesce string, orderByClauses ...string) SQLSelectChainBuilder
	// Read documentation for SelectJSONAggregate since the function is similar but with additional DISTINCT
	// Generates:
	//
	//	jsonb_agg(DISTINCT jsonb_build_object('id', items.id, 'name', items.name) ORDER BY items.created_at)
	//	  FILTER (WHERE items.is_active = TRUE) AS order_items
	SelectJSONAggregateDistinct(alias string, dto any, condition string, asArrayAggregation bool, orderByClauses ...string) SQLSelectChainBuilder
	// SelectJSONAggregateFunc builds a nested JSON object by executing a callback
	// that itself adds JSON aggregate fields. The resulting fields are combined
	// into a single jsonb_build_object aliased as `alias`.
	//
	// Example:
	//
	//	builder.SelectJSONAggregateFunc("hasSystemRole", func(b *sql_query.SelectBuilder) {
	//	    b.SelectJSONAggregate(
	//	        string(rt),
	//	        map[string]string{
	//	            "create": getSystemRolePermission("create"),
	//	            "view":   getSystemRolePermission("view"),
	//	            "update": getSystemRolePermission("update"),
	//	            "delete": getSystemRolePermission("delete"),
	//	        },
	//	        fmt.Sprintf("usr.role_attribute = $%d", len(builder.Args)+1),
	//	        false,
	//	    )
	//	})
	//
	// Generates:
	//
	//	jsonb_build_object(
	//	    'SomeRole', CASE WHEN usr.role_attribute = $1 THEN jsonb_build_object(
	//	        'create', <expr>, 'view', <expr>, 'update', <expr>, 'delete', <expr>
	//	    ) ELSE NULL END
	//	) AS hasSystemRole
	SelectJSONAggregateFunc(alias string, fn func(builder *SelectBuilder)) SQLSelectChainBuilder

	// Where implements SQLSelectChainBuilder. (Accumulates previous value if called again)
	Where(filters map[string]SQLCondition) SQLSelectChainBuilder
	// WhereOr implements SQLSelectChainBuilder. (Accumulates previous value if called again)
	WhereOr(filters ...map[string]SQLCondition) SQLSelectChainBuilder

	// Search implements SQLSelectChainBuilder and accumulates conditions if called multiple times.
	// Adds a case-insensitive ILIKE condition across multiple fields, combined with OR.
	//
	// To search inside array columns, add suffix ":array" to the column name.
	// This uses EXISTS + unnest for better performance & usabilitiy than array_to_string.
	//
	// Example:
	//
	//	builder.Search("john", []string{"status::text", "last_name", "tags:array"})
	//
	// Generates:
	//
	//	(first_name ILIKE $1 OR last_name ILIKE $1 OR EXISTS (SELECT 1 FROM unnest(tags) AS val WHERE val ILIKE $1))
	Search(keyword string, fields []string) SQLSelectChainBuilder
	// SetLimit sets a fixed LIMIT value for the query (overwrites any previous limit).
	//
	// Example:
	//
	//	builder.SetLimit(5)
	SetLimit(limit int) SQLSelectChainBuilder
	// Join adds an INNER JOIN clause with the specified ON condition.
	//
	// Example:
	//
	//	builder.Join("roles r", "r.id = u.role_id")
	Join(table string, onCondition string, additionalConditions ...map[string]SQLCondition) SQLSelectChainBuilder
	// LeftJoin adds a LEFT JOIN clause with the specified ON condition.
	//
	// Example:
	//
	//	builder.LeftJoin("roles r", "r.id = u.role_id")
	LeftJoin(table string, onCondition string, additionalConditions ...map[string]SQLCondition) SQLSelectChainBuilder

	// Example:
	//
	// LeftJoinLateralWithQuery("ta", categoryWithRecursiveBuilder().(*sql_query.SelectBuilder).SQLEloquentQuery, "TRUE").
	//
	// Output:
	//
	// 	LEFT JOIN LATERAL (
	//     `Sql queries here`
	//   ) `alias` ON `condition`
	LeftJoinLateralWithQuery(joinName string, joinQueryBuilder *SQLEloquentQuery, mainCondition string, additionalConditions ...map[string]SQLCondition) SQLSelectChainBuilder

	// Paginate implements SQLSelectChainBuilder. (Overrides previous value if called again).
	// Paginate applies LIMIT, OFFSET, and ORDER BY using a Pagination struct.
	// It supports single or multiple sorting rules.
	//
	// Example:
	//
	//	builder.Paginate(Pagination{Page: 2, Limit: 10, SortBy: "name", SortOrder: 1})
	Paginate(query Pagination) SQLSelectChainBuilder
	// OrderBy adds sorting rules to the query. Multiple calls accumulate sorting.
	//
	// Example:
	//
	//	builder.OrderBy([]string{"created_at"}, false) // DESC
	OrderBy(sortBy []string, asc bool) SQLSelectChainBuilder
	// GroupBy adds one or more columns to the GROUP BY clause.
	// Multiple calls accumulate columns.
	//
	// Example:
	//
	//	builder.GroupBy("department", "role")
	GroupBy(groupBy ...string) SQLSelectChainBuilder
	// Having implements SQLSelectChainBuilder. (Overrides previous value if called again).
	// Having adds a HAVING clause for grouped queries.
	// Overwrites any previous HAVING condition.
	//
	// Example:
	//
	//	builder.GroupBy("role").Having(map[string]SQLCondition{
	//	    "count(*)": {Op: ">", Value: 5},
	//	})
	Having(havingClauses map[string]SQLCondition) SQLSelectChainBuilder

	// WithCTEBuilder adds a Common Table Expression (CTE) to the query.
	// It adjusts argument placeholders to avoid conflicts.
	// This function just add the defined CTE to the top of query.
	// You need to JOIN/LEFT JOIN the CTE builder to let main expression know that it should use CTE.
	//
	// Example:
	//
	//	cte := NewSQLSelectBuilder[Order]("orders").Select("id", "user_id")
	//	builder.WithCTEBuilder("recent_orders", cte.(*sql_query.SelectBuilder).SQLEloquentQuery)
	//
	// Generates:
	//
	//	WITH recent_orders AS (SELECT id, user_id FROM orders) ...
	WithCTEBuilder(cteName string, cteBuilder *SQLEloquentQuery) SQLSelectChainBuilder

	// WithRecursiveCTEBuilder adds a Common Table Expression (CTE) to the query.
	// It adjusts argument placeholders to avoid conflicts.
	// This function just add the defined CTE to the top of query.
	// You need to JOIN/LEFT JOIN the CTE builder to let main expression know that it should use CTE.
	//
	// Example:
	//
	//	cte := NewSQLSelectBuilder[Order]("orders").Select("id", "user_id")
	//	builder.WithRecursiveCTEBuilder("recent_orders", cte.(*sql_query.SelectBuilder).SQLEloquentQuery)
	//
	// Generates:
	//
	//	WITH RECURSIVE recent_orders AS (SELECT id, user_id FROM orders) ...
	WithRecursiveCTEBuilder(cteName string, cteBuilder *SQLEloquentQuery) SQLSelectChainBuilder

	// Add "UNION ALL" in between the queries
	// example:
	//  SELECT id
	// FROM categories
	// WHERE id = 0
	//
	// UNION ALL
	//
	// SELECT c.id
	// FROM categories c
	// INNER JOIN category_tree ct ON c.parent_id = ct.id
	UnionAll(cteBuilders ...*SQLEloquentQuery) SQLSelectChainBuilder

	// Build finalizes the SELECT query and returns the query string and arguments.
	// Returns an error if the query is invalid (e.g., HAVING without GROUP BY).
	Build() (string, []interface{}, error)
}

func NewSQLCountBuilder

func NewSQLCountBuilder(tableName string, alias ...string) SQLSelectChainBuilder

Example:

builder := NewSQLCountBuilder("users","u").Where(...)

func NewSQLSelectBuilder

func NewSQLSelectBuilder[T any](tableName string, alias ...string) SQLSelectChainBuilder

NewSQLSelectBuilder creates a new chainable SELECT builder for a given table. It extracts JSON tags from the struct type T as default columns.

Example:

builder := NewSQLSelectBuilder[User]("users","u").Select("id", "name")

func NewSQLSelectSubQueryBuilder

func NewSQLSelectSubQueryBuilder[T any](tableName string, alias ...string) SQLSelectChainBuilder

type SQLSort

type SQLSort struct {
	SortBy    string `json:"sortBy"`
	SortOrder string `json:"sortOrder"` // ASC | DESC
}

type SQLUpdateChainBuilder

type SQLUpdateChainBuilder interface {
	// AddCase initializes a conditional CASE expression for the given column in an UPDATE statement.
	// It clears any existing CASE expressions and allows chaining multiple conditional branches using Case() and Else().
	//
	// Example:
	//   builder.AddCase("status", func(b UpdateCases) {
	//       b.Case(...)
	//       b.Else(...)
	//   })
	//
	// Parameters:
	//   - setColumn: the name of the column to be conditionally updated.
	//   - fn: a function that defines the CASE conditions using the provided UpdateCases interface.
	//
	// Returns:
	//   - SQLUpdateChainBuilder: the builder itself for method chaining.
	AddCase(setColumn string, fn func(b UpdateCases)) SQLUpdateChainBuilder

	// By default, the Update builder includes all fields, even if their values are zero or nil.
	// Calling this method tells the builder to skip fields with zero values when generating the SET clause.
	//
	// Note: This option only affects single-row Update operations.
	// It has no effect on bulk UPDATE, because all rows in those operations must have the same set of columns.
	ExcludeEmpty() SQLUpdateChainBuilder
	// Update implements SQLUpdateChainBuilder. (Only able to be called once, will override previous call).
	// Conflict adds an ON CONFLICT clause with the specified constraint and action.
	//
	// Example:
	//
	//	builder.Conflict("(id)", "NOTHING")
	//
	// → ON CONFLICT (id) DO NOTHING
	Conflict(constraint, do string) SQLUpdateChainBuilder
	// Where implements SQLUpdateChainBuilder. (Accumulates previous value if called again)
	Where(filters map[string]SQLCondition) SQLUpdateChainBuilder
	// WhereOr implements SQLUpdateChainBuilder. (Accumulates previous value if called again)
	WhereOr(filters ...map[string]SQLCondition) SQLUpdateChainBuilder

	// Join adds an INNER JOIN clause with the specified ON condition.
	//
	// Example:
	//
	//	builder.Join("roles r", "r.id = u.role_id")
	Join(table string, onCondition string, additionalConditions ...map[string]SQLCondition) SQLUpdateChainBuilder
	// LeftJoin adds a LEFT JOIN clause with the specified ON condition.
	//
	// Example:
	//
	//	builder.LeftJoin("roles r", "r.id = u.role_id")
	LeftJoin(table string, onCondition string, additionalConditions ...map[string]SQLCondition) SQLUpdateChainBuilder

	// WithCTEBuilder adds a Common Table Expression (CTE) to the query.
	// It adjusts argument placeholders to avoid conflicts.
	// This function just add the defined CTE to the top of query.
	// You need to JOIN/LEFT JOIN the CTE builder to let main expression know that it should use CTE.
	//
	// Example:
	//
	//	builder.WithCTEBuilder("recent_orders", cte.(*sql_query.SelectBuilder).SQLEloquentQuery)
	//
	// Generates:
	//
	//	WITH recent_orders AS (SELECT id, user_id FROM orders) ...
	WithCTEBuilder(cteName string, cteBuilder *SQLEloquentQuery) SQLUpdateChainBuilder

	// WithRecursiveCTEBuilder adds a Common Table Expression (CTE) to the query.
	// It adjusts argument placeholders to avoid conflicts.
	// This function just add the defined CTE to the top of query.
	// You need to JOIN/LEFT JOIN the CTE builder to let main expression know that it should use CTE.
	//
	// Example:
	//
	//	cte := NewSQLSelectBuilder[Order]("orders").Select("id", "user_id")
	//	builder.WithRecursiveCTEBuilder("recent_orders", cte.(*sql_query.SelectBuilder).SQLEloquentQuery)
	//
	// Generates:
	//
	//	WITH RECURSIVE recent_orders AS (SELECT id, user_id FROM orders) ...
	WithRecursiveCTEBuilder(cteName string, cteBuilder *SQLEloquentQuery) SQLUpdateChainBuilder

	// Return implements SQLUpdateChainBuilder. (Only able to be called once, overrides previous values if re-called).
	// Return sets the columns to return after the update.
	// Defaults to RETURNING id if no column is provided.
	Return(columns ...string) SQLUpdateChainBuilder

	// From implements SQLUpdateChainBuilder. (Overrides previous value if called again)
	// From adds a FROM clause to the UPDATE query, allowing joins with other tables.
	//
	// Example:
	//
	//	builder.From([]string{"users u", "roles r"})
	//
	// → FROM users u, roles r
	From(tables []string) SQLUpdateChainBuilder

	// buildUpdateQuery constructs the final UPDATE query string and its arguments.
	// Ensures that CustomQuery is set and that a WHERE clause exists for safety.
	Build() (string, []interface{}, error)
}

To ensure method .Update() has its own chaining methods e.g. .Update(...).From(...).Build()

type SQLUpdateInitBuilder

type SQLUpdateInitBuilder interface {
	// Update builds an UPDATE query for a single struct or map using reflection.
	//
	// It supports two value types:
	//   • Normal values (int, string, time.Time, etc.): converted into positional parameters ($1, $2, …).
	//   • Raw SQL expressions: allows embedding expressions with placeholders ("?")
	//     which will be replaced with proper PostgreSQL-style parameters ($1, $2, …).
	//
	// Example using struct:
	//
	//     type GroupQuota struct {
	//         Used int `column:"used"`
	//         Available sql_query.UpdateRawSQL `column:"available"`
	//     }
	//
	//     builder.Update(GroupQuota{
	//         Used: 5,
	//         Available: sql_query.UpdateRawSQL{
	//             Expr: "CASE WHEN available IS NULL THEN NULL ELSE available - ? END",
	//             Args: []any{5},
	//         },
	//     })
	//
	// Example using map:
	//
	//     builder.Update(map[string]any{
	//         "used": 5,
	//         "available": sql_query.UpdateRawSQL{
	//             Expr: "CASE WHEN available IS NULL THEN NULL ELSE available - ? END",
	//             Args: []any{5},
	//         },
	//     })
	//
	// By default, it will automatically add `updated_at = NOW()` if not explicitly provided.
	Update(values interface{}) SQLUpdateChainBuilder
	// UpdateEach updates multiple rows at once using VALUES() with a slice of structs.
	// Matches rows using the given rowIdentifier (e.g., "id").
	//
	// Example:
	//
	//	builder.UpdateEach([]User{{ID: 1, Name: "A"}, {ID: 2, Name: "B"}}, "id")
	//
	// → UPDATE users SET name = v.name, updated_at = NOW()
	//
	//	FROM (VALUES ($1,$2),($3,$4)) AS v(id,name,updated_at)
	//	WHERE users.id = v.id
	UpdateEach(values interface{}, rowIdentifier string) SQLUpdateChainBuilder

	// AddCase initializes a conditional CASE expression for the given column in an UPDATE statement.
	// It use completely different CASE expressions from previous AddCase and allows chaining multiple conditional branches using Case() and Else().
	//
	// Example:
	//   builder.AddCase("status", func(b UpdateCases) {
	//       b.Case(...)
	//       b.Else(...)
	//   })
	//
	// Parameters:
	//   - setColumn: the name of the column to be conditionally updated.
	//   - fn: a function that defines the CASE conditions using the provided UpdateCases interface.
	//
	// Returns:
	//   - SQLUpdateChainBuilder: the builder itself for method chaining.
	AddCase(setColumn string, fn func(b UpdateCases)) SQLUpdateChainBuilder

	// Increment is used to replace Update() for adding int value cause update cant handle that
	// Increment builds an UPDATE query that increases integer columns by a given value.
	// Automatically sets updated_at = NOW().
	//
	// Example:
	//
	//	builder.Increment(map[string]any{"count": 1})
	//
	// → UPDATE table SET "count" = "count" + $1, "updated_at" = NOW()
	Increment(values map[string]any) SQLUpdateChainBuilder
	// contains filtered or unexported methods
}

To ensure SQLUpdateBuilder has its own initial methods e.g. UpdateBuilder(...).Update()...Rest

func NewSQLUpdateBuilder

func NewSQLUpdateBuilder(tableName string, alias ...string) SQLUpdateInitBuilder

NewSQLUpdateBuilder creates a new UpdateBuilder for the given table.

Example:

builder := NewSQLUpdateBuilder("users","u")
builder.Update(user).Return("id").Build()

type SelectBuilder

type SelectBuilder struct {
	*SQLEloquentQuery
}

func (*SelectBuilder) AddArgs

func (s *SelectBuilder) AddArgs(arg ...interface{}) SQLSelectChainBuilder

func (*SelectBuilder) ClearSelects

func (s *SelectBuilder) ClearSelects() SQLSelectChainBuilder

func (*SelectBuilder) Distinct

func (s *SelectBuilder) Distinct(alias string, columns ...string) SQLSelectChainBuilder

func (*SelectBuilder) GetCurrentArgIndex

func (s *SelectBuilder) GetCurrentArgIndex() int

func (*SelectBuilder) GroupBy

func (s *SelectBuilder) GroupBy(groupBy ...string) SQLSelectChainBuilder

func (*SelectBuilder) Having

func (s *SelectBuilder) Having(havingClause map[string]SQLCondition) SQLSelectChainBuilder

func (*SelectBuilder) Join

func (s *SelectBuilder) Join(table string, onCondition string, additionalConditions ...map[string]SQLCondition) SQLSelectChainBuilder

func (*SelectBuilder) LeftJoin

func (s *SelectBuilder) LeftJoin(table string, mainCondition string, additionalConditions ...map[string]SQLCondition) SQLSelectChainBuilder

func (*SelectBuilder) LeftJoinLateralWithQuery

func (s *SelectBuilder) LeftJoinLateralWithQuery(joinName string, joinQueryBuilder *SQLEloquentQuery, mainCondition string, additionalConditions ...map[string]SQLCondition) SQLSelectChainBuilder

func (*SelectBuilder) OrderBy

func (s *SelectBuilder) OrderBy(sortBy []string, asc bool) SQLSelectChainBuilder

func (*SelectBuilder) Paginate

func (s *SelectBuilder) Paginate(query Pagination) SQLSelectChainBuilder

func (*SelectBuilder) Search

func (s *SelectBuilder) Search(keyword string, fields []string) SQLSelectChainBuilder

func (*SelectBuilder) Select

func (s *SelectBuilder) Select(columns ...string) SQLSelectChainBuilder

func (*SelectBuilder) SelectArrayAggregation

func (s *SelectBuilder) SelectArrayAggregation(alias string, source string, config ArrayAggConfig) SQLSelectChainBuilder

func (*SelectBuilder) SelectBoolAnd

func (s *SelectBuilder) SelectBoolAnd(expr, alias string, args ...interface{}) SQLSelectChainBuilder

func (*SelectBuilder) SelectBoolOr

func (s *SelectBuilder) SelectBoolOr(expr, alias string, args ...interface{}) SQLSelectChainBuilder

func (*SelectBuilder) SelectCaseWhen

func (s *SelectBuilder) SelectCaseWhen(thenExpr, elseExpr, alias string, whenClause string, whenArgs ...interface{}) SQLSelectChainBuilder

func (*SelectBuilder) SelectJSONAggregate

func (s *SelectBuilder) SelectJSONAggregate(alias string, dto any, condition string, asArrayAggregation bool, orderByClauses ...string) SQLSelectChainBuilder

func (*SelectBuilder) SelectJSONAggregateCoalesce

func (s *SelectBuilder) SelectJSONAggregateCoalesce(alias string, dto any, condition string, asArrayAggregation bool, coalesce string, orderByClauses ...string) SQLSelectChainBuilder

func (*SelectBuilder) SelectJSONAggregateDistinct

func (s *SelectBuilder) SelectJSONAggregateDistinct(alias string, dto any, condition string, asArrayAggregation bool, orderByClauses ...string) SQLSelectChainBuilder

func (*SelectBuilder) SelectJSONAggregateFunc

func (s *SelectBuilder) SelectJSONAggregateFunc(alias string, fn func(builder *SelectBuilder)) SQLSelectChainBuilder

func (*SelectBuilder) SelectJSONArrayElements

func (s *SelectBuilder) SelectJSONArrayElements(alias string, arrayElements []map[string]string) SQLSelectChainBuilder

func (*SelectBuilder) SetLimit

func (s *SelectBuilder) SetLimit(limit int) SQLSelectChainBuilder

func (*SelectBuilder) StartPlaceholderFrom

func (s *SelectBuilder) StartPlaceholderFrom(index int) SQLSelectChainBuilder

func (*SelectBuilder) UnionAll

func (s *SelectBuilder) UnionAll(cteBuilders ...*SQLEloquentQuery) SQLSelectChainBuilder

func (*SelectBuilder) Where

func (*SelectBuilder) WhereOr

func (s *SelectBuilder) WhereOr(filters ...map[string]SQLCondition) SQLSelectChainBuilder

func (*SelectBuilder) WithCTEBuilder

func (s *SelectBuilder) WithCTEBuilder(cteName string, cteBuilder *SQLEloquentQuery) SQLSelectChainBuilder

func (*SelectBuilder) WithRecursiveCTEBuilder

func (s *SelectBuilder) WithRecursiveCTEBuilder(cteName string, cteBuilder *SQLEloquentQuery) SQLSelectChainBuilder

type Sort

type Sort struct {
	SortBy    string `json:"sortBy"`
	SortOrder int    `json:"sortOrder"`
}

type UpdateBuilder

type UpdateBuilder struct {
	*SQLEloquentQuery
}

func (*UpdateBuilder) AddCase

func (s *UpdateBuilder) AddCase(setColumn string, fn func(b UpdateCases)) SQLUpdateChainBuilder

func (*UpdateBuilder) Case

func (s *UpdateBuilder) Case(conditions MultiFilterCondition, value interface{}, isRef bool)

func (*UpdateBuilder) Conflict

func (s *UpdateBuilder) Conflict(constraint, do string) SQLUpdateChainBuilder

func (*UpdateBuilder) Else

func (s *UpdateBuilder) Else(value interface{}, isRef bool)

func (*UpdateBuilder) ExcludeEmpty

func (s *UpdateBuilder) ExcludeEmpty() SQLUpdateChainBuilder

func (*UpdateBuilder) From

func (s *UpdateBuilder) From(tables []string) SQLUpdateChainBuilder

func (*UpdateBuilder) Increment

func (s *UpdateBuilder) Increment(
	values map[string]any,
) SQLUpdateChainBuilder

func (*UpdateBuilder) Join

func (s *UpdateBuilder) Join(
	table string,
	onCondition string,
	additionalConditions ...map[string]SQLCondition,
) SQLUpdateChainBuilder

func (*UpdateBuilder) LeftJoin

func (s *UpdateBuilder) LeftJoin(
	table string,
	mainCondition string,
	additionalConditions ...map[string]SQLCondition,
) SQLUpdateChainBuilder

func (*UpdateBuilder) Return

func (s *UpdateBuilder) Return(column ...string) SQLUpdateChainBuilder

func (*UpdateBuilder) Update

func (s *UpdateBuilder) Update(values interface{}) SQLUpdateChainBuilder

func (*UpdateBuilder) UpdateEach

func (s *UpdateBuilder) UpdateEach(values interface{}, rowIdentifier string) SQLUpdateChainBuilder

func (*UpdateBuilder) Where

func (*UpdateBuilder) WhereOr

func (s *UpdateBuilder) WhereOr(filters ...map[string]SQLCondition) SQLUpdateChainBuilder

func (*UpdateBuilder) WithCTEBuilder

func (s *UpdateBuilder) WithCTEBuilder(cteName string, cteBuilder *SQLEloquentQuery) SQLUpdateChainBuilder

func (*UpdateBuilder) WithRecursiveCTEBuilder

func (s *UpdateBuilder) WithRecursiveCTEBuilder(cteName string, cteBuilder *SQLEloquentQuery) SQLUpdateChainBuilder

type UpdateCaseParam

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

type UpdateCases

type UpdateCases interface {
	// Case adds a WHEN clause to the current CASE expression defined by AddCase.
	// It appends the condition and its resulting value.
	//
	// Parameters:
	//   - conditions: a MultiFilterCondition containing AND and OR filters used for this WHEN clause.
	//   - value: the resulting value when the conditions match.
	//   - isRef: whether the value is a SQL column reference (true) or a literal that should be parameterized (false).
	//
	// Example:
	//   c.Case(MultiFilterCondition{
	//     And: map[string]SQLCondition{
	//       "status":       {Operator: SQLOperatorEqual, Value: "Emergency"},
	//       body.BloodType:   {SourceIsValue: true, Operator: SQLOperatorEqual, Value: "B"},
	//     },
	//   }, "Available", false)
	Case(conditions MultiFilterCondition, value interface{}, isRef bool)
	// Else sets the default ELSE value for the current CASE expression.
	// It is used when none of the WHEN conditions match.
	//
	// Parameters:
	//   - value: the fallback value if no WHEN clause matches.
	//   - isRef: whether the value is a SQL column reference (true) or a literal that should be parameterized (false).
	//
	// Example:
	//   c.Else("status", true) // fallback to the original "status" column value
	Else(value interface{}, isRef bool)
}

type UpdateRawSQL

type UpdateRawSQL struct {
	Expr string
	Args []interface{}
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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