@@ -12,14 +12,17 @@ import (
1212
1313 "github.com/jackc/pgx/v5"
1414 "github.com/jackc/pgx/v5/pgconn"
15+ "github.com/jackc/pgx/v5/pgtype"
1516 "github.com/jackc/puddle/v2"
1617)
1718
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+ )
2326
2427type connResource struct {
2528 conn * pgx.Conn
@@ -100,6 +103,11 @@ type Pool struct {
100103
101104 closeOnce sync.Once
102105 closeChan chan struct {}
106+
107+ autoLoadTypes []string
108+ reuseTypeMap bool
109+ autoLoadMutex * sync.Mutex
110+ autoLoadTypeInfos []* pgtype.DerivedTypeInfo
103111}
104112
105113// Config is the configuration struct for creating a pool. It must be created by [ParseConfig] and then it can be
@@ -147,6 +155,15 @@ type Config struct {
147155 // HealthCheckPeriod is the duration between checks of the health of idle connections.
148156 HealthCheckPeriod time.Duration
149157
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 related types, directly
160+ // or indirectly required to handle these types.
161+ AutoLoadTypes []string
162+
163+ // ReuseTypeMaps, if enabled, will reuse the typemap information being used by AutoLoadTypes.
164+ // This removes the need to query the database each time a new connection is created.
165+ ReuseTypeMaps bool
166+
150167 createdByParseConfig bool // Used to enforce created by ParseConfig rule.
151168}
152169
@@ -185,6 +202,8 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
185202 config : config ,
186203 beforeConnect : config .BeforeConnect ,
187204 afterConnect : config .AfterConnect ,
205+ autoLoadTypes : config .AutoLoadTypes ,
206+ reuseTypeMap : config .ReuseTypeMaps ,
188207 beforeAcquire : config .BeforeAcquire ,
189208 afterRelease : config .AfterRelease ,
190209 beforeClose : config .BeforeClose ,
@@ -196,6 +215,7 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
196215 healthCheckPeriod : config .HealthCheckPeriod ,
197216 healthCheckChan : make (chan struct {}, 1 ),
198217 closeChan : make (chan struct {}),
218+ autoLoadMutex : new (sync.Mutex ),
199219 }
200220
201221 if t , ok := config .ConnConfig .Tracer .(AcquireTracer ); ok {
@@ -237,6 +257,19 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
237257 }
238258 }
239259
260+ if len (p .autoLoadTypes ) > 0 {
261+ types , err := p .loadTypes (ctx , conn , p .autoLoadTypes )
262+ if err != nil {
263+ conn .Close (ctx )
264+ panic (err )
265+ }
266+ if err = conn .TypeMap ().RegisterDerivedTypes (types ); err != nil {
267+ conn .Close (ctx )
268+
269+ panic (err )
270+ }
271+ }
272+
240273 jitterSecs := rand .Float64 () * config .MaxConnLifetimeJitter .Seconds ()
241274 maxAgeTime := time .Now ().Add (config .MaxConnLifetime ).Add (time .Duration (jitterSecs ) * time .Second )
242275
@@ -388,6 +421,27 @@ func (p *Pool) Close() {
388421 })
389422}
390423
424+ // loadTypes is used internally to autoload the custom types for a connection,
425+ // potentially reusing previously-loaded typemap information.
426+ func (p * Pool ) loadTypes (ctx context.Context , conn * pgx.Conn , typeNames []string ) ([]* pgtype.DerivedTypeInfo , error ) {
427+ if p .reuseTypeMap {
428+ p .autoLoadMutex .Lock ()
429+ defer p .autoLoadMutex .Unlock ()
430+ if p .autoLoadTypeInfos != nil {
431+ return p .autoLoadTypeInfos , nil
432+ }
433+ types , err := pgx .LoadDerivedTypes (ctx , conn , typeNames )
434+ if err != nil {
435+ return nil , err
436+ }
437+ p .autoLoadTypeInfos = types
438+ return types , err
439+ }
440+ // Avoid needing to acquire the mutex and allow connections to initialise in parallel
441+ // if we have chosen to not reuse the type mapping
442+ return pgx .LoadDerivedTypes (ctx , conn , typeNames )
443+ }
444+
391445func (p * Pool ) isExpired (res * puddle.Resource [* connResource ]) bool {
392446 return time .Now ().After (res .Value ().maxAgeTime )
393447}
@@ -482,7 +536,6 @@ func (p *Pool) checkMinConns() error {
482536func (p * Pool ) createIdleResources (parentCtx context.Context , targetResources int ) error {
483537 ctx , cancel := context .WithCancel (parentCtx )
484538 defer cancel ()
485-
486539 errs := make (chan error , targetResources )
487540
488541 for i := 0 ; i < targetResources ; i ++ {
@@ -495,7 +548,6 @@ func (p *Pool) createIdleResources(parentCtx context.Context, targetResources in
495548 errs <- err
496549 }()
497550 }
498-
499551 var firstError error
500552 for i := 0 ; i < targetResources ; i ++ {
501553 err := <- errs
0 commit comments