Database : Add decoder for stored procedures results
This commit is contained in:
parent
4e9c083b0e
commit
435a12a6bb
@ -25,7 +25,7 @@ func Init(router *httprouter.Router) {
|
||||
router.GET("/", apiHandler(apiRoot))
|
||||
|
||||
router.GET(apiRootPath+"/auth", apiHandler(apiAuth))
|
||||
router.GET(apiRootPath+"/auth/login", apiHandler(apiAuthLogin))
|
||||
router.POST(apiRootPath+"/auth/login", apiHandler(apiAuthLogin))
|
||||
router.GET(apiRootPath+"/auth/logout", apiHandler(apiAuthLogout))
|
||||
|
||||
router.GET(apiRootPath+"/page", apiHandler(apiPage))
|
||||
@ -34,6 +34,7 @@ func Init(router *httprouter.Router) {
|
||||
router.GET(apiRootPath+"/page/:folder/:page/content", apiHandler(apiPageFolderPageContent))
|
||||
|
||||
router.GET(apiRootPath+"/user", apiHandler(apiUser))
|
||||
router.POST(apiRootPath+"/user/:name", apiHandler(apiUser_Create))
|
||||
router.GET(apiRootPath+"/user/:name", apiHandler(apiUserName))
|
||||
|
||||
router.GET(apiRootPath+"/tag", apiHandler(apiTag))
|
||||
@ -72,6 +73,18 @@ func apiAuth(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
|
||||
func apiAuthLogin(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
fmt.Fprintf(w, "API Auth Login")
|
||||
r.ParseForm()
|
||||
var username string = r.FormValue("username")
|
||||
var password string = r.FormValue("password")
|
||||
fmt.Fprintf(w, "Login : User : %s, Password : %s\n\n", username, password)
|
||||
|
||||
var result database.DatabaseResult = database.ExecuteStoredRoutine("CheckUser", username, password)
|
||||
database.DecodeDatabaseResult(&result)
|
||||
fmt.Println(result)
|
||||
|
||||
if result.Error.HasError() {
|
||||
fmt.Fprintf(w, "Error : %s\n\n", &result.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func apiAuthLogout(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
@ -100,10 +113,29 @@ func apiPageFolderPageContent(w http.ResponseWriter, r *http.Request, params htt
|
||||
|
||||
func apiUser(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
fmt.Fprintf(w, "API User")
|
||||
var result database.DatabaseResult = database.ExecuteStoredRoutine("ListUsers")
|
||||
database.DecodeDatabaseResult(&result)
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
func apiUser_Create(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
r.ParseForm()
|
||||
var username string = params.ByName("name")
|
||||
var password string = r.FormValue("password")
|
||||
fmt.Fprintf(w, "API User Create\n")
|
||||
fmt.Fprintf(w, "User : %s, Password : %s\n\n", username, password)
|
||||
|
||||
var result database.DatabaseResult = database.ExecuteStoredRoutine("CreateUser", username, password)
|
||||
database.DecodeDatabaseResult(&result)
|
||||
fmt.Println(result)
|
||||
|
||||
if result.Error.HasError() {
|
||||
fmt.Fprintf(w, "Error : %s\n\n", &result.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func apiUserName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
fmt.Fprintf(w, "API User Name(%s)", params.ByName("page"))
|
||||
fmt.Fprintf(w, "API User Name(%s)", params.ByName("name"))
|
||||
}
|
||||
|
||||
// API Tag
|
||||
@ -113,7 +145,7 @@ func apiTag(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
}
|
||||
|
||||
func apiTagName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
fmt.Fprintf(w, "API Tag Name(%s)", params.ByName("page"))
|
||||
fmt.Fprintf(w, "API Tag Name(%s)", params.ByName("name"))
|
||||
}
|
||||
|
||||
// API Debug Profiler
|
||||
@ -129,7 +161,7 @@ func apiDebugProfiler(w http.ResponseWriter, r *http.Request, _ httprouter.Param
|
||||
}
|
||||
|
||||
func apiDebugProfilerName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
fmt.Fprintf(w, "API Debug Profiler(%s)\n%s", params.ByName("page"), string(profiler.Get(params.ByName("page")).ToString()))
|
||||
fmt.Fprintf(w, "API Debug Profiler(%s)\n%s", params.ByName("name"), string(profiler.Get(params.ByName("name")).ToString()))
|
||||
}
|
||||
|
||||
// API Debug Database
|
||||
|
@ -12,6 +12,32 @@ import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
type DatabaseError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func createDatabaseError(message string) DatabaseError {
|
||||
log.Printf("Database error : %s\n", message)
|
||||
return DatabaseError{
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *DatabaseError) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
func (e *DatabaseError) HasError() bool {
|
||||
return e.message != ""
|
||||
}
|
||||
|
||||
type DatabaseResult struct {
|
||||
Error DatabaseError
|
||||
SqlRows *sql.Rows
|
||||
SqlResult sql.Result
|
||||
ParsedResult map[string][]sql.NullString
|
||||
}
|
||||
|
||||
const (
|
||||
profiler_DATABASE_CONNECT string = "Database Connect"
|
||||
profiler_DATABASE_QUERY string = "Database Query"
|
||||
@ -372,9 +398,8 @@ var databaseStructure databaseDefinition = databaseDefinition{
|
||||
},
|
||||
Command: `
|
||||
DECLARE f_user_id INT(11);
|
||||
DECLARE f_salt VARCHAR(32);
|
||||
DECLARE f_password VARCHAR(256);
|
||||
DECLARE f_user VARCHAR(256);
|
||||
DECLARE f_salt VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
|
||||
DECLARE f_password VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
|
||||
|
||||
SELECT TO_BASE64(RANDOM_BYTES(16)) INTO f_salt;
|
||||
SELECT SHA2(CONCAT(LOWER(f_user_name), f_user_password, f_salt), 512) INTO f_password;
|
||||
@ -383,6 +408,40 @@ INSERT INTO user(name, password, password_salt) VALUES (f_user_name, f_password,
|
||||
|
||||
SELECT LAST_INSERT_ID() INTO f_user_id;
|
||||
|
||||
RETURN f_user_id;`,
|
||||
},
|
||||
{
|
||||
Name: "CheckUser",
|
||||
Parameters: []storedRoutineParameterDefinition{
|
||||
{
|
||||
Name: "f_user_name",
|
||||
Type: "VARCHAR(256)",
|
||||
Charset: "utf8mb4",
|
||||
Collation: "utf8mb4_unicode_ci",
|
||||
},
|
||||
{
|
||||
Name: "f_user_password",
|
||||
Type: "VARCHAR(256)",
|
||||
Charset: "utf8mb4",
|
||||
Collation: "utf8mb4_bin",
|
||||
},
|
||||
},
|
||||
Return: storedRoutineParameterDefinition{
|
||||
Type: "INT(11)",
|
||||
},
|
||||
Command: `
|
||||
DECLARE f_user_id INT(11) DEFAULT NULL;
|
||||
DECLARE f_salt VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
|
||||
DECLARE f_password VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
|
||||
DECLARE f_user VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
SET f_user = LOWER(f_user_name);
|
||||
|
||||
SELECT password_salt INTO f_salt FROM user WHERE name = f_user LIMIT 1;
|
||||
SELECT SHA2(CONCAT(f_user, f_user_password, f_salt), 512) INTO f_password;
|
||||
|
||||
SELECT id INTO f_user_id FROM user WHERE name = f_user AND password = f_password LIMIT 1;
|
||||
|
||||
RETURN f_user_id;`,
|
||||
},
|
||||
},
|
||||
@ -439,9 +498,11 @@ func connect() {
|
||||
profiler.Add(profiler_DATABASE_CONNECT, time.Duration(time.Since(tStart).Microseconds()))
|
||||
}
|
||||
|
||||
func ExecuteStoredRoutine(name string, args ...any) {
|
||||
func ExecuteStoredRoutine(routineName string, args ...any) DatabaseResult {
|
||||
if !isInit {
|
||||
return
|
||||
return DatabaseResult{
|
||||
Error: createDatabaseError("Database not Init"),
|
||||
}
|
||||
}
|
||||
if storedRoutines == nil {
|
||||
fetchStoredRoutines()
|
||||
@ -449,16 +510,76 @@ func ExecuteStoredRoutine(name string, args ...any) {
|
||||
|
||||
var exist bool
|
||||
var routineType uint8
|
||||
routineType, exist = storedRoutines[name]
|
||||
routineType, exist = storedRoutines[routineName]
|
||||
if !exist {
|
||||
return
|
||||
return DatabaseResult{
|
||||
Error: createDatabaseError(fmt.Sprintf("Routine \"%s\" does not exist", routineName)),
|
||||
}
|
||||
}
|
||||
|
||||
var databaseResult DatabaseResult
|
||||
|
||||
switch routineType {
|
||||
case database_ROUTINE_TYPE_PROCEDURE:
|
||||
callStoredProcedure(name, args...)
|
||||
databaseResult = callStoredProcedure(routineName, args...)
|
||||
case database_ROUTINE_TYPE_FUNCTION:
|
||||
callStoredFunction(name, args...)
|
||||
databaseResult = callStoredFunction(routineName, args...)
|
||||
default:
|
||||
return DatabaseResult{
|
||||
Error: createDatabaseError(fmt.Sprintf("Routine \"%s\" isn't a FUNCTION nor a PROCEDURE", routineName)),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return databaseResult
|
||||
}
|
||||
|
||||
func DecodeDatabaseResult(databaseResult *DatabaseResult) {
|
||||
var err error
|
||||
var rows *sql.Rows = databaseResult.SqlRows
|
||||
|
||||
if rows != nil {
|
||||
defer func() {
|
||||
databaseResult.SqlRows.Close()
|
||||
databaseResult.SqlRows = nil
|
||||
}()
|
||||
var columns []string
|
||||
if columns, err = rows.Columns(); err != nil {
|
||||
databaseResult.Error = createDatabaseError(fmt.Sprintf("Error reading columns name : %s", err))
|
||||
rows.Close()
|
||||
databaseResult.SqlRows = nil
|
||||
return
|
||||
}
|
||||
|
||||
if rows.Err() != nil {
|
||||
fmt.Printf("Error : %s\n", rows.Err())
|
||||
}
|
||||
|
||||
databaseResult.ParsedResult = map[string][]sql.NullString{}
|
||||
var numCol int = len(columns)
|
||||
for _, c := range columns {
|
||||
databaseResult.ParsedResult[c] = make([]sql.NullString, 0)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var args []any = make([]any, numCol)
|
||||
var strArr []sql.NullString = make([]sql.NullString, numCol)
|
||||
for i := range numCol {
|
||||
args[i] = &strArr[i]
|
||||
}
|
||||
if err = rows.Scan(args...); err != nil {
|
||||
databaseResult.Error = createDatabaseError(fmt.Sprintf("Error Scanning database response : %s", err))
|
||||
break
|
||||
}
|
||||
for i, c := range columns {
|
||||
databaseResult.ParsedResult[c] = append(databaseResult.ParsedResult[c], strArr[i])
|
||||
}
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
databaseResult.Error = createDatabaseError(fmt.Sprintf("Error Scanning database response : %s", err))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,9 +602,11 @@ func ping() {
|
||||
}
|
||||
}
|
||||
|
||||
func executeQuery(query string, args ...any) *sql.Rows {
|
||||
func executeQuery(query string, args ...any) DatabaseResult {
|
||||
if !isInit {
|
||||
return nil
|
||||
return DatabaseResult{
|
||||
Error: createDatabaseError("Database not Init"),
|
||||
}
|
||||
}
|
||||
if db == nil {
|
||||
connect()
|
||||
@ -497,6 +620,7 @@ func executeQuery(query string, args ...any) *sql.Rows {
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
log.Printf("Error while querying the database [%d/%d] : %s\n", i+1, dbConfig.RetriesOnError, err)
|
||||
if i+1 == dbConfig.RetriesOnError {
|
||||
break
|
||||
@ -508,15 +632,22 @@ func executeQuery(query string, args ...any) *sql.Rows {
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Cannot query the database ! Query : %s", query)
|
||||
log.Fatalln("With args :", args)
|
||||
log.Printf("With args : %s", args...)
|
||||
return DatabaseResult{
|
||||
Error: createDatabaseError("Database Query Failed"),
|
||||
}
|
||||
}
|
||||
|
||||
return rows
|
||||
return DatabaseResult{
|
||||
SqlRows: rows,
|
||||
}
|
||||
}
|
||||
|
||||
func executeExec(query string, args ...any) sql.Result {
|
||||
func executeExec(query string, args ...any) DatabaseResult {
|
||||
if !isInit {
|
||||
return nil
|
||||
return DatabaseResult{
|
||||
Error: createDatabaseError("Database not Init"),
|
||||
}
|
||||
}
|
||||
if db == nil {
|
||||
connect()
|
||||
@ -541,15 +672,21 @@ func executeExec(query string, args ...any) sql.Result {
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Cannot execute on the database ! Command : %s", query)
|
||||
log.Fatalln("With args : ", args)
|
||||
log.Printf("With args : %s\n", args)
|
||||
return DatabaseResult{
|
||||
Error: createDatabaseError("Database Exec Failed"),
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
return DatabaseResult{
|
||||
SqlResult: result,
|
||||
}
|
||||
}
|
||||
|
||||
func fetchStoredRoutines() {
|
||||
storedRoutines = nil
|
||||
var rows *sql.Rows = executeQuery("SELECT ROUTINE_NAME, ROUTINE_TYPE FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA=?;", dbConfig.Database)
|
||||
var dbResult DatabaseResult = executeQuery("SELECT ROUTINE_NAME, ROUTINE_TYPE FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA=?;", dbConfig.Database)
|
||||
var rows *sql.Rows = dbResult.SqlRows
|
||||
if rows != nil {
|
||||
storedRoutines = make(map[string]uint8, 0)
|
||||
defer rows.Close()
|
||||
@ -568,7 +705,7 @@ func fetchStoredRoutines() {
|
||||
fmt.Println(storedRoutines)
|
||||
}
|
||||
|
||||
func callStoredProcedure(name string, args ...any) {
|
||||
func callStoredProcedure(name string, args ...any) DatabaseResult {
|
||||
var query string
|
||||
if len(args) == 0 {
|
||||
query = fmt.Sprintf("CALL %s()", name)
|
||||
@ -579,39 +716,28 @@ func callStoredProcedure(name string, args ...any) {
|
||||
query = fmt.Sprintf("CALL %s(?%s)", name, query)
|
||||
}
|
||||
|
||||
var rows *sql.Rows = executeQuery(query, args...)
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var res string
|
||||
rows.Scan(&res)
|
||||
log.Println(res)
|
||||
}
|
||||
var dbResult DatabaseResult = executeQuery(query, args...)
|
||||
return dbResult
|
||||
}
|
||||
|
||||
func callStoredFunction(name string, args ...any) {
|
||||
func callStoredFunction(name string, args ...any) DatabaseResult {
|
||||
var query string
|
||||
if len(args) == 0 {
|
||||
query = fmt.Sprintf("SELECT %s()", name)
|
||||
query = fmt.Sprintf("SELECT %s() AS result", name)
|
||||
} else if len(args) == 1 {
|
||||
query = fmt.Sprintf("SELECT %s(?)", name)
|
||||
query = fmt.Sprintf("SELECT %s(?) AS result", name)
|
||||
} else {
|
||||
query = strings.Repeat(", ?", len(args)-1)
|
||||
query = fmt.Sprintf("SELECT %s(?%s)", name, query)
|
||||
query = fmt.Sprintf("SELECT %s(?%s) AS result", name, query)
|
||||
}
|
||||
|
||||
var rows *sql.Rows = executeQuery(query, args...)
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var res string
|
||||
rows.Scan(&res)
|
||||
log.Println(res)
|
||||
}
|
||||
var dbResult DatabaseResult = executeQuery(query, args...)
|
||||
return dbResult
|
||||
}
|
||||
|
||||
func fetchTables() []string {
|
||||
var rows *sql.Rows = executeQuery("SHOW TABLES;")
|
||||
var queryResult DatabaseResult = executeQuery("SHOW TABLES;")
|
||||
var rows *sql.Rows = queryResult.SqlRows
|
||||
//var rows *sql.Rows = executeQuery("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA=?;", dbConfig.Database)
|
||||
var tables []string = make([]string, 0)
|
||||
if rows != nil {
|
||||
@ -626,7 +752,8 @@ func fetchTables() []string {
|
||||
}
|
||||
|
||||
func fetchColumns(table string) []columsDefinition {
|
||||
var rows *sql.Rows = executeQuery("SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_DEFAULT, IS_NULLABLE, CHARACTER_SET_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=? AND TABLE_NAME=?;", dbConfig.Database, table)
|
||||
var queryResult DatabaseResult = executeQuery("SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_DEFAULT, IS_NULLABLE, CHARACTER_SET_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=? AND TABLE_NAME=?;", dbConfig.Database, table)
|
||||
var rows *sql.Rows = queryResult.SqlRows
|
||||
var columns []columsDefinition = make([]columsDefinition, 0)
|
||||
if rows != nil {
|
||||
defer rows.Close()
|
||||
@ -654,7 +781,8 @@ func fetchIndexes() []indexDefinition {
|
||||
// Add PRIMARY : ALTER TABLE <table> ADD CONSTRAINT PRIMARY KEY(<columns>);
|
||||
// Add UNIQUE : ALTER TABLE <table> ADD CONSTRAINT <constraint_name> UNIQUE(<columns>);
|
||||
|
||||
var rows *sql.Rows = executeQuery("SELECT S.COLUMN_NAME as COLUMN_NAME, S.INDEX_NAME AS INDEX_NAME, TC.CONSTRAINT_TYPE AS CONSTRAINT_TYPE, S.TABLE_NAME AS TABLE_NAME FROM information_schema.STATISTICS as S INNER JOIN information_schema.TABLE_CONSTRAINTS as TC ON TC.TABLE_SCHEMA = S.TABLE_SCHEMA AND TC.CONSTRAINT_NAME = S.INDEX_NAME AND TC.TABLE_NAME = S.TABLE_NAME WHERE S.TABLE_SCHEMA = ? AND TC.CONSTRAINT_TYPE IN(\"UNIQUE\", \"PRIMARY KEY\");", dbConfig.Database)
|
||||
var queryResult DatabaseResult = executeQuery("SELECT S.COLUMN_NAME as COLUMN_NAME, S.INDEX_NAME AS INDEX_NAME, TC.CONSTRAINT_TYPE AS CONSTRAINT_TYPE, S.TABLE_NAME AS TABLE_NAME FROM information_schema.STATISTICS as S INNER JOIN information_schema.TABLE_CONSTRAINTS as TC ON TC.TABLE_SCHEMA = S.TABLE_SCHEMA AND TC.CONSTRAINT_NAME = S.INDEX_NAME AND TC.TABLE_NAME = S.TABLE_NAME WHERE S.TABLE_SCHEMA = ? AND TC.CONSTRAINT_TYPE IN(\"UNIQUE\", \"PRIMARY KEY\");", dbConfig.Database)
|
||||
var rows *sql.Rows = queryResult.SqlRows
|
||||
var indexes []indexDefinition = make([]indexDefinition, 0)
|
||||
if rows != nil {
|
||||
defer rows.Close()
|
||||
@ -681,7 +809,8 @@ func fetchForeignKeys() []foreignKeyDefinition {
|
||||
// ADD ALTER TABLE <table> ADD CONSTRAINT <constraint_name> FOREIGN KEY(<column>) REFERENCES <ref_table>(<ref_column>) ON DELETE <CASCADE / SET NULL> UPDATE <CASCADE / SET NULL>;
|
||||
|
||||
// SELECT RC.CONSTRAINT_NAME as CONTRAINT_NAME, KCU.TABLE_NAME, KCU.COLUMN_NAME, KCU.REFERENCED_TABLE_NAME, KCU.REFERENCED_COLUMN_NAME, RC.UPDATE_RULE, RC.DELETE_RULE FROM REFERENTIAL_CONSTRAINTS AS RC INNER JOIN KEY_COLUMN_USAGE AS KCU ON KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME WHERE KCU.TABLE_SCHEMA = "RPG";
|
||||
var rows *sql.Rows = executeQuery("SELECT RC.CONSTRAINT_NAME as CONTRAINT_NAME, KCU.TABLE_NAME, KCU.COLUMN_NAME, KCU.REFERENCED_TABLE_NAME, KCU.REFERENCED_COLUMN_NAME, RC.UPDATE_RULE, RC.DELETE_RULE FROM information_schema.REFERENTIAL_CONSTRAINTS AS RC INNER JOIN information_schema.KEY_COLUMN_USAGE AS KCU ON KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME WHERE KCU.TABLE_SCHEMA = ?;", dbConfig.Database)
|
||||
var queryResult DatabaseResult = executeQuery("SELECT RC.CONSTRAINT_NAME as CONTRAINT_NAME, KCU.TABLE_NAME, KCU.COLUMN_NAME, KCU.REFERENCED_TABLE_NAME, KCU.REFERENCED_COLUMN_NAME, RC.UPDATE_RULE, RC.DELETE_RULE FROM information_schema.REFERENTIAL_CONSTRAINTS AS RC INNER JOIN information_schema.KEY_COLUMN_USAGE AS KCU ON KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME WHERE KCU.TABLE_SCHEMA = ?;", dbConfig.Database)
|
||||
var rows *sql.Rows = queryResult.SqlRows
|
||||
var foreignKeys []foreignKeyDefinition = make([]foreignKeyDefinition, 0)
|
||||
if rows != nil {
|
||||
defer rows.Close()
|
||||
|
Loading…
x
Reference in New Issue
Block a user