From dd9a30caedd3025e9e0b1e5984aaa21b1931ac4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Sun, 12 Mar 2023 13:06:50 +0300 Subject: [PATCH] update --- postgres/README.md | 61 ++--------- postgres/config.go | 79 +++++--------- postgres/go.mod | 19 ++-- postgres/go.sum | 82 +++------------ postgres/postgres.go | 86 +++++---------- postgres/postgres_test.go | 21 ++-- postgres/v2/README.md | 87 --------------- postgres/v2/config.go | 110 ------------------- postgres/v2/postgres.go | 199 ----------------------------------- postgres/v2/postgres_test.go | 180 ------------------------------- 10 files changed, 96 insertions(+), 828 deletions(-) delete mode 100644 postgres/v2/README.md delete mode 100644 postgres/v2/config.go delete mode 100644 postgres/v2/postgres.go delete mode 100644 postgres/v2/postgres_test.go diff --git a/postgres/README.md b/postgres/README.md index 60ea5101..fe6b3d2c 100644 --- a/postgres/README.md +++ b/postgres/README.md @@ -1,6 +1,6 @@ # Postgres -A Postgres storage driver using [lib/pq](https://github.com/lib/pq). +A Postgres storage driver using [jackc/pgx](https://github.com/jackc/pgx). ### Table of Contents - [Signatures](#signatures) @@ -17,7 +17,7 @@ func (s *Storage) Set(key string, val []byte, exp time.Duration) error func (s *Storage) Delete(key string) error func (s *Storage) Reset() error func (s *Storage) Close() error -func (s *Storage) Conn() *sql.DB +func (s *Storage) Conn() *pgxpool.Pool ``` ### Installation Postgres is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: @@ -42,20 +42,10 @@ store := postgres.New() // Initialize custom config store := postgres.New(postgres.Config{ - Host: "127.0.0.1", - Port: 5432, - Database: "fiber", + Db: dbPool, Table: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, - SslMode: "disable", -}) - -// Initialize custom config using connection string -store := postgres.New(postgres.Config{ - ConnectionURI: "postgresql://user:password@localhost:5432/fiber" - Reset: false, - GCInterval: 10 * time.Second, }) ``` @@ -63,35 +53,10 @@ store := postgres.New(postgres.Config{ ```go // Config defines the config for storage. type Config struct { - // Connection string to use for DB. Will override all other authentication values if used - // - // Optional. Default is "" - ConnectionURI string - - // Host name where the DB is hosted - // - // Optional. Default is "127.0.0.1" - Host string - - // Port where the DB is listening on - // - // Optional. Default is 5432 - Port int - - // Server username - // - // Optional. Default is "" - Username string - - // Server password - // - // Optional. Default is "" - Password string - - // Database name - // - // Optional. Default is "fiber" - Database string + // Db pgxpool.Pool object + // + // Required + Db pgxpool.Pool // Table name // @@ -107,24 +72,16 @@ type Config struct { // // Optional. Default is 10 * time.Second GCInterval time.Duration - - // The SSL mode for the connection - // - // Optional. Default is "disable" - SslMode string } ``` ### Default Config ```go +// ConfigDefault is the default config var ConfigDefault = Config{ - ConnectionURI: "", - Host: "127.0.0.1", - Port: 5432, - Database: "fiber", + Db: pgxpool.Pool{}, Table: "fiber_storage", Reset: false, GCInterval: 10 * time.Second, - SslMode: "disable", } ``` diff --git a/postgres/config.go b/postgres/config.go index c9354e4a..64ed4269 100644 --- a/postgres/config.go +++ b/postgres/config.go @@ -1,11 +1,19 @@ package postgres import ( + "fmt" "time" + + "github.com/jackc/pgx/v5/pgxpool" ) // Config defines the config for storage. type Config struct { + // DB pgxpool.Pool object will override connection uri and other connection fields + // + // Optional. Default is nil + DB *pgxpool.Pool + // Connection string to use for DB. Will override all other authentication values if used // // Optional. Default is "" @@ -41,11 +49,6 @@ type Config struct { // Optional. Default is "fiber_storage" Table string - // The SSL mode for the connection - // - // Optional. Default is "disable" - SslMode string - // Reset clears any existing keys in existing Table // // Optional. Default is false @@ -55,57 +58,24 @@ type Config struct { // // Optional. Default is 10 * time.Second GCInterval time.Duration - - //////////////////////////////////// - // Adaptor related config options // - //////////////////////////////////// - - // Maximum wait for connection, in seconds. Zero or - // n < 0 means wait indefinitely. - timeout time.Duration - - // The maximum number of connections in the idle connection pool. - // - // If MaxOpenConns is greater than 0 but less than the new MaxIdleConns, - // then the new MaxIdleConns will be reduced to match the MaxOpenConns limit. - // - // If n <= 0, no idle connections are retained. - // - // The default max idle connections is currently 2. This may change in - // a future release. - maxIdleConns int - - // The maximum number of open connections to the database. - // - // If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than - // MaxIdleConns, then MaxIdleConns will be reduced to match the new - // MaxOpenConns limit. - // - // If n <= 0, then there is no limit on the number of open connections. - // The default is 0 (unlimited). - maxOpenConns int - - // The maximum amount of time a connection may be reused. - // - // Expired connections may be closed lazily before reuse. - // - // If d <= 0, connections are reused forever. - connMaxLifetime time.Duration } // ConfigDefault is the default config var ConfigDefault = Config{ - ConnectionURI: "", - Host: "127.0.0.1", - Port: 5432, - Database: "fiber", - Table: "fiber_storage", - SslMode: "disable", - Reset: false, - GCInterval: 10 * time.Second, - maxOpenConns: 100, - maxIdleConns: 100, - connMaxLifetime: 1 * time.Second, + ConnectionURI: "", + Host: "127.0.0.1", + Port: 5432, + Database: "fiber", + Table: "fiber_storage", + Reset: false, + GCInterval: 10 * time.Second, +} + +func (c Config) dsn() string { + if c.ConnectionURI != "" { + return c.ConnectionURI + } + return fmt.Sprintf("postgres://%s:%s@%s:%d/%s", c.Username, c.Password, c.Host, c.Port, c.Database) } // Helper function to set default values @@ -114,7 +84,6 @@ func configDefault(config ...Config) Config { if len(config) < 1 { return ConfigDefault } - // Override default config cfg := config[0] @@ -131,8 +100,8 @@ func configDefault(config ...Config) Config { if cfg.Table == "" { cfg.Table = ConfigDefault.Table } - if cfg.SslMode == "" { - cfg.SslMode = ConfigDefault.SslMode + if cfg.Table == "" { + cfg.Table = ConfigDefault.Table } if int(cfg.GCInterval.Seconds()) <= 0 { cfg.GCInterval = ConfigDefault.GCInterval diff --git a/postgres/go.mod b/postgres/go.mod index 79cce9af..cb053a99 100644 --- a/postgres/go.mod +++ b/postgres/go.mod @@ -1,12 +1,17 @@ -module github.com/gofiber/storage/postgres +module github.com/gofiber/storage/postgres/v2 -go 1.16 +go 1.19 require ( github.com/gofiber/utils v1.0.1 - github.com/jackc/pgx/v5 v5.0.4 - github.com/lib/pq v1.10.7 - github.com/rogpeppe/go-internal v1.8.1 // indirect - github.com/stretchr/testify v1.8.1 // indirect - golang.org/x/crypto v0.1.0 // indirect + github.com/jackc/pgx/v5 v5.3.1 +) + +require ( + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/puddle/v2 v2.2.0 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/text v0.8.0 // indirect ) diff --git a/postgres/go.sum b/postgres/go.sum index 80381a67..390bc2e3 100644 --- a/postgres/go.sum +++ b/postgres/go.sum @@ -1,83 +1,27 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gofiber/utils v1.0.1 h1:knct4cXwBipWQqFrOy1Pv6UcgPM+EXo9jDgc66V1Qio= github.com/gofiber/utils v1.0.1/go.mod h1:pacRFtghAE3UoknMOUiXh2Io/nLWSUHtQCi/3QASsOc= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgx/v5 v5.0.4 h1:r5O6y84qHX/z/HZV40JBdx2obsHz7/uRj5b+CcYEdeY= -github.com/jackc/pgx/v5 v5.0.4/go.mod h1:U0ynklHtgg43fue9Ly30w3OCSTDPlXjig9ghrNGaguQ= -github.com/jackc/puddle/v2 v2.0.0 h1:Kwk/AlLigcnZsDssc3Zun1dk1tAtQNPaBBxBHWn0Mjc= -github.com/jackc/puddle/v2 v2.0.0/go.mod h1:itE7ZJY8xnoo0JqJEpSMprN0f+NQkMCuEV/N9j8h0oc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU= +github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8= +github.com/jackc/puddle/v2 v2.2.0 h1:RdcDk92EJBuBS55nQMMYFXTxwstHug4jkhT5pq8VxPk= +github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= -github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/postgres/postgres.go b/postgres/postgres.go index b08d8576..348dfefd 100644 --- a/postgres/postgres.go +++ b/postgres/postgres.go @@ -1,19 +1,20 @@ package postgres import ( - "database/sql" + "context" "errors" "fmt" - "net/url" + "os" "strings" "time" - _ "github.com/lib/pq" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" ) // Storage interface that is implemented by storage providers type Storage struct { - db *sql.DB + db *pgxpool.Pool gcInterval time.Duration done chan struct{} @@ -45,62 +46,33 @@ func New(config ...Config) *Storage { // Set default config cfg := configDefault(config...) - // Create data source name - var dsn string - if cfg.ConnectionURI != "" { - dsn = cfg.ConnectionURI - } else { - dsn = "postgresql://" - if cfg.Username != "" { - dsn += url.QueryEscape(cfg.Username) + // Select db connection + var err error + db := cfg.DB + if db == nil { + db, err = pgxpool.New(context.Background(), cfg.dsn()) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to create connection pool: %v\n", err) } - if cfg.Password != "" { - dsn += ":" + url.QueryEscape(cfg.Password) - } - if cfg.Username != "" || cfg.Password != "" { - dsn += "@" - } - // unix socket host path - if strings.HasPrefix(cfg.Host, "/") { - dsn += fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) - } else { - dsn += fmt.Sprintf("%s:%d", url.QueryEscape(cfg.Host), cfg.Port) - } - dsn += fmt.Sprintf("/%s?connect_timeout=%d&sslmode=%s", - url.QueryEscape(cfg.Database), - int64(cfg.timeout.Seconds()), - cfg.SslMode) } - // Create db - db, err := sql.Open("postgres", dsn) - if err != nil { - panic(err) - } - - // Set database options - db.SetMaxOpenConns(cfg.maxOpenConns) - db.SetMaxIdleConns(cfg.maxIdleConns) - db.SetConnMaxLifetime(cfg.connMaxLifetime) - // Ping database - if err := db.Ping(); err != nil { + if err := db.Ping(context.Background()); err != nil { panic(err) } // Drop table if set to true if cfg.Reset { - if _, err = db.Exec(fmt.Sprintf(dropQuery, cfg.Table)); err != nil { - _ = db.Close() + if _, err := db.Exec(context.Background(), fmt.Sprintf(dropQuery, cfg.Table)); err != nil { + db.Close() panic(err) } } // Init database queries for _, query := range initQuery { - if _, err := db.Exec(fmt.Sprintf(query, cfg.Table)); err != nil { - _ = db.Close() - + if _, err := db.Exec(context.Background(), fmt.Sprintf(query, cfg.Table)); err != nil { + db.Close() panic(err) } } @@ -125,21 +97,19 @@ func New(config ...Config) *Storage { return store } -var noRows = errors.New("sql: no rows in result set") - // Get value by key func (s *Storage) Get(key string) ([]byte, error) { if len(key) <= 0 { return nil, nil } - row := s.db.QueryRow(s.sqlSelect, key) + row := s.db.QueryRow(context.Background(), s.sqlSelect, key) // Add db response to data var ( - data = []byte{} + data []byte exp int64 = 0 ) if err := row.Scan(&data, &exp); err != nil { - if err == sql.ErrNoRows { + if errors.Is(err, pgx.ErrNoRows) { return nil, nil } return nil, err @@ -163,7 +133,7 @@ func (s *Storage) Set(key string, val []byte, exp time.Duration) error { if exp != 0 { expSeconds = time.Now().Add(exp).Unix() } - _, err := s.db.Exec(s.sqlInsert, key, val, expSeconds, val, expSeconds) + _, err := s.db.Exec(context.Background(), s.sqlInsert, key, val, expSeconds, val, expSeconds) return err } @@ -173,24 +143,26 @@ func (s *Storage) Delete(key string) error { if len(key) <= 0 { return nil } - _, err := s.db.Exec(s.sqlDelete, key) + _, err := s.db.Exec(context.Background(), s.sqlDelete, key) return err } // Reset all entries, including unexpired func (s *Storage) Reset() error { - _, err := s.db.Exec(s.sqlReset) + _, err := s.db.Exec(context.Background(), s.sqlReset) return err } // Close the database func (s *Storage) Close() error { s.done <- struct{}{} - return s.db.Close() + s.db.Stat() + s.db.Close() + return nil } // Return database client -func (s *Storage) Conn() *sql.DB { +func (s *Storage) Conn() *pgxpool.Pool { return s.db } @@ -210,13 +182,13 @@ func (s *Storage) gcTicker() { // gc deletes all expired entries func (s *Storage) gc(t time.Time) { - _, _ = s.db.Exec(s.sqlGC, t.Unix()) + _, _ = s.db.Exec(context.Background(), s.sqlGC, t.Unix()) } func (s *Storage) checkSchema(tableName string) { var data []byte - row := s.db.QueryRow(fmt.Sprintf(checkSchemaQuery, tableName)) + row := s.db.QueryRow(context.Background(), fmt.Sprintf(checkSchemaQuery, tableName)) if err := row.Scan(&data); err != nil { panic(err) } diff --git a/postgres/postgres_test.go b/postgres/postgres_test.go index c2af76bd..fddf0223 100644 --- a/postgres/postgres_test.go +++ b/postgres/postgres_test.go @@ -1,12 +1,13 @@ package postgres import ( - "database/sql" + "context" "os" "testing" "time" "github.com/gofiber/utils" + "github.com/jackc/pgx/v5" ) var testStore = New(Config{ @@ -133,9 +134,9 @@ func Test_Postgres_GC(t *testing.T) { utils.AssertEqual(t, nil, err) testStore.gc(time.Now()) - row := testStore.db.QueryRow(testStore.sqlSelect, "john") + row := testStore.db.QueryRow(context.Background(), testStore.sqlSelect, "john") err = row.Scan(nil, nil) - utils.AssertEqual(t, sql.ErrNoRows, err) + utils.AssertEqual(t, pgx.ErrNoRows, err) // This key should not expire err = testStore.Set("john", testVal, 0) @@ -166,18 +167,14 @@ func Test_SslRequiredMode(t *testing.T) { } }() _ = New(Config{ - Database: "fiber", - Username: "username", - Password: "password", - Reset: true, - SslMode: "require", + Reset: true, }) } -func Test_Postgres_Close(t *testing.T) { - utils.AssertEqual(t, nil, testStore.Close()) -} - func Test_Postgres_Conn(t *testing.T) { utils.AssertEqual(t, true, testStore.Conn() != nil) } + +func Test_Postgres_Close(t *testing.T) { + utils.AssertEqual(t, nil, testStore.Close()) +} diff --git a/postgres/v2/README.md b/postgres/v2/README.md deleted file mode 100644 index fe6b3d2c..00000000 --- a/postgres/v2/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# Postgres - -A Postgres storage driver using [jackc/pgx](https://github.com/jackc/pgx). - -### Table of Contents -- [Signatures](#signatures) -- [Installation](#installation) -- [Examples](#examples) -- [Config](#config) -- [Default Config](#default-config) - -### Signatures -```go -func New(config ...Config) Storage -func (s *Storage) Get(key string) ([]byte, error) -func (s *Storage) Set(key string, val []byte, exp time.Duration) error -func (s *Storage) Delete(key string) error -func (s *Storage) Reset() error -func (s *Storage) Close() error -func (s *Storage) Conn() *pgxpool.Pool -``` -### Installation -Postgres is tested on the 2 last [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: -```bash -go mod init github.com// -``` -And then install the postgres implementation: -```bash -go get github.com/gofiber/storage/postgres -``` - -### Examples -Import the storage package. -```go -import "github.com/gofiber/storage/postgres" -``` - -You can use the following possibilities to create a storage: -```go -// Initialize default config -store := postgres.New() - -// Initialize custom config -store := postgres.New(postgres.Config{ - Db: dbPool, - Table: "fiber_storage", - Reset: false, - GCInterval: 10 * time.Second, -}) -``` - -### Config -```go -// Config defines the config for storage. -type Config struct { - // Db pgxpool.Pool object - // - // Required - Db pgxpool.Pool - - // Table name - // - // Optional. Default is "fiber_storage" - Table string - - // Reset clears any existing keys in existing Table - // - // Optional. Default is false - Reset bool - - // Time before deleting expired keys - // - // Optional. Default is 10 * time.Second - GCInterval time.Duration -} -``` - -### Default Config -```go -// ConfigDefault is the default config -var ConfigDefault = Config{ - Db: pgxpool.Pool{}, - Table: "fiber_storage", - Reset: false, - GCInterval: 10 * time.Second, -} -``` diff --git a/postgres/v2/config.go b/postgres/v2/config.go deleted file mode 100644 index 82a36f37..00000000 --- a/postgres/v2/config.go +++ /dev/null @@ -1,110 +0,0 @@ -package v2 - -import ( - "fmt" - "time" - - "github.com/jackc/pgx/v5/pgxpool" -) - -// Config defines the config for storage. -type Config struct { - // DB pgxpool.Pool object will override connection uri and other connection fields - // - // Optional. Default is nil - DB *pgxpool.Pool - - // Connection string to use for DB. Will override all other authentication values if used - // - // Optional. Default is "" - ConnectionURI string - - // Host name where the DB is hosted - // - // Optional. Default is "127.0.0.1" - Host string - - // Port where the DB is listening on - // - // Optional. Default is 5432 - Port int - - // Server username - // - // Optional. Default is "" - Username string - - // Server password - // - // Optional. Default is "" - Password string - - // Database name - // - // Optional. Default is "fiber" - Database string - - // Table name - // - // Optional. Default is "fiber_storage" - Table string - - // Reset clears any existing keys in existing Table - // - // Optional. Default is false - Reset bool - - // Time before deleting expired keys - // - // Optional. Default is 10 * time.Second - GCInterval time.Duration -} - -// ConfigDefault is the default config -var ConfigDefault = Config{ - ConnectionURI: "", - Host: "127.0.0.1", - Port: 5432, - Database: "fiber", - Table: "fiber_storage", - Reset: false, - GCInterval: 10 * time.Second, -} - -func (c Config) dsn() string { - if c.ConnectionURI != "" { - return c.ConnectionURI - } - return fmt.Sprintf("postgres://%s:%s@%s:%d/%s", c.Username, c.Password, c.Host, c.Port, c.Database) -} - -// Helper function to set default values -func configDefault(config ...Config) Config { - // Return default config if nothing provided - if len(config) < 1 { - return ConfigDefault - } - // Override default config - cfg := config[0] - - // Set default values - if cfg.Host == "" { - cfg.Host = ConfigDefault.Host - } - if cfg.Port <= 0 { - cfg.Port = ConfigDefault.Port - } - if cfg.Database == "" { - cfg.Database = ConfigDefault.Database - } - if cfg.Table == "" { - cfg.Table = ConfigDefault.Table - } - if cfg.Table == "" { - cfg.Table = ConfigDefault.Table - } - if int(cfg.GCInterval.Seconds()) <= 0 { - cfg.GCInterval = ConfigDefault.GCInterval - } - return cfg -} diff --git a/postgres/v2/postgres.go b/postgres/v2/postgres.go deleted file mode 100644 index f8e0ca0c..00000000 --- a/postgres/v2/postgres.go +++ /dev/null @@ -1,199 +0,0 @@ -package v2 - -import ( - "context" - "errors" - "fmt" - "os" - "strings" - "time" - - "github.com/jackc/pgx/v5" - "github.com/jackc/pgx/v5/pgxpool" -) - -// Storage interface that is implemented by storage providers -type Storage struct { - db *pgxpool.Pool - gcInterval time.Duration - done chan struct{} - - sqlSelect string - sqlInsert string - sqlDelete string - sqlReset string - sqlGC string -} - -var ( - checkSchemaMsg = "The `v` row has an incorrect data type. " + - "It should be BYTEA but is instead %s. This will cause encoding-related panics if the DB is not migrated (see https://github.com/gofiber/storage/blob/main/MIGRATE.md)." - dropQuery = `DROP TABLE IF EXISTS %s;` - initQuery = []string{ - `CREATE TABLE IF NOT EXISTS %s ( - k VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT '', - v BYTEA NOT NULL, - e BIGINT NOT NULL DEFAULT '0' - );`, - `CREATE INDEX IF NOT EXISTS e ON %s (e);`, - } - checkSchemaQuery = `SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS - WHERE table_name = '%s' AND COLUMN_NAME = 'v';` -) - -// New creates a new storage -func New(config ...Config) *Storage { - // Set default config - cfg := configDefault(config...) - - // Select db connection - var err error - db := cfg.DB - if db == nil { - db, err = pgxpool.New(context.Background(), cfg.dsn()) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to create connection pool: %v\n", err) - } - } - - // Ping database - if err := db.Ping(context.Background()); err != nil { - panic(err) - } - - // Drop table if set to true - if cfg.Reset { - if _, err := db.Exec(context.Background(), fmt.Sprintf(dropQuery, cfg.Table)); err != nil { - db.Close() - panic(err) - } - } - - // Init database queries - for _, query := range initQuery { - if _, err := db.Exec(context.Background(), fmt.Sprintf(query, cfg.Table)); err != nil { - db.Close() - panic(err) - } - } - - // Create storage - store := &Storage{ - db: db, - gcInterval: cfg.GCInterval, - done: make(chan struct{}), - sqlSelect: fmt.Sprintf(`SELECT v, e FROM %s WHERE k=$1;`, cfg.Table), - sqlInsert: fmt.Sprintf("INSERT INTO %s (k, v, e) VALUES ($1, $2, $3) ON CONFLICT (k) DO UPDATE SET v = $4, e = $5", cfg.Table), - sqlDelete: fmt.Sprintf("DELETE FROM %s WHERE k=$1", cfg.Table), - sqlReset: fmt.Sprintf("TRUNCATE TABLE %s;", cfg.Table), - sqlGC: fmt.Sprintf("DELETE FROM %s WHERE e <= $1 AND e != 0", cfg.Table), - } - - store.checkSchema(cfg.Table) - - // Start garbage collector - go store.gcTicker() - - return store -} - -// Get value by key -func (s *Storage) Get(key string) ([]byte, error) { - if len(key) <= 0 { - return nil, nil - } - row := s.db.QueryRow(context.Background(), s.sqlSelect, key) - // Add db response to data - var ( - data []byte - exp int64 = 0 - ) - if err := row.Scan(&data, &exp); err != nil { - if errors.Is(err, pgx.ErrNoRows) { - return nil, nil - } - return nil, err - } - - // If the expiration time has already passed, then return nil - if exp != 0 && exp <= time.Now().Unix() { - return nil, nil - } - - return data, nil -} - -// Set key with value -func (s *Storage) Set(key string, val []byte, exp time.Duration) error { - // Ain't Nobody Got Time For That - if len(key) <= 0 || len(val) <= 0 { - return nil - } - var expSeconds int64 - if exp != 0 { - expSeconds = time.Now().Add(exp).Unix() - } - _, err := s.db.Exec(context.Background(), s.sqlInsert, key, val, expSeconds, val, expSeconds) - return err -} - -// Delete entry by key -func (s *Storage) Delete(key string) error { - // Ain't Nobody Got Time For That - if len(key) <= 0 { - return nil - } - _, err := s.db.Exec(context.Background(), s.sqlDelete, key) - return err -} - -// Reset all entries, including unexpired -func (s *Storage) Reset() error { - _, err := s.db.Exec(context.Background(), s.sqlReset) - return err -} - -// Close the database -func (s *Storage) Close() error { - s.done <- struct{}{} - s.db.Stat() - s.db.Close() - return nil -} - -// Return database client -func (s *Storage) Conn() *pgxpool.Pool { - return s.db -} - -// gcTicker starts the gc ticker -func (s *Storage) gcTicker() { - ticker := time.NewTicker(s.gcInterval) - defer ticker.Stop() - for { - select { - case <-s.done: - return - case t := <-ticker.C: - s.gc(t) - } - } -} - -// gc deletes all expired entries -func (s *Storage) gc(t time.Time) { - _, _ = s.db.Exec(context.Background(), s.sqlGC, t.Unix()) -} - -func (s *Storage) checkSchema(tableName string) { - var data []byte - - row := s.db.QueryRow(context.Background(), fmt.Sprintf(checkSchemaQuery, tableName)) - if err := row.Scan(&data); err != nil { - panic(err) - } - - if strings.ToLower(string(data)) != "bytea" { - fmt.Printf(checkSchemaMsg, string(data)) - } -} diff --git a/postgres/v2/postgres_test.go b/postgres/v2/postgres_test.go deleted file mode 100644 index bfe3697a..00000000 --- a/postgres/v2/postgres_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package v2 - -import ( - "context" - "os" - "testing" - "time" - - "github.com/gofiber/utils" - "github.com/jackc/pgx/v5" -) - -var testStore = New(Config{ - Database: os.Getenv("POSTGRES_DATABASE"), - Username: os.Getenv("POSTGRES_USERNAME"), - Password: os.Getenv("POSTGRES_PASSWORD"), - Reset: true, -}) - -func Test_Postgres_Set(t *testing.T) { - var ( - key = "john" - val = []byte("doe") - ) - - err := testStore.Set(key, val, 0) - utils.AssertEqual(t, nil, err) -} - -func Test_Postgres_Set_Override(t *testing.T) { - var ( - key = "john" - val = []byte("doe") - ) - - err := testStore.Set(key, val, 0) - utils.AssertEqual(t, nil, err) - - err = testStore.Set(key, val, 0) - utils.AssertEqual(t, nil, err) -} - -func Test_Postgres_Get(t *testing.T) { - var ( - key = "john" - val = []byte("doe") - ) - - err := testStore.Set(key, val, 0) - utils.AssertEqual(t, nil, err) - - result, err := testStore.Get(key) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, val, result) -} - -func Test_Postgres_Set_Expiration(t *testing.T) { - var ( - key = "john" - val = []byte("doe") - exp = 1 * time.Second - ) - - err := testStore.Set(key, val, exp) - utils.AssertEqual(t, nil, err) - - time.Sleep(1100 * time.Millisecond) -} - -func Test_Postgres_Get_Expired(t *testing.T) { - var ( - key = "john" - ) - - result, err := testStore.Get(key) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, len(result) == 0) -} - -func Test_Postgres_Get_NotExist(t *testing.T) { - - result, err := testStore.Get("notexist") - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, len(result) == 0) -} - -func Test_Postgres_Delete(t *testing.T) { - var ( - key = "john" - val = []byte("doe") - ) - - err := testStore.Set(key, val, 0) - utils.AssertEqual(t, nil, err) - - err = testStore.Delete(key) - utils.AssertEqual(t, nil, err) - - result, err := testStore.Get(key) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, len(result) == 0) -} - -func Test_Postgres_Reset(t *testing.T) { - var ( - val = []byte("doe") - ) - - err := testStore.Set("john1", val, 0) - utils.AssertEqual(t, nil, err) - - err = testStore.Set("john2", val, 0) - utils.AssertEqual(t, nil, err) - - err = testStore.Reset() - utils.AssertEqual(t, nil, err) - - result, err := testStore.Get("john1") - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, len(result) == 0) - - result, err = testStore.Get("john2") - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, len(result) == 0) -} - -func Test_Postgres_GC(t *testing.T) { - var ( - testVal = []byte("doe") - ) - - // This key should expire - err := testStore.Set("john", testVal, time.Nanosecond) - utils.AssertEqual(t, nil, err) - - testStore.gc(time.Now()) - row := testStore.db.QueryRow(context.Background(), testStore.sqlSelect, "john") - err = row.Scan(nil, nil) - utils.AssertEqual(t, pgx.ErrNoRows, err) - - // This key should not expire - err = testStore.Set("john", testVal, 0) - utils.AssertEqual(t, nil, err) - - testStore.gc(time.Now()) - val, err := testStore.Get("john") - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, testVal, val) - -} - -func Test_Postgres_Non_UTF8(t *testing.T) { - val := []byte("0xF5") - - err := testStore.Set("0xF6", val, 0) - utils.AssertEqual(t, nil, err) - - result, err := testStore.Get("0xF6") - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, val, result) -} - -func Test_SslRequiredMode(t *testing.T) { - defer func() { - if recover() == nil { - utils.AssertEqual(t, true, nil, "Connection was established with a `require`") - } - }() - _ = New(Config{ - Reset: true, - }) -} - -func Test_Postgres_Conn(t *testing.T) { - utils.AssertEqual(t, true, testStore.Conn() != nil) -} - -func Test_Postgres_Close(t *testing.T) { - utils.AssertEqual(t, nil, testStore.Close()) -}