@@ -12,14 +12,17 @@ import (
12
12
13
13
"github.com/jackc/pgx/v5"
14
14
"github.com/jackc/pgx/v5/pgconn"
15
+ "github.com/jackc/pgx/v5/pgtype"
15
16
"github.com/jackc/puddle/v2"
16
17
)
17
18
18
- var defaultMaxConns = int32 (4 )
19
- var defaultMinConns = int32 (0 )
20
- var defaultMaxConnLifetime = time .Hour
21
- var defaultMaxConnIdleTime = time .Minute * 30
22
- var defaultHealthCheckPeriod = time .Minute
19
+ var (
20
+ defaultMaxConns = int32 (4 )
21
+ defaultMinConns = int32 (0 )
22
+ defaultMaxConnLifetime = time .Hour
23
+ defaultMaxConnIdleTime = time .Minute * 30
24
+ defaultHealthCheckPeriod = time .Minute
25
+ )
23
26
24
27
type connResource struct {
25
28
conn * pgx.Conn
@@ -100,6 +103,11 @@ type Pool struct {
100
103
101
104
closeOnce sync.Once
102
105
closeChan chan struct {}
106
+
107
+ autoLoadTypeNames []string
108
+ reuseTypeMap bool
109
+ autoLoadMutex * sync.Mutex
110
+ autoLoadTypes []* pgtype.Type
103
111
}
104
112
105
113
// Config is the configuration struct for creating a pool. It must be created by [ParseConfig] and then it can be
@@ -147,6 +155,23 @@ type Config struct {
147
155
// HealthCheckPeriod is the duration between checks of the health of idle connections.
148
156
HealthCheckPeriod time.Duration
149
157
158
+ // AutoLoadTypes is a list of user-defined types which should automatically be loaded
159
+ // as each new connection is created. This will also load any derived types, directly
160
+ // or indirectly required to handle these types.
161
+ // This is equivalent to manually calling pgx.LoadTypes()
162
+ // followed by conn.TypeMap().RegisterTypes()
163
+ // This will occur after the AfterConnect hook. If manual type
164
+ // registrating is performed during AfterConnect, the autoloading
165
+ // will be aware of those registrations.
166
+ AutoLoadTypes []string
167
+
168
+ // ReuseTypeMaps, if enabled, will reuse the typemap information being used by AutoLoadTypes.
169
+ // This removes the need to query the database each time a new connection is created;
170
+ // only RegisterDerivedTypes will need to be called for each new connection.
171
+ // In some situations, where OID mapping can differ between pg servers in the pool, perhaps due
172
+ // to certain replication strategies, this should be left disabled.
173
+ ReuseTypeMaps bool
174
+
150
175
createdByParseConfig bool // Used to enforce created by ParseConfig rule.
151
176
}
152
177
@@ -185,6 +210,8 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
185
210
config : config ,
186
211
beforeConnect : config .BeforeConnect ,
187
212
afterConnect : config .AfterConnect ,
213
+ autoLoadTypeNames : config .AutoLoadTypes ,
214
+ reuseTypeMap : config .ReuseTypeMaps ,
188
215
beforeAcquire : config .BeforeAcquire ,
189
216
afterRelease : config .AfterRelease ,
190
217
beforeClose : config .BeforeClose ,
@@ -196,6 +223,7 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
196
223
healthCheckPeriod : config .HealthCheckPeriod ,
197
224
healthCheckChan : make (chan struct {}, 1 ),
198
225
closeChan : make (chan struct {}),
226
+ autoLoadMutex : new (sync.Mutex ),
199
227
}
200
228
201
229
if t , ok := config .ConnConfig .Tracer .(AcquireTracer ); ok {
@@ -237,6 +265,15 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
237
265
}
238
266
}
239
267
268
+ if p .autoLoadTypeNames != nil && len (p .autoLoadTypeNames ) > 0 {
269
+ types , err := p .loadTypes (ctx , conn , p .autoLoadTypeNames )
270
+ if err != nil {
271
+ conn .Close (ctx )
272
+ return nil , err
273
+ }
274
+ conn .TypeMap ().RegisterTypes (types )
275
+ }
276
+
240
277
jitterSecs := rand .Float64 () * config .MaxConnLifetimeJitter .Seconds ()
241
278
maxAgeTime := time .Now ().Add (config .MaxConnLifetime ).Add (time .Duration (jitterSecs ) * time .Second )
242
279
@@ -388,6 +425,27 @@ func (p *Pool) Close() {
388
425
})
389
426
}
390
427
428
+ // loadTypes is used internally to autoload the custom types for a connection,
429
+ // potentially reusing previously-loaded typemap information.
430
+ func (p * Pool ) loadTypes (ctx context.Context , conn * pgx.Conn , typeNames []string ) ([]* pgtype.Type , error ) {
431
+ if p .reuseTypeMap {
432
+ p .autoLoadMutex .Lock ()
433
+ defer p .autoLoadMutex .Unlock ()
434
+ if p .autoLoadTypes != nil {
435
+ return p .autoLoadTypes , nil
436
+ }
437
+ types , err := pgx .LoadTypes (ctx , conn , typeNames )
438
+ if err != nil {
439
+ return nil , err
440
+ }
441
+ p .autoLoadTypes = types
442
+ return types , err
443
+ }
444
+ // Avoid needing to acquire the mutex and allow connections to initialise in parallel
445
+ // if we have chosen to not reuse the type mapping
446
+ return pgx .LoadTypes (ctx , conn , typeNames )
447
+ }
448
+
391
449
func (p * Pool ) isExpired (res * puddle.Resource [* connResource ]) bool {
392
450
return time .Now ().After (res .Value ().maxAgeTime )
393
451
}
@@ -482,7 +540,6 @@ func (p *Pool) checkMinConns() error {
482
540
func (p * Pool ) createIdleResources (parentCtx context.Context , targetResources int ) error {
483
541
ctx , cancel := context .WithCancel (parentCtx )
484
542
defer cancel ()
485
-
486
543
errs := make (chan error , targetResources )
487
544
488
545
for i := 0 ; i < targetResources ; i ++ {
@@ -495,7 +552,6 @@ func (p *Pool) createIdleResources(parentCtx context.Context, targetResources in
495
552
errs <- err
496
553
}()
497
554
}
498
-
499
555
var firstError error
500
556
for i := 0 ; i < targetResources ; i ++ {
501
557
err := <- errs
0 commit comments