package fic import ( "database/sql" _ "github.com/go-sql-driver/mysql" "log" "os" "time" ) // db stores the connection to the database var db *sql.DB // DSNGenerator returns DSN filed with values from environment func DSNGenerator() string { db_user := "fic" db_password := "fic" db_host := "" db_db := "fic" if v, exists := os.LookupEnv("MYSQL_HOST"); exists { db_host = "tcp(" + v + ":" if p, exists := os.LookupEnv("MYSQL_PORT"); exists { db_host += p + ")" } else { db_host += "3306)" } } if v, exists := os.LookupEnv("MYSQL_PASSWORD"); exists { db_password = v } else if v, exists := os.LookupEnv("MYSQL_ROOT_PASSWORD"); exists { db_user = "root" db_password = v } if v, exists := os.LookupEnv("MYSQL_USER"); exists { db_user = v } if v, exists := os.LookupEnv("MYSQL_DATABASE"); exists { db_db = v } return db_user + ":" + db_password + "@" + db_host + "/" + db_db } // DBInit establishes the connection to the database func DBInit(dsn string) (err error) { if db, err = sql.Open("mysql", dsn+"?parseTime=true&foreign_key_checks=1"); err != nil { return } _, err = db.Exec(`SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';`) for i := 0; err != nil && i < 45; i += 1 { if _, err = db.Exec(`SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';`); err != nil && i <= 45 { log.Println("An error occurs when trying to connect to DB, will retry in 2 seconds: ", err) time.Sleep(2 * time.Second) } } return } // DBCreate creates all necessary tables used by the package func DBCreate() error { if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS events( id_event INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, txt VARCHAR(255) NOT NULL, kind ENUM('primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark') NOT NULL, time TIMESTAMP NOT NULL ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS themes( id_theme INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, url_id VARCHAR(191) NOT NULL UNIQUE, path VARCHAR(191) NOT NULL UNIQUE, headline TEXT NOT NULL, intro TEXT NOT NULL, image VARCHAR(255) NOT NULL, authors TEXT NOT NULL ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS teams( id_team INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, color INTEGER NOT NULL, active BOOLEAN NOT NULL DEFAULT 1 ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS certificates( id_cert BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, creation TIMESTAMP NOT NULL, password VARCHAR(255) NOT NULL, revoked TIMESTAMP NULL ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS team_members( id_member INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_team INTEGER, firstname VARCHAR(255) NOT NULL, lastname VARCHAR(255) NOT NULL, nickname VARCHAR(255) NOT NULL, company VARCHAR(255) NOT NULL, FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercices( id_exercice INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_theme INTEGER NOT NULL, title VARCHAR(255) NOT NULL, headline TEXT NOT NULL, url_id VARCHAR(255) NOT NULL, path VARCHAR(191) NOT NULL UNIQUE, statement TEXT NOT NULL, overview TEXT NOT NULL, issue TEXT NOT NULL, issue_kind ENUM('primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark') NOT NULL DEFAULT 'info', depend INTEGER, gain INTEGER NOT NULL, coefficient_cur FLOAT NOT NULL DEFAULT 1.0, video_uri VARCHAR(255) NOT NULL, finished TEXT NOT NULL, FOREIGN KEY(id_theme) REFERENCES themes(id_theme), FOREIGN KEY(depend) REFERENCES exercices(id_exercice) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_files( id_file INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, origin VARCHAR(255) NOT NULL, path VARCHAR(191) NOT NULL UNIQUE, id_exercice INTEGER NOT NULL, name VARCHAR(255) NOT NULL, cksum BINARY(64) NOT NULL, size BIGINT UNSIGNED NOT NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_hints( id_hint INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_exercice INTEGER NOT NULL, title VARCHAR(255) NOT NULL, content TEXT NOT NULL, cost INTEGER NOT NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_flags( id_flag INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_exercice INTEGER NOT NULL, type VARCHAR(255) NOT NULL, help VARCHAR(255) NOT NULL, ignorecase BOOLEAN NOT NULL DEFAULT 0, multiline BOOLEAN NOT NULL DEFAULT 0, validator_regexp VARCHAR(255) NULL, cksum BINARY(64) NOT NULL, choices_cost INTEGER NOT NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_hints_okey_deps( id_hint INTEGER NOT NULL, id_flag_dep INTEGER NOT NULL, FOREIGN KEY(id_hint) REFERENCES exercice_hints(id_hint), FOREIGN KEY(id_flag_dep) REFERENCES exercice_flags(id_flag) ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_flags_deps( id_flag INTEGER NOT NULL, id_flag_dep INTEGER NOT NULL, FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag), FOREIGN KEY(id_flag_dep) REFERENCES exercice_flags(id_flag) ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS flag_choices( id_choice INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_flag INTEGER NOT NULL, label VARCHAR(255) NOT NULL, response VARCHAR(255) NOT NULL, FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_files_okey_deps( id_file INTEGER NOT NULL, id_flag INTEGER NOT NULL, FOREIGN KEY(id_file) REFERENCES exercice_files(id_file), FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag) ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_mcq( id_mcq INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_exercice INTEGER NOT NULL, title VARCHAR(255) NOT NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_files_omcq_deps( id_file INTEGER NOT NULL, id_mcq INTEGER NOT NULL, FOREIGN KEY(id_file) REFERENCES exercice_files(id_file), FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq) ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_hints_omcq_deps( id_hint INTEGER NOT NULL, id_mcq_dep INTEGER NOT NULL, FOREIGN KEY(id_hint) REFERENCES exercice_hints(id_hint), FOREIGN KEY(id_mcq_dep) REFERENCES exercice_flags(id_flag) ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_mcq_okey_deps( id_mcq INTEGER NOT NULL, id_flag_dep INTEGER NOT NULL, FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq), FOREIGN KEY(id_flag_dep) REFERENCES exercice_flags(id_flag) ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_flags_omcq_deps( id_flag INTEGER NOT NULL, id_mcq_dep INTEGER NOT NULL, FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag), FOREIGN KEY(id_mcq_dep) REFERENCES exercice_mcq(id_mcq) ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_mcq_omcq_deps( id_mcq INTEGER NOT NULL, id_mcq_dep INTEGER NOT NULL, FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq), FOREIGN KEY(id_mcq_dep) REFERENCES exercice_mcq(id_mcq) ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS mcq_entries( id_mcq_entry INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_mcq INTEGER NOT NULL, label VARCHAR(255) NOT NULL, response BOOLEAN NOT NULL, FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS mcq_found( id_mcq INTEGER NOT NULL, id_team INTEGER NOT NULL, time TIMESTAMP NOT NULL, CONSTRAINT uq_found UNIQUE (id_mcq,id_team), FOREIGN KEY(id_mcq) REFERENCES exercice_mcq(id_mcq), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS flag_found( id_flag INTEGER NOT NULL, id_team INTEGER NOT NULL, time TIMESTAMP NOT NULL, CONSTRAINT uc_found UNIQUE (id_flag,id_team), FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_solved( id_exercice INTEGER NOT NULL, id_team INTEGER NOT NULL, time TIMESTAMP NOT NULL, coefficient FLOAT NOT NULL DEFAULT 1.0, CONSTRAINT uc_solved UNIQUE (id_exercice,id_team), FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_tries( id_exercice INTEGER NOT NULL, id_team INTEGER NOT NULL, time TIMESTAMP NOT NULL, cksum BINARY(64) NOT NULL, nbdiff INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercice_tags( id_exercice INTEGER NOT NULL, tag VARCHAR(255) NOT NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS team_hints( id_team INTEGER, id_hint INTEGER, time TIMESTAMP NOT NULL, coefficient FLOAT NOT NULL DEFAULT 1.0, CONSTRAINT uc_displayed UNIQUE (id_team,id_hint), FOREIGN KEY(id_hint) REFERENCES exercice_hints(id_hint), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS team_wchoices( id_team INTEGER, id_flag INTEGER, time TIMESTAMP NOT NULL, coefficient FLOAT NOT NULL DEFAULT 1.0, CONSTRAINT uc_displayed UNIQUE (id_team,id_flag), FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS claim_assignees( id_assignee INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(191) NOT NULL UNIQUE ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS claims( id_claim INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, subject VARCHAR(255) NOT NULL, id_team INTEGER, id_exercice INTEGER, id_assignee INTEGER, creation TIMESTAMP NOT NULL, state ENUM('new', 'need-info', 'confirmed', 'in-progress', 'need-review', 'closed', 'invalid') NOT NULL, priority ENUM('low', 'medium', 'high', 'critical') NOT NULL, FOREIGN KEY(id_assignee) REFERENCES claim_assignees(id_assignee), FOREIGN KEY(id_team) REFERENCES teams(id_team), FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS claim_descriptions( id_description INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_claim INTEGER NOT NULL, id_assignee INTEGER, date TIMESTAMP NOT NULL, content TEXT NOT NULL, publish BOOLEAN NOT NULL DEFAULT 0, FOREIGN KEY(id_claim) REFERENCES claims(id_claim) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS exercices_qa( id_qa INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_exercice INTEGER NOT NULL, id_team INTEGER NULL, authuser VARCHAR(255) NOT NULL, subject VARCHAR(255) NOT NULL, creation TIMESTAMP NOT NULL, state VARCHAR(255) NOT NULL, solved TIMESTAMP NULL, closed TIMESTAMP NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS qa_comments( id_comment INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_qa INTEGER NOT NULL, id_team INTEGER NULL, authuser VARCHAR(255) NOT NULL, date TIMESTAMP NOT NULL, content TEXT NOT NULL, FOREIGN KEY(id_qa) REFERENCES exercices_qa(id_qa), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS teams_qa_todo( id_todo INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_team INTEGER NOT NULL, id_exercice INTEGER NOT NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec(` CREATE TABLE IF NOT EXISTS teams_qa_view( id_view INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_team INTEGER NOT NULL, id_exercice INTEGER NOT NULL, FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice), FOREIGN KEY(id_team) REFERENCES teams(id_team) ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; `); err != nil { return err } if _, err := db.Exec("CREATE OR REPLACE VIEW exercice_distinct_tries AS SELECT id_exercice, id_team, MAX(time) AS time, cksum, nbdiff FROM exercice_tries GROUP BY id_team, id_exercice, cksum;"); err != nil { return err } return nil } // DBClose closes the connection to the database func DBClose() error { return db.Close() } func DBPrepare(query string) (*sql.Stmt, error) { return db.Prepare(query) } func DBQuery(query string, args ...interface{}) (*sql.Rows, error) { return db.Query(query, args...) } func DBExec(query string, args ...interface{}) (sql.Result, error) { return db.Exec(query, args...) } func DBQueryRow(query string, args ...interface{}) *sql.Row { return db.QueryRow(query, args...) }