diff --git a/src/database/database.go b/src/database/database.go index 107dabf..dff5e9e 100644 --- a/src/database/database.go +++ b/src/database/database.go @@ -22,6 +22,112 @@ const ( database_ROUTINE_TYPE_FUNCTION uint8 = 2 ) +type databaseEnumIndexType string + +const ( + database_ENUM_INDEX_PRIMARY databaseEnumIndexType = "PRIMARY" + database_ENUM_INDEX_UNIQUE databaseEnumIndexType = "UNIQUE" +) + +type columsDefinition struct { + Name string + Type string + Default any + IsNullable bool + Extra string + Charset any + Collation any + Table string +} + +type indexDefinition struct { + Type databaseEnumIndexType + Name string + Table string + Columns []string +} + +type foreignKeyDefinition struct { + Name string + Table string + ColumnName string + PointingToTable string + PointingToColumn string + UpdateRule string + DeleteRule string +} + +type tableDefinition struct { + Name string + Columns []columsDefinition +} + +type databaseDefinition struct { + Name string + Tables []tableDefinition + Indexes []indexDefinition + ForeignKeys []foreignKeyDefinition +} + +var listTables = []tableDefinition{ + { + Name: "users", + Columns: []columsDefinition{ + { + Name: "id", + Type: "int(11)", + Default: nil, + IsNullable: false, + Extra: "AUTO_INCREMENT", + }, + { + Name: "name", + Type: "varchar(256)", + Default: nil, + IsNullable: false, + Extra: "", + Charset: "utf8mb4", + Collation: "utf8mb4_unicode_ci", + }, + { + Name: "password", + Type: "varchar(256)", + Default: nil, + IsNullable: false, + Extra: "", + Charset: "utf8mb4", + Collation: "utf8mb4_unicode", + }, + { + Name: "password-salt", + Type: "varchar(256)", + Default: nil, + IsNullable: false, + Extra: "", + Charset: "utf8mb4", + Collation: "utf8mb4_unicode", + }, + { + Name: "permission_level", + Type: "enum('ADMIN', 'MODERATOR', 'USER')", + Default: "USER", + IsNullable: false, + Extra: "", + Charset: nil, + }, + { + Name: "email", + Type: "varchar(256)", + Default: nil, + IsNullable: true, + Extra: "", + Charset: "utf8mb4", + Collation: "utf8mb4_unicode", + }, + }, + }, +} + var dbPath string var dbConfig config.Database var isInit bool @@ -40,14 +146,10 @@ func Init() { profiler.Register(profiler_DATABASE_QUERY, 4000, "µs") isInit = true - fetchStoredRoutines() + initDatabaseStructure() } func connect() { - if !isInit { - return - } - var err error log.Printf("Connecting to database %s at %s:%d as %s\n", dbConfig.Database, dbConfig.Host, dbConfig.Port, dbConfig.User) @@ -170,6 +272,7 @@ func fetchStoredRoutines() { } } } + fmt.Print("ROUTINES : ") fmt.Println(storedRoutines) } @@ -192,9 +295,145 @@ func callStoredProcedure(name string, args ...any) { rows.Scan(&res) log.Println(res) } - } func callStoredFunction(name string, args ...any) { + var query string + if len(args) == 0 { + query = fmt.Sprintf("SELECT %s()", name) + } else if len(args) == 1 { + query = fmt.Sprintf("SELECT %s(?)", name) + } else { + query = strings.Repeat(", ?", len(args)-1) + query = fmt.Sprintf("SELECT %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) + } +} + +func fetchTables() []string { + var query string = fmt.Sprintf("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA=\"%s\";", dbConfig.Database) + var rows *sql.Rows = executeQuery(query) + var tables []string = make([]string, 0) + if rows != nil { + defer rows.Close() + for rows.Next() { + var resName string + rows.Scan(&resName) + tables = append(tables, resName) + } + } + return tables +} + +func fetchColumns(table string) []columsDefinition { + var query string = fmt.Sprintf("SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_DEFAULT, IS_NULLABLE, EXTRA, CHARACTER_SET_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=\"%s\" AND TABLE_NAME=\"%s\";", dbConfig.Database, table) + var rows *sql.Rows = executeQuery(query) + var columns []columsDefinition = make([]columsDefinition, 0) + if rows != nil { + defer rows.Close() + for rows.Next() { + var resName, resType, resDefault, resNullable, resExtra, resCharset, resCollation string + rows.Scan(&resName, &resType, &resDefault, &resNullable, &resExtra, &resCharset, &resCollation) + columns = append(columns, columsDefinition{ + Name: resName, + Type: resType, + Default: resDefault, + IsNullable: resNullable == "YES", + Extra: resExtra, + Charset: resCharset, + Collation: resCollation, + Table: table, + }) + } + } + return columns +} + +func fetchIndexes() []indexDefinition { + // SELECT * FROM STATISTICS as S INNER JOIN 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 = "RPG"; + // SELECT S.COLUMN_NAME as COLUMN_NAME, S.INDEX_NAME AS INDEX_NAME, TC.CONSTRAINT_TYPE AS CONSTRAINT_TYPE FROM STATISTICS as S INNER JOIN 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 = "RPG" AND S.TABLE_NAME = "folders" AND TC.CONSTRAINT_TYPE IN("UNIQUE", "PRIMARY KEY"); + + // Add PRIMARY : ALTER TABLE ADD CONSTRAINT PRIMARY KEY(); + // Add UNIQUE : ALTER TABLE
ADD CONSTRAINT UNIQUE(); + + var query string = fmt.Sprintf("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 = \"%s\" AND TC.CONSTRAINT_TYPE IN(\"UNIQUE\", \"PRIMARY KEY\");", dbConfig.Database) + fmt.Println(query) + var rows *sql.Rows = executeQuery(query) + var indexes []indexDefinition = make([]indexDefinition, 0) + if rows != nil { + defer rows.Close() + for rows.Next() { + var resColumn, resIndex, resType, resTable string + rows.Scan(&resColumn, &resIndex, &resType, &resTable) + var index indexDefinition = indexDefinition{ + Table: resTable, + Name: resIndex, + Type: database_ENUM_INDEX_PRIMARY, + Columns: []string{resColumn}, + } + if resType == "UNIQUE" { + index.Type = database_ENUM_INDEX_UNIQUE + } + indexes = append(indexes, index) + } + } + return indexes +} + +func fetchForeignKeys() []foreignKeyDefinition { + + // ADD ALTER TABLE
ADD CONSTRAINT FOREIGN KEY() REFERENCES () ON DELETE UPDATE ; + + // 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 query string = fmt.Sprintf("SELECT INDEX_NAME, COLUMN_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=\"%s\";", dbConfig.Database) + var rows *sql.Rows = executeQuery(query) + var fk []foreignKeyDefinition = make([]foreignKeyDefinition, 0) + if rows != nil { + defer rows.Close() + for rows.Next() { + var resName string + rows.Scan(&resName) + fk = append(fk, foreignKeyDefinition{ + Name: resName, + }) + } + } + return fk +} + +func initDatabaseStructure() { + fetchStoredRoutines() + fetchIndexes() + fetchForeignKeys() + + var tables []string = fetchTables() + + for i := range tables { + var columns []columsDefinition = fetchColumns(tables[i]) + fmt.Printf("Table %s : \n", tables[i]) + for j := range columns { + fmt.Printf(" - Column %s %s IsNullable:%t Default:%s Extra:%s Charset:%s/%s\n", + columns[j].Name, columns[j].Type, columns[j].IsNullable, columns[j].Default, columns[j].Extra, columns[j].Charset, columns[j].Collation) + } + } + + var indexes []indexDefinition = fetchIndexes() + + for i := range indexes { + fmt.Printf("Index %s : \n", indexes[i]) + } + + var fk []foreignKeyDefinition = fetchForeignKeys() + + for i := range fk { + fmt.Printf("Foreign key %s : \n", fk[i]) + } }