4 Commits
v0.1.1 ... main

Author SHA1 Message Date
6f5748d3d3 fix 2025-08-11 23:45:36 +05:30
b25f9367ed using any for In/NotIn 2025-08-11 23:42:58 +05:30
8750f3ad95 method "In" 2025-08-11 22:37:25 +05:30
ad1faf2056 join with conditions 2025-08-10 20:26:09 +05:30
6 changed files with 89 additions and 31 deletions

View File

@@ -2,3 +2,6 @@ run:
go run ./cmd -o ./example/db ./example/schema.sql
bench-select:
go test ./example -bench BenchmarkSelect -memprofile memprofile.out -cpuprofile profile.out
test:
go test ./playground

28
pgm.go
View File

@@ -98,19 +98,19 @@ func (f Field) IsNotNull() Conditioner {
return &Cond{Field: col, op: " IS NOT NULL", len: len(col) + 12}
}
// Eq is equal
func (f Field) Eq(val any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " = $", len: len(col) + 5}
}
// 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}
}
func (f Field) NEq(val any) Conditioner {
// 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}
}
@@ -151,14 +151,22 @@ func (f Field) ILike(val string) Conditioner {
return &Cond{Field: col, Val: val, op: " ILIKE $", len: len(col) + 5}
}
func (f Field) NotIn(val ...any) Conditioner {
// In using ANY
func (f Field) In(val ...any) Conditioner {
col := f.String()
return &Cond{Field: col, Val: val, op: " NOT IN($", action: CondActionNeedToClose, len: len(col) + 5}
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: " NOT IN($)", action: CondActionSubQuery}
return &Cond{Field: col, Val: qry, op: " != ANY($)", action: CondActionSubQuery}
}
//

View File

@@ -28,7 +28,7 @@ func TestQryBuilder2(t *testing.T) {
),
).
Where(
user.LastName.NEq(7),
user.LastName.NotEq(7),
user.Phone.Like("%123%"),
user.UpdatedAt.IsNotNull(),
user.Email.NotInSubQuery(db.User.Select(user.ID).Where(user.ID.Eq(123))),
@@ -60,7 +60,31 @@ func TestSelectWithHaving(t *testing.T) {
}
}
// BenchmarkSelect-12 668817 1753 ns/op 4442 B/op 59 allocs/op
func TestSelectWithJoin(t *testing.T) {
got := db.User.Select(user.Email, user.FirstName).
Join(db.UserSession, user.ID, usersession.UserID).
LeftJoin(db.BranchUser, user.ID, branchuser.UserID, pgm.Or(branchuser.RoleID.Eq("1"), branchuser.RoleID.Eq("2"))).
Where(
user.ID.Eq(3),
pgm.Or(
user.StatusID.Eq(4),
user.UpdatedAt.Eq(5),
),
).
Limit(10).
Offset(100).
String()
expected := "SELECT users.email, users.first_name " +
"FROM users JOIN user_sessions ON users.id = user_sessions.user_id " +
"LEFT JOIN branch_users ON users.id = branch_users.user_id AND (branch_users.role_id = $1 OR branch_users.role_id = $2) " +
"WHERE users.id = $3 AND (users.status_id = $4 OR users.updated_at = $5) " +
"LIMIT 10 OFFSET 100"
if expected != got {
t.Errorf("\nexpected: %q\ngot: %q", expected, got)
}
}
// BenchmarkSelect-12 638901 1860 ns/op 4266 B/op 61 allocs/op
func BenchmarkSelect(b *testing.B) {
for b.Loop() {
@@ -80,7 +104,7 @@ func BenchmarkSelect(b *testing.B) {
),
).
Where(
user.LastName.NEq(7),
user.LastName.NotEq(7),
user.Phone.Like("%123%"),
user.Email.NotInSubQuery(db.User.Select(user.ID).Where(user.ID.Eq(123))),
).

View File

@@ -17,7 +17,7 @@ func TestUpdateQuery(t *testing.T) {
user.Email.Eq("aa@aa.com"),
).
Where(
user.StatusID.NEq(1),
user.StatusID.NotEq(1),
).
String()
@@ -38,7 +38,7 @@ func TestUpdateSetMap(t *testing.T) {
user.Email.Eq("aa@aa.com"),
).
Where(
user.StatusID.NEq(1),
user.StatusID.NotEq(1),
).
String()
@@ -59,7 +59,7 @@ func BenchmarkUpdateQuery(b *testing.B) {
user.Email.Eq("aa@aa.com"),
).
Where(
user.StatusID.NEq(1),
user.StatusID.NotEq(1),
).
String()
}

8
qry.go
View File

@@ -21,10 +21,10 @@ type (
SelectClause interface {
// Join and Inner Join are same
Join(m Table, t1Field, t2Field Field) SelectClause
LeftJoin(m Table, t1Field, t2Field Field) SelectClause
RightJoin(m Table, t1Field, t2Field Field) SelectClause
FullJoin(m Table, t1Field, t2Field Field) SelectClause
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

View File

@@ -62,23 +62,46 @@ func (t Table) Select(field ...Field) SelectClause {
return qb
}
func (q *selectQry) Join(t Table, t1Field, t2Field Field) SelectClause {
q.join = append(q.join, "JOIN "+t.Name+" ON "+t1Field.String()+" = "+t2Field.String())
func (q *selectQry) Join(t Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause {
return q.buildJoin(t, "JOIN", t1Field, t2Field, cond...)
}
func (q *selectQry) LeftJoin(t Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause {
return q.buildJoin(t, "LEFT JOIN", t1Field, t2Field, cond...)
}
func (q *selectQry) RightJoin(t Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause {
return q.buildJoin(t, "RIGHT JOIN", t1Field, t2Field, cond...)
}
func (q *selectQry) FullJoin(t Table, t1Field, t2Field Field, cond ...Conditioner) SelectClause {
return q.buildJoin(t, "FULL JOIN", t1Field, t2Field, cond...)
}
func (q *selectQry) buildJoin(t Table, joinKW string, t1Field, t2Field Field, cond ...Conditioner) SelectClause {
str := joinKW + " " + t.Name + " ON " + t1Field.String() + " = " + t2Field.String()
if len(cond) == 0 { // Join with no condition
q.join = append(q.join, str)
return q
}
func (q *selectQry) LeftJoin(t Table, t1Field, t2Field Field) SelectClause {
q.join = append(q.join, "LEFT JOIN "+t.Name+" ON "+t1Field.String()+" = "+t2Field.String())
return q
// Join has condition(s)
sb := getSB()
defer putSB(sb)
sb.Grow(len(str) * 2)
sb.WriteString(str + " AND ")
var argIdx int
for i, c := range cond {
argIdx = len(q.args)
if i > 0 {
sb.WriteString(" AND ")
}
sb.WriteString(c.Condition(&q.args, argIdx))
}
func (q *selectQry) RightJoin(t Table, t1Field, t2Field Field) SelectClause {
q.join = append(q.join, "RIGHT JOIN "+t.Name+" ON "+t1Field.String()+" = "+t2Field.String())
return q
}
func (q *selectQry) FullJoin(t Table, t1Field, t2Field Field) SelectClause {
q.join = append(q.join, "FULL JOIN "+t.Name+" ON "+t1Field.String()+" = "+t2Field.String())
q.join = append(q.join, sb.String())
return q
}