66 "fmt"
77 "reflect"
88 "strings"
9+ "sync"
910
1011 "github.com/bitechdev/ResolveSpec/pkg/common"
1112 "github.com/bitechdev/ResolveSpec/pkg/logger"
@@ -17,6 +18,8 @@ import (
1718// This provides a lightweight PostgreSQL adapter without ORM overhead
1819type PgSQLAdapter struct {
1920 db * sql.DB
21+ dbMu sync.RWMutex
22+ dbFactory func () (* sql.DB , error )
2023 driverName string
2124}
2225
@@ -31,14 +34,44 @@ func NewPgSQLAdapter(db *sql.DB, driverName ...string) *PgSQLAdapter {
3134 return & PgSQLAdapter {db : db , driverName : name }
3235}
3336
37+ // WithDBFactory configures a factory used to reopen the database connection if it is closed.
38+ func (p * PgSQLAdapter ) WithDBFactory (factory func () (* sql.DB , error )) * PgSQLAdapter {
39+ p .dbFactory = factory
40+ return p
41+ }
42+
43+ func (p * PgSQLAdapter ) getDB () * sql.DB {
44+ p .dbMu .RLock ()
45+ defer p .dbMu .RUnlock ()
46+ return p .db
47+ }
48+
49+ func (p * PgSQLAdapter ) reconnectDB () error {
50+ if p .dbFactory == nil {
51+ return fmt .Errorf ("no db factory configured for reconnect" )
52+ }
53+ newDB , err := p .dbFactory ()
54+ if err != nil {
55+ return err
56+ }
57+ p .dbMu .Lock ()
58+ p .db = newDB
59+ p .dbMu .Unlock ()
60+ return nil
61+ }
62+
63+ func isDBClosed (err error ) bool {
64+ return err != nil && strings .Contains (err .Error (), "sql: database is closed" )
65+ }
66+
3467// EnableQueryDebug enables query debugging for development
3568func (p * PgSQLAdapter ) EnableQueryDebug () {
3669 logger .Info ("PgSQL query debug mode - logging enabled via logger" )
3770}
3871
3972func (p * PgSQLAdapter ) NewSelect () common.SelectQuery {
4073 return & PgSQLSelectQuery {
41- db : p .db ,
74+ db : p .getDB () ,
4275 driverName : p .driverName ,
4376 columns : []string {"*" },
4477 args : make ([]interface {}, 0 ),
@@ -47,15 +80,15 @@ func (p *PgSQLAdapter) NewSelect() common.SelectQuery {
4780
4881func (p * PgSQLAdapter ) NewInsert () common.InsertQuery {
4982 return & PgSQLInsertQuery {
50- db : p .db ,
83+ db : p .getDB () ,
5184 driverName : p .driverName ,
5285 values : make (map [string ]interface {}),
5386 }
5487}
5588
5689func (p * PgSQLAdapter ) NewUpdate () common.UpdateQuery {
5790 return & PgSQLUpdateQuery {
58- db : p .db ,
91+ db : p .getDB () ,
5992 driverName : p .driverName ,
6093 sets : make (map [string ]interface {}),
6194 args : make ([]interface {}, 0 ),
@@ -65,7 +98,7 @@ func (p *PgSQLAdapter) NewUpdate() common.UpdateQuery {
6598
6699func (p * PgSQLAdapter ) NewDelete () common.DeleteQuery {
67100 return & PgSQLDeleteQuery {
68- db : p .db ,
101+ db : p .getDB () ,
69102 driverName : p .driverName ,
70103 args : make ([]interface {}, 0 ),
71104 whereClauses : make ([]string , 0 ),
@@ -79,7 +112,14 @@ func (p *PgSQLAdapter) Exec(ctx context.Context, query string, args ...interface
79112 }
80113 }()
81114 logger .Debug ("PgSQL Exec: %s [args: %v]" , query , args )
82- result , err := p .db .ExecContext (ctx , query , args ... )
115+ var result sql.Result
116+ run := func () error { var e error ; result , e = p .getDB ().ExecContext (ctx , query , args ... ); return e }
117+ err = run ()
118+ if isDBClosed (err ) {
119+ if reconnErr := p .reconnectDB (); reconnErr == nil {
120+ err = run ()
121+ }
122+ }
83123 if err != nil {
84124 logger .Error ("PgSQL Exec failed: %v" , err )
85125 return nil , err
@@ -94,7 +134,14 @@ func (p *PgSQLAdapter) Query(ctx context.Context, dest interface{}, query string
94134 }
95135 }()
96136 logger .Debug ("PgSQL Query: %s [args: %v]" , query , args )
97- rows , err := p .db .QueryContext (ctx , query , args ... )
137+ var rows * sql.Rows
138+ run := func () error { var e error ; rows , e = p .getDB ().QueryContext (ctx , query , args ... ); return e }
139+ err = run ()
140+ if isDBClosed (err ) {
141+ if reconnErr := p .reconnectDB (); reconnErr == nil {
142+ err = run ()
143+ }
144+ }
98145 if err != nil {
99146 logger .Error ("PgSQL Query failed: %v" , err )
100147 return err
@@ -105,7 +152,7 @@ func (p *PgSQLAdapter) Query(ctx context.Context, dest interface{}, query string
105152}
106153
107154func (p * PgSQLAdapter ) BeginTx (ctx context.Context ) (common.Database , error ) {
108- tx , err := p .db .BeginTx (ctx , nil )
155+ tx , err := p .getDB () .BeginTx (ctx , nil )
109156 if err != nil {
110157 return nil , err
111158 }
@@ -127,7 +174,7 @@ func (p *PgSQLAdapter) RunInTransaction(ctx context.Context, fn func(common.Data
127174 }
128175 }()
129176
130- tx , err := p .db .BeginTx (ctx , nil )
177+ tx , err := p .getDB () .BeginTx (ctx , nil )
131178 if err != nil {
132179 return err
133180 }
0 commit comments