mysql OK, partially tested

This commit is contained in:
ycc 2023-07-03 15:26:16 +02:00
parent 31690925c1
commit 5ce4f94524
8 changed files with 180 additions and 27 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/
__debug_bin __debug_bin
gen/

153
db.go
View File

@ -9,6 +9,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"reflect"
"strconv" "strconv"
"strings" "strings"
@ -79,7 +80,7 @@ func (db *Db) QueryAssociativeArray(query string) (Rows, error) {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
// get rows
results := Rows{} results := Rows{}
cols, err := rows.Columns() cols, err := rows.Columns()
if err != nil { if err != nil {
@ -87,6 +88,16 @@ func (db *Db) QueryAssociativeArray(query string) (Rows, error) {
log.Println(query) log.Println(query)
return nil, err return nil, err
} }
// make types map
columnTypes, err := rows.ColumnTypes()
if err != nil {
return nil, err
}
columnType := make(map[string]string)
for _, colType := range columnTypes {
columnType[colType.Name()] = colType.DatabaseTypeName()
}
for rows.Next() { for rows.Next() {
// Create a slice of interface{}'s to represent each column, // Create a slice of interface{}'s to represent each column,
// and a second slice to contain pointers to each item in the columns slice. // and a second slice to contain pointers to each item in the columns slice.
@ -97,19 +108,69 @@ func (db *Db) QueryAssociativeArray(query string) (Rows, error) {
} }
// Scan the result into the column pointers... // Scan the result into the column pointers...
if err := rows.Scan(columnPointers...); err != nil { err = rows.Scan(columnPointers...)
if err != nil {
return nil, err
} }
// Create our map, and retrieve the value for each column from the pointers slice, // Create our map, and retrieve the value for each column from the pointers slice,
// storing it in the map with the name of the column as the key. // storing it in the map with the name of the column as the key.
m := make(AssRow) m := make(AssRow)
for i, colName := range cols { for i, colName := range cols {
//fmt.Println(colName)
val := columnPointers[i].(*interface{}) val := columnPointers[i].(*interface{})
m[colName] = fmt.Sprintf("%v", *val) if db.Driver == "mysql" {
if (*val) == nil {
m[colName] = nil
} else {
switch columnType[colName] {
case "INT", "BIGINT":
i, err := strconv.ParseInt(fmt.Sprintf("%s", *val), 10, 64)
if err != nil {
return nil, err
}
m[colName] = i
case "UNSIGNED BIGINT":
u, err := strconv.ParseUint(fmt.Sprintf("%s", *val), 10, 64)
if err != nil {
return nil, err
}
m[colName] = u
case "FLOAT":
f, err := strconv.ParseFloat(fmt.Sprintf("%s", *val), 64)
if err != nil {
return nil, err
}
m[colName] = f
case "TINYINT":
i, err := strconv.ParseInt(fmt.Sprintf("%s", *val), 10, 64)
if err != nil {
return nil, err
}
if i == 1 {
m[colName] = true
} else {
m[colName] = false
}
case "VARCHAR", "TEXT", "TIMESTAMP":
m[colName] = fmt.Sprintf("%s", *val)
default:
if reflect.ValueOf(val).IsNil() {
m[colName] = nil
} else {
fmt.Printf("Unknow type : %s", columnType[colName])
m[colName] = fmt.Sprintf("%v", *val)
}
}
}
}
if db.Driver == "postgres" {
m[colName] = *val
}
} }
results = append(results, m) results = append(results, m)
} }
return results, nil return results, nil
} }
@ -117,7 +178,7 @@ func (db *Db) QueryAssociativeArray(query string) (Rows, error) {
// GetSchema : Provide table schema as an associative array // GetSchema : Provide table schema as an associative array
func (t *TableInfo) GetSchema() (*TableInfo, error) { 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 + "';" 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 + "';" mySchema := "SELECT COLUMN_NAME as name, CONCAT(DATA_TYPE, COALESCE(CONCAT('(' , CHARACTER_MAXIMUM_LENGTH, ')'), '')) as type FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + t.Name + "';"
var schemaQuery string var schemaQuery string
var ti TableInfo var ti TableInfo
ti.Name = t.Name ti.Name = t.Name
@ -138,13 +199,13 @@ func (t *TableInfo) GetSchema() (*TableInfo, error) {
var name, rowtype, comment string var name, rowtype, comment string
for key, element := range row { for key, element := range row {
if key == "name" { if key == "name" {
name = fmt.Sprintf("%v", element) name = fmt.Sprintf("%s", element)
} }
if key == "type" { if key == "type" {
rowtype = fmt.Sprintf("%v", element) rowtype = fmt.Sprintf("%s", element)
} }
if key == "comment" { if key == "comment" {
comment = fmt.Sprintf("%v", element) comment = fmt.Sprintf("%s", element)
} }
} }
ti.Columns[name] = rowtype ti.Columns[name] = rowtype
@ -200,6 +261,16 @@ func (db *Db) myListTables() (Rows, error) {
} }
func (db *Db) CreateTable(t TableInfo) error { func (db *Db) CreateTable(t TableInfo) error {
if db.Driver == "postgres" {
return db.pgCreateTable(t)
}
if db.Driver == "mysql" {
return db.myCreateTable(t)
}
return errors.New("no driver")
}
func (db *Db) pgCreateTable(t TableInfo) error {
t.db = db t.db = db
query := "create table " + t.Name + " ( " query := "create table " + t.Name + " ( "
columns := "" columns := ""
@ -234,6 +305,32 @@ func (db *Db) CreateTable(t TableInfo) error {
return nil return nil
} }
func (db *Db) myCreateTable(t TableInfo) error {
t.db = db
query := "create table " + t.Name + " ( "
columns := ""
for name, rowtype := range t.Columns {
if fmt.Sprintf("%v", name) == "id" {
columns += fmt.Sprintf("%v", name) + " " + "SERIAL PRIMARY KEY,"
} else {
desc := strings.Split(fmt.Sprintf("%v", rowtype), "|")
columns += fmt.Sprintf("%v", name) + " " + desc[0]
if len(desc) > 1 {
columns += " COMMENT " + pq.QuoteLiteral(desc[1])
}
columns += ","
}
}
query += columns
query = query[:len(query)-1] + " )"
_, err := t.db.conn.Query(query)
if err != nil {
log.Println(err.Error())
return err
}
return nil
}
func (t *TableInfo) DeleteTable() error { func (t *TableInfo) DeleteTable() error {
query := "drop table " + t.Name query := "drop table " + t.Name
_, err := t.db.conn.Query(query) _, err := t.db.conn.Query(query)
@ -251,6 +348,16 @@ func (t *TableInfo) DeleteTable() error {
} }
func (t *TableInfo) AddColumn(name string, sqltype string, comment string) error { func (t *TableInfo) AddColumn(name string, sqltype string, comment string) error {
if t.db.Driver == "postgres" {
return t.pgAddColumn(name, sqltype, comment)
}
if t.db.Driver == "mysql" {
return t.myAddColumn(name, sqltype, comment)
}
return errors.New("no driver")
}
func (t *TableInfo) pgAddColumn(name string, sqltype string, comment string) error {
query := "alter table " + t.Name + " add " + name + " " + sqltype query := "alter table " + t.Name + " add " + name + " " + sqltype
rows, err := t.db.conn.Query(query) rows, err := t.db.conn.Query(query)
if err != nil { if err != nil {
@ -269,6 +376,20 @@ func (t *TableInfo) AddColumn(name string, sqltype string, comment string) error
return nil return nil
} }
func (t *TableInfo) myAddColumn(name string, sqltype string, comment string) error {
query := "alter table " + t.Name + " add " + name + " " + sqltype
if strings.TrimSpace(comment) != "" {
query += " COMMENT " + pq.QuoteLiteral(comment)
}
rows, err := t.db.conn.Query(query)
if err != nil {
log.Println(err)
return err
}
defer rows.Close()
return nil
}
func (t *TableInfo) DeleteColumn(name string) error { func (t *TableInfo) DeleteColumn(name string) error {
query := "alter table " + t.Name + " drop " + name query := "alter table " + t.Name + " drop " + name
rows, err := t.db.conn.Query(query) rows, err := t.db.conn.Query(query)
@ -355,8 +476,16 @@ func (t *TableInfo) Insert(record AssRow) (int, error) {
columns += key + "," columns += key + ","
values += FormatForSQL(t.Columns[key], element) + "," values += FormatForSQL(t.Columns[key], element) + ","
} }
if t.db.Driver == "postgres" {
err = t.db.conn.QueryRow("INSERT INTO " + t.Name + "(" + removeLastChar(columns) + ") VALUES (" + removeLastChar(values) + ") RETURNING id").Scan(&id) err = t.db.conn.QueryRow("INSERT INTO " + t.Name + "(" + removeLastChar(columns) + ") VALUES (" + removeLastChar(values) + ") RETURNING id").Scan(&id)
}
if t.db.Driver == "mysql" {
_, err = t.db.conn.Query("INSERT INTO " + t.Name + "(" + removeLastChar(columns) + ") VALUES (" + removeLastChar(values) + ")")
if err != nil {
return id, err
}
err = t.db.conn.QueryRow("LAST_INSERT_ID();").Scan(&id)
}
return id, err return id, err
} }

9
go.mod
View File

@ -3,6 +3,13 @@ module forge.redroom.link/yves/sqldb
go 1.17 go 1.17
require ( require (
github.com/go-sql-driver/mysql v1.7.1
github.com/lib/pq v1.10.4 github.com/lib/pq v1.10.4
gorm.io/driver/mysql v1.5.1
)
require (
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
gorm.io/gorm v1.25.1 // indirect
) )

9
go.sum
View File

@ -1,4 +1,13 @@
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= 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/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64=
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=

View File

@ -9,7 +9,7 @@ import (
) )
func TestMyCreateTable(t *testing.T) { func TestMyCreateTable(t *testing.T) {
db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
jsonFile, err := os.Open("test_table.json") jsonFile, err := os.Open("test_table.json")
@ -39,7 +39,7 @@ func TestMyCreateTable(t *testing.T) {
func TestMyAddColumn(t *testing.T) { func TestMyAddColumn(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
old, err := db.Table("test").GetSchema() old, err := db.Table("test").GetSchema()
@ -59,12 +59,15 @@ func TestMyAddColumn(t *testing.T) {
func TestMyInsert(t *testing.T) { func TestMyInsert(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
vl := make(AssRow) vl := make(AssRow)
vl["name"] = "toto" vl["name"] = "toto"
vl["description"] = "tata" vl["description"] = "tata"
vl["longitude"] = 1.38
vl["enddate"] = "2022-09-01"
vl["boolvalue"] = "true"
old, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") old, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "")
if err != nil { if err != nil {
@ -94,7 +97,7 @@ func TestMyInsert(t *testing.T) {
func TestMyUpdate(t *testing.T) { func TestMyUpdate(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
vl := make(AssRow) vl := make(AssRow)
@ -126,7 +129,7 @@ func TestMyUpdate(t *testing.T) {
func TestMyDelete(t *testing.T) { func TestMyDelete(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
vl := make(AssRow) vl := make(AssRow)
@ -155,7 +158,7 @@ func TestMyDelete(t *testing.T) {
func TestMyDeleteColumn(t *testing.T) { func TestMyDeleteColumn(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
old, err := db.Table("test").GetSchema() old, err := db.Table("test").GetSchema()
@ -174,26 +177,26 @@ func TestMyDeleteColumn(t *testing.T) {
} }
func TestMyDeleteTable(t *testing.T) { func TestMyDeleteTable(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
db.Table("test").DeleteTable() db.Table("test").DeleteTable()
} }
func TestMyImportSchema(t *testing.T) { func TestMyImportSchema(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
db.ImportSchema("pfn.json") db.ImportSchema("pfn.json")
} }
func TestMyClearImportSchema(t *testing.T) { func TestMyClearImportSchema(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
db.ClearImportSchema("pfn.json") db.ClearImportSchema("pfn.json")
} }
func TestMyGetSchema(t *testing.T) { func TestMyGetSchema(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true?charset=utf8mb4&collation=utf8mb4_unicode_ci")
defer db.Close() defer db.Close()
data, err := db.GetSchema() data, err := db.GetSchema()
if err != nil { if err != nil {
@ -204,7 +207,7 @@ func TestMyGetSchema(t *testing.T) {
} }
func TestMySaveSchema(t *testing.T) { func TestMySaveSchema(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
err := db.SaveSchema("schema.json") err := db.SaveSchema("schema.json")
if err != nil { if err != nil {
@ -212,7 +215,7 @@ func TestMySaveSchema(t *testing.T) {
} }
} }
func TestMyGenerateTemplate(t *testing.T) { func TestMyGenerateTemplate(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
err := db.GenerateTemplate("plantuml.tmpl", "schema.puml") err := db.GenerateTemplate("plantuml.tmpl", "schema.puml")
if err != nil { if err != nil {
@ -221,7 +224,7 @@ func TestMyGenerateTemplate(t *testing.T) {
} }
func TestMyGenerateTableTemplate(t *testing.T) { func TestMyGenerateTableTemplate(t *testing.T) {
db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") db := Open("mysql", "test:test@tcp(127.0.0.1:3306)/test?parseTime=true")
defer db.Close() defer db.Close()
err := db.GenerateTableTemplates("table.tmpl", "gen", "html") err := db.GenerateTableTemplates("table.tmpl", "gen", "html")
if err != nil { if err != nil {

View File

@ -65,6 +65,9 @@ func TestPgInsert(t *testing.T) {
vl := make(AssRow) vl := make(AssRow)
vl["name"] = "toto" vl["name"] = "toto"
vl["description"] = "tata" vl["description"] = "tata"
vl["longitude"] = 1.38
vl["enddate"] = "2022-09-01"
vl["boolvalue"] = "true"
old, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") old, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "")
if err != nil { if err != nil {

BIN
sqldb

Binary file not shown.

View File

@ -12,6 +12,7 @@
"intvalue":"integer", "intvalue":"integer",
"floatvalue":"float", "floatvalue":"float",
"price":"float", "price":"float",
"testtype_id":"integer" "testtype_id":"integer",
"boolvalue":"boolean"
} }
} }