sqli

package module
v0.1.6 Latest Latest
Warning

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

Go to latest
Published: May 17, 2025 License: MIT Imports: 11 Imported by: 0

README

SQLi

Type-safe generative SQL Query Builder based on you DB schema.


// SQLi generates Predefined Queries

newUserId := uuid.MustParse("ebb5dd71-1214-40dc-b602-bb9af74b3aae")

id, err := InsertIntoUserReturningID( // This is a generated function
    ctx,
    db,
    yourdb.InsertableUserModel{ 
        ID: newUserId,
        Name: "User 1",
    },
)

println(id) // ebb5dd71-1214-40dc-b602-bb9af74b3aae

// ... and you can also use Dynamic Queries

insertQuery, err := sqli.Query(
    sqli.INSERT_INTO(yourdb.User),
    sqli.VALUES(
        sqli.ValueSet(
            VALUE(yourdb.User.ID, newUserId), // VALUE function will validate the value UUID type
            VALUE(yourdb.User.Name, "User 1"), // And this will validate the value string type
        ),
    ),
    sqli.RETURNING(
        yourdb.User.ID,
    ),
)

println(insertQuery.SQL) // `INSERT INTO "user" (VALUES ($1, $2));`
println(insertQuery.Args) // [ebb5dd71-1214-40dc-b602-bb9af74b3aae, User 1]

row := db.QueryRowxContext(ctx, insertQuery.SQL, insertQuery.Args...)

var id uuid.UUID
err = row.Scan(&id)

println(id) // ebb5dd71-1214-40dc-b602-bb9af74b3aae

Install

By go install

go install github.com/Dionid/sqli/cmd/sqli@latest

Releases

Download from Release

From sources

git clone git@github.com:Dionid/sqli.git
cd sqli
make build
# you will find executables in ./dist folder

Features

SQLi generates:

  1. Constants
  2. Predefined Queries
  3. Dynamic Queries

Constants

All tables as types and names can be found in constants.sqli.go

type TablesSt struct {
	Office         string `json:"office" db:"office"`
	OfficeUser     string `json:"office_user" db:"office_user"`
	User           string `json:"user" db:"user"`
}

var Tables = TablesSt{
	Office:         "office",
	OfficeUser:     "office_user",
	User:           "user",
}

// Named "T" for shortness
var T = Tables

Predefined Queries

This functions are generated for each table in your DB schema and capture most common queries, like Insert, Update, Delete, Select by Primary key / Unique key / etc.

Lets look at the example of generated function for user table:


newUserId := uuid.MustParse("ebb5dd71-1214-40dc-b602-bb9af74b3aae")

id, err := InsertIntoUserReturningID(
	ctx,
	db,
    InsertableUserModel{
        ID: newUserId,
        Name: "User 1",
    },
)

println(id) // ebb5dd71-1214-40dc-b602-bb9af74b3aae

// Not lets select it by primary key

userByPrimaryKey, err := SelectUserByID(
    ctx,
    db,
    id,
)

println(userByPrimaryKey) // {ID: "ebb5dd71-1214-40dc-b602-bb9af74b3aae", Name: "User 1"}

// Now lets update it

err = UpdateUserByID(
    ctx,
    db,
    id,
    UpdatableOfficeModel{
        Name: "Updated User 1",
    }
)

// And delete

err = DeleteFromUserByID(
    ctx,
    db,
    id,
)

Insert
  1. Returning Result
  2. Returning All
  3. Returning Primary key
  4. Returning Unique key
Select By
  1. Primary
  2. By Primary compound
  3. Sequence
Delete By
  1. Primary
  2. By Primary compound
  3. Sequence
Update
  1. By Primary
  2. By Primary compound
  3. By Sequence

Dynamic Type-safe Queries

This is a dynamic query builder, that allows you to build queries in a type-safe way, using the generated constants and functions.


import (
    .   "github.com/Dionid/sqli"
    .   "github.com/Dionid/sqli/examples/pgdb/db"
)

// Insert user

newUserId := uuid.MustParse("ebb5dd71-1214-40dc-b602-bb9af74b3aae")

insertQuery, err := Query(
    INSERT_INTO(User),
    VALUES(
        ValueSet(
            VALUE(User.ID, newUserId), // VALUE function will validate the value UUID type
            VALUE(User.Name, "User 1"), // And this will validate the value string type
        ),
    ),
    RETURNING(
        User.ID,
    ),
)

// `insertQuery` will have raw SQL and raw arguments, that can be used to execute the query

println(insertQuery.SQL) // `INSERT INTO "user" (VALUES ($1, $2));`
println(insertQuery.Args) // [ebb5dd71-1214-40dc-b602-bb9af74b3aae, User 1]

// Now lets execute the query
row := db.QueryRowxContext(ctx, insertQuery.SQL, insertQuery.Args...)

var id uuid.UUID
err = row.Scan(&id)

println(id) // ebb5dd71-1214-40dc-b602-bb9af74b3aae

// Select it by primary key

selectQuery, err := Query(
    SELECT(
        User.AllColumns(), // *
    ),
    FROM(User),
    WHERE(
        EQUAL(User.ID, id),
    ),
)

println(selectQuery.SQL) // SELECT * FROM "user" AS "user" WHERE "user"."id" = $1;
println(selectQuery.Args) // [ebb5dd71-1214-40dc-b602-bb9af74b3aae]

row := db.QueryRowxContext(ctx, selectQuery.SQL, selectQuery.Args...)
user := &UserModel{} // Also generated by SQLi
err = row.Scan(
    &user.ID,
    &user.Name,
)
println(user) // {ID: "ebb5dd71-1214-40dc-b602-bb9af74b3aae", Name: "User 1"}

// Now lets update it

query, err := Query(
    UPDATE(User),
    SET(
        SET_VALUE(User.Name, "Updated User 1"),
    ),
    WHERE(
        EQUAL(User.ID, id),
    ),
)

println(query.SQL) // UPDATE "user" SET "user"."name" = $1 WHERE "user"."id" = $2;
println(query.Args) // [Updated User 1, ebb5dd71-1214-40dc-b602-bb9af74b3aae]

row := db.ExecContext(ctx, query.SQL, query.Args...)
println(row.RowsAffected()) // 1

// And delete

query, err := Query(
    DELETE_FROM(User),
    WHERE(
        EQUAL(User.ID, id),
    ),
)

println(query.SQL) // DELETE FROM "user" WHERE "user"."id" = $1;
println(query.Args) // [ebb5dd71-1214-40dc-b602-bb9af74b3aae]

row := db.ExecContext(ctx, query.SQL, query.Args...)
println(row.RowsAffected()) // 1

Examples

For more examples, see the examples folder.

What is the difference between SQLi and other query builders?

Generative

Type-safe

Every function is generated for each table in your DB schema and typed according to the table schema.


EQUAL(User.ID, 123) // This will not compile, because ID is UUID
EQUAL(User.ID, uuid.MustParse("ebb5dd71-1214-40dc-b602-bb9af74b3aae")) // This will compile

Extensible

Most Query Builders uses dot notation to build queries, like db.Table("user").Where("id = ?", id), but SQLi uses functional approach Query(SELECT(User.ID), FROM(User) WHERE(EQUAL(User.ID, id)) where each function needs to return a Statement struct, that contains SQL and arguments.

That gives us ability to extend the library and add new functions, like JSON_AGG, SUM, COUNT, etc. WITHOUT even commiting to the library itself.

Example:

We got some database that has operators like MERGE table WHERE ... COLLISION free | restricted, but we don't has this functions in SQLi, so we can create our own functions and use them in the query builder:

func MERGE(table NameWithAliaser) string {
    stmt := fmt.Sprintf("FROM %s", table.GetNameWithAlias())

    return sqli.Statement{
		SQL:  stmt,
		Args: []interface{}{},
	}
}

func COLLISION_FREE() string {
    return sqli.Statement{
		SQL:  "COLLISION free",
		Args: []interface{}{},
	}
}

func COLLISION_RESTRICTED() string {
    return sqli.Statement{
		SQL:  "COLLISION restricted",
		Args: []interface{}{},
	}
}

func main() {
    query := Query(
        MERGE("table"),
        WHERE(
            EQUAL("id", 1),
        ),
        COLLISION_FREE(),
    )
    fmt.Println(query.SQL) // MERGE table WHERE id = 1 COLLISION free
}

So you don't even need to wait for the library to implement this functions, you can do it yourself.

TODO

  1. Upsert
  2. SUM
  3. JSON_AGG
  4. InsertOnConflict
  5. CopySimple
  6. Add pgx types
  7. Safe-mode (validating every field)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ASC = OrderDirection{
	Value: "ASC",
}
View Source
var AllColumn = Column[string]{"*", "*", "", ""}
View Source
var DESC = OrderDirection{
	Value: "DESC",
}
View Source
var Equal = CompareOperator{
	Value: "=",
}
View Source
var Greater = CompareOperator{
	Value: ">",
}
View Source
var GreaterOrEqual = CompareOperator{
	Value: ">=",
}
View Source
var ILike = CompareOperator{
	Value: "ILIKE",
}
View Source
var In = CompareOperator{
	Value: "IN",
}
View Source
var Less = CompareOperator{
	Value: "<",
}
View Source
var LessOrEqual = CompareOperator{
	Value: "<=",
}
View Source
var Like = CompareOperator{
	Value: "LIKE",
}
View Source
var NotEqual = CompareOperator{
	Value: "!=",
}
View Source
var NotIn = CompareOperator{
	Value: "NOT IN",
}
View Source
var QUERY_ARG = "$$DIO_ARG$$"
View Source
var XoTemplates embed.FS

Functions

func DoubleQuotes

func DoubleQuotes(val string) string

func Generate

func Generate(
	ctx context.Context,
	opts GenerateCmdOpts,
) error

func NewTemplateSet

func NewTemplateSet(ctx context.Context) (*templates.Set, error)

func StringWithWithoutComma

func StringWithWithoutComma(len int, i int, val string) string

func TrimQuotes

func TrimQuotes(val string) string

func VALUE

func VALUE[V any](c Column[V], value V) V

Types

type Column

type Column[V any] struct {
	ColumnName  string
	ColumnAlias string
	TableName   string
	TableAlias  string
}

func NewColumn

func NewColumn[V any](table Table, columnName string) Column[V]

func NewColumnWithAlias

func NewColumnWithAlias[V any](table Table, columnName string, columnAlias string) Column[V]

func (Column[V]) GetAlias

func (c Column[V]) GetAlias() string

func (Column[V]) GetAliasWithTableAlias

func (c Column[V]) GetAliasWithTableAlias() string

func (Column[V]) GetName

func (c Column[V]) GetName() string

func (Column[V]) GetNameAsAlias

func (c Column[V]) GetNameAsAlias() string

func (Column[V]) GetStatement

func (c Column[V]) GetStatement() Statement

func (Column[V]) SetAlias

func (c Column[V]) SetAlias(alias string) Column[V]

type ColumnOrder

type ColumnOrder struct {
	ColumnWithTable
	Direction OrderDirection
}

func NewColumnOrder

func NewColumnOrder(table TableType, column ColumnType, direction OrderDirection) ColumnOrder

func (ColumnOrder) String

func (c ColumnOrder) String() string

type ColumnType

type ColumnType interface {
	GetName() string
	GetAlias() string
	GetNameAsAlias() string
	GetAliasWithTableAlias() string
}

type ColumnWithTable

type ColumnWithTable struct {
	Table  TableType
	Column ColumnType
}

func NewColumnWithTable

func NewColumnWithTable(table TableType, column ColumnType) ColumnWithTable

func (ColumnWithTable) GetAlias

func (c ColumnWithTable) GetAlias() string

type CompareOperator

type CompareOperator struct {
	Value string
}

func (CompareOperator) String

func (c CompareOperator) String() string

type GenerateCmdOpts

type GenerateCmdOpts struct {
	Out      string
	DbSchema string

	DbUrl string
}

type NameWithAliaser

type NameWithAliaser interface {
	GetNameWithAlias() string
}

type OrderDirection

type OrderDirection struct {
	Value string
}

type QueryOptions

type QueryOptions struct {
	CheckForDeleteWhere bool
	CheckForUpdateWhere bool
	EndWithSemicolon    bool
}

type Statement

type Statement struct {
	SQL  string
	Args []interface{}
}

func AND

func AND(c ...Statement) Statement

func AggregateStatements

func AggregateStatements(queries ...Statement) Statement

func COUNT

func COUNT[V any](column Column[V]) Statement

func CROSS_JOIN

func CROSS_JOIN(table NameWithAliaser, on Statement) Statement

func Compare

func Compare[V any](c Column[V], operator CompareOperator, value V) Statement

func CompareColumns

func CompareColumns[A any, B any](a Column[A], operator CompareOperator, b Column[B]) Statement

func DELETE_FROM

func DELETE_FROM(t TableType) Statement

DELETE_FROM

func EQUAL

func EQUAL[V any](c Column[V], value V) Statement

func EQUAL_COLUMNS

func EQUAL_COLUMNS[A any, B any](a Column[A], b Column[B]) Statement

func FROM

func FROM(table NameWithAliaser) Statement

func FROM_RAW

func FROM_RAW(sq Statement) Statement

func FULL_JOIN

func FULL_JOIN(table NameWithAliaser, on Statement) Statement

func GREATER

func GREATER[V any](c Column[V], value V) Statement

func GREATER_OR_EQUAL

func GREATER_OR_EQUAL[V any](c Column[V], value V) Statement

func GROUP_BY

func GROUP_BY(c ...ColumnWithTable) Statement

func ILIKE

func ILIKE[V any](c Column[V], value V) Statement

func IN

func IN[V any](c Column[V], value V) Statement

func INNER_JOIN

func INNER_JOIN(table NameWithAliaser, on Statement) Statement

func INSERT_INTO

func INSERT_INTO(t TableType, columns ...ColumnType) Statement

func LEFT_JOIN

func LEFT_JOIN(table NameWithAliaser, on Statement) Statement

func LESS

func LESS[V any](c Column[V], value V) Statement

func LESS_OR_EQUAL

func LESS_OR_EQUAL[V any](c Column[V], value V) Statement

func LIKE

func LIKE[V any](c Column[V], value V) Statement

func LIMIT

func LIMIT(limit int) Statement

func NOT_EQUAL

func NOT_EQUAL[V any](c Column[V], value V) Statement

func NOT_IN

func NOT_IN[V any](c Column[V], value V) Statement

func NewEmptyStatement

func NewEmptyStatement() Statement

func NewStatement

func NewStatement(sql string, args ...interface{}) Statement

func OFFSET

func OFFSET(limit int) Statement

func OR

func OR(c ...Statement) Statement

func ORDER_BY

func ORDER_BY(c ...ColumnOrder) Statement

func Query

func Query(exprs ...Statement) (Statement, error)

func QueryMust

func QueryMust(exprs ...Statement) Statement

func QueryWithOptions

func QueryWithOptions(options QueryOptions, exprs ...Statement) (Statement, error)

func RETURNING

func RETURNING(c ...ColumnType) Statement

func RIGHT_JOIN

func RIGHT_JOIN(table NameWithAliaser, on Statement) Statement

func SELECT

func SELECT(columns ...ColumnType) Statement

func SELECT_FROM added in v0.1.6

func SELECT_FROM(
	table TableType,
	columns ...ColumnType,
) (Statement, error)

SELECT_FROM validates the columns against the table's column names and returns a Statement. If any column is not found in the table, an error is returned. This function is useful for ensuring that the columns being selected are valid

func SELECT_FROM_P added in v0.1.6

func SELECT_FROM_P(
	table TableType,
	columns ...ColumnType,
) Statement

SELECT_FROM_P same as SELECT_FROM, but panic

func SET

func SET(c ...Statement) Statement

func SET_VALUE

func SET_VALUE[V any](c Column[V], value V) Statement

func SET_VALUE_COLUMN

func SET_VALUE_COLUMN[A any, B any](a Column[A], b Column[B]) Statement

func SetArgsSequence

func SetArgsSequence(val Statement) Statement

func StatementFromTableAlias

func StatementFromTableAlias(table TableType) Statement

func SubQuery

func SubQuery(exprs ...Statement) Statement

func TABLE_WITH_COLUMNS

func TABLE_WITH_COLUMNS(t TableType, columns ...ColumnType) Statement

func UPDATE

func UPDATE(t TableType) Statement

func VALUES

func VALUES(valueGroupList ...ValuesSetSt) Statement

func WHERE

func WHERE(c Statement) Statement

func WITH

func WITH(t Statement) Statement

func (Statement) GetAlias

func (s Statement) GetAlias() string

func (Statement) GetAliasWithTableAlias

func (s Statement) GetAliasWithTableAlias() string

func (Statement) GetArguments

func (s Statement) GetArguments() []interface{}

func (Statement) GetName

func (s Statement) GetName() string

func (Statement) GetNameAsAlias

func (s Statement) GetNameAsAlias() string

func (Statement) GetSQL

func (s Statement) GetSQL() string

func (Statement) GetStatement

func (s Statement) GetStatement() Statement

func (Statement) String

func (s Statement) String() string

type StatementType

type StatementType interface {
	GetStatement() Statement
}

type Table

type Table struct {
	TableName   string
	TableAlias  string
	ColumnNames map[string]bool
}

func NewTableSt

func NewTableSt(name string, alias string) Table

func (Table) AllColumns

func (t Table) AllColumns() Column[string]

func (Table) As

func (t Table) As(alias string) TableType

func (Table) GetAlias

func (t Table) GetAlias() string

func (Table) GetAliasAsStatement

func (t Table) GetAliasAsStatement() Statement

func (Table) GetColumnsNames added in v0.1.6

func (t Table) GetColumnsNames() []string

func (Table) GetColumnsNamesMap added in v0.1.6

func (t Table) GetColumnsNamesMap() map[string]bool

func (Table) GetName

func (t Table) GetName() string

func (Table) GetNameWithAlias

func (t Table) GetNameWithAlias() string

func (Table) GetStatement

func (t Table) GetStatement() Statement

type TableType

type TableType interface {
	GetAlias() string
	GetName() string
	GetNameWithAlias() string
	GetColumnsNames() []string
	GetColumnsNamesMap() map[string]bool
}

type UUIDArray

type UUIDArray struct {
	Elements []uuid.UUID
}

func NewUUIDArray

func NewUUIDArray(uuids []uuid.UUID) *UUIDArray

func (*UUIDArray) Scan

func (ua *UUIDArray) Scan(src interface{}) error

func (UUIDArray) Value

func (ua UUIDArray) Value() (driver.Value, error)

type ValidationError added in v0.1.6

type ValidationError struct {
	Msg string
}

func (*ValidationError) Error added in v0.1.6

func (e *ValidationError) Error() string

type ValuesSetSt

type ValuesSetSt []interface{}

func ValueSet

func ValueSet(values ...interface{}) ValuesSetSt

Directories

Path Synopsis
cmd
sqli command
examples
pgdb/db
Package db contains generated code for schema 'public'.
Package db contains generated code for schema 'public'.

Jump to

Keyboard shortcuts

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