package postgres import ( "context" "fmt" "strings" "github.com/go-logr/logr" "github.com/jackc/pgconn" "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" ) type defaultClientV4 struct { ctx context.Context logger logr.Logger pool *pgxpool.Pool dialFunc pgconn.DialFunc } // assert interface implementation var _ Client = &defaultClient{} type defaultRowsV4 struct { pgx.Rows } // assert interface implementation var _ Rows = &defaultRowsV4{} func (r *defaultRowsV4) Close() error { r.Rows.Close() return nil } // TODO(help_wanted) - look into better solution that supporting multiple versions of pgx. Potential // option is https://github.com/GoogleCloudPlatform/cloud-sql-go-connector/compare/main...pgxv5 // NewWithV4Dialer creates a PostgreSQL client that can work with connectors. This should only be // used for CloudSQL until support for pgx/v5 is supported. func NewWithV4Dialer(ctx context.Context, logger logr.Logger, opts DSNOptions, dialFunc pgconn.DialFunc) (Client, error) { l := logger.WithValues("sql", "postgres") dsn := opts.ToString() config, err := pgxpool.ParseConfig(dsn) if err != nil { return nil, err } l.Info("using dsn config", "connString", redactPW(config.ConnString())) config.ConnConfig.DialFunc = dialFunc pool, err := pgxpool.ConnectConfig(ctx, config) if err != nil { return nil, fmt.Errorf("sql.Open: %v", err) } return &defaultClientV4{ ctx: ctx, logger: l, pool: pool, dialFunc: dialFunc, }, nil } // Connect is a no-op func (c *defaultClientV4) Connect() error { return nil } // Close closes all connections in the pool and rejects future Acquire calls. Blocks until all // connections are returned to pool and closed func (c *defaultClientV4) Close() error { c.pool.Close() return nil } // IsConnected checks to see if connection is in a healthy state func (c *defaultClientV4) IsConnected() bool { if c.pool == nil { return false } return c.pool.Ping(c.ctx) == nil } // Insert ... func (c *defaultClientV4) Insert(statement string, args ...interface{}) error { s := strings.TrimSpace(strings.ToLower(statement)) if !strings.HasPrefix(s, commandInsert) { return fmt.Errorf("invalid sql command") } if _, err := c.pool.Exec(c.ctx, statement, args...); err != nil { return err } return nil } // Query ... func (c *defaultClientV4) Query(statement string, args ...interface{}) (Rows, error) { s := strings.TrimSpace(strings.ToLower(statement)) if !strings.HasPrefix(s, commandSelect) { return nil, fmt.Errorf("invalid sql command") } rows, err := c.pool.Query(c.ctx, statement, args...) if err != nil { return nil, err } return &defaultRowsV4{rows}, nil }