From eae0a37f50ffa34a7cbf0ec09bc39c098523e3b1 Mon Sep 17 00:00:00 2001 From: redr00m <46130884+redr00m@users.noreply.github.com> Date: Fri, 30 Jul 2021 15:50:25 +0200 Subject: [PATCH 1/3] Initial commit --- .gitignore | 15 +++++++++++++++ LICENSE | 21 +++++++++++++++++++++ README.md | 2 ++ 3 files changed, 38 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66fd13c --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..aa7678a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 redr00m + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ba8e90 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# sqldb +Generic SQL database access library From 6f1cb9a2d07feadc1134a0134175bd1821c4fdbe Mon Sep 17 00:00:00 2001 From: ycc Date: Fri, 30 Jul 2021 15:52:01 +0200 Subject: [PATCH 2/3] import --- README.md | 2 +- go.mod | 5 + go.sum | 2 + pg.go | 281 ++++++++++++++++++++++++++++++++++++++++++++ pg_init.sql | 7 ++ pg_test.go | 146 +++++++++++++++++++++++ test_table.json | 17 +++ testtype_table.json | 17 +++ 8 files changed, 476 insertions(+), 1 deletion(-) create mode 100644 go.mod create mode 100644 go.sum create mode 100755 pg.go create mode 100644 pg_init.sql create mode 100755 pg_test.go create mode 100644 test_table.json create mode 100644 testtype_table.json diff --git a/README.md b/README.md index 2ba8e90..f782e22 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # sqldb -Generic SQL database access library +Generic SQL access libradry diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ceeb144 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/redr00m/sqldb + +go 1.15 + +require github.com/lib/pq v1.9.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a4a764e --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= +github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= diff --git a/pg.go b/pg.go new file mode 100755 index 0000000..94f63a6 --- /dev/null +++ b/pg.go @@ -0,0 +1,281 @@ +package sqldb + +import ( + "database/sql" + "fmt" + "log" + "strconv" + "strings" + + "github.com/lib/pq" +) + +var db *sql.DB + +// AssRow : associative row type +type AssRow map[string]interface{} + +// Table is a table structure description +type Table struct { + Name string `json:"name"` + Columns map[string]string `json:"columns"` +} + +// Open the database +func Open(driver string, url string) { + var err error + db, err = sql.Open(driver, url) + if err != nil { + log.Println(err) + } +} + +// Close the database connection +func Close() { + db.Close() +} + +// GetAssociativeArray : Provide results as an associative array +func GetAssociativeArray(table string, columns []string, restriction string, sortkeys []string, dir string) []AssRow { + return QueryAssociativeArray(buildSelect(table, "", columns, restriction, sortkeys, dir)) +} + +// QueryAssociativeArray : Provide results as an associative array +func QueryAssociativeArray(query string) []AssRow { + rows, err := db.Query(query) + if err != nil { + log.Println(err) + log.Println(query) + } + defer rows.Close() + + var results []AssRow + cols, _ := rows.Columns() + + for rows.Next() { + // Create a slice of interface{}'s to represent each column, + // and a second slice to contain pointers to each item in the columns slice. + columns := make([]interface{}, len(cols)) + columnPointers := make([]interface{}, len(cols)) + for i := range columns { + columnPointers[i] = &columns[i] + } + + // Scan the result into the column pointers... + if err := rows.Scan(columnPointers...); err != nil { + + } + // 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. + m := make(AssRow) + for i, colName := range cols { + val := columnPointers[i].(*interface{}) + m[colName] = *val + } + // jsonString, _ := json.Marshal(m) + // Outputs: map[columnName:value columnName2:value2 columnName3:value3 ...] + // fmt.Println(string(jsonString)) + + results = append(results, m) + + } + return results +} + +// GetSchema : Provide results as an associative array +func GetSchema(table string) Table { + t := Table{Name: table} + cols := QueryAssociativeArray("SELECT column_name :: varchar as name, REPLACE(REPLACE(data_type,'character varying','varchar'),'character','char') || COALESCE('(' || character_maximum_length || ')', '') as type from INFORMATION_SCHEMA.COLUMNS where table_name ='" + table + "';") + t.Columns = make(map[string]string) + for _, row := range cols { + var name, rowtype string + for key, element := range row { + if key == "name" { + name = fmt.Sprintf("%v", element) + } + if key == "type" { + rowtype = fmt.Sprintf("%v", element) + } + } + t.Columns[name] = rowtype + } + return t +} + +func ListTables() []AssRow { + return QueryAssociativeArray("SELECT table_name :: varchar FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name;") +} + +func CreateTable(t Table) { + 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 { + + columns += fmt.Sprintf("%v", name) + " " + fmt.Sprintf("%v", rowtype) + columns += "," + } + } + query += columns + query = query[:len(query)-1] + " )" + _, err := db.Query(query) + if err != nil { + log.Println(query) + } + query = "create sequence if not exists sq_" + t.Name + _, err = db.Query(query) + if err != nil { + log.Println(query) + } +} + +func DeleteTable(table string) { + query := "drop table " + table + _, err := db.Query(query) + if err != nil { + log.Println(err) + } + query = "drop sequence if exists sq_" + table + _, err = db.Query(query) + if err != nil { + log.Println(err) + } +} + +func AddColumn(table string, name string, sqltype string) { + query := "alter table " + table + " add " + name + " " + sqltype + rows, err := db.Query(query) + if err != nil { + log.Println(err) + } + defer rows.Close() +} + +func DeleteColumn(table string, name string) { + query := "alter table " + table + " drop " + name + rows, err := db.Query(query) + if err != nil { + log.Println(err) + } + defer rows.Close() +} + +func ListSequences() []AssRow { + return QueryAssociativeArray("SELECT sequence_name :: varchar FROM information_schema.sequences WHERE sequence_schema = 'public' ORDER BY sequence_name;") +} + +func buildSelect(table string, key string, columns []string, restriction string, sortkeys []string, dir ...string) string { + if key != "" { + columns = append(columns, key) + } + query := "select " + strings.Join(columns, ",") + " from " + table + if restriction != "" { + query += " where " + restriction + } + if len(sortkeys) > 0 { + query += " order by " + strings.Join(sortkeys, ",") + } + if len(dir) > 0 { + query += " " + dir[0] + } + return query +} + +func Insert(table string, record AssRow) int { + columns := "" + values := "" + schema := GetSchema(table) + var id int + + for key, element := range record { + + if strings.Contains(schema.Columns[key], "char") || strings.Contains(schema.Columns[key], "date") { + columns += key + "," + values += fmt.Sprintf(pq.QuoteLiteral(fmt.Sprintf("%v", element))) + "," + } else { + + columns += key + "," + values += fmt.Sprintf("%v", element) + "," + } + } + + db.QueryRow("INSERT INTO " + table + "(" + removeLastChar(columns) + ") VALUES (" + removeLastChar(values) + ") RETURNING id").Scan(&id) + return id +} + +func Update(table string, record AssRow) string { + + schema := GetSchema(table) + id := "" + stack := "" + + for key, element := range record { + + if strings.Contains(schema.Columns[key], "char") || strings.Contains(schema.Columns[key], "date") { + + stack = stack + " " + key + " = " + pq.QuoteLiteral(fmt.Sprintf("%v", element)) + "," + + } else { + + if key == "id" { + id = fmt.Sprintf("%v", element) + } else { + stack = stack + " " + key + " = " + fmt.Sprintf("%v", element) + "," + } + } + } + stack = removeLastChar(stack) + query := ("UPDATE " + table + " SET " + stack + " WHERE id = " + id) + rows, err := db.Query(query) + if err != nil { + log.Println(query) + log.Println(err) + } + defer rows.Close() + return query +} + +func Delete(table string, record AssRow) string { + id := "" + values := "" + + for key, element := range record { + if key == "id" { + values += fmt.Sprintf("%v", element) + "," + id = removeLastChar(values) + + } + } + query := ("DELETE FROM " + table + " WHERE id = " + id) + rows, err := db.Query(query) + if err != nil { + log.Println(query) + log.Println(err) + } + defer rows.Close() + return query +} + +func UpdateOrInsert(table string, record AssRow) int { + id := 0 + + for key, element := range record { + if key == "id" { + sid := fmt.Sprintf("%v", element) + id, _ = strconv.Atoi(sid) + } + } + if id == 0 { + return Insert(table, record) + } else { + Update(table, record) + return id + } +} + +func removeLastChar(s string) string { + r := []rune(s) + return string(r[:len(r)-1]) +} diff --git a/pg_init.sql b/pg_init.sql new file mode 100644 index 0000000..96ec4d0 --- /dev/null +++ b/pg_init.sql @@ -0,0 +1,7 @@ +-- Init on Linux : +-- 1) login as postgres user : sudo su postgres +-- 2) start script : psql -f pg_init.sql + +CREATE ROLE test WITH PASSWORD 'test' NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN; +CREATE DATABASE test OWNER test encoding 'UTF8'; +GRANT ALL PRIVILEGES ON DATABASE test TO test; diff --git a/pg_test.go b/pg_test.go new file mode 100755 index 0000000..4112b6f --- /dev/null +++ b/pg_test.go @@ -0,0 +1,146 @@ +package sqldb + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "testing" +) + +func TestCreateTable(t *testing.T) { + Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer Close() + + jsonFile, err := os.Open("test_table.json") + if err != nil { + fmt.Println(err) + } + defer jsonFile.Close() + + byteValue, _ := ioutil.ReadAll(jsonFile) + + var data Table + json.Unmarshal([]byte(byteValue), &data) + + CreateTable(data) + + tbl := GetSchema(data.Name) + if len(tbl.Columns) == 0 { + t.Errorf("Create table failed") + } +} + +func TestAddColumn(t *testing.T) { + + Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer Close() + + old := GetSchema("test") + AddColumn("test", "addcolumn", "integer") + new := GetSchema("test") + + if len(old.Columns) == len(new.Columns) { + t.Errorf("Column already exist") + } +} + +func TestInsert(t *testing.T) { + + Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer Close() + + vl := make(AssRow) + vl["name"] = "toto" + vl["description"] = "tata" + + old := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + jsonStringOld, _ := json.Marshal(old) + fmt.Println(string(jsonStringOld)) + + UpdateOrInsert("test", vl) + + new := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + jsonStringNew, _ := json.Marshal(new) + fmt.Println(string(jsonStringNew)) + + if len(jsonStringOld) == len(jsonStringNew) { + t.Errorf("Error row not created") + } +} + +func TestUpdate(t *testing.T) { + + Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer Close() + + vl := make(AssRow) + vl["id"] = 1 + vl["name"] = "titi" + vl["description"] = "toto" + + old := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + jsonStringOld, _ := json.Marshal(old) + fmt.Println(string(jsonStringOld)) + + UpdateOrInsert("test", vl) + + new := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + jsonStringNew, _ := json.Marshal(new) + fmt.Println(string(jsonStringNew)) + + if string(jsonStringOld) == string(jsonStringNew) { + t.Errorf("Error row not updated") + } + +} + +func TestDelete(t *testing.T) { + + Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer Close() + + vl := make(AssRow) + vl["id"] = 1 + + old := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + jsonStringOld, _ := json.Marshal(old) + fmt.Println(string(jsonStringOld)) + + Delete("test", vl) + + new := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + jsonStringNew, _ := json.Marshal(new) + fmt.Println(string(jsonStringNew)) + + if len(jsonStringOld) == len(jsonStringNew) { + t.Errorf("Error row not deleted") + } +} + +func TestDeleteColumn(t *testing.T) { + + Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer Close() + + old := GetSchema("test") + DeleteColumn("test", "addcolumn") + new := GetSchema("test") + + if len(old.Columns) == len(new.Columns) { + t.Errorf("Error column not deleted") + } +} + +func TestDeleteTable(t *testing.T) { + Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer Close() + + DeleteTable("test") + + tbl := GetSchema("test") + + if len(tbl.Columns) != 0 { + t.Errorf("Delete table failed") + } +} diff --git a/test_table.json b/test_table.json new file mode 100644 index 0000000..507166b --- /dev/null +++ b/test_table.json @@ -0,0 +1,17 @@ +{ + "name":"test", + "columns": + { + "id":"integer", + "name":"varchar(255)", + "description":"varchar(1000)", + "startdate":"timestamp", + "enddate":"timestamp", + "latitude":"float", + "longitude":"float", + "intvalue":"integer", + "floatvalue":"float", + "price":"money", + "testtype_id":"integer" + } +} \ No newline at end of file diff --git a/testtype_table.json b/testtype_table.json new file mode 100644 index 0000000..63a00db --- /dev/null +++ b/testtype_table.json @@ -0,0 +1,17 @@ +{ + "name":"testtype", + "columns":[ + { + "name":"id", + "type":"integer" + }, + { + "name":"name", + "type":"varchar(255)" + }, + { + "name":"detail", + "type":"varchar(255)" + } + ] +} \ No newline at end of file From 7f0268386646bd234a83bd2c7baf558e010ba0fe Mon Sep 17 00:00:00 2001 From: ycc Date: Thu, 28 Oct 2021 10:34:10 +0200 Subject: [PATCH 3/3] Global refactor --- LICENSE | 21 ------ README.md | 2 - go.mod | 2 +- pg.go | 190 ++++++++++++++++++++++++++++++++--------------------- pg_test.go | 112 ++++++++++++++++++++----------- 5 files changed, 192 insertions(+), 135 deletions(-) delete mode 100644 LICENSE delete mode 100644 README.md diff --git a/LICENSE b/LICENSE deleted file mode 100644 index aa7678a..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 redr00m - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index f782e22..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# sqldb -Generic SQL access libradry diff --git a/go.mod b/go.mod index ceeb144..03dae42 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/redr00m/sqldb +module forge.redroom.link/yves/sqldb go 1.15 diff --git a/pg.go b/pg.go index 94f63a6..952559e 100755 --- a/pg.go +++ b/pg.go @@ -10,48 +10,72 @@ import ( "github.com/lib/pq" ) -var db *sql.DB +type Db struct { + Driver string + Url string + conn *sql.DB +} // AssRow : associative row type type AssRow map[string]interface{} +// Select Result +type Rows []AssRow + // Table is a table structure description -type Table struct { +type TableInfo struct { Name string `json:"name"` Columns map[string]string `json:"columns"` + db *Db } // Open the database -func Open(driver string, url string) { +func Open(driver string, url string) *Db { + var database Db var err error - db, err = sql.Open(driver, url) + database.Driver = driver + database.Url = url + database.conn, err = sql.Open(driver, url) if err != nil { log.Println(err) } + return &database } // Close the database connection -func Close() { - db.Close() +func (db *Db) Close() { + db.conn.Close() +} + +func (db *Db) Table(name string) *TableInfo { + var ti TableInfo + ti.Name = name + ti.db = db + return &ti } // GetAssociativeArray : Provide results as an associative array -func GetAssociativeArray(table string, columns []string, restriction string, sortkeys []string, dir string) []AssRow { - return QueryAssociativeArray(buildSelect(table, "", columns, restriction, sortkeys, dir)) +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 -func QueryAssociativeArray(query string) []AssRow { - rows, err := db.Query(query) +func (db *Db) QueryAssociativeArray(query string) (Rows, error) { + rows, err := db.conn.Query(query) if err != nil { log.Println(err) log.Println(query) + return nil, err } defer rows.Close() - var results []AssRow - cols, _ := rows.Columns() - + var results Rows + cols, err := rows.Columns() + if err != nil { + log.Println(err) + log.Println(query) + return nil, err + } for rows.Next() { // Create a slice of interface{}'s to represent each column, // and a second slice to contain pointers to each item in the columns slice. @@ -70,23 +94,26 @@ func QueryAssociativeArray(query string) []AssRow { m := make(AssRow) for i, colName := range cols { val := columnPointers[i].(*interface{}) - m[colName] = *val + m[colName] = fmt.Sprintf("%v", *val) } - // jsonString, _ := json.Marshal(m) - // Outputs: map[columnName:value columnName2:value2 columnName3:value3 ...] - // fmt.Println(string(jsonString)) results = append(results, m) } - return results + return results, nil } // GetSchema : Provide results as an associative array -func GetSchema(table string) Table { - t := Table{Name: table} - cols := QueryAssociativeArray("SELECT column_name :: varchar as name, REPLACE(REPLACE(data_type,'character varying','varchar'),'character','char') || COALESCE('(' || character_maximum_length || ')', '') as type from INFORMATION_SCHEMA.COLUMNS where table_name ='" + table + "';") - t.Columns = make(map[string]string) +func (t *TableInfo) GetSchema() (*TableInfo, error) { + 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 from INFORMATION_SCHEMA.COLUMNS where table_name ='" + t.Name + "';") + if err != nil { + log.Println(err) + return nil, err + } + ti.Columns = make(map[string]string) for _, row := range cols { var name, rowtype string for key, element := range row { @@ -97,16 +124,17 @@ func GetSchema(table string) Table { rowtype = fmt.Sprintf("%v", element) } } - t.Columns[name] = rowtype + ti.Columns[name] = rowtype } - return t + return &ti, nil } -func ListTables() []AssRow { - return QueryAssociativeArray("SELECT table_name :: varchar FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name;") +func (db *Db) ListTables() (Rows, error) { + return db.QueryAssociativeArray("SELECT table_name :: varchar FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name;") } -func CreateTable(t Table) { +func (db *Db) CreateTable(t TableInfo) error { + t.db = db query := "create table " + t.Name + " ( " columns := "" for name, rowtype := range t.Columns { @@ -120,57 +148,61 @@ func CreateTable(t Table) { } query += columns query = query[:len(query)-1] + " )" - _, err := db.Query(query) + _, err := t.db.conn.Query(query) if err != nil { - log.Println(query) - } - query = "create sequence if not exists sq_" + t.Name - _, err = db.Query(query) - if err != nil { - log.Println(query) + log.Println(err.Error()) + return err } + return nil } -func DeleteTable(table string) { - query := "drop table " + table - _, err := db.Query(query) +func (t *TableInfo) DeleteTable() error { + query := "drop table " + t.Name + _, err := t.db.conn.Query(query) if err != nil { - log.Println(err) + log.Println(err.Error()) + return err } - query = "drop sequence if exists sq_" + table - _, err = db.Query(query) + query = "drop sequence if exists sq_" + t.Name + _, err = t.db.conn.Query(query) if err != nil { - log.Println(err) + log.Println(err.Error()) + return err } + return nil } -func AddColumn(table string, name string, sqltype string) { - query := "alter table " + table + " add " + name + " " + sqltype - rows, err := db.Query(query) +func (t *TableInfo) AddColumn(name string, sqltype string) error { + query := "alter table " + t.Name + " add " + name + " " + sqltype + rows, err := t.db.conn.Query(query) if err != nil { log.Println(err) + return err } defer rows.Close() + return nil } -func DeleteColumn(table string, name string) { - query := "alter table " + table + " drop " + name - rows, err := db.Query(query) +func (t *TableInfo) DeleteColumn(name string) error { + query := "alter table " + t.Name + " drop " + name + rows, err := t.db.conn.Query(query) if err != nil { log.Println(err) + return err } defer rows.Close() + return nil } -func ListSequences() []AssRow { - return QueryAssociativeArray("SELECT sequence_name :: varchar FROM information_schema.sequences WHERE sequence_schema = 'public' ORDER BY sequence_name;") +func (db *Db) ListSequences() (Rows, error) { + return db.QueryAssociativeArray("SELECT sequence_name :: varchar FROM information_schema.sequences WHERE sequence_schema = 'public' ORDER BY sequence_name;") } -func buildSelect(table string, key string, columns []string, restriction string, sortkeys []string, dir ...string) string { +func (t *TableInfo) buildSelect(key string, columns []string, restriction string, sortkeys []string, dir ...string) string { if key != "" { columns = append(columns, key) } - query := "select " + strings.Join(columns, ",") + " from " + table + query := "select " + strings.Join(columns, ",") + " from " + t.Name if restriction != "" { query += " where " + restriction } @@ -183,17 +215,21 @@ func buildSelect(table string, key string, columns []string, restriction string, return query } -func Insert(table string, record AssRow) int { +func (t *TableInfo) Insert(record AssRow) (int, error) { columns := "" values := "" - schema := GetSchema(table) + t, err := t.GetSchema() + if err != nil { + log.Println(err) + return -1, err + } var id int for key, element := range record { - if strings.Contains(schema.Columns[key], "char") || strings.Contains(schema.Columns[key], "date") { + if strings.Contains(t.Columns[key], "char") || strings.Contains(t.Columns[key], "date") { columns += key + "," - values += fmt.Sprintf(pq.QuoteLiteral(fmt.Sprintf("%v", element))) + "," + values += fmt.Sprint(pq.QuoteLiteral(fmt.Sprintf("%v", element))) + "," } else { columns += key + "," @@ -201,19 +237,23 @@ func Insert(table string, record AssRow) int { } } - db.QueryRow("INSERT INTO " + table + "(" + removeLastChar(columns) + ") VALUES (" + removeLastChar(values) + ") RETURNING id").Scan(&id) - return id + t.db.conn.QueryRow("INSERT INTO " + t.Name + "(" + removeLastChar(columns) + ") VALUES (" + removeLastChar(values) + ") RETURNING id").Scan(&id) + return id, nil } -func Update(table string, record AssRow) string { +func (t *TableInfo) Update(record AssRow) error { - schema := GetSchema(table) + t, err := t.GetSchema() + if err != nil { + log.Println(err) + return err + } id := "" stack := "" for key, element := range record { - if strings.Contains(schema.Columns[key], "char") || strings.Contains(schema.Columns[key], "date") { + if strings.Contains(t.Columns[key], "char") || strings.Contains(t.Columns[key], "date") { stack = stack + " " + key + " = " + pq.QuoteLiteral(fmt.Sprintf("%v", element)) + "," @@ -227,17 +267,18 @@ func Update(table string, record AssRow) string { } } stack = removeLastChar(stack) - query := ("UPDATE " + table + " SET " + stack + " WHERE id = " + id) - rows, err := db.Query(query) + query := ("UPDATE " + t.Name + " SET " + stack + " WHERE id = " + id) + rows, err := t.db.conn.Query(query) if err != nil { log.Println(query) log.Println(err) + return err } defer rows.Close() - return query + return nil } -func Delete(table string, record AssRow) string { +func (t *TableInfo) Delete(record AssRow) error { id := "" values := "" @@ -248,31 +289,32 @@ func Delete(table string, record AssRow) string { } } - query := ("DELETE FROM " + table + " WHERE id = " + id) - rows, err := db.Query(query) + query := ("DELETE FROM " + t.Name + " WHERE id = " + id) + rows, err := t.db.conn.Query(query) if err != nil { log.Println(query) log.Println(err) + return err } defer rows.Close() - return query + return nil } -func UpdateOrInsert(table string, record AssRow) int { - id := 0 - +func (t *TableInfo) UpdateOrInsert(record AssRow) (int, error) { + id := -1 for key, element := range record { if key == "id" { sid := fmt.Sprintf("%v", element) id, _ = strconv.Atoi(sid) } } - if id == 0 { - return Insert(table, record) + if id == -1 { + return t.Insert(record) } else { - Update(table, record) - return id + t.Update(record) + return id, nil } + } func removeLastChar(s string) string { diff --git a/pg_test.go b/pg_test.go index 4112b6f..32475c4 100755 --- a/pg_test.go +++ b/pg_test.go @@ -9,8 +9,8 @@ import ( ) func TestCreateTable(t *testing.T) { - Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") - defer Close() + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() jsonFile, err := os.Open("test_table.json") if err != nil { @@ -20,25 +20,37 @@ func TestCreateTable(t *testing.T) { byteValue, _ := ioutil.ReadAll(jsonFile) - var data Table - json.Unmarshal([]byte(byteValue), &data) + var jsonSource TableInfo + json.Unmarshal([]byte(byteValue), &jsonSource) - CreateTable(data) + err = db.CreateTable(jsonSource) + if err != nil { + fmt.Println(err.Error()) + } - tbl := GetSchema(data.Name) - if len(tbl.Columns) == 0 { + sch, err := db.Table("test").GetSchema() + if err != nil { + fmt.Println(err.Error()) + } + if len(sch.Columns) == 0 { t.Errorf("Create table failed") } } func TestAddColumn(t *testing.T) { - Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") - defer Close() + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() - old := GetSchema("test") - AddColumn("test", "addcolumn", "integer") - new := GetSchema("test") + old, err := db.Table("test").GetSchema() + if err != nil { + fmt.Println(err.Error()) + } + db.Table("test").AddColumn("addcolumn", "integer") + 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") @@ -47,20 +59,26 @@ func TestAddColumn(t *testing.T) { func TestInsert(t *testing.T) { - Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") - defer Close() + 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 := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + old, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } jsonStringOld, _ := json.Marshal(old) fmt.Println(string(jsonStringOld)) - UpdateOrInsert("test", vl) + db.Table("test").UpdateOrInsert(vl) - new := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + new, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } jsonStringNew, _ := json.Marshal(new) fmt.Println(string(jsonStringNew)) @@ -71,21 +89,27 @@ func TestInsert(t *testing.T) { func TestUpdate(t *testing.T) { - Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") - defer Close() + 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 := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + old, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } jsonStringOld, _ := json.Marshal(old) fmt.Println(string(jsonStringOld)) - UpdateOrInsert("test", vl) + db.Table("test").UpdateOrInsert(vl) - new := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + new, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } jsonStringNew, _ := json.Marshal(new) fmt.Println(string(jsonStringNew)) @@ -97,19 +121,25 @@ func TestUpdate(t *testing.T) { func TestDelete(t *testing.T) { - Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") - defer Close() + 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 := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + old, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } jsonStringOld, _ := json.Marshal(old) fmt.Println(string(jsonStringOld)) - Delete("test", vl) + db.Table("test").Delete(vl) - new := GetAssociativeArray("test", []string{"*"}, "", []string{}, "") + new, err := db.Table("test").GetAssociativeArray([]string{"*"}, "", []string{}, "") + if err != nil { + fmt.Println(err.Error()) + } jsonStringNew, _ := json.Marshal(new) fmt.Println(string(jsonStringNew)) @@ -120,12 +150,18 @@ func TestDelete(t *testing.T) { func TestDeleteColumn(t *testing.T) { - Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") - defer Close() + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() - old := GetSchema("test") - DeleteColumn("test", "addcolumn") - new := GetSchema("test") + 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") @@ -133,13 +169,15 @@ func TestDeleteColumn(t *testing.T) { } func TestDeleteTable(t *testing.T) { - Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") - defer Close() + db := Open("postgres", "host=127.0.0.1 port=5432 user=test password=test dbname=test sslmode=disable") + defer db.Close() - DeleteTable("test") - - tbl := GetSchema("test") + db.Table("test").DeleteTable() + tbl, err := db.Table("test").GetSchema() + if err != nil { + fmt.Println(err.Error()) + } if len(tbl.Columns) != 0 { t.Errorf("Delete table failed") }