From 974fab1d64e469b2483638a3625641551c6bd9ae Mon Sep 17 00:00:00 2001 From: ycc Date: Tue, 27 Jun 2023 23:19:50 +0200 Subject: [PATCH] adding mysql support, table creation tested, TODO schema --- pg.go => db.go | 35 ++++++- docker-compose.yml | 29 ++++++ go.mod | 5 +- go.sum | 2 + my_test.go | 230 +++++++++++++++++++++++++++++++++++++++++++++ pg_test.go | 26 ++--- survey.json | 58 ++++++++++++ test_table.json | 2 +- 8 files changed, 368 insertions(+), 19 deletions(-) rename pg.go => db.go (85%) create mode 100644 docker-compose.yml create mode 100755 my_test.go create mode 100644 survey.json diff --git a/pg.go b/db.go similarity index 85% rename from pg.go rename to db.go index 34e293b..2f3cb85 100755 --- a/pg.go +++ b/db.go @@ -3,6 +3,7 @@ package sqldb import ( "database/sql" "encoding/json" + "errors" "fmt" "html/template" "io/ioutil" @@ -11,6 +12,7 @@ import ( "strconv" "strings" + _ "github.com/go-sql-driver/mysql" "github.com/lib/pq" ) @@ -63,12 +65,12 @@ func (db *Db) Table(name string) *TableInfo { return &ti } -// GetAssociativeArray : Provide results as an associative array +// GetAssociativeArray : Provide table data as an associative array func (t *TableInfo) GetAssociativeArray(columns []string, restriction string, sortkeys []string, dir string) ([]AssRow, error) { return t.db.QueryAssociativeArray(t.buildSelect("", columns, restriction, sortkeys, dir)) } -// QueryAssociativeArray : Provide results as an associative array +// QueryAssociativeArray : Provide query result as an associative array func (db *Db) QueryAssociativeArray(query string) (Rows, error) { rows, err := db.conn.Query(query) if err != nil { @@ -112,12 +114,21 @@ func (db *Db) QueryAssociativeArray(query string) (Rows, error) { return results, nil } -// GetSchema : Provide results as an associative array +// GetSchema : Provide table schema as an associative array func (t *TableInfo) GetSchema() (*TableInfo, error) { + pgSchema := "SELECT column_name :: varchar as name, REPLACE(REPLACE(data_type,'character varying','varchar'),'character','char') || COALESCE('(' || character_maximum_length || ')', '') as type, col_description('public." + t.Name + "'::regclass, ordinal_position) as comment from INFORMATION_SCHEMA.COLUMNS where table_name ='" + t.Name + "';" + mySchema := "SELECT COLUMN_NAME as name, DATA_TYPE || CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + t.Name + "';" + var schemaQuery string var ti TableInfo ti.Name = t.Name ti.db = t.db - cols, err := t.db.QueryAssociativeArray("SELECT column_name :: varchar as name, REPLACE(REPLACE(data_type,'character varying','varchar'),'character','char') || COALESCE('(' || character_maximum_length || ')', '') as type, col_description('public." + t.Name + "'::regclass, ordinal_position) as comment from INFORMATION_SCHEMA.COLUMNS where table_name ='" + t.Name + "';") + if t.db.Driver == "postgres" { + schemaQuery = pgSchema + } + if t.db.Driver == "mysql" { + schemaQuery = mySchema + } + cols, err := t.db.QueryAssociativeArray(schemaQuery) if err != nil { log.Println(err) return nil, err @@ -144,6 +155,7 @@ func (t *TableInfo) GetSchema() (*TableInfo, error) { return &ti, nil } +// GetSchema : Provide full database schema as an associative array func (db *Db) GetSchema() ([]TableInfo, error) { var res []TableInfo tables, err := db.ListTables() @@ -168,10 +180,25 @@ func (db *Db) GetSchema() ([]TableInfo, error) { return res, nil } +// GetSchema : Provide database tables list func (db *Db) ListTables() (Rows, error) { + if db.Driver == "postgres" { + return db.pgListTables() + } + if db.Driver == "mysql" { + return db.myListTables() + } + return nil, errors.New("no driver") +} + +func (db *Db) pgListTables() (Rows, error) { return db.QueryAssociativeArray("SELECT table_name :: varchar as name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name;") } +func (db *Db) myListTables() (Rows, error) { + return db.QueryAssociativeArray("SELECT TABLE_NAME as name FROM information_schema.TABLES WHERE TABLE_TYPE LIKE 'BASE_TABLE';") +} + func (db *Db) CreateTable(t TableInfo) error { t.db = db query := "create table " + t.Name + " ( " diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..295e155 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +version: '3.1' + +services: + + pg: + image: postgres:alpine + restart: always + environment: + POSTGRES_USER: test + POSTGRES_PASSWORD: test + ports: + - 5432:5432 + + my: + image: mariadb:latest + restart: always + environment: + MYSQL_DATABASE: test + MYSQL_USER: test + MYSQL_PASSWORD: test + MARIADB_ROOT_PASSWORD: root + ports: + - 3306:3306 + + adminer: + image: adminer + restart: always + ports: + - 8080:8080 \ No newline at end of file diff --git a/go.mod b/go.mod index 1f07233..834c90d 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module forge.redroom.link/yves/sqldb go 1.17 -require github.com/lib/pq v1.10.4 +require ( + github.com/go-sql-driver/mysql v1.7.1 + github.com/lib/pq v1.10.4 +) diff --git a/go.sum b/go.sum index 08b950f..8ddf20c 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,4 @@ +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= diff --git a/my_test.go b/my_test.go new file mode 100755 index 0000000..bc1f9cf --- /dev/null +++ b/my_test.go @@ -0,0 +1,230 @@ +package sqldb + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "testing" +) + +func TestMyCreateTable(t *testing.T) { + db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test") + defer db.Close() + + jsonFile, err := os.Open("test_table.json") + if err != nil { + fmt.Println(err) + } + defer jsonFile.Close() + + byteValue, _ := ioutil.ReadAll(jsonFile) + + var jsonSource TableInfo + json.Unmarshal([]byte(byteValue), &jsonSource) + + err = db.CreateTable(jsonSource) + if err != nil { + fmt.Println(err.Error()) + } + + sch, err := db.Table("test").GetSchema() + if err != nil { + fmt.Println(err.Error()) + } + if len(sch.Columns) == 0 { + t.Errorf("Create table failed") + } +} + +func TestMyAddColumn(t *testing.T) { + + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + + old, err := db.Table("test").GetSchema() + if err != nil { + fmt.Println(err.Error()) + } + db.Table("test").AddColumn("addcolumn", "integer", "comment") + new, err := db.Table("test").GetSchema() + if err != nil { + fmt.Println(err.Error()) + } + + if len(old.Columns) == len(new.Columns) { + t.Errorf("Column already exist") + } +} + +func TestMyInsert(t *testing.T) { + + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + + vl := make(AssRow) + vl["name"] = "toto" + vl["description"] = "tata" + + old, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } + jsonStringOld, _ := json.Marshal(old) + fmt.Println(string(jsonStringOld)) + + db.Table("test").UpdateOrInsert(vl) + + new, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } + jsonStringNew, _ := json.Marshal(new) + fmt.Println(string(jsonStringNew)) + + if len(jsonStringOld) == len(jsonStringNew) { + t.Errorf("Error row not created") + } + jsonFile, err := os.Open("insert.json") + defer jsonFile.Close() + var result map[string]interface{} + byteValue, _ := ioutil.ReadAll(jsonFile) + json.Unmarshal(byteValue, &result) +} + +func TestMyUpdate(t *testing.T) { + + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + + vl := make(AssRow) + vl["id"] = 1 + vl["name"] = "titi" + vl["description"] = "toto" + + old, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } + jsonStringOld, _ := json.Marshal(old) + fmt.Println(string(jsonStringOld)) + + db.Table("test").UpdateOrInsert(vl) + + new, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } + jsonStringNew, _ := json.Marshal(new) + fmt.Println(string(jsonStringNew)) + + if string(jsonStringOld) == string(jsonStringNew) { + t.Errorf("Error row not updated") + } + +} + +func TestMyDelete(t *testing.T) { + + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + + vl := make(AssRow) + vl["id"] = 1 + + old, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } + jsonStringOld, _ := json.Marshal(old) + fmt.Println(string(jsonStringOld)) + + db.Table("test").Delete(vl) + + new, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } + jsonStringNew, _ := json.Marshal(new) + fmt.Println(string(jsonStringNew)) + + if len(jsonStringOld) == len(jsonStringNew) { + t.Errorf("Error row not deleted") + } +} + +func TestMyDeleteColumn(t *testing.T) { + + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + + old, err := db.Table("test").GetSchema() + if err != nil { + fmt.Println(err.Error()) + } + db.Table("test").DeleteColumn("addcolumn") + new, err := db.Table("test").GetSchema() + if err != nil { + fmt.Println(err.Error()) + } + + if len(old.Columns) == len(new.Columns) { + t.Errorf("Error column not deleted") + } +} + +func TestMyDeleteTable(t *testing.T) { + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + db.Table("test").DeleteTable() + +} + +func TestMyImportSchema(t *testing.T) { + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + db.ImportSchema("pfn.json") +} + +func TestMyClearImportSchema(t *testing.T) { + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + db.ClearImportSchema("pfn.json") +} + +func TestMyGetSchema(t *testing.T) { + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + data, err := db.GetSchema() + if err != nil { + fmt.Println(err.Error()) + } + val, _ := json.Marshal(data) + fmt.Println(string(val)) +} + +func TestMySaveSchema(t *testing.T) { + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + err := db.SaveSchema("schema.json") + if err != nil { + fmt.Println(err.Error()) + } +} +func TestMyGenerateTemplate(t *testing.T) { + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + err := db.GenerateTemplate("plantuml.tmpl", "schema.puml") + if err != nil { + fmt.Println(err.Error()) + } +} + +func TestMyGenerateTableTemplate(t *testing.T) { + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() + err := db.GenerateTableTemplates("table.tmpl", "gen", "html") + if err != nil { + fmt.Println(err.Error()) + } +} diff --git a/pg_test.go b/pg_test.go index c24fde9..52f3c08 100755 --- a/pg_test.go +++ b/pg_test.go @@ -8,7 +8,7 @@ import ( "testing" ) -func TestCreateTable(t *testing.T) { +func TestPgCreateTable(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() @@ -37,7 +37,7 @@ func TestCreateTable(t *testing.T) { } } -func TestAddColumn(t *testing.T) { +func TestPgAddColumn(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() @@ -57,7 +57,7 @@ func TestAddColumn(t *testing.T) { } } -func TestInsert(t *testing.T) { +func TestPgInsert(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() @@ -92,7 +92,7 @@ func TestInsert(t *testing.T) { json.Unmarshal(byteValue, &result) } -func TestUpdate(t *testing.T) { +func TestPgUpdate(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() @@ -124,7 +124,7 @@ func TestUpdate(t *testing.T) { } -func TestDelete(t *testing.T) { +func TestPgDelete(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() @@ -153,7 +153,7 @@ func TestDelete(t *testing.T) { } } -func TestDeleteColumn(t *testing.T) { +func TestPgDeleteColumn(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() @@ -173,26 +173,26 @@ func TestDeleteColumn(t *testing.T) { } } -func TestDeleteTable(t *testing.T) { +func TestPgDeleteTable(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() db.Table("test").DeleteTable() } -func TestImportSchema(t *testing.T) { +func TestPgImportSchema(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() db.ImportSchema("pfn.json") } -func TestClearImportSchema(t *testing.T) { +func TestPgClearImportSchema(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() db.ClearImportSchema("pfn.json") } -func TestGetSchema(t *testing.T) { +func TestPgGetSchema(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() data, err := db.GetSchema() @@ -203,7 +203,7 @@ func TestGetSchema(t *testing.T) { fmt.Println(string(val)) } -func TestSaveSchema(t *testing.T) { +func TestPgSaveSchema(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() err := db.SaveSchema("schema.json") @@ -211,7 +211,7 @@ func TestSaveSchema(t *testing.T) { fmt.Println(err.Error()) } } -func TestGenerateTemplate(t *testing.T) { +func TestPgGenerateTemplate(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() err := db.GenerateTemplate("plantuml.tmpl", "schema.puml") @@ -220,7 +220,7 @@ func TestGenerateTemplate(t *testing.T) { } } -func TestGenerateTableTemplate(t *testing.T) { +func TestPgGenerateTableTemplate(t *testing.T) { db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") defer db.Close() err := db.GenerateTableTemplates("table.tmpl", "gen", "html") diff --git a/survey.json b/survey.json new file mode 100644 index 0000000..29d0065 --- /dev/null +++ b/survey.json @@ -0,0 +1,58 @@ +[ + { + "name": "survey", + "columns": { + "id": "integer", + "name": "varchar(255)", + "description": "varchar(1000)", + "start":"timestmp", + "stop":"timestmpestamp", + "published":"bool" + } + }, + { + "name": "surveyquestion", + "columns": { + "id": "integer", + "survey_id": "integer", + "question_id": "integer" + } + }, + { + "name": "chapter", + "columns": { + "id": "integer", + "name": "varchar(255)", + "order": "integer" + } + }, + { + "name": "question", + "columns": { + "name": "varchar(255)", + "text": "varchar(1000)", + "type": "varchar(255)", + "options": "varchar(1000)", + "id": "integer", + "order": "integer", + "chapter_id": "integer" + } + }, + { + "name": "authuser", + "columns": { + "survey_id": "integer", + "code": "varchar(1000)", + "id": "integer" + } + }, + { + "name": "answer", + "columns": { + "question_id": "integer", + "code_id": "integer", + "value":"integer", + "text": "varchar(2000)" + } + } +] \ No newline at end of file diff --git a/test_table.json b/test_table.json index b37dd18..309d3f2 100644 --- a/test_table.json +++ b/test_table.json @@ -11,7 +11,7 @@ "longitude":"float|map", "intvalue":"integer", "floatvalue":"float", - "price":"money", + "price":"float", "testtype_id":"integer" } } \ No newline at end of file