2020-12-01 20:06:49 +01:00
|
|
|
package sql
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
2024-02-26 12:03:38 +01:00
|
|
|
"embed"
|
2020-12-01 20:06:49 +01:00
|
|
|
"fmt"
|
2024-02-26 12:03:38 +01:00
|
|
|
"reflect"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2024-03-06 13:20:48 +01:00
|
|
|
"github.com/Masterminds/squirrel"
|
2020-12-01 20:06:49 +01:00
|
|
|
"github.com/ansible-semaphore/semaphore/db"
|
|
|
|
"github.com/ansible-semaphore/semaphore/util"
|
|
|
|
"github.com/go-gorp/gorp/v3"
|
|
|
|
_ "github.com/go-sql-driver/mysql" // imports mysql driver
|
2021-08-24 17:20:34 +02:00
|
|
|
_ "github.com/lib/pq"
|
2024-02-26 12:03:38 +01:00
|
|
|
log "github.com/sirupsen/logrus"
|
2020-12-01 20:06:49 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type SqlDb struct {
|
|
|
|
sql *gorp.DbMap
|
|
|
|
}
|
|
|
|
|
|
|
|
var initialSQL = `
|
|
|
|
create table ` + "`migrations`" + ` (
|
|
|
|
` + "`version`" + ` varchar(255) not null primary key,
|
|
|
|
` + "`upgraded_date`" + ` datetime null,
|
|
|
|
` + "`notes`" + ` text null
|
|
|
|
);
|
|
|
|
`
|
2024-02-26 12:03:38 +01:00
|
|
|
|
|
|
|
//go:embed migrations/*.sql
|
|
|
|
var dbAssets embed.FS
|
2020-12-01 20:06:49 +01:00
|
|
|
|
2020-12-08 08:23:33 +01:00
|
|
|
func containsStr(arr []string, str string) bool {
|
|
|
|
for _, a := range arr {
|
|
|
|
if a == str {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-12-01 20:06:49 +01:00
|
|
|
func handleRollbackError(err error) {
|
|
|
|
if err != nil {
|
|
|
|
log.Warn(err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2021-08-24 17:20:34 +02:00
|
|
|
identifierQuoteRE = regexp.MustCompile("`")
|
2020-12-01 20:06:49 +01:00
|
|
|
)
|
|
|
|
|
2020-12-03 14:51:15 +01:00
|
|
|
// validateMutationResult checks the success of the update query
|
|
|
|
func validateMutationResult(res sql.Result, err error) error {
|
|
|
|
if err != nil {
|
2021-09-16 23:35:44 +02:00
|
|
|
if strings.Contains(err.Error(), "foreign key") {
|
|
|
|
err = db.ErrInvalidOperation
|
|
|
|
}
|
2020-12-03 14:51:15 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-08-24 17:20:34 +02:00
|
|
|
func (d *SqlDb) prepareQueryWithDialect(query string, dialect gorp.Dialect) string {
|
|
|
|
switch dialect.(type) {
|
|
|
|
case gorp.PostgresDialect:
|
|
|
|
var queryBuilder strings.Builder
|
|
|
|
argNum := 1
|
2021-08-24 19:56:40 +02:00
|
|
|
for _, r := range query {
|
2021-08-24 17:20:34 +02:00
|
|
|
switch r {
|
|
|
|
case '?':
|
|
|
|
queryBuilder.WriteString("$" + strconv.Itoa(argNum))
|
|
|
|
argNum++
|
|
|
|
case '`':
|
|
|
|
queryBuilder.WriteRune('"')
|
|
|
|
default:
|
|
|
|
queryBuilder.WriteRune(r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
query = queryBuilder.String()
|
|
|
|
}
|
|
|
|
return query
|
|
|
|
}
|
|
|
|
|
2022-01-31 22:30:36 +01:00
|
|
|
func (d *SqlDb) PrepareQuery(query string) string {
|
2021-08-24 17:20:34 +02:00
|
|
|
return d.prepareQueryWithDialect(query, d.sql.Dialect)
|
|
|
|
}
|
|
|
|
|
2021-08-24 19:52:35 +02:00
|
|
|
func (d *SqlDb) insert(primaryKeyColumnName string, query string, args ...interface{}) (int, error) {
|
|
|
|
var insertId int64
|
|
|
|
|
|
|
|
switch d.sql.Dialect.(type) {
|
|
|
|
case gorp.PostgresDialect:
|
|
|
|
query += " returning " + primaryKeyColumnName
|
|
|
|
|
2022-01-31 22:30:36 +01:00
|
|
|
err := d.sql.QueryRow(d.PrepareQuery(query), args...).Scan(&insertId)
|
2021-08-24 19:52:35 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
res, err := d.exec(query, args...)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
insertId, err = res.LastInsertId()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return int(insertId), nil
|
|
|
|
}
|
|
|
|
|
2021-08-24 17:20:34 +02:00
|
|
|
func (d *SqlDb) exec(query string, args ...interface{}) (sql.Result, error) {
|
2022-01-31 22:30:36 +01:00
|
|
|
q := d.PrepareQuery(query)
|
2021-08-24 19:52:35 +02:00
|
|
|
return d.sql.Exec(q, args...)
|
2021-08-24 17:20:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *SqlDb) selectOne(holder interface{}, query string, args ...interface{}) error {
|
2022-01-31 22:30:36 +01:00
|
|
|
return d.sql.SelectOne(holder, d.PrepareQuery(query), args...)
|
2021-08-24 17:20:34 +02:00
|
|
|
}
|
|
|
|
|
2021-08-24 19:52:35 +02:00
|
|
|
func (d *SqlDb) selectAll(i interface{}, query string, args ...interface{}) ([]interface{}, error) {
|
2022-01-31 22:30:36 +01:00
|
|
|
q := d.PrepareQuery(query)
|
2021-08-24 19:52:35 +02:00
|
|
|
return d.sql.Select(i, q, args...)
|
|
|
|
}
|
|
|
|
|
2020-12-01 20:06:49 +01:00
|
|
|
func connect() (*sql.DB, error) {
|
|
|
|
cfg, err := util.Config.GetDBConfig()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
connectionString, err := cfg.GetConnectionString(true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-09-14 19:55:09 +02:00
|
|
|
dialect := cfg.Dialect
|
2021-08-24 17:20:34 +02:00
|
|
|
return sql.Open(dialect, connectionString)
|
2020-12-01 20:06:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func createDb() error {
|
|
|
|
cfg, err := util.Config.GetDBConfig()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !cfg.HasSupportMultipleDatabases() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
connectionString, err := cfg.GetConnectionString(false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-09-14 19:55:09 +02:00
|
|
|
conn, err := sql.Open(cfg.Dialect, connectionString)
|
2020-12-01 20:06:49 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-01-28 00:25:25 +01:00
|
|
|
_, err = conn.Exec("create database " + cfg.GetDbName())
|
2020-12-01 20:06:49 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Warn(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-02-03 08:05:13 +01:00
|
|
|
func (d *SqlDb) getObject(projectID int, props db.ObjectProps, objectID int, object interface{}) (err error) {
|
2020-12-20 19:00:59 +01:00
|
|
|
q := squirrel.Select("*").
|
|
|
|
From(props.TableName).
|
|
|
|
Where("id=?", objectID)
|
|
|
|
|
|
|
|
if props.IsGlobal {
|
|
|
|
q = q.Where("project_id is null")
|
|
|
|
} else {
|
|
|
|
q = q.Where("project_id=?", projectID)
|
|
|
|
}
|
|
|
|
|
|
|
|
query, args, err := q.ToSql()
|
2020-12-07 13:13:59 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-24 17:20:34 +02:00
|
|
|
err = d.selectOne(object, query, args...)
|
2020-12-07 13:13:59 +01:00
|
|
|
|
2020-12-16 20:19:20 +01:00
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
err = db.ErrNotFound
|
2020-12-07 13:13:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-02 19:23:45 +02:00
|
|
|
func (d *SqlDb) makeObjectsQuery(projectID int, props db.ObjectProps, params db.RetrieveQueryParams) squirrel.SelectBuilder {
|
2020-12-07 13:13:59 +01:00
|
|
|
q := squirrel.Select("*").
|
2023-09-11 01:02:10 +02:00
|
|
|
From(props.TableName + " pe")
|
|
|
|
|
2024-03-06 13:20:48 +01:00
|
|
|
if !props.IsGlobal {
|
|
|
|
q = q.Where("pe.project_id=?", projectID)
|
2023-09-11 01:02:10 +02:00
|
|
|
}
|
2020-12-07 13:13:59 +01:00
|
|
|
|
2020-12-08 08:23:33 +01:00
|
|
|
orderDirection := "ASC"
|
2020-12-07 13:13:59 +01:00
|
|
|
if params.SortInverted {
|
2020-12-08 08:23:33 +01:00
|
|
|
orderDirection = "DESC"
|
2020-12-07 13:13:59 +01:00
|
|
|
}
|
|
|
|
|
2021-10-27 18:22:52 +02:00
|
|
|
orderColumn := props.DefaultSortingColumn
|
2020-12-20 19:00:59 +01:00
|
|
|
if containsStr(props.SortableColumns, params.SortBy) {
|
2020-12-08 08:23:33 +01:00
|
|
|
orderColumn = params.SortBy
|
2020-12-07 13:13:59 +01:00
|
|
|
}
|
|
|
|
|
2021-10-27 18:22:52 +02:00
|
|
|
if orderColumn != "" {
|
|
|
|
q = q.OrderBy("pe." + orderColumn + " " + orderDirection)
|
|
|
|
}
|
2020-12-08 08:23:33 +01:00
|
|
|
|
2024-01-28 13:18:07 +01:00
|
|
|
if params.Count > 0 {
|
|
|
|
q = q.Limit(uint64(params.Count))
|
|
|
|
}
|
|
|
|
|
|
|
|
if params.Offset > 0 {
|
|
|
|
q = q.Offset(uint64(params.Offset))
|
|
|
|
}
|
|
|
|
|
2024-07-02 19:23:45 +02:00
|
|
|
return q
|
|
|
|
}
|
|
|
|
|
2024-07-07 19:12:21 +02:00
|
|
|
func (d *SqlDb) getObjects(projectID int, props db.ObjectProps, params db.RetrieveQueryParams, prepare func(squirrel.SelectBuilder) squirrel.SelectBuilder, objects interface{}) (err error) {
|
|
|
|
q := d.makeObjectsQuery(projectID, props, params)
|
|
|
|
|
|
|
|
if prepare != nil {
|
|
|
|
q = prepare(q)
|
|
|
|
}
|
|
|
|
|
|
|
|
query, args, err := q.ToSql()
|
2020-12-07 13:13:59 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-24 19:52:35 +02:00
|
|
|
_, err = d.selectAll(objects, query, args...)
|
2020-12-07 13:13:59 +01:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-08 13:15:04 +02:00
|
|
|
func (d *SqlDb) deleteObject(projectID int, props db.ObjectProps, objectID any) error {
|
2023-09-11 01:02:10 +02:00
|
|
|
if props.IsGlobal {
|
|
|
|
return validateMutationResult(
|
|
|
|
d.exec(
|
|
|
|
"delete from "+props.TableName+" where id=?",
|
|
|
|
objectID))
|
|
|
|
} else {
|
|
|
|
return validateMutationResult(
|
|
|
|
d.exec(
|
|
|
|
"delete from "+props.TableName+" where project_id=? and id=?",
|
|
|
|
projectID,
|
|
|
|
objectID))
|
|
|
|
}
|
2020-12-07 13:13:59 +01:00
|
|
|
}
|
|
|
|
|
2022-11-18 23:23:30 +01:00
|
|
|
func (d *SqlDb) Close(token string) {
|
|
|
|
err := d.sql.Db.Close()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2020-12-01 20:06:49 +01:00
|
|
|
}
|
|
|
|
|
2022-11-18 23:23:30 +01:00
|
|
|
func (d *SqlDb) PermanentConnection() bool {
|
2022-11-09 17:30:35 +01:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2022-11-18 23:23:30 +01:00
|
|
|
func (d *SqlDb) Connect(token string) {
|
2020-12-04 23:41:26 +01:00
|
|
|
sqlDb, err := connect()
|
2020-12-01 20:06:49 +01:00
|
|
|
if err != nil {
|
2022-11-18 23:23:30 +01:00
|
|
|
panic(err)
|
2020-12-01 20:06:49 +01:00
|
|
|
}
|
|
|
|
|
2020-12-04 23:41:26 +01:00
|
|
|
if err := sqlDb.Ping(); err != nil {
|
2020-12-01 20:06:49 +01:00
|
|
|
if err = createDb(); err != nil {
|
2022-11-18 23:23:30 +01:00
|
|
|
panic(err)
|
2020-12-01 20:06:49 +01:00
|
|
|
}
|
|
|
|
|
2020-12-04 23:41:26 +01:00
|
|
|
sqlDb, err = connect()
|
2020-12-01 20:06:49 +01:00
|
|
|
if err != nil {
|
2022-11-18 23:23:30 +01:00
|
|
|
panic(err)
|
2020-12-01 20:06:49 +01:00
|
|
|
}
|
|
|
|
|
2020-12-04 23:41:26 +01:00
|
|
|
if err = sqlDb.Ping(); err != nil {
|
2022-11-18 23:23:30 +01:00
|
|
|
panic(err)
|
2020-12-01 20:06:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg, err := util.Config.GetDBConfig()
|
|
|
|
if err != nil {
|
2022-11-18 23:23:30 +01:00
|
|
|
panic(err)
|
2020-12-01 20:06:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var dialect gorp.Dialect
|
|
|
|
|
|
|
|
switch cfg.Dialect {
|
|
|
|
case util.DbDriverMySQL:
|
|
|
|
dialect = gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}
|
2021-08-24 17:20:34 +02:00
|
|
|
case util.DbDriverPostgres:
|
|
|
|
dialect = gorp.PostgresDialect{}
|
2020-12-01 20:06:49 +01:00
|
|
|
}
|
|
|
|
|
2020-12-04 23:41:26 +01:00
|
|
|
d.sql = &gorp.DbMap{Db: sqlDb, Dialect: dialect}
|
2020-12-01 20:06:49 +01:00
|
|
|
|
2020-12-04 23:41:26 +01:00
|
|
|
d.sql.AddTableWithName(db.APIToken{}, "user__token").SetKeys(false, "id")
|
|
|
|
d.sql.AddTableWithName(db.AccessKey{}, "access_key").SetKeys(true, "id")
|
|
|
|
d.sql.AddTableWithName(db.Environment{}, "project__environment").SetKeys(true, "id")
|
|
|
|
d.sql.AddTableWithName(db.Inventory{}, "project__inventory").SetKeys(true, "id")
|
|
|
|
d.sql.AddTableWithName(db.Project{}, "project").SetKeys(true, "id")
|
|
|
|
d.sql.AddTableWithName(db.Repository{}, "project__repository").SetKeys(true, "id")
|
|
|
|
d.sql.AddTableWithName(db.Task{}, "task").SetKeys(true, "id")
|
|
|
|
d.sql.AddTableWithName(db.TaskOutput{}, "task__output").SetUniqueTogether("task_id", "time")
|
|
|
|
d.sql.AddTableWithName(db.Template{}, "project__template").SetKeys(true, "id")
|
|
|
|
d.sql.AddTableWithName(db.User{}, "user").SetKeys(true, "id")
|
|
|
|
d.sql.AddTableWithName(db.Session{}, "session").SetKeys(true, "id")
|
2020-12-01 20:06:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func getSqlForTable(tableName string, p db.RetrieveQueryParams) (string, []interface{}, error) {
|
2021-12-15 22:22:52 +01:00
|
|
|
if p.Offset > 0 && p.Count <= 0 {
|
2020-12-04 23:22:05 +01:00
|
|
|
return "", nil, fmt.Errorf("offset cannot be without limit")
|
|
|
|
}
|
|
|
|
|
2020-12-01 20:06:49 +01:00
|
|
|
q := squirrel.Select("*").
|
2021-08-24 19:52:35 +02:00
|
|
|
From("`" + tableName + "`")
|
2020-12-01 20:06:49 +01:00
|
|
|
|
|
|
|
if p.SortBy != "" {
|
|
|
|
sortDirection := "ASC"
|
|
|
|
if p.SortInverted {
|
|
|
|
sortDirection = "DESC"
|
|
|
|
}
|
|
|
|
|
|
|
|
q = q.OrderBy(p.SortBy + " " + sortDirection)
|
|
|
|
}
|
|
|
|
|
2021-12-15 22:22:52 +01:00
|
|
|
if p.Offset > 0 || p.Count > 0 {
|
2020-12-04 23:22:05 +01:00
|
|
|
q = q.Offset(uint64(p.Offset))
|
|
|
|
}
|
2020-12-01 20:06:49 +01:00
|
|
|
|
|
|
|
if p.Count > 0 {
|
|
|
|
q = q.Limit(uint64(p.Count))
|
|
|
|
}
|
|
|
|
|
|
|
|
return q.ToSql()
|
|
|
|
}
|
|
|
|
|
2022-02-03 08:05:13 +01:00
|
|
|
func (d *SqlDb) getObjectRefs(projectID int, objectProps db.ObjectProps, objectID int) (refs db.ObjectReferrers, err error) {
|
|
|
|
refs.Templates, err = d.getObjectRefsFrom(projectID, objectProps, objectID, db.TemplateProps)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
refs.Repositories, err = d.getObjectRefsFrom(projectID, objectProps, objectID, db.RepositoryProps)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
refs.Inventories, err = d.getObjectRefsFrom(projectID, objectProps, objectID, db.InventoryProps)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *SqlDb) getObjectRefsFrom(
|
|
|
|
projectID int,
|
|
|
|
objectProps db.ObjectProps,
|
|
|
|
objectID int,
|
|
|
|
referringObjectProps db.ObjectProps,
|
|
|
|
) (referringObjs []db.ObjectReferrer, err error) {
|
|
|
|
referringObjs = make([]db.ObjectReferrer, 0)
|
|
|
|
|
|
|
|
fields, err := objectProps.GetReferringFieldsFrom(referringObjectProps.Type)
|
|
|
|
|
|
|
|
cond := ""
|
|
|
|
vals := []interface{}{projectID}
|
|
|
|
|
|
|
|
for _, f := range fields {
|
|
|
|
if cond != "" {
|
2022-02-03 12:33:25 +01:00
|
|
|
cond += " or "
|
2022-02-03 08:05:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cond += f + " = ?"
|
|
|
|
|
|
|
|
vals = append(vals, objectID)
|
|
|
|
}
|
|
|
|
|
|
|
|
if cond == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var referringObjects reflect.Value
|
|
|
|
|
|
|
|
if referringObjectProps.Type == db.ScheduleProps.Type {
|
|
|
|
var referringSchedules []db.Schedule
|
2022-02-03 12:33:25 +01:00
|
|
|
_, err = d.selectAll(&referringSchedules, "select template_id id from project__schedule where project_id = ? and ("+cond+")", vals...)
|
2022-02-03 08:05:13 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(referringSchedules) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var ids []string
|
|
|
|
for _, schedule := range referringSchedules {
|
|
|
|
ids = append(ids, strconv.Itoa(schedule.ID))
|
|
|
|
}
|
|
|
|
|
|
|
|
referringObjects = reflect.New(reflect.SliceOf(db.TemplateProps.Type))
|
|
|
|
_, err = d.selectAll(referringObjects.Interface(),
|
|
|
|
"select id, name from project__template where id in ("+strings.Join(ids, ",")+")")
|
|
|
|
} else {
|
|
|
|
referringObjects = reflect.New(reflect.SliceOf(referringObjectProps.Type))
|
|
|
|
_, err = d.selectAll(
|
|
|
|
referringObjects.Interface(),
|
|
|
|
"select id, name from "+referringObjectProps.TableName+" where project_id = ? and "+cond,
|
|
|
|
vals...)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < referringObjects.Elem().Len(); i++ {
|
|
|
|
id := int(referringObjects.Elem().Index(i).FieldByName("ID").Int())
|
|
|
|
name := referringObjects.Elem().Index(i).FieldByName("Name").String()
|
|
|
|
referringObjs = append(referringObjs, db.ObjectReferrer{ID: id, Name: name})
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-05-06 15:12:21 +02:00
|
|
|
func (d *SqlDb) Sql() *gorp.DbMap {
|
|
|
|
return d.sql
|
|
|
|
}
|
2021-12-18 14:16:34 +01:00
|
|
|
|
|
|
|
func (d *SqlDb) IsInitialized() (bool, error) {
|
2022-01-31 22:30:36 +01:00
|
|
|
_, err := d.sql.SelectInt(d.PrepareQuery("select count(1) from migrations"))
|
2022-01-23 17:34:42 +01:00
|
|
|
return err == nil, nil
|
2021-12-18 14:16:34 +01:00
|
|
|
}
|
2024-02-11 22:13:05 +01:00
|
|
|
|
|
|
|
func (d *SqlDb) getObjectByReferrer(referrerID int, referringObjectProps db.ObjectProps, props db.ObjectProps, objectID int, object interface{}) (err error) {
|
|
|
|
query, args, err := squirrel.Select("*").
|
|
|
|
From(props.TableName).
|
|
|
|
Where("id=?", objectID).
|
|
|
|
Where(referringObjectProps.ReferringColumnSuffix+"=?", referrerID).
|
|
|
|
ToSql()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = d.selectOne(object, query, args...)
|
|
|
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
err = db.ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *SqlDb) getObjectsByReferrer(referrerID int, referringObjectProps db.ObjectProps, props db.ObjectProps, params db.RetrieveQueryParams, objects interface{}) (err error) {
|
|
|
|
var referringColumn = referringObjectProps.ReferringColumnSuffix
|
|
|
|
|
|
|
|
q := squirrel.Select("*").
|
|
|
|
From(props.TableName + " pe")
|
|
|
|
|
|
|
|
if props.IsGlobal {
|
|
|
|
q = q.Where("pe." + referringColumn + " is null")
|
|
|
|
} else {
|
|
|
|
q = q.Where("pe."+referringColumn+"=?", referrerID)
|
|
|
|
}
|
|
|
|
|
|
|
|
orderDirection := "ASC"
|
|
|
|
if params.SortInverted {
|
|
|
|
orderDirection = "DESC"
|
|
|
|
}
|
|
|
|
|
|
|
|
orderColumn := props.DefaultSortingColumn
|
|
|
|
if containsStr(props.SortableColumns, params.SortBy) {
|
|
|
|
orderColumn = params.SortBy
|
|
|
|
}
|
|
|
|
|
|
|
|
if orderColumn != "" {
|
|
|
|
q = q.OrderBy("pe." + orderColumn + " " + orderDirection)
|
|
|
|
}
|
|
|
|
|
|
|
|
query, args, err := q.ToSql()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = d.selectAll(objects, query, args...)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *SqlDb) deleteByReferrer(referrerID int, referringObjectProps db.ObjectProps, props db.ObjectProps, objectID int) error {
|
|
|
|
var referringColumn = referringObjectProps.ReferringColumnSuffix
|
|
|
|
|
|
|
|
return validateMutationResult(
|
|
|
|
d.exec(
|
|
|
|
"delete from "+props.TableName+" where "+referringColumn+"=? and id=?",
|
|
|
|
referrerID,
|
|
|
|
objectID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *SqlDb) deleteObjectByReferencedID(referencedID int, referencedProps db.ObjectProps, props db.ObjectProps, objectID int) error {
|
|
|
|
field := referencedProps.ReferringColumnSuffix
|
|
|
|
|
|
|
|
return validateMutationResult(
|
|
|
|
d.exec("delete from "+props.TableName+" t where t."+field+"=? and t.id=?", referencedID, objectID))
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
GENERIC IMPLEMENTATION
|
|
|
|
**/
|
|
|
|
|
|
|
|
func InsertTemplateFromType(typeInstance interface{}) (string, []interface{}) {
|
|
|
|
val := reflect.Indirect(reflect.ValueOf(typeInstance))
|
|
|
|
typeFieldSize := val.Type().NumField()
|
|
|
|
|
|
|
|
fields := ""
|
|
|
|
values := ""
|
|
|
|
args := make([]interface{}, 0)
|
|
|
|
|
|
|
|
if typeFieldSize > 1 {
|
|
|
|
fields += "("
|
|
|
|
values += "("
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < typeFieldSize; i++ {
|
|
|
|
if val.Type().Field(i).Name == "ID" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fields += val.Type().Field(i).Tag.Get("db")
|
|
|
|
values += "?"
|
|
|
|
args = append(args, val.Field(i))
|
|
|
|
if i != (typeFieldSize - 1) {
|
|
|
|
fields += ", "
|
|
|
|
values += ", "
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if typeFieldSize > 1 {
|
|
|
|
fields += ")"
|
|
|
|
values += ")"
|
|
|
|
}
|
|
|
|
|
|
|
|
return fields + " values " + values, args
|
|
|
|
}
|
|
|
|
|
|
|
|
func AddParams(params db.RetrieveQueryParams, q *squirrel.SelectBuilder, props db.ObjectProps) {
|
|
|
|
orderDirection := "ASC"
|
|
|
|
if params.SortInverted {
|
|
|
|
orderDirection = "DESC"
|
|
|
|
}
|
|
|
|
|
|
|
|
orderColumn := props.DefaultSortingColumn
|
|
|
|
if containsStr(props.SortableColumns, params.SortBy) {
|
|
|
|
orderColumn = params.SortBy
|
|
|
|
}
|
|
|
|
|
|
|
|
if orderColumn != "" {
|
|
|
|
q.OrderBy("t." + orderColumn + " " + orderDirection)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *SqlDb) GetObject(props db.ObjectProps, ID int) (object interface{}, err error) {
|
|
|
|
query, args, err := squirrel.Select("t.*").
|
|
|
|
From(props.TableName + " as t").
|
|
|
|
Where(squirrel.Eq{"t.id": ID}).
|
|
|
|
OrderBy("t.id").
|
|
|
|
ToSql()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = d.selectOne(&object, query, args...)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *SqlDb) CreateObject(props db.ObjectProps, object interface{}) (newObject interface{}, err error) {
|
|
|
|
//err = newObject.Validate()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
template, args := InsertTemplateFromType(newObject)
|
|
|
|
insertID, err := d.insert(
|
|
|
|
"id",
|
|
|
|
"insert into "+props.TableName+" "+template, args...)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
newObject = object
|
|
|
|
|
|
|
|
v := reflect.ValueOf(newObject)
|
|
|
|
field := v.FieldByName("ID")
|
|
|
|
field.SetInt(int64(insertID))
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *SqlDb) GetObjectsByForeignKeyQuery(props db.ObjectProps, foreignID int, foreignProps db.ObjectProps, params db.RetrieveQueryParams, objects interface{}) (err error) {
|
|
|
|
q := squirrel.Select("*").
|
|
|
|
From(props.TableName+" as t").
|
|
|
|
Where(foreignProps.ReferringColumnSuffix+"=?", foreignID)
|
|
|
|
|
|
|
|
AddParams(params, &q, props)
|
|
|
|
|
|
|
|
query, args, err := q.
|
|
|
|
OrderBy("t.id").
|
|
|
|
ToSql()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = d.selectOne(&objects, query, args...)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *SqlDb) GetAllObjectsByForeignKey(props db.ObjectProps, foreignID int, foreignProps db.ObjectProps) (objects interface{}, err error) {
|
|
|
|
query, args, err := squirrel.Select("*").
|
|
|
|
From(props.TableName+" as t").
|
|
|
|
Where(foreignProps.ReferringColumnSuffix+"=?", foreignID).
|
|
|
|
OrderBy("t.id").
|
|
|
|
ToSql()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
results, errQuery := d.selectAll(&objects, query, args...)
|
|
|
|
|
|
|
|
return results, errQuery
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *SqlDb) GetAllObjects(props db.ObjectProps) (objects interface{}, err error) {
|
|
|
|
query, args, err := squirrel.Select("*").
|
|
|
|
From(props.TableName + " as t").
|
|
|
|
OrderBy("t.id").
|
|
|
|
ToSql()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var results []interface{}
|
|
|
|
results, err = d.selectAll(&objects, query, args...)
|
|
|
|
|
|
|
|
return results, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-04-14 14:22:07 +02:00
|
|
|
// Retrieve the Matchers & Values referencing `id' from WebhookExtractor
|
2024-02-11 22:13:05 +01:00
|
|
|
// --
|
|
|
|
// Examples:
|
|
|
|
// referrerCollection := db.ObjectReferrers{}
|
|
|
|
//
|
|
|
|
// d.GetReferencesForForeignKey(db.ProjectProps, id, map[string]db.ObjectProps{
|
|
|
|
// 'Templates': db.TemplateProps,
|
|
|
|
// 'Inventories': db.InventoryProps,
|
|
|
|
// 'Repositories': db.RepositoryProps
|
|
|
|
// }, &referrerCollection)
|
|
|
|
//
|
|
|
|
// //
|
|
|
|
//
|
|
|
|
// referrerCollection := db.WebhookExtractorReferrers{}
|
|
|
|
//
|
|
|
|
// d.GetReferencesForForeignKey(db.WebhookProps, id, map[string]db.ObjectProps{
|
|
|
|
// "Matchers": db.WebhookMatcherProps,
|
|
|
|
// "Values": db.WebhookExtractValueProps
|
|
|
|
// }, &referrerCollection)
|
|
|
|
func (d *SqlDb) GetReferencesForForeignKey(objectProps db.ObjectProps, objectID int, referrerMapping map[string]db.ObjectProps, referrerCollection *interface{}) (err error) {
|
|
|
|
|
|
|
|
for key, value := range referrerMapping {
|
|
|
|
//v := reflect.ValueOf(referrerCollection)
|
|
|
|
referrers, errRef := d.GetObjectReferences(objectProps, value, objectID)
|
|
|
|
|
|
|
|
if errRef != nil {
|
|
|
|
return errRef
|
|
|
|
}
|
|
|
|
reflect.ValueOf(referrerCollection).FieldByName(key).Set(reflect.ValueOf(referrers))
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find Object Referrers for objectID based on referring column taken from referringObjectProps
|
|
|
|
// Example:
|
2024-03-06 15:51:59 +01:00
|
|
|
// GetObjectReferences(db.WebhookMatchers, db.WebhookExtractorProps, integrationID)
|
2024-02-11 22:13:05 +01:00
|
|
|
func (d *SqlDb) GetObjectReferences(objectProps db.ObjectProps, referringObjectProps db.ObjectProps, objectID int) (referringObjs []db.ObjectReferrer, err error) {
|
|
|
|
referringObjs = make([]db.ObjectReferrer, 0)
|
|
|
|
|
|
|
|
fields, err := objectProps.GetReferringFieldsFrom(objectProps.Type)
|
|
|
|
|
|
|
|
cond := ""
|
|
|
|
vals := []interface{}{}
|
|
|
|
|
|
|
|
for _, f := range fields {
|
|
|
|
if cond != "" {
|
|
|
|
cond += " or "
|
|
|
|
}
|
|
|
|
|
|
|
|
cond += f + " = ?"
|
|
|
|
|
|
|
|
vals = append(vals, objectID)
|
|
|
|
}
|
|
|
|
|
|
|
|
if cond == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
referringObjects := reflect.New(reflect.SliceOf(referringObjectProps.Type))
|
|
|
|
_, err = d.selectAll(
|
|
|
|
referringObjects.Interface(),
|
|
|
|
"select id, name from "+referringObjectProps.TableName+" where "+objectProps.ReferringColumnSuffix+" = ? and "+cond,
|
|
|
|
vals...)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < referringObjects.Elem().Len(); i++ {
|
|
|
|
id := int(referringObjects.Elem().Index(i).FieldByName("ID").Int())
|
|
|
|
name := referringObjects.Elem().Index(i).FieldByName("Name").String()
|
|
|
|
referringObjs = append(referringObjs, db.ObjectReferrer{ID: id, Name: name})
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|