@@ -23,7 +23,6 @@ import (
2323 "net"
2424 "net/url"
2525 "strconv"
26- "strings"
2726 "sync"
2827 "time"
2928)
@@ -222,150 +221,46 @@ func NewDbFromConfig(c *Config, logger *logging.Logger, connectorCallbacks Retry
222221 }, nil
223222}
224223
224+ // QueryBuilder returns a fully initialised and ready to use *[QueryBuilder] instance for the given subject/struct.
225+ func (db * DB ) QueryBuilder (subject any ) * QueryBuilder {
226+ return & QueryBuilder {db : db , subject : subject }
227+ }
228+
225229// GetAddr returns the database host:port or Unix socket address.
226230func (db * DB ) GetAddr () string {
227231 return db .addr
228232}
229233
230234// BuildDeleteStmt returns a DELETE statement for the given struct.
231235func (db * DB ) BuildDeleteStmt (from interface {}) string {
232- return fmt .Sprintf (
233- `DELETE FROM "%s" WHERE id IN (?)` ,
234- TableName (from ),
235- )
236+ return db .QueryBuilder (from ).Delete ()
236237}
237238
238239// BuildInsertStmt returns an INSERT INTO statement for the given struct.
239240func (db * DB ) BuildInsertStmt (into interface {}) (string , int ) {
240- columns := db .columnMap .Columns (into )
241-
242- return fmt .Sprintf (
243- `INSERT INTO "%s" ("%s") VALUES (%s)` ,
244- TableName (into ),
245- strings .Join (columns , `", "` ),
246- fmt .Sprintf (":%s" , strings .Join (columns , ", :" )),
247- ), len (columns )
241+ return db .QueryBuilder (into ).Insert ()
248242}
249243
250244// BuildInsertIgnoreStmt returns an INSERT statement for the specified struct for
251245// which the database ignores rows that have already been inserted.
252246func (db * DB ) BuildInsertIgnoreStmt (into interface {}) (string , int ) {
253- table := TableName (into )
254- columns := db .columnMap .Columns (into )
255- var clause string
256-
257- switch db .DriverName () {
258- case MySQL :
259- // MySQL treats UPDATE id = id as a no-op.
260- clause = fmt .Sprintf (`ON DUPLICATE KEY UPDATE "%s" = "%s"` , columns [0 ], columns [0 ])
261- case PostgreSQL :
262- var constraint string
263- if constrainter , ok := into .(PgsqlOnConflictConstrainter ); ok {
264- constraint = constrainter .PgsqlOnConflictConstraint ()
265- } else {
266- constraint = "pk_" + table
267- }
268-
269- clause = fmt .Sprintf ("ON CONFLICT ON CONSTRAINT %s DO NOTHING" , constraint )
270- }
271-
272- return fmt .Sprintf (
273- `INSERT INTO "%s" ("%s") VALUES (%s) %s` ,
274- table ,
275- strings .Join (columns , `", "` ),
276- fmt .Sprintf (":%s" , strings .Join (columns , ", :" )),
277- clause ,
278- ), len (columns )
247+ return db .QueryBuilder (into ).InsertIgnore ()
279248}
280249
281250// BuildSelectStmt returns a SELECT query that creates the FROM part from the given table struct
282251// and the column list from the specified columns struct.
283252func (db * DB ) BuildSelectStmt (table interface {}, columns interface {}) string {
284- q := fmt .Sprintf (
285- `SELECT "%s" FROM "%s"` ,
286- strings .Join (db .columnMap .Columns (columns ), `", "` ),
287- TableName (table ),
288- )
289-
290- if scoper , ok := table .(Scoper ); ok {
291- where , _ := db .BuildWhere (scoper .Scope ())
292- q += ` WHERE ` + where
293- }
294-
295- return q
253+ return db .QueryBuilder (table ).SetColumns (db .columnMap .Columns (columns )... ).Select ()
296254}
297255
298256// BuildUpdateStmt returns an UPDATE statement for the given struct.
299257func (db * DB ) BuildUpdateStmt (update interface {}) (string , int ) {
300- columns := db .columnMap .Columns (update )
301- set := make ([]string , 0 , len (columns ))
302-
303- for _ , col := range columns {
304- set = append (set , fmt .Sprintf (`"%s" = :%s` , col , col ))
305- }
306-
307- return fmt .Sprintf (
308- `UPDATE "%s" SET %s WHERE id = :id` ,
309- TableName (update ),
310- strings .Join (set , ", " ),
311- ), len (columns ) + 1 // +1 because of WHERE id = :id
258+ return db .QueryBuilder (update ).Update ()
312259}
313260
314261// BuildUpsertStmt returns an upsert statement for the given struct.
315262func (db * DB ) BuildUpsertStmt (subject interface {}) (stmt string , placeholders int ) {
316- insertColumns := db .columnMap .Columns (subject )
317- table := TableName (subject )
318- var updateColumns []string
319-
320- if upserter , ok := subject .(Upserter ); ok {
321- updateColumns = db .columnMap .Columns (upserter .Upsert ())
322- } else {
323- updateColumns = insertColumns
324- }
325-
326- var clause , setFormat string
327- switch db .DriverName () {
328- case MySQL :
329- clause = "ON DUPLICATE KEY UPDATE"
330- setFormat = `"%[1]s" = VALUES("%[1]s")`
331- case PostgreSQL :
332- var constraint string
333- if constrainter , ok := subject .(PgsqlOnConflictConstrainter ); ok {
334- constraint = constrainter .PgsqlOnConflictConstraint ()
335- } else {
336- constraint = "pk_" + table
337- }
338-
339- clause = fmt .Sprintf ("ON CONFLICT ON CONSTRAINT %s DO UPDATE SET" , constraint )
340- setFormat = `"%[1]s" = EXCLUDED."%[1]s"`
341- }
342-
343- set := make ([]string , 0 , len (updateColumns ))
344-
345- for _ , col := range updateColumns {
346- set = append (set , fmt .Sprintf (setFormat , col ))
347- }
348-
349- return fmt .Sprintf (
350- `INSERT INTO "%s" ("%s") VALUES (%s) %s %s` ,
351- table ,
352- strings .Join (insertColumns , `", "` ),
353- fmt .Sprintf (":%s" , strings .Join (insertColumns , ",:" )),
354- clause ,
355- strings .Join (set , "," ),
356- ), len (insertColumns )
357- }
358-
359- // BuildWhere returns a WHERE clause with named placeholder conditions built from the specified struct
360- // combined with the AND operator.
361- func (db * DB ) BuildWhere (subject interface {}) (string , int ) {
362- columns := db .columnMap .Columns (subject )
363- where := make ([]string , 0 , len (columns ))
364- for _ , col := range columns {
365- where = append (where , fmt .Sprintf (`"%s" = :%s` , col , col ))
366- }
367-
368- return strings .Join (where , ` AND ` ), len (columns )
263+ return db .QueryBuilder (subject ).Upsert ()
369264}
370265
371266// OnSuccess is a callback for successful (bulk) DML operations.
0 commit comments