// Patial Tech. // Author, Ankit Patial package pgm import ( "context" "strconv" "strings" "github.com/jackc/pgx/v5" ) type ( Clause interface { Select(fields ...Field) SelectClause // Insert() InsertSet // Update() UpdateSet // Delete() WhereOrExec } 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 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 { Exec(ctx context.Context) error ExecTx(ctx context.Context, tx pgx.Tx) error Stringer } Stringer interface { String() string } RowScanner interface { Scan(dest ...any) error } 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 { return &CondGroup{op: " AND ", cond: cond} } func Or(cond ...Conditioner) Conditioner { return &CondGroup{op: " OR ", cond: cond} } func (cv *Cond) Condition(args *[]any, argIdx int) string { // 1. condition with subquery if cv.action == CondActionSubQuery { qStr, newArgs := cv.Val.(SelectClause).raw(*args) *args = newArgs return cv.Field + strings.Replace(cv.op, "$", qStr, 1) } // 2. normal condition var op string if cv.Val != nil { *args = append(*args, cv.Val) if strings.HasSuffix(cv.op, "$") { op = cv.op + strconv.Itoa(argIdx+1) } else { op = strings.Replace(cv.op, "$", "$"+strconv.Itoa(argIdx+1), 1) } } else { op = cv.op } if cv.action == CondActionNeedToClose { return cv.Field + op + ")" } return cv.Field + op } func (c *CondGroup) Condition(args *[]any, argIdx int) string { sb := getSB() defer putSB(sb) sb.WriteString("(") for i, cond := range c.cond { if i == 0 { sb.WriteString(cond.Condition(args, argIdx+i)) } else { sb.WriteString(c.op) sb.WriteString(cond.Condition(args, argIdx+i)) } } sb.WriteString(")") return sb.String() }