@@ -27,7 +27,6 @@ import (
2727
2828 _ "time/tzdata"
2929
30- chproto "github.com/ClickHouse/ch-go/proto"
3130 "github.com/ClickHouse/clickhouse-go/v2/contributors"
3231 "github.com/ClickHouse/clickhouse-go/v2/lib/column"
3332 "github.com/ClickHouse/clickhouse-go/v2/lib/driver"
@@ -83,19 +82,43 @@ func Open(opt *Options) (driver.Conn, error) {
8382 opt = & Options {}
8483 }
8584 o := opt .setDefaults ()
85+
8686 conn := & clickhouse {
8787 opt : o ,
88- idle : make (chan * connect , o .MaxIdleConns ),
88+ idle : make (chan nativeTransport , o .MaxIdleConns ),
8989 open : make (chan struct {}, o .MaxOpenConns ),
9090 exit : make (chan struct {}),
9191 }
9292 go conn .startAutoCloseIdleConnections ()
9393 return conn , nil
9494}
9595
96+ // nativeTransport represents an implementation (TCP or HTTP) that can be pooled by the main clickhouse struct.
97+ // Implementations are not expected to be thread safe, which is why we provide acquire/release functions.
98+ type nativeTransport interface {
99+ serverVersion () (* ServerVersion , error )
100+ query (ctx context.Context , release nativeTransportRelease , query string , args ... any ) (* rows , error )
101+ queryRow (ctx context.Context , release nativeTransportRelease , query string , args ... any ) * row
102+ prepareBatch (ctx context.Context , release nativeTransportRelease , acquire nativeTransportAcquire , query string , opts driver.PrepareBatchOptions ) (driver.Batch , error )
103+ exec (ctx context.Context , query string , args ... any ) error
104+ asyncInsert (ctx context.Context , query string , wait bool , args ... any ) error
105+ ping (context.Context ) error
106+ isBad () bool
107+ connID () int
108+ connectedAtTime () time.Time
109+ isReleased () bool
110+ setReleased (released bool )
111+ debugf (format string , v ... any )
112+ // freeBuffer is called if Options.FreeBufOnConnRelease is set
113+ freeBuffer ()
114+ close () error
115+ }
116+ type nativeTransportAcquire func (context.Context ) (nativeTransport , error )
117+ type nativeTransportRelease func (nativeTransport , error )
118+
96119type clickhouse struct {
97120 opt * Options
98- idle chan * connect
121+ idle chan nativeTransport
99122 open chan struct {}
100123 exit chan struct {}
101124 connID int64
@@ -118,27 +141,27 @@ func (ch *clickhouse) ServerVersion() (*driver.ServerVersion, error) {
118141 if err != nil {
119142 return nil , err
120143 }
121- ch .release (conn , nil )
122- return & conn .server , nil
144+ defer ch .release (conn , nil )
145+ return conn .serverVersion ()
123146}
124147
125148func (ch * clickhouse ) Query (ctx context.Context , query string , args ... any ) (rows driver.Rows , err error ) {
126149 conn , err := ch .acquire (ctx )
127150 if err != nil {
128151 return nil , err
129152 }
130- conn .debugf ("[acquired] connection [%d]" , conn .id )
153+ conn .debugf ("[acquired] connection [%d]" , conn .connID () )
131154 return conn .query (ctx , ch .release , query , args ... )
132155}
133156
134- func (ch * clickhouse ) QueryRow (ctx context.Context , query string , args ... any ) ( rows driver.Row ) {
157+ func (ch * clickhouse ) QueryRow (ctx context.Context , query string , args ... any ) driver.Row {
135158 conn , err := ch .acquire (ctx )
136159 if err != nil {
137160 return & row {
138161 err : err ,
139162 }
140163 }
141- conn .debugf ("[acquired] connection [%d]" , conn .id )
164+ conn .debugf ("[acquired] connection [%d]" , conn .connID () )
142165 return conn .queryRow (ctx , ch .release , query , args ... )
143166}
144167
@@ -147,7 +170,7 @@ func (ch *clickhouse) Exec(ctx context.Context, query string, args ...any) error
147170 if err != nil {
148171 return err
149172 }
150- conn .debugf ("[acquired] connection [%d]" , conn .id )
173+ conn .debugf ("[acquired] connection [%d]" , conn .connID () )
151174
152175 if err := conn .exec (ctx , query , args ... ); err != nil {
153176 ch .release (conn , err )
@@ -162,7 +185,7 @@ func (ch *clickhouse) PrepareBatch(ctx context.Context, query string, opts ...dr
162185 if err != nil {
163186 return nil , err
164187 }
165- batch , err := conn .prepareBatch (ctx , query , getPrepareBatchOptions (opts ... ), ch . release , ch . acquire )
188+ batch , err := conn .prepareBatch (ctx , ch . release , ch . acquire , query , getPrepareBatchOptions (opts ... ))
166189 if err != nil {
167190 return nil , err
168191 }
@@ -214,11 +237,18 @@ func (ch *clickhouse) Stats() driver.Stats {
214237 }
215238}
216239
217- func (ch * clickhouse ) dial (ctx context.Context ) (conn * connect , err error ) {
240+ func (ch * clickhouse ) dial (ctx context.Context ) (conn nativeTransport , err error ) {
218241 connID := int (atomic .AddInt64 (& ch .connID , 1 ))
219242
220243 dialFunc := func (ctx context.Context , addr string , opt * Options ) (DialResult , error ) {
221- conn , err := dial (ctx , addr , connID , opt )
244+ var conn nativeTransport
245+ var err error
246+ switch opt .Protocol {
247+ case HTTP :
248+ conn , err = dialHttp (ctx , addr , connID , opt )
249+ default :
250+ conn , err = dial (ctx , addr , connID , opt )
251+ }
222252
223253 return DialResult {conn }, err
224254 }
@@ -260,7 +290,7 @@ func DefaultDialStrategy(ctx context.Context, connID int, opt *Options, dial Dia
260290 return r , err
261291}
262292
263- func (ch * clickhouse ) acquire (ctx context.Context ) (conn * connect , err error ) {
293+ func (ch * clickhouse ) acquire (ctx context.Context ) (conn nativeTransport , err error ) {
264294 timer := time .NewTimer (ch .opt .DialTimeout )
265295 defer timer .Stop ()
266296 select {
@@ -293,7 +323,7 @@ func (ch *clickhouse) acquire(ctx context.Context) (conn *connect, err error) {
293323 return nil , err
294324 }
295325 }
296- conn .released = false
326+ conn .setReleased ( false )
297327 return conn , nil
298328 default :
299329 }
@@ -326,7 +356,7 @@ func (ch *clickhouse) closeIdleExpired() {
326356 for {
327357 select {
328358 case conn := <- ch .idle :
329- if conn .connectedAt .Before (cutoff ) {
359+ if conn .connectedAtTime () .Before (cutoff ) {
330360 conn .close ()
331361 } else {
332362 select {
@@ -342,24 +372,23 @@ func (ch *clickhouse) closeIdleExpired() {
342372 }
343373}
344374
345- func (ch * clickhouse ) release (conn * connect , err error ) {
346- if conn .released {
375+ func (ch * clickhouse ) release (conn nativeTransport , err error ) {
376+ if conn .isReleased () {
347377 return
348378 }
349- conn .released = true
350- conn .debugf ("[released] connection [%d]" , conn .id )
379+ conn .setReleased ( true )
380+ conn .debugf ("[released] connection [%d]" , conn .connID () )
351381
352382 select {
353383 case <- ch .open :
354384 default :
355385 }
356- if err != nil || time .Since (conn .connectedAt ) >= ch .opt .ConnMaxLifetime {
386+ if err != nil || time .Since (conn .connectedAtTime () ) >= ch .opt .ConnMaxLifetime {
357387 conn .close ()
358388 return
359389 }
360390 if ch .opt .FreeBufOnConnRelease {
361- conn .buffer = new (chproto.Buffer )
362- conn .compressor .Data = nil
391+ conn .freeBuffer ()
363392 }
364393 select {
365394 case ch .idle <- conn :
@@ -374,7 +403,12 @@ func (ch *clickhouse) Close() error {
374403 case c := <- ch .idle :
375404 c .close ()
376405 default :
377- ch .exit <- struct {}{}
406+ // In rare cases, close may be called multiple times, don't block
407+ //TODO: add proper close flag to indicate this pool is unusable after Close
408+ select {
409+ case ch .exit <- struct {}{}:
410+ default :
411+ }
378412 return nil
379413 }
380414 }
0 commit comments