I am inserting some data in table using SQLx like this
func (*RideRepositoryImpl) insert(entity interface{}, tx persistence.Transaction) (sql.Result, error) {
ride := entity.(*model.Ride)
placeHolders := repository.InsertPlaceholders(len(rideColumns))
sql := fmt.Sprintf("INSERT INTO %s(%s) VALUES(%s)", TableName, strings.Join(Columns, ","), placeHolders)
return tx.Exec(sql, ride.ID.String(), ride.DeviceIotID, ride.VehicleID.String(), ride.UserID.String(),ride.AdditionComments)
}
and calling this function like this
func (p *RideRepositoryImpl) Save(ride *model.Ride, tx persistence.Transaction) error {
return repository.Save(ride, p.insert, tx)
Now I want to get UUID of saved record instantly after saving this record . Is there any clean way to do this instantly ?
PostgreSQL has the RETURNING clause for this.
Sometimes it is useful to obtain data from modified rows while they
are being manipulated. The INSERT, UPDATE, and DELETE commands
all have an optional RETURNING clause that supports this. Use of
RETURNING avoids performing an extra database query to collect the
data, and is especially valuable when it would otherwise be difficult
to identify the modified rows reliably.
// add the RETURNING clause to your INSERT query
sql := fmt.Sprintf("INSERT INTO %s(%s) VALUES(%s) RETURNING <name_of_uuid_column>", TableName, strings.Join(Columns, ","), placeHolders)
// use QueryRow instead of Exec
row := tx.QueryRow(sql, ride.ID.String(), ride.DeviceIotID, ride.VehicleID.String(), ride.UserID.String(),ride.AdditionComments)
// scan the result of the query
var uuid string
if err := row.Scan(&uuid); err != nil {
panic(err)
}
// ...
For additional INSERT-specific info related to RETURNING you can go to the INSERT documentation and search the page for "returning" with CTRL/CMD+F.
If, in addition, you need your function to still return an sql.Result value to satisfy some requirement, then you can return your own implementation.
var _ sql.Result = sqlresult{} // compiler check
type sqlresult struct { lastid, nrows int64 }
func (r sqlresult) LastInsertId() (int64, error) { return r.lastid, nil }
func (r sqlresult) RowsAffected() (int64, error) { return r.nrows, nil }
func (*RideRepositoryImpl) insert(entity interface{}, tx persistence.Transaction) (sql.Result, error) {
ride := entity.(*model.Ride)
placeHolders := repository.InsertPlaceholders(len(rideColumns))
sql := fmt.Sprintf("INSERT INTO %s(%s) VALUES(%s) RETURNING <name_of_uuid_column>", TableName, strings.Join(Columns, ","), placeHolders)
row := tx.QueryRow(sql, ride.ID.String(), ride.DeviceIotID, ride.VehicleID.String(), ride.UserID.String(),ride.AdditionComments)
if err := row.Scan(&ride.<NameOfUUIDField>); err != nil {
return nil, err
}
return sqlresult{0, 1}, nil
}
Related
I have a function GetAccount which is generated by sqlc.
When I call GetAccount(/*unused id*/), An ErrNoRows error should be returned. Instead I am getting no error and an Account with default values (zeros and empty strings) returned.
GetAccount implementation:
const getAccount = `-- name: GetAccount :one
SELECT id, owner, balance, currency, created_at
FROM accounts
WHERE id = $1
`
func (q *Queries) GetAccount(ctx context.Context, id int64) (Account, error) {
row := q.db.QueryRowContext(ctx, getAccount, id)
var i Account
err := row.Scan(
&i.ID,
&i.Owner,
&i.Balance,
&i.Currency,
&i.CreatedAt,
)
return i, err
}
Why I am not getting any error when there are no rows to return?
Edit:
As requested, here is how I am calling GetAccount. It is a Gin request handler.
type getAccountRequest struct {
ID int64 `uri:"id" binding:"required,min=1"`
}
func (server *Server) getAccount(ctx *gin.Context) {
var request getAccountRequest
err := ctx.ShouldBindUri(&request)
if err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err))
return
}
account, err := server.store.GetAccount(ctx, request.ID) //<-called here
if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err))
return
} else if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
return
}
ctx.JSON(http.StatusOK, account)
}
Edit 2:
For clarity, when I say
An ErrNoRows error should be returned
I state this because of the call to row.Scan which should produce the error.
Documentation:
func (r *Row) Scan(dest ...any) error
Scan copies the columns from the matched row into the values pointed at by dest. See the documentation on Rows.Scan for details. If more than one row matches the query, Scan uses the first row and discards the rest. If no row matches the query, Scan returns ErrNoRows.
You are overwriting the sql error:
account, err := server.store.GetAccount(ctx, request.ID) //<-called here
err = ctx.ShouldBindUri(&request)
if err == sql.ErrNoRows {
You should check the error immediately after the GetAccount call:
account, err := server.store.GetAccount(ctx, request.ID) //<-called here
if err == sql.ErrNoRows {
I'am trying to create registration in my Telegram Bot with Golang and Postgres. When user writes "register", bot has to check if user's uuid already exists in DB, and if not to create row with his uuid.
Here is my function to check if uuid already exists in DB:
func IsUserInDB(uuid int64) (bool, error) {
var exists bool
query := fmt.Sprintf("SELECT EXISTS(SELECT 1 FROM users WHERE uuid = %d);", uuid)
err := Db.QueryRow(query).Scan(&exists)
return exists, err
}
Here is my function for adding user's uuid to DB:
func AddUserToDB(column string, row interface{}) error {
query := fmt.Sprintf("INSERT INTO users (%s) VALUES (%v);", column, row)
_, err := Db.Exec(query)
return err
}
And the logic for bot:
func (b *Bot) handleMessages(message *tgbotapi.Message) error {
switch message.Text {
case "register":
exists, err := data.IsUserInDB(message.From.ID)
if err != nil {
return err
}
if !exists {
err := data.AddUserToDB("uuid", message.From.ID)
return err
}
return nil
default:
msg := tgbotapi.NewMessage(message.Chat.ID, "unknown message...")
_, err := b.bot.Send(msg)
return err
}
}
First time, when I send "register", bot successfully adds user's id to db, but the problem happens if I try to send "register" 1 more time. IsUserInDB() returns me false and bot adds 1 more row with the same uuid. So, I think problem is with my IsUserInDb() function
Why not just a unique index on your users table?
CREATE UNIQUE INDEX unq_uuid ON users (uuid);
Then you don't have to check, you just try to insert and it will return an error if it already exists.
I use Go with PostgreSQL using github.com/lib/pq and able to successfully fetch the records when my structure is known.
Now my query is how to fetch records when my structure changes dynamically?
By rows.columns I am able to fetch the column names, but could you help me with fetching the values of these columns for all the rows. I referred this link answered by #Luke, still, here the person has a structure defined.
Is it possible to retrieve a column value by name using GoLang database/sql
type Person struct {
Id int
Name string
}
Meanwhile I do not have a structure that is fixed, so how will I iterate through all the columns that too again for all rows. My approach would be a pointer to loop through all columns at first, then another one for going to next row.
Still not able to code this, Could you please help me with this, like how to proceed and get the values.
Since you don't know the structure up front you can return the rows as a two dimensional slice of empty interfaces. However for the row scan to work you'll need to pre-allocate the values to the appropriate type and to do this you can use the ColumnTypes method and the reflect package. Keep in mind that not every driver provides access to the columns' types so make sure the one you use does.
rows, err := db.Query("select * from foobar")
if err != nil {
return err
}
defer rows.Close()
// get column type info
columnTypes, err := rows.ColumnTypes()
if err != nil {
return err
}
// used for allocation & dereferencing
rowValues := make([]reflect.Value, len(columnTypes))
for i := 0; i < len(columnTypes); i++ {
// allocate reflect.Value representing a **T value
rowValues[i] = reflect.New(reflect.PtrTo(columnTypes[i].ScanType()))
}
resultList := [][]interface{}{}
for rows.Next() {
// initially will hold pointers for Scan, after scanning the
// pointers will be dereferenced so that the slice holds actual values
rowResult := make([]interface{}, len(columnTypes))
for i := 0; i < len(columnTypes); i++ {
// get the **T value from the reflect.Value
rowResult[i] = rowValues[i].Interface()
}
// scan each column value into the corresponding **T value
if err := rows.Scan(rowResult...); err != nil {
return err
}
// dereference pointers
for i := 0; i < len(rowValues); i++ {
// first pointer deref to get reflect.Value representing a *T value,
// if rv.IsNil it means column value was NULL
if rv := rowValues[i].Elem(); rv.IsNil() {
rowResult[i] = nil
} else {
// second deref to get reflect.Value representing the T value
// and call Interface to get T value from the reflect.Value
rowResult[i] = rv.Elem().Interface()
}
}
resultList = append(resultList, rowResult)
}
if err := rows.Err(); err != nil {
return err
}
fmt.Println(resultList)
This function prints the result of a query without knowing anything about the column types and count. It is a variant of the previous answer without using the reflect package.
func printQueryResult(db *sql.DB, query string) error {
rows, err := db.Query(query)
if err != nil {
return fmt.Errorf("canot run query %s: %w", query, err)
}
defer rows.Close()
cols, _ := rows.Columns()
row := make([]interface{}, len(cols))
rowPtr := make([]interface{}, len(cols))
for i := range row {
rowPtr[i] = &row[i]
}
fmt.Println(cols)
for rows.Next() {
err = rows.Scan(rowPtr...)
if err != nil {
fmt.Println("cannot scan row:", err)
}
fmt.Println(row...)
}
return rows.Err()
}
The trick is that rows.Scan can scan values into *interface{} but you have to wrap it in interface{} to be able to pass it to Scan using ....
I have a Golang function to fetch all the records from Postgres database, this function is simply using :
SELECT * from stock_transactions
I want to apply filter to this function to fetch records with some conditions, in-short I want to use :
SELECT * from stock_transactions WHERE symbol = $symb
The problem is to handle the case where if $symb = null the query should act as SELECT * from stock_transactions. I can write an if-else clause for the same but if the number of parameters are more than 2 it could be messy. Is there a better way to handle this?
My function:
func showstocks (w http.ResponseWriter, r *http.Request){
var err error
if r.Method != "GET" {
http.Error(w, http.StatusText(405), http.StatusMethodNotAllowed)
return
}
rows, err := db.Query("SELECT * FROM stock_transaction ORDER BY id DESC")
if err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
defer rows.Close()
sks := make([]stockdata, 0)
for rows.Next() {
sk := stockdata{}
err := rows.Scan(&sk.Sname, &sk.Ttype, &sk.Uprice, &sk.Qty, &sk.Bfee, &sk.Ddate)
if err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
sks = append(sks, sk)
}
if err = rows.Err(); err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
tpl.ExecuteTemplate(w, "dashboard.gohtml", sks)
}
//Suggested by #mkopriva. Tried and tested.
package main
import (
"fmt"
)
func main() {
// ...
// use interface{} because string types cannot be nil
var stock_symbol interface{} // the "zero-value of interface types is nil, so stock_symbol here is nil
// if empty then stock_symbol will be left as nil
if val := r.FormValue("stock_symbol"); len(val) > 0 {
stock_symbol = val // set to the provided value
}
// ...
// now stock_symbol is either the provided string value or nil
db.Query("SELECT ... WHERE (stock_symbol = $1 OR $1 IS NULL)", stock_symbol)
}
You can use the (stock_symbol = $1 OR $1 IS NULL) trick as you have already seen, which is programmatically convenient. However this can often lead to inefficient queries, as the query planner may not be smart enough to optimize them correctly. It might be better to write some code which removes the tautological phrases and also removes the corresponding bind variables, rather than passing each of them to the database. It is slightly messy, but it should be encapsulated into a function, not rewritten each time.
I have a MongoDB collection with an example document like this:
What I want to do (as you can see from the actual code) is to update a role field in members.x.role where members.x.id equals given ID (ID is UUID so it's unique; this part of code works without problem) and then I want to return that members.x. But the problem is that it always returns first member instead of the one that has been just updated. I've tried some methods of mgo and found Distinct() be closest to my expectations, but that doesn't work as I want.
My question is how can I return member embedded document with specified ID?
I've already looked on this and this but it didn't help me.
func (r MongoRepository) UpdateMemberRole(id string, role int8) (*Member, error) {
memberQuery := &bson.M{"members": &bson.M{"$elemMatch": &bson.M{"id": id}}}
change := &bson.M{"members.$.role": role}
err := r.db.C("groups").Update(memberQuery, &bson.M{"$set": &change})
if err == mgo.ErrNotFound {
return nil, fmt.Errorf("member with ID '%s' does not exist", id)
}
// FIXME: Retrieve this member from query below. THIS ALWAYS RETURNS FIRST MEMBER!!!
var member []Member
r.db.C("groups").Find(&bson.M{"members.id": id}).Distinct("members.0", &member)
return &member[0], nil
}
I found a workaround, it's not stricte Mongo query that is returning this embedded document, but this code is IMO more clear and understandable than some fancy Mongo query that fetches whole document anyway.
func (r MongoRepository) UpdateMemberRole(id string, role int8) (*Member, error) {
change := mgo.Change{
Update: bson.M{"$set": bson.M{"members.$.role": role}},
ReturnNew: true,
}
var updatedGroup Group
_, err := r.db.C("groups").Find(bson.M{"members": bson.M{"$elemMatch": bson.M{"id": id}}}).Apply(change, &updatedGroup)
if err == mgo.ErrNotFound {
return nil, fmt.Errorf("member with ID '%s' does not exist", id)
} else if err != nil {
return nil, err
}
for _, member := range updatedGroup.Members {
if member.Id == id {
return &member, nil
}
}
return nil, fmt.Errorf("weird error, Id cannot be found")
}