Skip to content

Commit edcdced

Browse files
committed
Updated dependencies and added conn url parser
1 parent cf82d3c commit edcdced

File tree

5 files changed

+121
-34
lines changed

5 files changed

+121
-34
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,4 @@ func main() {
7474

7575
## LICENSE
7676

77-
See `LICENSE` file for details.
77+
See [LICENSE](/LICENSE) file for details.

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ module github.com/randlabs/go-postgres
22

33
go 1.19
44

5-
require github.com/jackc/pgx/v5 v5.5.1
5+
require github.com/jackc/pgx/v5 v5.5.4
66

77
require (
88
github.com/jackc/pgpassfile v1.0.0 // indirect
99
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
1010
github.com/jackc/puddle/v2 v2.2.1 // indirect
11-
golang.org/x/crypto v0.17.0 // indirect
11+
golang.org/x/crypto v0.21.0 // indirect
1212
golang.org/x/sync v0.5.0 // indirect
1313
golang.org/x/text v0.14.0 // indirect
1414
)

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
44
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
55
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
66
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
7-
github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI=
8-
github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
7+
github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8=
8+
github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
99
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
1010
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
1111
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -14,8 +14,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
1414
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
1515
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
1616
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
17-
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
18-
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
17+
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
18+
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
1919
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
2020
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
2121
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=

postgres.go

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"net/url"
8+
"strconv"
9+
"strings"
710
"sync"
811
"time"
912

@@ -58,16 +61,19 @@ const (
5861

5962
// New creates a new postgresql database driver.
6063
func New(ctx context.Context, opts Options) (*Database, error) {
61-
var sslMode string
62-
63-
// Create database object
64-
db := Database{}
65-
db.err.mutex = sync.Mutex{}
66-
67-
// Setup basic configuration options
64+
// Validate options
65+
if len(opts.Host) == 0 {
66+
return nil, errors.New("invalid host")
67+
}
68+
if len(opts.User) == 0 {
69+
return nil, errors.New("invalid user name")
70+
}
71+
if len(opts.Name) == 0 {
72+
return nil, errors.New("invalid database name")
73+
}
74+
sslMode := "disable"
6875
switch opts.SSLMode {
6976
case SSLModeDisable:
70-
sslMode = "disable"
7177
case SSLModeAllow:
7278
sslMode = "prefer"
7379
case SSLModeRequired:
@@ -76,6 +82,10 @@ func New(ctx context.Context, opts Options) (*Database, error) {
7682
return nil, errors.New("invalid SSL mode")
7783
}
7884

85+
// Create database object
86+
db := Database{}
87+
db.err.mutex = sync.Mutex{}
88+
7989
connString := fmt.Sprintf(
8090
"host='%s' port=%d user='%s' password='%s' dbname='%s' sslmode=%s",
8191
encodeDSN(opts.Host), opts.Port, encodeDSN(opts.User), encodeDSN(opts.Password), encodeDSN(opts.Name),
@@ -110,6 +120,82 @@ func New(ctx context.Context, opts Options) (*Database, error) {
110120
return &db, nil
111121
}
112122

123+
// NewFromURL creates a new postgresql database driver from an URL
124+
func NewFromURL(ctx context.Context, rawUrl string) (*Database, error) {
125+
opts := Options{}
126+
127+
u, err := url.ParseRequestURI(rawUrl)
128+
if err != nil {
129+
return nil, errors.New("invalid url provided")
130+
}
131+
132+
// Check schema
133+
if u.Scheme != "pg" && u.Scheme != "postgres" && u.Scheme != "postgresql" {
134+
return nil, errors.New("invalid url schema")
135+
}
136+
137+
// Check host name and port
138+
opts.Host = u.Hostname()
139+
if len(opts.Host) == 0 {
140+
return nil, errors.New("invalid host")
141+
}
142+
s := u.Port()
143+
if len(s) == 0 {
144+
opts.Port = 5432
145+
} else {
146+
val, err2 := strconv.Atoi(s)
147+
if err2 != nil || val < 1 || val > 65535 {
148+
return nil, errors.New("invalid port")
149+
}
150+
opts.Port = uint16(val)
151+
}
152+
153+
// Check user and password
154+
if u.User == nil {
155+
return nil, errors.New("invalid user name")
156+
}
157+
opts.User = u.User.Username()
158+
if len(opts.User) == 0 {
159+
return nil, errors.New("invalid user name")
160+
}
161+
162+
// Check database name
163+
if len(u.Path) < 1 || (!strings.HasPrefix(u.Path, "/")) || strings.Index(u.Path[1:], "/") >= 0 {
164+
return nil, errors.New("invalid database name")
165+
}
166+
opts.Name = u.Path[1:]
167+
168+
// Check ssl mode
169+
opts.SSLMode = SSLModeDisable
170+
switch u.Query().Get("sslmode") {
171+
case "allow":
172+
opts.SSLMode = SSLModeAllow
173+
174+
case "required":
175+
opts.SSLMode = SSLModeRequired
176+
177+
case "disabled":
178+
fallthrough
179+
case "":
180+
181+
default:
182+
return nil, errors.New("invalid SSL mode")
183+
}
184+
185+
// Check max connections count
186+
s = u.Query().Get("maxconn")
187+
if len(s) > 0 {
188+
val, err2 := strconv.Atoi(s)
189+
if err2 != nil || val < 0 {
190+
return nil, errors.New("invalid max connections count")
191+
}
192+
opts.MaxConns = int32(val)
193+
}
194+
195+
// Create
196+
return New(ctx, opts)
197+
}
198+
113199
// Close shutdown the connection pool
114200
func (db *Database) Close() {
115201
if db.pool != nil {

postgres_test.go

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ type TestJSON struct {
6666
}
6767

6868
var (
69+
pgUrl string
6970
pgHost string
7071
pgPort uint
7172
pgUsername string
@@ -82,10 +83,11 @@ var (
8283
// -----------------------------------------------------------------------------
8384

8485
func init() {
86+
flag.StringVar(&pgUrl, "url", "", "Specifies the Postgres URL.")
8587
flag.StringVar(&pgHost, "host", "127.0.0.1", "Specifies the Postgres server host. (Defaults to '127.0.0.1')")
8688
flag.UintVar(&pgPort, "port", 5432, "Specifies the Postgres server port. (Defaults to 5432)")
8789
flag.StringVar(&pgUsername, "user", "postgres", "Specifies the user name. (Defaults to 'postgres')")
88-
flag.StringVar(&pgPassword, "password", "", "Specifies the user passwonrd.")
90+
flag.StringVar(&pgPassword, "password", "", "Specifies the user password.")
8991
flag.StringVar(&pgDatabaseName, "db", "", "Specifies the database name.")
9092

9193
testJSON = TestJSON{
@@ -102,56 +104,55 @@ func init() {
102104
// -----------------------------------------------------------------------------
103105

104106
func TestPostgres(t *testing.T) {
107+
var db *postgres.Database
108+
var err error
109+
105110
// Parse and check command-line parameters
106111
flag.Parse()
107112
checkSettings(t)
108113

114+
ctx := context.Background()
115+
109116
// Create database driver
110-
db, err := postgres.New(context.Background(), postgres.Options{
111-
Host: pgHost,
112-
Port: uint16(pgPort),
113-
User: pgUsername,
114-
Password: pgPassword,
115-
Name: pgDatabaseName,
116-
})
117+
if len(pgUrl) > 0 {
118+
db, err = postgres.NewFromURL(ctx, pgUrl)
119+
} else {
120+
db, err = postgres.New(ctx, postgres.Options{
121+
Host: pgHost,
122+
Port: uint16(pgPort),
123+
User: pgUsername,
124+
Password: pgPassword,
125+
Name: pgDatabaseName,
126+
})
127+
}
117128
if err != nil {
118129
t.Fatalf("%v", err.Error())
119130
}
120-
// We comment the next defer line because we want to do a clean database pool shutdown on errors and
121-
// calling fatal exits the process.
122-
// defer db.Close()
123-
124-
ctx := context.Background()
131+
defer db.Close()
125132

126133
t.Log("Creating test table")
127134
err = createTestTable(ctx, db)
128135
if err != nil {
129-
db.Close()
130136
t.Fatalf("%v", err.Error())
131137
}
132138

133139
t.Log("Inserting test data")
134140
err = insertTestData(ctx, db)
135141
if err != nil {
136-
db.Close()
137142
t.Fatalf("%v", err.Error())
138143
}
139144

140145
t.Log("Reading test data")
141146
err = readTestData(ctx, db)
142147
if err != nil {
143-
db.Close()
144148
t.Fatalf("%v", err.Error())
145149
}
146150

147151
t.Log("Reading test data (multi-row)")
148152
err = readMultiTestData(ctx, db)
149153
if err != nil {
150-
db.Close()
151154
t.Fatalf("%v", err.Error())
152155
}
153-
154-
db.Close()
155156
}
156157

157158
// -----------------------------------------------------------------------------

0 commit comments

Comments
 (0)