You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mercury/src/db/database-primitives.go

216 lines
6.1 KiB
Go

package db
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"io/ioutil"
"log"
"os"
"strings"
)
const (
dbCredsEnvName = "DB_CREDS"
dbCredsHostName = "DB_HOST"
dsnTemplate = "dtookey:%s@tcp(%s)/%s?parseTime=true"
ClarityDatabaseName = "projects"
InsightDatabaseName = "insight"
MercuryDatabaseName = "mercury"
)
//<editor-fold name="ConnectorGeneric">
/*======================================================================================
ConnectorGeneric
======================================================================================*/
type Blocker interface {
ToQueryBlock() string
}
type ConnectorGeneric struct {
cachedConnection *sql.DB
}
type SqlScriptRunner struct {
ScriptName string
DatabaseName string
}
func (c *ConnectorGeneric) ExecuteSqlScript(runner *SqlScriptRunner) {
c.startConnection(runner.DatabaseName)
defer c.returnConnection()
queryWhole := *loadSqlFile(runner.ScriptName)
queryParts := strings.Split(queryWhole, ";")
log.Printf("\n=============================================\n%s\n==============================================\n", queryWhole)
for _, query := range queryParts {
if len(query) == 0 || query == "\n" {
continue
}
_, err := c.cachedConnection.Exec(query + ";")
if err != nil {
log.Printf("Query involved in error: %s\n", query)
log.Panic(err)
}
}
}
func (c *ConnectorGeneric) ExecuteString(dbName string, payload string) {
c.startConnection(dbName)
defer c.returnConnection()
//log.Println("==================================================================================================")
//log.Println(payload)
//log.Println("==================================================================================================")
_, err := c.cachedConnection.Exec(payload)
if err != nil {
log.Printf("Query involved in error: %s\n", payload)
log.Panic(err)
}
}
// startConnection this initializes, caches, and returns a connection for the desired database. If a connection already exists,
// it will be terminated and a new connection will be opened.
func (c *ConnectorGeneric) startConnection(dataBase string) {
if c.cachedConnection != nil {
c.returnConnection()
}
db := createDbConnection(dataBase)
c.cachedConnection = db
}
// returnConnection This just closes the cached db connection
func (c *ConnectorGeneric) returnConnection() {
err := c.cachedConnection.Close()
if err != nil {
log.Panic(err)
}
c.cachedConnection = nil
}
func (c *ConnectorGeneric) QueryFromScript(scriptName string) *sql.Rows {
query := loadSqlFile(scriptName)
rows, err := c.cachedConnection.Query(*query)
if err != nil {
log.Panic(err)
}
return rows
}
func (c *ConnectorGeneric) ProcessClarityScripts() { //@dream standardize these script names
tableCreationRunners := []*SqlScriptRunner{
NewRunner("0-run-first/1-sanitize_init.sql", ClarityDatabaseName),
NewRunner("0-run-first/all_projects.sql", ClarityDatabaseName),
NewRunner("0-run-first/billing.sql", ClarityDatabaseName),
NewRunner("0-run-first/contributions.sql", ClarityDatabaseName),
NewRunner("0-run-first/durations.sql", ClarityDatabaseName),
NewRunner("0-run-first/lifecycle.sql", ClarityDatabaseName),
}
for _, runner := range tableCreationRunners {
c.ExecuteSqlScript(runner)
}
}
func (c *ConnectorGeneric) CreateTables() {
tableCreationRunners := []*SqlScriptRunner{
NewRunner("create-any-database.sql", ""),
NewRunner("create-insight-user-table.sql", InsightDatabaseName),
NewRunner("create-insight-timeEntry-table.sql", InsightDatabaseName),
NewRunner("create-mercury-picturePatterns-table.sql", MercuryDatabaseName),
NewRunner("create-mercury-arReports-table.sql", MercuryDatabaseName),
NewRunner("create-mercury-telecomVoice-table.sql", MercuryDatabaseName),
NewRunner("create-mercury-failedReview-view.sql", MercuryDatabaseName),
}
for _, runner := range tableCreationRunners {
c.ExecuteSqlScript(runner)
}
}
//</editor-fold>
//<editor-fold name="Utility Functions">
/*======================================================================================
Utility Functions
======================================================================================*/
func createDbConnection(database string) *sql.DB {
cred := os.Getenv(dbCredsEnvName)
host := os.Getenv(dbCredsHostName)
dbString := dsnTemplate
connectString := fmt.Sprintf(dbString, cred, host, database)
fmt.Printf("Beginning connection to database\n")
db, err := sql.Open("mysql", connectString)
if err != nil {
log.Panic(err)
}
return db
}
func loadSqlFile(scriptName string) *string {
file, err := os.OpenFile("src/sql/"+scriptName, os.O_RDONLY, 0755)
if err != nil {
log.Panic(err)
}
defer file.Close()
raw, err := ioutil.ReadAll(file)
if err != nil {
log.Panic(err)
}
str := strings.Trim(string(raw), "\n")
return &str
}
func QueryForObjects[K any](db *ConnectorGeneric, dbName string, queryScript string, mapping func(rows *sql.Rows) *K) *[]*K {
rs := make([]*K, 0, 10000)
db.startConnection(dbName)
defer db.returnConnection()
res := db.QueryFromScript(queryScript)
for res.Next() {
o := mapping(res)
rs = append(rs, o)
}
return &rs
}
func BulkUpdate[K any](connector *ConnectorGeneric, dbName string, updateScript string, items *[]*K, mapping func(s *sql.Stmt, item *K)) {
connector.startConnection(dbName)
defer connector.returnConnection()
statement := loadSqlFile(updateScript)
s, err := connector.cachedConnection.Prepare(*statement)
if err != nil {
log.Panic(err)
}
for _, item := range *items {
mapping(s, item)
}
}
func BlockUpdate[K Blocker](connector *ConnectorGeneric, dbName string, updateScript string, items *[]*K) {
template := loadSqlFile(updateScript)
buff := strings.Builder{}
l := len(*items)
for i, item := range *items {
buff.WriteString((*item).ToQueryBlock())
if i < l-1 {
buff.WriteByte(',')
}
}
query := fmt.Sprintf(*template, buff.String())
connector.ExecuteString(dbName, query)
}
func NewRunner(scriptName string, databaseName string) *SqlScriptRunner {
return &SqlScriptRunner{scriptName, databaseName}
}
//</editor-fold>