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
Related
I have a Snowflake query where I'm trying to update a field on all items where another field is in a list which is submitted to the query as a variable:
UPDATE my_table SET download_enabled = ? WHERE provider_id = ? AND symbol IN (?)
I've tried doing this query using the gosnowflake.Array function like this:
enable := true
provider := 1
query := "UPDATE my_table SET download_enabled = ? WHERE provider_id = ? AND symbol IN (?)"
if _, err := client.db.ExecContext(ctx, query, enable, provider,
gosnowflake.Array(assets)); err != nil {
fmt.Printf("Error: %v", err)
}
However, this code fails with the following error:
002099 (42601): SQL compilation error: Batch size of 1 for bind variable 1 not the same as previous size of 2.
So then, how can I submit a variable representing a list of values to an SQL query?
I found a potential workaround, which is to submit each item in the list as a separate parameter explicitly:
func Delimit(s string, sep string, count uint) string {
return strings.Repeat(s+sep, int(count)-1) + s
}
func doQuery(enable bool, provider int, assets ...string) error {
query := fmt.Sprintf("UPDATE my_table SET download_enabled = ? " +
"WHERE provider_id = ? AND symbol IN (%s)", Delimit("?", ", ", uint(len(assets))))
params := []interface{}{enable, provider}
for _, asset := range assets {
params = append(params, asset)
}
if _, err := client.db.ExecContext(ctx, query, params...); err != nil {
return err
}
return nil
}
Needless to say this is a less elegant solution then what I wanted but it does work.
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())
}
Am running a query to check if a table exists or not using the gorm orm for golang. Below is my code.
package main
import (
"fmt"
"log"
"gorm.io/driver/postgres"
"gorm.io/gorm"
_ "github.com/lib/pq"
)
// App sets up and runs the app
type App struct {
DB *gorm.DB
}
`const tableCreationQuery = `SELECT count (*)
FROM information_schema.TABLES
WHERE (TABLE_SCHEMA = 'api_test') AND (TABLE_NAME = 'Users')`
func ensureTableExists() {
if err := a.DB.Exec(tableCreationQuery); err != nil {
log.Fatal(err)
}
}`
The expected response should be either 1 or 0. I got this from another SO answer. Instead I get this
2020/09/03 00:27:18 &{0xc000148900 1 0xc000119ba0 0}
exit status 1
FAIL go-auth 0.287s
My untrained mind says its a pointer but how do I reference the returned values to determine what was contained within?
If you want to check if your SQL statement was successfully executed in GORM you can use the following:
tx := DB.Exec(sqlStr, args...)
if tx.Error != nil {
return false
}
return true
However in your example are using a SELECT statement then you need to check the result, which will be better suited to use the DB.Raw() method like below
var exists bool
DB.Raw(sqlStr).Row().Scan(&exists)
return exists
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
}
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"