How to update several columns in PostgresQL table with a Go function - sql

I use PostgresQL and Go. I have a table which is called users. I try to write a function which will be able to update several columns. This function takes column names and user object (type struct). Can you help me with this?
This is a User struct in Go:
type User struct {
ID int json:"id"
Username string json:"username"
Password string json:"password"
FirstName string json:"first_name"
LastName string json:"last_name"
Email string json:"email"
}
This is SQL script which creates users table:
create table "users"
(
id serial not null
constraint user_pk
primary key,
username varchar(64) not null,
password varchar(128) not null,
first_name varchar(64) not null,
last_name varchar(64) not null,
email varchar(64) not null
);
1st example: I can pass first_name & last_name + full User object (postgres should update only these 2 fields)
2nd example: I can pass first_name & email & username + full User object (postgres should update only these 3 fields)
I have tried to do it with map but I could not:
func UpdateUser(db *sql.DB, m map[string]interface{}) (*User, error) {
for key, value := range m {
}
err := db.QueryRow(UPDATE "users" SET ())
}

Use UPDATE
In the example i assume you have written the function, which takes variables. Lets assume the variables are:
firstName
lastName
The SQL code is as follows:
UPDATE users
SET first_name = firstName, last_name= lastName
WHERE [condition]
You may find that you need to write a class for this with an overloaded constructor so that it takes any number of variables that you want.
Or better still write a function for each of the rows:
UPDATE users
SET first_name = firstName
WHERE [condition]
UPDATE users
SET last_name= lastName
WHERE [condition]
etc. It just means that the user will have to enter the items one at a time.
https://www.w3schools.com/sql/sql_update.asp

Here's my user update func:
I pass a User struct to the function and don't worry about which fields need to be updated, I just update them all. The caller is working with a User they got from a previous func that returned it from the DB (I'll show you the API handler if you want to see that).
After the update I call the Get function to get the freshly updated record from the DB and return the User struct. This is for certainty that the caller can see precisely what they just did.
// Update a User identified by id
func (u *UserModel) Update(user *models.User) (*models.User, error) {
stmt := `UPDATE user SET
first_name = ?,
last_name = ?,
email = ?,
phone = ?,
status_id = ?
WHERE id = ?`
var userStatus models.UserStatus
userStatusID := userStatus.GetID(user.Status)
_, err := u.DB.Exec(stmt, user.FirstName, user.LastName, user.Email, user.Phone, userStatusID, user.ID)
if err != nil {
if mysqlErr, ok := err.(*mysql.MySQLError); ok {
if mysqlErr.Number == 1062 && strings.Contains(mysqlErr.Message, "uk_user_email") {
return nil, models.ErrDuplicateEmail
}
}
return nil, err
}
user, err = u.Get(int(user.ID))
if err != nil {
return nil, err
}
return user, nil
}

Related

How to delete related models of a relation in Gorm?

So basically I have 3 models: User, Profile, and Post.
They are related like so: User has one Profile. Profile has many Post
They look like this:
type User struct {
Base // holds this object's uuid, createdAt, updatedAt
Role string `json:"role"`
Username string `json:"username" gorm:"unique"`
Password string `json:"password"`
Profile Profile `gorm:"constraint:OnDelete:CASCADE;"`
}
type Profile struct {
Base // holds this object's uuid, createdAt, updatedAt
UserId string `json:"user_id"`
Name string `json:"name"`
Bio string `json:"bio"`
Age uint8 `json:"age"`
Posts []Post `gorm:"constraint:OnDelete:CASCADE;"`
}
type Post struct {
Base // holds this object's uuid, createdAt, updatedAt
ProfileId string `json:"profile_id"`
Caption string `json:"caption"`
Likes uint32 `json:"num_likes" gorm:"default:0"`
}
What I want to happen is when I delete the user, I want the profile to be deleted and all the posts that are related to it. My only other experience with relational databases are Django where this is automatic.
What actually happens is when I delete the user, the profile gets deleted but the posts remain in the database.
This is how I am deleting the user:
...
base := models.Base{Id: id}
if err := configs.Database.Select(clause.Associations).Delete(&models.User{Base: base}).Error; err != nil {
return c.Status(400).JSON(err.Error())
}
...
I've already looked at this but its not very helpful. How could I accomplish this?
Thank you!
Based on the issue link you posted, and other related issues, it might not be possible to use clause.Associations for relations nested deeper than one level. In your case, the Profile related to your User is deleted, but not the Posts related to the Profile.
One way to delete all wanted associations is to use a delete hook. Either BeforeDelete or AfterDelete, depending on your setup and how strong your relationships are. For example:
func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
if err := tx.Joins("profiles p ON p.id = posts.profile_id").Joins("users u ON u.id = p.user_id").Where("u.id = ?", u.Base.Id).Delete(&Post{}).Error; err != nil {
return err
}
return tx.Joins("users u ON u.id = profiles.user_id").Where("u.id = ?", u.Base.Id).Delete(&Profile{}).Error
}
This way, when you execute configs.Database.Delete(&models.User{Base: base}), it will execute the hook first, then this query.
Another way would be to execute all the queries from the hook function separately:
base := models.Base{Id: id}
if err := configs.Database.Joins("profiles p ON p.id = posts.profile_id").Joins("users u ON u.id = p.user_id").Where("u.id = ?", base.Id).Delete(&Post{}).Error; err != nil {
return c.Status(400).JSON(err.Error())
}
if err := configs.Database.Joins("users u ON u.id = profiles.user_id").Where("u.id = ?", base.Id).Delete(&Profile{}).Error; err != nil {
return c.Status(400).JSON(err.Error())
}
if err := configs.Database.Delete(&models.User{Base: base}).Error; err != nil {
return c.Status(400).JSON(err.Error())
}

Postgresql golang sqlx package query

I am using golang sqlx package and postgresql as the database. I have two simple tables
create table items (
id varchar unique primary key not null,
is_active bool not null default false
);
create table images (
item_id varchar not null,
link varchar unique not null,
index int not null,
foreign key (item_id) references items(id) on delete cascade
);
What I need to perform is to get all of the items that have assigned images to them in one query and do not manipulate with go variables afterwards
I have such code in order to perform that
type Image struct {
ItemID string `db:"item_id" json:"item_id"`
Index int `db:"index" json:"index"`
Link string `db:"link" json:"link"`
}
type Images []Image
func (t Images) Value() (driver.Value, error) {
b, err := json.Marshal(t); if err != nil {
return driver.Value(""), err
}
return driver.Value(string(b)), nil
}
func (t *Images) Scan (src interface{}) error {
var source []byte
if reflect.TypeOf(src) == nil {
return nil
}
switch src.(type) {
case string:
source = []byte(src.(string))
case []byte:
source = src.([]byte)
default:
return errors.New("incompatible type for images")
}
return json.Unmarshal(source, t)
}
func GetItems (active bool) (items []Item, err error) {
conn := postgres.Connection()
const query = `SELECT *, (
SELECT json_agg(images) AS images FROM (
SELECT images.link, images.index FROM images GROUP BY images.link, images.index HAVING bool_or(item_id=items.id) AND count(item_id) > 0
) as images
) FROM items`
if err := conn.Select(&items, query, active); err != nil {
logrus.WithError(err).Errorf("Can not get all items")
}
return
}
This works but I can have results with items that has no assigned images for them and this code seems tooo complicated for such simple at first sight task. Wish anyone can help me with that
If I understand correctly you wanna do this:
select i.id
from images img
join items i
on img.item_id = i.id
where i.isactive =1
group by i.id
With the help of #eshirvana I came to this solution:
SELECT items.*, json_agg(images) as images
FROM items
JOIN images
ON images.item_id = items.id
WHERE is_active=$1
GROUP BY items.id

sql: expected 0 arguments, got 2

I'm struggling to properly utilize sqlx and the pq driver for Postgres to create a row in the database. Let's start simple:
I have a user, role and user_role table. I want to insert a role into the database and get the ID of the inserted row. This works flawlessly using the following sql:
const createRoleSQL = "INSERT INTO role (name) VALUES (:name) RETURNING id"
To make that work in go, I prepare the statement at some point:
createStmt, err := db.PrepareNamed(createRoleSQL)
if err != nil {
// ...
}
When creating, I run the query as part of a transaction tx. role is obviously a struct with the correct fields and db tags:
if err := tx.NamedStmt(createStmt).QueryRow(role).Scan(&role.ID); err != nil {
// ...
}
This works perfectly fine.
Now I wanted to extend that and insert a new role and assign it to a user:
const createUserRoleSQL = `
DO $$
DECLARE role_id role.id%TYPE;
BEGIN
INSERT INTO role (name) VALUES ($2) RETURNING id INTO role_id;
INSERT INTO user_role (user_id, role_id) VALUES ($1, role_id);
END $$`
createStmt, err := db.Preparex(createUserRoleSQL)
if err != nil {
// ...
}
if err := tx.Stmtx(createStmt).QueryRow(userID, role.Name).Scan(&role.ID); err != nil {
// ...
}
Unfortunately this fails with sql: expected 0 arguments, got 2. Is it possible to achieve what I want to do, with a single query?

making a general select function for sql library operations

well i am trying to make a select function which will work like this,
From main function i will call this select function with necessary variables. and the select function will run the query in the database and give me the reselt.
Now for go lang if we want to do a select query for the results we need a struct where we can get the results. Since the function will be called from main func and the variables can not be predicted we cant declare a struct before hand so i am stuck here. can anyone give me a solution?
so basically what i am trying to do is we can simply call the query like
-- select name,phone from users where userid=1 ---
so from the main func we will get the values like column name(name,phone), table name (users) and the condition (userid=1)
we will pass these info to the select func and it will run the query and give us back the results.
no matter what is the query is it should work like it. can anyone give me an sample or idea how to work with this
func select() {
//DB CONNECTION HERE
type User struct {
Name string `json:"name"`
Age string `json:"age"`
Email string `json:"email"`
Phone string `json:"phone"`
Address string `json:"address"`
}
results, err := db.Query("SELECT Name, Age, Email, Phone, Address FROM `users` where personId=12 ")
if err != nil {
panic(err.Error())
}
for results.Next() {
var user User
var email User
var age User
var phone User
var address User
err = results.Scan(&user.Name, &age.Age, &email.Email, &phone.Phone, &address.Address)
if err != nil {
panic(err.Error())
}
fmt.Println(user.Name)
fmt.Println(age.Age)
fmt.Println(email.Email)
fmt.Println(phone.Phone)
fmt.Println(address.Address)
}
Now as zou can see this is the code where we know the query so we made a struct according to it but what about the problem i described above?

Retrieving autoincrement column after insert in SQLite3

I have a table in SQLite3 with the following schema:
create table threads(
id integer primary key autoincrement,
submitter text,
body text,
title text,
subtime int -- Unix time
);
I'm inserting rows like this:
insert into threads (title, body, subtime, submitter) values
("I like ducks", "Don't you?", 1467664977640, "tom");
I'd like to to get the id column after I insert the thread. How can I do this? Ideally I could insert and retrieve the column in the same statement.
The Result interface allows you to access this value without having to execute another query:
res, err := db.Exec("INSERT ..."), someParam)
if err != nil {
println("Exec err:", err.Error())
} else {
id, err := res.LastInsertId()
if err != nil {
println("Error:", err.Error())
} else {
println("LastInsertId:", id)
}
}
I don't know if you can do that in one statement but immediately after the insert statement you can use the following statement to get the last auto incremented id:
SELECT last_insert_rowid()
Another way is to use the following statement:
select seq from sqlite_sequence where name="threads"