@@ -15,6 +15,8 @@ import (
1515 "github.com/pkg/errors"
1616)
1717
18+ // Config is a connection setting to MySQL.
19+ // Exists to generate a Golang connection DSN to MySQL
1820type Config struct {
1921 DSN string
2022 User string
@@ -24,6 +26,7 @@ type Config struct {
2426 Database string
2527}
2628
29+ // NewDefaultConfig returns the config for connecting to the local MySQL server
2730func NewDefaultConfig () * Config {
2831 return & Config {
2932 User : "root" ,
@@ -32,6 +35,7 @@ func NewDefaultConfig() *Config {
3235 }
3336}
3437
38+ //GetDSN returns a DSN dedicated to connecting to MySQL.
3539func (c * Config ) GetDSN () string {
3640 if c .DSN == "" {
3741 return fmt .Sprintf (
@@ -46,32 +50,43 @@ func (c *Config) GetDSN() string {
4650 return strings .TrimPrefix (c .DSN , "mysql://" )
4751}
4852
53+ //Executer queries the DB. There is no parallelism
4954type Executer struct {
5055 mu sync.Mutex
51- dsn string
5256 db * sql.DB
5357 lastExecuteTime time.Time
5458 selectHook func (query string , columns []string , rows [][]string )
5559 executeHook func (query string , rowsAffected int64 , lastInsertId int64 )
60+ isSelectFunc func (query string ) bool
61+ timeCheckQuery string
5662}
5763
64+ //New return Executer with config
5865func New (config * Config ) (* Executer , error ) {
5966 return Open (config .GetDSN ())
6067}
6168
69+ //Open with dsn
6270func Open (dsn string ) (* Executer , error ) {
6371 db , err := sql .Open ("mysql" , dsn )
6472 if err != nil {
6573 return nil , errors .Wrap (err , "mysql connect failed" )
6674 }
75+ return NewWithDB (db ), nil
76+ }
77+
78+ // NewWithDB returns Executer with *sql.DB
79+ // Note: Since it is made assuming MySQL, it may be inconvenient for other DBs.
80+ func NewWithDB (db * sql.DB ) * Executer {
6781 db .SetMaxIdleConns (1 )
6882 db .SetMaxOpenConns (1 )
6983 return & Executer {
70- dsn : dsn ,
71- db : db ,
72- }, nil
84+ db : db ,
85+ timeCheckQuery : "SELECT NOW()" ,
86+ }
7387}
7488
89+ // Close DB
7590func (e * Executer ) Close () error {
7691 e .mu .Lock ()
7792 defer e .mu .Unlock ()
@@ -81,10 +96,12 @@ func (e *Executer) Close() error {
8196 return e .db .Close ()
8297}
8398
99+ //Execute SQL
84100func (e * Executer ) Execute (queryReader io.Reader ) error {
85101 return e .ExecuteContext (context .Background (), queryReader )
86102}
87103
104+ //ExecuteContext SQL execute with context.Context
88105func (e * Executer ) ExecuteContext (ctx context.Context , queryReader io.Reader ) error {
89106 e .mu .Lock ()
90107 defer e .mu .Unlock ()
@@ -95,7 +112,7 @@ func (e *Executer) ExecuteContext(ctx context.Context, queryReader io.Reader) er
95112}
96113
97114func (e * Executer ) updateLastExecuteTime (ctx context.Context ) error {
98- row := e .db .QueryRowContext (ctx , "SELECT NOW()" )
115+ row := e .db .QueryRowContext (ctx , e . timeCheckQuery )
99116 if err := row .Err (); err != nil {
100117 return errors .Wrap (err , "get db time" )
101118 }
@@ -116,7 +133,15 @@ func (e *Executer) executeContext(ctx context.Context, queryReader io.Reader) er
116133 }
117134 if e .selectHook != nil {
118135 upperedQuery := strings .ToUpper (query )
119- if strings .HasPrefix (upperedQuery , "SELECT" ) || strings .HasPrefix (upperedQuery , "SHOW" ) {
136+ var isSelect bool
137+ if e .isSelectFunc == nil {
138+ if strings .HasPrefix (upperedQuery , "SELECT" ) || strings .HasPrefix (upperedQuery , "SHOW" ) || strings .HasPrefix (upperedQuery , `\` ) {
139+ isSelect = true
140+ }
141+ } else {
142+ isSelect = e .isSelectFunc (upperedQuery )
143+ }
144+ if isSelect {
120145 if err := e .queryContext (ctx , query ); err != nil {
121146 return errors .Wrap (err , "query rows failed" )
122147 }
@@ -175,18 +200,32 @@ func (e *Executer) queryContext(ctx context.Context, query string) error {
175200 return nil
176201}
177202
203+ //LastExecuteTime returns last execute time on DB
178204func (e * Executer ) LastExecuteTime () time.Time {
179205 return e .lastExecuteTime
180206}
181207
208+ //SetExecuteHook set non select query hook
182209func (e * Executer ) SetExecuteHook (hook func (query string , rowsAffected , lastInsertId int64 )) {
183210 e .executeHook = hook
184211}
185212
213+ //SetSelectHook set select query hook
186214func (e * Executer ) SetSelectHook (hook func (query string , columns []string , rows [][]string )) {
187215 e .selectHook = hook
188216}
189217
218+ //SetIsSelectFunc :Set the function to decide whether to execute in QueryContext
219+ func (e * Executer ) SetIsSelectFunc (f func (query string ) bool ) {
220+ e .isSelectFunc = f
221+ }
222+
223+ //SetTimeCheckQuery set time check query for non mysql db
224+ func (e * Executer ) SetTimeCheckQuery (query string ) {
225+ e .timeCheckQuery = query
226+ }
227+
228+ //SetTimeCheckQuery set select query hook, but result is table string
190229func (e * Executer ) SetTableSelectHook (hook func (query , table string )) {
191230 e .selectHook = func (query string , columns []string , rows [][]string ) {
192231 var buf strings.Builder
@@ -198,10 +237,12 @@ func (e *Executer) SetTableSelectHook(hook func(query, table string)) {
198237 }
199238}
200239
240+ // QueryScanner separate string by ; and delete newline
201241type QueryScanner struct {
202242 * bufio.Scanner
203243}
204244
245+ //NewQueryScanner returns QueryScanner
205246func NewQueryScanner (queryReader io.Reader ) * QueryScanner {
206247 scanner := bufio .NewScanner (queryReader )
207248 onSplit := func (data []byte , atEOF bool ) (advance int , token []byte , err error ) {
@@ -224,6 +265,7 @@ func NewQueryScanner(queryReader io.Reader) *QueryScanner {
224265 }
225266}
226267
268+ //Query return
227269func (s * QueryScanner ) Query () string {
228270 return strings .Trim (strings .NewReplacer (
229271 "\r \n " , " " ,
0 commit comments