summaryrefslogtreecommitdiff
path: root/vendor/github.com/jackc/pgconn/stmtcache
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2021-08-25 15:34:33 +0200
committerLibravatar GitHub <noreply@github.com>2021-08-25 15:34:33 +0200
commit2dc9fc1626507bb54417fc4a1920b847cafb27a2 (patch)
tree4ddeac479b923db38090aac8bd9209f3646851c1 /vendor/github.com/jackc/pgconn/stmtcache
parentManually approves followers (#146) (diff)
downloadgotosocial-2dc9fc1626507bb54417fc4a1920b847cafb27a2.tar.xz
Pg to bun (#148)
* start moving to bun * changing more stuff * more * and yet more * tests passing * seems stable now * more big changes * small fix * little fixes
Diffstat (limited to 'vendor/github.com/jackc/pgconn/stmtcache')
-rw-r--r--vendor/github.com/jackc/pgconn/stmtcache/lru.go157
-rw-r--r--vendor/github.com/jackc/pgconn/stmtcache/stmtcache.go58
2 files changed, 215 insertions, 0 deletions
diff --git a/vendor/github.com/jackc/pgconn/stmtcache/lru.go b/vendor/github.com/jackc/pgconn/stmtcache/lru.go
new file mode 100644
index 000000000..f58f2ac34
--- /dev/null
+++ b/vendor/github.com/jackc/pgconn/stmtcache/lru.go
@@ -0,0 +1,157 @@
+package stmtcache
+
+import (
+ "container/list"
+ "context"
+ "fmt"
+ "sync/atomic"
+
+ "github.com/jackc/pgconn"
+)
+
+var lruCount uint64
+
+// LRU implements Cache with a Least Recently Used (LRU) cache.
+type LRU struct {
+ conn *pgconn.PgConn
+ mode int
+ cap int
+ prepareCount int
+ m map[string]*list.Element
+ l *list.List
+ psNamePrefix string
+ stmtsToClear []string
+}
+
+// NewLRU creates a new LRU. mode is either ModePrepare or ModeDescribe. cap is the maximum size of the cache.
+func NewLRU(conn *pgconn.PgConn, mode int, cap int) *LRU {
+ mustBeValidMode(mode)
+ mustBeValidCap(cap)
+
+ n := atomic.AddUint64(&lruCount, 1)
+
+ return &LRU{
+ conn: conn,
+ mode: mode,
+ cap: cap,
+ m: make(map[string]*list.Element),
+ l: list.New(),
+ psNamePrefix: fmt.Sprintf("lrupsc_%d", n),
+ }
+}
+
+// Get returns the prepared statement description for sql preparing or describing the sql on the server as needed.
+func (c *LRU) Get(ctx context.Context, sql string) (*pgconn.StatementDescription, error) {
+ // flush an outstanding bad statements
+ txStatus := c.conn.TxStatus()
+ if (txStatus == 'I' || txStatus == 'T') && len(c.stmtsToClear) > 0 {
+ for _, stmt := range c.stmtsToClear {
+ err := c.clearStmt(ctx, stmt)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ if el, ok := c.m[sql]; ok {
+ c.l.MoveToFront(el)
+ return el.Value.(*pgconn.StatementDescription), nil
+ }
+
+ if c.l.Len() == c.cap {
+ err := c.removeOldest(ctx)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ psd, err := c.prepare(ctx, sql)
+ if err != nil {
+ return nil, err
+ }
+
+ el := c.l.PushFront(psd)
+ c.m[sql] = el
+
+ return psd, nil
+}
+
+// Clear removes all entries in the cache. Any prepared statements will be deallocated from the PostgreSQL session.
+func (c *LRU) Clear(ctx context.Context) error {
+ for c.l.Len() > 0 {
+ err := c.removeOldest(ctx)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (c *LRU) StatementErrored(sql string, err error) {
+ pgErr, ok := err.(*pgconn.PgError)
+ if !ok {
+ return
+ }
+
+ isInvalidCachedPlanError := pgErr.Severity == "ERROR" &&
+ pgErr.Code == "0A000" &&
+ pgErr.Message == "cached plan must not change result type"
+ if isInvalidCachedPlanError {
+ c.stmtsToClear = append(c.stmtsToClear, sql)
+ }
+}
+
+func (c *LRU) clearStmt(ctx context.Context, sql string) error {
+ elem, inMap := c.m[sql]
+ if !inMap {
+ // The statement probably fell off the back of the list. In that case, we've
+ // ensured that it isn't in the cache, so we can declare victory.
+ return nil
+ }
+
+ c.l.Remove(elem)
+
+ psd := elem.Value.(*pgconn.StatementDescription)
+ delete(c.m, psd.SQL)
+ if c.mode == ModePrepare {
+ return c.conn.Exec(ctx, fmt.Sprintf("deallocate %s", psd.Name)).Close()
+ }
+ return nil
+}
+
+// Len returns the number of cached prepared statement descriptions.
+func (c *LRU) Len() int {
+ return c.l.Len()
+}
+
+// Cap returns the maximum number of cached prepared statement descriptions.
+func (c *LRU) Cap() int {
+ return c.cap
+}
+
+// Mode returns the mode of the cache (ModePrepare or ModeDescribe)
+func (c *LRU) Mode() int {
+ return c.mode
+}
+
+func (c *LRU) prepare(ctx context.Context, sql string) (*pgconn.StatementDescription, error) {
+ var name string
+ if c.mode == ModePrepare {
+ name = fmt.Sprintf("%s_%d", c.psNamePrefix, c.prepareCount)
+ c.prepareCount += 1
+ }
+
+ return c.conn.Prepare(ctx, name, sql, nil)
+}
+
+func (c *LRU) removeOldest(ctx context.Context) error {
+ oldest := c.l.Back()
+ c.l.Remove(oldest)
+ psd := oldest.Value.(*pgconn.StatementDescription)
+ delete(c.m, psd.SQL)
+ if c.mode == ModePrepare {
+ return c.conn.Exec(ctx, fmt.Sprintf("deallocate %s", psd.Name)).Close()
+ }
+ return nil
+}
diff --git a/vendor/github.com/jackc/pgconn/stmtcache/stmtcache.go b/vendor/github.com/jackc/pgconn/stmtcache/stmtcache.go
new file mode 100644
index 000000000..d083e1b4f
--- /dev/null
+++ b/vendor/github.com/jackc/pgconn/stmtcache/stmtcache.go
@@ -0,0 +1,58 @@
+// Package stmtcache is a cache that can be used to implement lazy prepared statements.
+package stmtcache
+
+import (
+ "context"
+
+ "github.com/jackc/pgconn"
+)
+
+const (
+ ModePrepare = iota // Cache should prepare named statements.
+ ModeDescribe // Cache should prepare the anonymous prepared statement to only fetch the description of the statement.
+)
+
+// Cache prepares and caches prepared statement descriptions.
+type Cache interface {
+ // Get returns the prepared statement description for sql preparing or describing the sql on the server as needed.
+ Get(ctx context.Context, sql string) (*pgconn.StatementDescription, error)
+
+ // Clear removes all entries in the cache. Any prepared statements will be deallocated from the PostgreSQL session.
+ Clear(ctx context.Context) error
+
+ // StatementErrored informs the cache that the given statement resulted in an error when it
+ // was last used against the database. In some cases, this will cause the cache to maer that
+ // statement as bad. The bad statement will instead be flushed during the next call to Get
+ // that occurs outside of a failed transaction.
+ StatementErrored(sql string, err error)
+
+ // Len returns the number of cached prepared statement descriptions.
+ Len() int
+
+ // Cap returns the maximum number of cached prepared statement descriptions.
+ Cap() int
+
+ // Mode returns the mode of the cache (ModePrepare or ModeDescribe)
+ Mode() int
+}
+
+// New returns the preferred cache implementation for mode and cap. mode is either ModePrepare or ModeDescribe. cap is
+// the maximum size of the cache.
+func New(conn *pgconn.PgConn, mode int, cap int) Cache {
+ mustBeValidMode(mode)
+ mustBeValidCap(cap)
+
+ return NewLRU(conn, mode, cap)
+}
+
+func mustBeValidMode(mode int) {
+ if mode != ModePrepare && mode != ModeDescribe {
+ panic("mode must be ModePrepare or ModeDescribe")
+ }
+}
+
+func mustBeValidCap(cap int) {
+ if cap < 1 {
+ panic("cache must have cap of >= 1")
+ }
+}