restructuring of files. added 2 new methods(Asc, Desc) to field

This commit is contained in:
2025-10-18 12:31:46 +05:30
parent 6f5748d3d3
commit 325103e8ef
13 changed files with 433 additions and 446 deletions

184
pgm.go
View File

@@ -1,178 +1,44 @@
// Patial Tech. // pgm
// Author, Ankit Patial //
// A simple PG query builder
//
// Author: Ankit Patial
package pgm package pgm
import ( import (
"errors"
"strings" "strings"
"sync"
"time" "time"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
// Table in database var poolStringBuilder = sync.Pool{
type Table struct { New: func() any {
Name string return new(strings.Builder)
PK []string },
FieldCount uint16
debug bool
} }
// Debug when set true will print generated query string in stdout // Errors
func (t Table) Debug() Clause { var (
t.debug = true ErrInitTX = errors.New("failed to init db.tx")
return t ErrCommitTX = errors.New("failed to commit db.tx")
ErrNoRows = errors.New("no data found")
)
// get string builder from pool
func getSB() *strings.Builder {
return poolStringBuilder.Get().(*strings.Builder)
} }
// // put string builder back to pool
// Field ==> func putSB(sb *strings.Builder) {
// sb.Reset()
poolStringBuilder.Put(sb)
// Field related to a table
type Field string
func (f Field) Name() string {
return strings.Split(string(f), ".")[1]
} }
func (f Field) String() string {
return string(f)
}
// Count fn wrapping of field
func (f Field) Count() Field {
return Field("COUNT(" + f.String() + ")")
}
// StringEscape will return a empty string for null value
func (f Field) StringEscape() Field {
return Field("COALESCE(" + f.String() + ", '')")
}
// NumberEscape will return a zero string for null value
func (f Field) NumberEscape() Field {
return Field("COALESCE(" + f.String() + ", 0)")
}
// BooleanEscape will return a false for null value
func (f Field) BooleanEscape() Field {
return Field("COALESCE(" + f.String() + ", FALSE)")
}
// Avg fn wrapping of field
func (f Field) Avg() Field {
return Field("AVG(" + f.String() + ")")
}
func (f Field) Sum() Field {
return Field("SUM(" + f.String() + ")")
}
func (f Field) Max() Field {
return Field("MAX(" + f.String() + ")")
}
func (f Field) Min() Field {
return Field("Min(" + f.String() + ")")
}
func (f Field) Lower() Field {
return Field("LOWER(" + f.String() + ")")
}
func (f Field) Upper() Field {
return Field("UPPER(" + f.String() + ")")
}
func (f Field) Trim() Field {
return Field("TRIM(" + f.String() + ")")
}
func (f Field) IsNull() Conditioner {
col := f.String()
return &Cond{Field: col, op: " IS NULL", len: len(col) + 8}
}
func (f Field) IsNotNull() Conditioner {
col := f.String()
return &Cond{Field: col, op: " IS NOT NULL", len: len(col) + 12}
}
// EqualFold will use LOWER(column_name) = LOWER(val) for comparision
func (f Field) EqFold(val string) Conditioner {
col := f.String()
return &Cond{Field: "LOWER(" + col + ")", Val: val, op: " = LOWER($", action: CondActionNeedToClose, len: len(col) + 5}
}
// Eq is equal
func (f Field) Eq(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " = $", len: len(col) + 5}
}
func (f Field) NotEq(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " != $", len: len(col) + 5}
}
func (f Field) Gt(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " > $", len: len(col) + 5}
}
func (f Field) Lt(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " < $", len: len(col) + 5}
}
func (f Field) Gte(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " >= $", len: len(col) + 5}
}
func (f Field) Lte(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " <= $", len: len(col) + 5}
}
func (f Field) Like(val string) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " LIKE $", len: len(f.String()) + 5}
}
func (f Field) LikeFold(val string) Conditioner {
col := f.String()
return &Cond{Field: "LOWER(" + col + ")", Val: val, op: " LIKE LOWER($", action: CondActionNeedToClose, len: len(col) + 5}
}
// ILIKE is case-insensitive
func (f Field) ILike(val string) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " ILIKE $", len: len(col) + 5}
}
// In using ANY
func (f Field) In(val ...any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " = ANY($", action: CondActionNeedToClose, len: len(col) + 5}
}
// NotIn using ANY
func (f Field) NotIn(val ...any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " != ANY($", action: CondActionNeedToClose, len: len(col) + 5}
}
// NotInSubQuery using ANY
func (f Field) NotInSubQuery(qry WhereClause) Conditioner {
col := f.String()
return &Cond{Field: col, Val: qry, op: " != ANY($)", action: CondActionSubQuery}
}
//
// Helper func ==>
//
// PgTime as in UTC // PgTime as in UTC
func PgTime(t time.Time) pgtype.Timestamptz { func PgTime(t time.Time) pgtype.Timestamptz {
return pgtype.Timestamptz{Time: t, Valid: true} return pgtype.Timestamptz{Time: t, Valid: true}

167
pgm_field.go Normal file
View File

@@ -0,0 +1,167 @@
package pgm
import "strings"
// Field related to a table
type Field string
func (f Field) Name() string {
return strings.Split(string(f), ".")[1]
}
func (f Field) String() string {
return string(f)
}
// Count fn wrapping of field
func (f Field) Count() Field {
return Field("COUNT(" + f.String() + ")")
}
// StringEscape will return a empty string for null value
func (f Field) StringEscape() Field {
return Field("COALESCE(" + f.String() + ", '')")
}
// NumberEscape will return a zero string for null value
func (f Field) NumberEscape() Field {
return Field("COALESCE(" + f.String() + ", 0)")
}
// BooleanEscape will return a false for null value
func (f Field) BooleanEscape() Field {
return Field("COALESCE(" + f.String() + ", FALSE)")
}
// Avg fn wrapping of field
func (f Field) Avg() Field {
return Field("AVG(" + f.String() + ")")
}
func (f Field) Sum() Field {
return Field("SUM(" + f.String() + ")")
}
func (f Field) Max() Field {
return Field("MAX(" + f.String() + ")")
}
func (f Field) Min() Field {
return Field("Min(" + f.String() + ")")
}
func (f Field) Lower() Field {
return Field("LOWER(" + f.String() + ")")
}
func (f Field) Upper() Field {
return Field("UPPER(" + f.String() + ")")
}
func (f Field) Trim() Field {
return Field("TRIM(" + f.String() + ")")
}
func (f Field) Asc() Field {
return Field(f.String() + " ASC")
}
func (f Field) Desc() Field {
return Field(f.String() + " DESC")
}
func (f Field) IsNull() Conditioner {
col := f.String()
return &Cond{Field: col, op: " IS NULL", len: len(col) + 8}
}
func (f Field) IsNotNull() Conditioner {
col := f.String()
return &Cond{Field: col, op: " IS NOT NULL", len: len(col) + 12}
}
// EqualFold will use LOWER(column_name) = LOWER(val) for comparision
func (f Field) EqFold(val string) Conditioner {
col := f.String()
return &Cond{Field: "LOWER(" + col + ")", Val: val, op: " = LOWER($", action: CondActionNeedToClose, len: len(col) + 5}
}
// Eq is equal
func (f Field) Eq(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " = $", len: len(col) + 5}
}
func (f Field) NotEq(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " != $", len: len(col) + 5}
}
func (f Field) Gt(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " > $", len: len(col) + 5}
}
func (f Field) Lt(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " < $", len: len(col) + 5}
}
func (f Field) Gte(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " >= $", len: len(col) + 5}
}
func (f Field) Lte(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " <= $", len: len(col) + 5}
}
func (f Field) Like(val string) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " LIKE $", len: len(f.String()) + 5}
}
func (f Field) LikeFold(val string) Conditioner {
col := f.String()
return &Cond{Field: "LOWER(" + col + ")", Val: val, op: " LIKE LOWER($", action: CondActionNeedToClose, len: len(col) + 5}
}
// ILIKE is case-insensitive
func (f Field) ILike(val string) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " ILIKE $", len: len(col) + 5}
}
// In using ANY
func (f Field) In(val ...any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " = ANY($", action: CondActionNeedToClose, len: len(col) + 5}
}
// NotIn using ANY
func (f Field) NotIn(val ...any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " != ANY($", action: CondActionNeedToClose, len: len(col) + 5}
}
// NotInSubQuery using ANY
func (f Field) NotInSubQuery(qry WhereClause) Conditioner {
col := f.String()
return &Cond{Field: col, Val: qry, op: " != ANY($)", action: CondActionSubQuery}
}
func joinFileds(fields []Field) string {
sb := getSB()
defer putSB(sb)
for i, f := range fields {
if i == 0 {
sb.WriteString(f.String())
} else {
sb.WriteString(", ")
sb.WriteString(f.String())
}
}
return sb.String()
}

59
pgm_table.go Normal file
View File

@@ -0,0 +1,59 @@
package pgm
// Table in database
type Table struct {
Name string
PK []string
FieldCount uint16
debug bool
}
// Debug when set true will print generated query string in stdout
func (t *Table) Debug() Clause {
t.debug = true
return t
}
// Insert table statement
func (t *Table) Insert() InsertClause {
qb := &insertQry{
debug: t.debug,
table: t.Name,
fields: make([]string, 0, t.FieldCount),
vals: make([]string, 0, t.FieldCount),
args: make([]any, 0, t.FieldCount),
}
return qb
}
// Select table statement
func (t *Table) Select(field ...Field) SelectClause {
qb := &selectQry{
debug: t.debug,
table: t.Name,
fields: field,
}
return qb
}
// Update table statement
func (t *Table) Update() UpdateClause {
qb := &updateQry{
debug: t.debug,
table: t.Name,
cols: make([]string, 0, t.FieldCount),
args: make([]any, 0, t.FieldCount),
}
return qb
}
// Detlete table statement
func (t *Table) Delete() DeleteCluase {
qb := &deleteQry{
debug: t.debug,
table: t.Name,
}
return qb
}

View File

@@ -8,7 +8,7 @@ import (
) )
func TestDelete(t *testing.T) { func TestDelete(t *testing.T) {
expected := "DELETE FROM users WHERE users.id = $1 AND users.status_id NOT IN($2)" expected := "DELETE FROM users WHERE users.id = $1 AND users.status_id != ANY($2)"
got := db.User.Delete(). got := db.User.Delete().
Where(user.ID.Eq(1), user.StatusID.NotIn(1, 2, 3)). Where(user.ID.Eq(1), user.StatusID.NotIn(1, 2, 3)).
String() String()

View File

@@ -23,23 +23,6 @@ func TestInsertQuery(t *testing.T) {
} }
} }
func TestInsertSetMap(t *testing.T) {
got := db.User.Insert().
SetMap(map[pgm.Field]any{
user.Email: "aa@aa.com",
user.Phone: 8889991234,
user.FirstName: "fname",
user.LastName: "lname",
}).
Returning(user.ID).
String()
expected := "INSERT INTO users(email, phone, first_name, last_name) VALUES($1, $2, $3, $4) RETURNING id"
if got != expected {
t.Errorf("\nexpected: %q\ngot: %q", expected, got)
}
}
func TestInsertQuery2(t *testing.T) { func TestInsertQuery2(t *testing.T) {
got := db.User.Insert(). got := db.User.Insert().
Set(user.Email, "aa@aa.com"). Set(user.Email, "aa@aa.com").
@@ -68,6 +51,7 @@ func BenchmarkInsertQuery(b *testing.B) {
} }
// BenchmarkInsertSetMap-12 1534039 777.1 ns/op 1480 B/op 20 allocs/op // BenchmarkInsertSetMap-12 1534039 777.1 ns/op 1480 B/op 20 allocs/op
// BenchmarkInsertSetMap-12 1361275 879.2 ns/op 1480 B/op 20 allocs/op
func BenchmarkInsertSetMap(b *testing.B) { func BenchmarkInsertSetMap(b *testing.B) {
for b.Loop() { for b.Loop() {
_ = db.User.Insert(). _ = db.User.Insert().

View File

@@ -40,7 +40,7 @@ func TestQryBuilder2(t *testing.T) {
expected := "SELECT users.email, users.first_name FROM users JOIN user_sessions ON users.id = user_sessions.user_id" + expected := "SELECT users.email, users.first_name FROM users JOIN user_sessions ON users.id = user_sessions.user_id" +
" JOIN branch_users ON users.id = branch_users.user_id WHERE users.id = $1 AND (users.status_id = $2 OR users.updated_at = $3)" + " JOIN branch_users ON users.id = branch_users.user_id WHERE users.id = $1 AND (users.status_id = $2 OR users.updated_at = $3)" +
" AND users.mfa_kind = $4 AND (users.first_name = $5 OR users.middle_name = $6) AND users.last_name != $7 AND users.phone" + " AND users.mfa_kind = $4 AND (users.first_name = $5 OR users.middle_name = $6) AND users.last_name != $7 AND users.phone" +
" LIKE $8 AND users.updated_at IS NOT NULL AND users.email NOT IN(SELECT users.id FROM users WHERE users.id = $9) LIMIT 10 OFFSET 100" " LIKE $8 AND users.updated_at IS NOT NULL AND users.email != ANY(SELECT users.id FROM users WHERE users.id = $9) LIMIT 10 OFFSET 100"
if expected != got { if expected != got {
t.Errorf("\nexpected: %q\ngot: %q", expected, got) t.Errorf("\nexpected: %q\ngot: %q", expected, got)
} }

View File

@@ -3,7 +3,6 @@ package playground
import ( import (
"testing" "testing"
"code.patial.tech/go/pgm"
"code.patial.tech/go/pgm/playground/db" "code.patial.tech/go/pgm/playground/db"
"code.patial.tech/go/pgm/playground/db/user" "code.patial.tech/go/pgm/playground/db/user"
) )
@@ -27,28 +26,8 @@ func TestUpdateQuery(t *testing.T) {
} }
} }
func TestUpdateSetMap(t *testing.T) {
got := db.User.Update().
SetMap(map[pgm.Field]any{
user.FirstName: "ankit",
user.MiddleName: "singh",
user.LastName: "patial",
}).
Where(
user.Email.Eq("aa@aa.com"),
).
Where(
user.StatusID.NotEq(1),
).
String()
expected := "UPDATE users SET first_name=$1, middle_name=$2, last_name=$3 WHERE users.email = $4 AND users.status_id != $5"
if got != expected {
t.Errorf("\nexpected: %q\ngot: %q", expected, got)
}
}
// BenchmarkUpdateQuery-12 2004985 592.2 ns/op 1176 B/op 20 allocs/op // BenchmarkUpdateQuery-12 2004985 592.2 ns/op 1176 B/op 20 allocs/op
// BenchmarkUpdateQuery-12 1792483 670.7 ns/op 1176 B/op 20 allocs/op
func BenchmarkUpdateQuery(b *testing.B) { func BenchmarkUpdateQuery(b *testing.B) {
for b.Loop() { for b.Loop() {
_ = db.User.Update(). _ = db.User.Update().

24
pool.go
View File

@@ -7,8 +7,6 @@ import (
"context" "context"
"errors" "errors"
"log/slog" "log/slog"
"strings"
"sync"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -17,17 +15,8 @@ import (
) )
var ( var (
poolPGX atomic.Pointer[pgxpool.Pool] poolPGX atomic.Pointer[pgxpool.Pool]
poolStringBuilder = sync.Pool{
New: func() any {
return new(strings.Builder)
},
}
ErrConnStringMissing = errors.New("connection string is empty") ErrConnStringMissing = errors.New("connection string is empty")
ErrInitTX = errors.New("failed to init db.tx")
ErrCommitTX = errors.New("failed to commit db.tx")
ErrNoRows = errors.New("no data found")
) )
type Config struct { type Config struct {
@@ -77,17 +66,6 @@ func InitPool(conf Config) {
poolPGX.Store(p) poolPGX.Store(p)
} }
// get string builder from pool
func getSB() *strings.Builder {
return poolStringBuilder.Get().(*strings.Builder)
}
// put string builder back to pool
func putSB(sb *strings.Builder) {
sb.Reset()
poolStringBuilder.Put(sb)
}
// GetPool instance // GetPool instance
func GetPool() *pgxpool.Pool { func GetPool() *pgxpool.Pool {
return poolPGX.Load() return poolPGX.Load()

167
qry.go
View File

@@ -13,150 +13,10 @@ import (
type ( type (
Clause interface { Clause interface {
Insert() InsertClause
Select(fields ...Field) SelectClause Select(fields ...Field) SelectClause
// Insert() InsertSet Update() UpdateClause
// Update() UpdateSet Delete() DeleteCluase
// Delete() WhereOrExec
}
SelectClause interface {
// Join and Inner Join are same
Join(m Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause
LeftJoin(m Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause
RightJoin(m Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause
FullJoin(m Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause
CrossJoin(m Table) SelectClause
WhereClause
OrderByClause
GroupByClause
LimitClause
OffsetClause
Query
raw(prefixArgs []any) (string, []any)
}
WhereClause interface {
Where(cond ...Conditioner) AfterWhere
}
AfterWhere interface {
WhereClause
GroupByClause
OrderByClause
LimitClause
OffsetClause
Query
}
GroupByClause interface {
GroupBy(fields ...Field) AfterGroupBy
}
AfterGroupBy interface {
HavinClause
OrderByClause
LimitClause
OffsetClause
Query
}
HavinClause interface {
Having(cond ...Conditioner) AfterHaving
}
AfterHaving interface {
OrderByClause
LimitClause
OffsetClause
Query
}
OrderByClause interface {
OrderBy(fields ...Field) AfterOrderBy
}
AfterOrderBy interface {
LimitClause
OffsetClause
Query
}
LimitClause interface {
Limit(v int) AfterLimit
}
AfterLimit interface {
OffsetClause
Query
}
OffsetClause interface {
Offset(v int) AfterOffset
}
AfterOffset interface {
LimitClause
Query
}
Conditioner interface {
Condition(args *[]any, idx int) string
}
Insert interface {
Set(field Field, val any) InsertClause
SetMap(fields map[Field]any) InsertClause
}
InsertClause interface {
Insert
Returning(field Field) First
OnConflict(fields ...Field) Do
Execute
Stringer
}
Do interface {
DoNothing() Execute
DoUpdate(fields ...Field) Execute
}
Update interface {
Set(field Field, val any) UpdateClause
SetMap(fields map[Field]any) UpdateClause
}
UpdateClause interface {
Update
Where(cond ...Conditioner) WhereOrExec
}
WhereOrExec interface {
Where(cond ...Conditioner) WhereOrExec
Execute
}
Query interface {
First
All
Stringer
}
First interface {
First(ctx context.Context, dest ...any) error
FirstTx(ctx context.Context, tx pgx.Tx, dest ...any) error
Stringer
}
All interface {
// Query rows
//
// don't forget to close() rows
All(ctx context.Context, rows RowsCb) error
// Query rows
//
// don't forget to close() rows
AllTx(ctx context.Context, tx pgx.Tx, rows RowsCb) error
} }
Execute interface { Execute interface {
@@ -169,28 +29,11 @@ type (
String() string String() string
} }
RowScanner interface { Conditioner interface {
Scan(dest ...any) error Condition(args *[]any, idx int) string
} }
RowsCb func(row RowScanner) error
) )
func joinFileds(fields []Field) string {
sb := getSB()
defer putSB(sb)
for i, f := range fields {
if i == 0 {
sb.WriteString(f.String())
} else {
sb.WriteString(", ")
sb.WriteString(f.String())
}
}
return sb.String()
}
func And(cond ...Conditioner) Conditioner { func And(cond ...Conditioner) Conditioner {
return &CondGroup{op: " AND ", cond: cond} return &CondGroup{op: " AND ", cond: cond}
} }

View File

@@ -7,6 +7,10 @@ import (
) )
type ( type (
DeleteCluase interface {
WhereOrExec
}
deleteQry struct { deleteQry struct {
table string table string
condition []Conditioner condition []Conditioner
@@ -15,15 +19,6 @@ type (
} }
) )
func (t *Table) Delete() WhereOrExec {
qb := &deleteQry{
table: t.Name,
debug: t.debug,
}
return qb
}
func (q *deleteQry) Where(cond ...Conditioner) WhereOrExec { func (q *deleteQry) Where(cond ...Conditioner) WhereOrExec {
q.condition = append(q.condition, cond...) q.condition = append(q.condition, cond...)
return q return q

View File

@@ -12,29 +12,33 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
) )
type insertQry struct { type (
returing *string InsertClause interface {
onConflict *string Insert
Returning(field Field) First
table string OnConflict(fields ...Field) Do
conflictAction string Execute
Stringer
fields []string
vals []string
args []any
debug bool
}
func (t *Table) Insert() Insert {
qb := &insertQry{
table: t.Name,
fields: make([]string, 0, t.FieldCount),
vals: make([]string, 0, t.FieldCount),
args: make([]any, 0, t.FieldCount),
debug: t.debug,
} }
return qb
} Insert interface {
Set(field Field, val any) InsertClause
SetMap(fields map[Field]any) InsertClause
}
insertQry struct {
returing *string
onConflict *string
table string
conflictAction string
fields []string
vals []string
args []any
debug bool
}
)
func (q *insertQry) Set(field Field, val any) InsertClause { func (q *insertQry) Set(field Field, val any) InsertClause {
q.fields = append(q.fields, field.Name()) q.fields = append(q.fields, field.Name())

View File

@@ -14,6 +14,120 @@ import (
) )
type ( type (
SelectClause interface {
// Join and Inner Join are same
Join(m Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause
LeftJoin(m Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause
RightJoin(m Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause
FullJoin(m Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause
CrossJoin(m Table) SelectClause
WhereClause
OrderByClause
GroupByClause
LimitClause
OffsetClause
Query
raw(prefixArgs []any) (string, []any)
}
WhereClause interface {
Where(cond ...Conditioner) AfterWhere
}
AfterWhere interface {
WhereClause
GroupByClause
OrderByClause
LimitClause
OffsetClause
Query
}
GroupByClause interface {
GroupBy(fields ...Field) AfterGroupBy
}
AfterGroupBy interface {
HavinClause
OrderByClause
LimitClause
OffsetClause
Query
}
HavinClause interface {
Having(cond ...Conditioner) AfterHaving
}
AfterHaving interface {
OrderByClause
LimitClause
OffsetClause
Query
}
OrderByClause interface {
OrderBy(fields ...Field) AfterOrderBy
}
AfterOrderBy interface {
LimitClause
OffsetClause
Query
}
LimitClause interface {
Limit(v int) AfterLimit
}
AfterLimit interface {
OffsetClause
Query
}
OffsetClause interface {
Offset(v int) AfterOffset
}
AfterOffset interface {
LimitClause
Query
}
Do interface {
DoNothing() Execute
DoUpdate(fields ...Field) Execute
}
Query interface {
First
All
Stringer
}
RowScanner interface {
Scan(dest ...any) error
}
RowsCb func(row RowScanner) error
First interface {
First(ctx context.Context, dest ...any) error
FirstTx(ctx context.Context, tx pgx.Tx, dest ...any) error
Stringer
}
All interface {
// Query rows
//
// don't forget to close() rows
All(ctx context.Context, rows RowsCb) error
// Query rows
//
// don't forget to close() rows
AllTx(ctx context.Context, tx pgx.Tx, rows RowsCb) error
}
selectQry struct { selectQry struct {
table string table string
fields []Field fields []Field
@@ -23,9 +137,11 @@ type (
groupBy []Field groupBy []Field
having []Conditioner having []Conditioner
orderBy []Field orderBy []Field
limit int
offset int limit int
debug bool offset int
debug bool
} }
CondAction uint8 CondAction uint8
@@ -51,17 +167,6 @@ const (
CondActionSubQuery CondActionSubQuery
) )
// Select clause
func (t Table) Select(field ...Field) SelectClause {
qb := &selectQry{
table: t.Name,
debug: t.debug,
fields: field,
}
return qb
}
func (q *selectQry) Join(t Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause { func (q *selectQry) Join(t Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause {
return q.buildJoin(t, "JOIN", t1Field, t2Field, cond...) return q.buildJoin(t, "JOIN", t1Field, t2Field, cond...)
} }

View File

@@ -11,23 +11,30 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
) )
type updateQry struct { type (
table string Update interface {
cols []string Set(field Field, val any) UpdateClause
condition []Conditioner SetMap(fields map[Field]any) UpdateClause
args []any
debug bool
}
func (t *Table) Update() Update {
qb := &updateQry{
table: t.Name,
debug: t.debug,
cols: make([]string, 0, t.FieldCount),
args: make([]any, 0, t.FieldCount),
} }
return qb
} UpdateClause interface {
Update
Where(cond ...Conditioner) WhereOrExec
}
WhereOrExec interface {
Where(cond ...Conditioner) WhereOrExec
Execute
}
updateQry struct {
table string
cols []string
condition []Conditioner
args []any
debug bool
}
)
func (q *updateQry) Set(field Field, val any) UpdateClause { func (q *updateQry) Set(field Field, val any) UpdateClause {
col := field.Name() col := field.Name()