grafana/pkg/services/sqlstore/migrator/postgres_dialect.go
Sofia Papagiannaki 7b70e7db2d
AuthProxy: Optimistic lock pattern for remote cache Set (#17485)
* Implementation of optimistic lock pattern

Try to insert the remote cache key and handle integrity error

* Remove transaction

Integrity error inside a transaction results in deadlock

* Remove check for existing remote cache key

Is no longer needed since integrity constrain violations are handled

* Add check for integrity constrain violation

Do not update the row if the insert statement fails
for other than an integrity constrain violation

* Handle failing inserts because of deadlocks

If the insert statement fails because of a deadlock
try to update the row

* Add utility function for returning SQL error code

Useful for debugging

* Add logging for failing expired cache key deletion

Do not shallow it completely

* Revert "Add utility function for returning SQL error code"

This reverts commit 8e0b82c79633e7d8bc350823cbbab2ac7a58c0a5.

* Better log for failing deletion of expired cache key

* Add some comments

* Remove check for existing cache key

Attempt to insert the key without checking if it's already there
and handle the error situations

* Do not propagate deadlocks created during update

Most probably somebody else is trying to insert/update
the key at the same time so it is safe enough to ignore it
2019-06-13 15:36:09 +02:00

158 lines
3.4 KiB
Go

package migrator
import (
"fmt"
"strconv"
"strings"
"github.com/go-xorm/xorm"
"github.com/lib/pq"
)
type Postgres struct {
BaseDialect
}
func NewPostgresDialect(engine *xorm.Engine) *Postgres {
d := Postgres{}
d.BaseDialect.dialect = &d
d.BaseDialect.engine = engine
d.BaseDialect.driverName = POSTGRES
return &d
}
func (db *Postgres) SupportEngine() bool {
return false
}
func (db *Postgres) Quote(name string) string {
return "\"" + name + "\""
}
func (b *Postgres) LikeStr() string {
return "ILIKE"
}
func (db *Postgres) AutoIncrStr() string {
return ""
}
func (db *Postgres) BooleanStr(value bool) string {
return strconv.FormatBool(value)
}
func (b *Postgres) Default(col *Column) string {
if col.Type == DB_Bool {
if col.Default == "0" {
return "FALSE"
}
return "TRUE"
}
return col.Default
}
func (db *Postgres) SqlType(c *Column) string {
var res string
switch t := c.Type; t {
case DB_TinyInt:
res = DB_SmallInt
return res
case DB_MediumInt, DB_Int, DB_Integer:
if c.IsAutoIncrement {
return DB_Serial
}
return DB_Integer
case DB_Serial, DB_BigSerial:
c.IsAutoIncrement = true
c.Nullable = false
res = t
case DB_Binary, DB_VarBinary:
return DB_Bytea
case DB_DateTime:
res = DB_TimeStamp
case DB_TimeStampz:
return "timestamp with time zone"
case DB_Float:
res = DB_Real
case DB_TinyText, DB_MediumText, DB_LongText:
res = DB_Text
case DB_NVarchar:
res = DB_Varchar
case DB_Uuid:
res = DB_Uuid
case DB_Blob, DB_TinyBlob, DB_MediumBlob, DB_LongBlob:
return DB_Bytea
case DB_Double:
return "DOUBLE PRECISION"
default:
if c.IsAutoIncrement {
return DB_Serial
}
res = t
}
var hasLen1 = (c.Length > 0)
var hasLen2 = (c.Length2 > 0)
if hasLen2 {
res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
} else if hasLen1 {
res += "(" + strconv.Itoa(c.Length) + ")"
}
return res
}
func (db *Postgres) IndexCheckSql(tableName, indexName string) (string, []interface{}) {
args := []interface{}{tableName, indexName}
sql := "SELECT 1 FROM " + db.Quote("pg_indexes") + " WHERE" + db.Quote("tablename") + "=? AND " + db.Quote("indexname") + "=?"
return sql, args
}
func (db *Postgres) DropIndexSql(tableName string, index *Index) string {
quote := db.Quote
idxName := index.XName(tableName)
return fmt.Sprintf("DROP INDEX %v CASCADE", quote(idxName))
}
func (db *Postgres) UpdateTableSql(tableName string, columns []*Column) string {
var statements = []string{}
for _, col := range columns {
statements = append(statements, "ALTER "+db.Quote(col.Name)+" TYPE "+db.SqlType(col))
}
return "ALTER TABLE " + db.Quote(tableName) + " " + strings.Join(statements, ", ") + ";"
}
func (db *Postgres) CleanDB() error {
sess := db.engine.NewSession()
defer sess.Close()
if _, err := sess.Exec("DROP SCHEMA public CASCADE;"); err != nil {
return fmt.Errorf("Failed to drop schema public")
}
if _, err := sess.Exec("CREATE SCHEMA public;"); err != nil {
return fmt.Errorf("Failed to create schema public")
}
return nil
}
func (db *Postgres) isThisError(err error, errcode string) bool {
if driverErr, ok := err.(*pq.Error); ok {
if string(driverErr.Code) == errcode {
return true
}
}
return false
}
func (db *Postgres) IsUniqueConstraintViolation(err error) bool {
return db.isThisError(err, "23505")
}
func (db *Postgres) IsDeadlock(err error) bool {
return db.isThisError(err, "40P01")
}