-1

My question is not related to that one. My question was mostly on preparex and ctx. I have already done with the implementation using the db.NamedExec and my code is working for those. What I am trying to do here is to understand context.context and preparex. Implement using these two.

CODE SNIPPET:

model.go

type User struct {
    ID        int       `db:"id" json:"id"`
    CreatedAt time.Time `db:"created_at" json:"created_at"`
    UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
    DeletedAt time.Time `db:"deleted_at" json:"deleted_at"`
    Username  string    `db:"username" json:"username"`
    Password  string    `db:"password" json:"password"`
    FirstName string    `db:"first_name" json:"first_name"`
    LastName  string    `db:"last_name" json:"last_name"`
    Phone     string    `db:"phone" json:"phone"`
    Status bool         `db:"status" json:"status"`
    Addrs  []UserAddress
}

Query:

queryInsertUserData = `
    INSERT INTO users (id, created_at, updated_at, deleted_at, username, password, first_name, last_name, phone, status) VALUES($1, now(), now(), $2, $3, $4, $5, $6, $7, $8)
`

type queries struct {
    insertUserData, 
    insertAddrData *sqlx.Stmt
}

//prepareStatement is a method for preparing sqlx statement
func (queries *queries) prepareStatement(db *sqlx.DB, query string) (*sqlx.Stmt, error) {
    stmt, err := db.Preparex(query) //https://go.dev/doc/database/prepared-statements
    if err != nil {
        return nil, err
    }

    return stmt, err
}

constructUserData, err := queries.prepareStatement(db, queryInsertUserData)
queries.insertUserData = constructUserData

Implementation:

// Insert User data
func (postgres *Postgres) InsertUserData(ctx context.Context) (*entity.User, error) {
    c := entity.User{}

    err := postgres.queries.insertUserData.SelectContext(ctx, &c) //<---here
    if err != nil {
        return nil, err
    }
    return &c, nil
}

my ctx is :

ctx = context.WithValue(ctx, "ID", 1)
ctx = context.WithValue(ctx, "Username", "John")
ctx = context.WithValue(ctx, "Password", "pass")

when I am passing to postgres.queries.insertUserData.SelectContext(ctx, &c)

I am getting: sql: expected 8 arguments, got 0

why it is saying got 0? Can anyone help me with this? How to pass ctx and provide the insert query values?

E_net4
  • 27,810
  • 13
  • 101
  • 139
Pygirl
  • 12,969
  • 5
  • 30
  • 43
  • Hi, You didnt use `stmt.Exec` for executing variables. – ttrasn Jun 06 '22 at 04:45
  • @ttrasn: How to use it. Sorry I am quite new. – Pygirl Jun 06 '22 at 04:45
  • 1
    You cannot map struct to SQL params in vanilla GO unless you do it explicitly for every attribute. You need to use https://jmoiron.github.io/sqlx/ or similar external lib. – Dmitry Harnitski Jun 06 '22 at 04:57
  • @DmitryHarnitski: I am using jmoiron.github.io/sqlx in the project. Yes sqlx have these `db.Select(), db.Get()` I was able to do everything without using this context.context. But now I have integrated it so I am facing issues :( – Pygirl Jun 06 '22 at 04:58
  • My bad. Chek params syntax. it could be `:id` instead of `$id`. It is not consistent in sqlx when you use named queries. – Dmitry Harnitski Jun 06 '22 at 05:00
  • It was giving me error :'( I did everything in that way only initially. this `preparex` doesn't understand that. :( – Pygirl Jun 06 '22 at 05:01
  • `INSERT INTO users (id, created_at, updated_at, deleted_at, username, password, first_name, last_name, phone, status) VALUES(:id, :created_at, :updated_at, :deleted_at, :username, :password, :first_name, :last_name, :phone, :status)` gives me `runtime error: invalid memory address or nil pointer dereference` – Pygirl Jun 06 '22 at 05:04
  • I posted a code that works for me. Sorry, It is late here. I cannot tune it for your sample. – Dmitry Harnitski Jun 06 '22 at 05:09

2 Answers2

1

I didn't get the structure of your code, but from the main context (inserting query) you should do something like this:

package main

import (
    "context"
    "database/sql"
    "log"
)

var db *sql.DB
type User struct {
    ID        int       `db:"id" json:"id"`
    CreatedAt time.Time `db:"created_at" json:"created_at"`
    UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
    DeletedAt time.Time `db:"deleted_at" json:"deleted_at"`
    Username  string    `db:"username" json:"username"`
    Password  string    `db:"password" json:"password"`
    FirstName string    `db:"first_name" json:"first_name"`
    LastName  string    `db:"last_name" json:"last_name"`
    Phone     string    `db:"phone" json:"phone"`
    Status bool         `db:"status" json:"status"`
    Addrs  []UserAddress
}
func main() {
    users := []User {
        {...User 1 data...},
        {...User 2 data...},
    }

    stmt, err := db.Prepare("INSERT INTO users (id, created_at, updated_at, deleted_at, username, password, first_name, last_name, phone, status) VALUES($1, now(), now(), $2, $3, $4, $5, $6, $7, $8)")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt.Close() // Prepared statements take up server resources and should be closed after use.

    for id, user := range users {
        if _, err := stmt.Exec(id+1, user.Username, user.Password,... other data); err != nil {
            log.Fatal(err)
        }
    }
}

also, you can use the Gorm powerful package for using all relational databases.

mrts
  • 16,697
  • 8
  • 89
  • 72
ttrasn
  • 4,322
  • 4
  • 26
  • 43
  • I am not allowed to use gorm :( – Pygirl Jun 06 '22 at 05:04
  • 1
    @Pygirl At the beginning of my answer I told you how can you fix the problem and how can you use pure Go to handle the query. – ttrasn Jun 06 '22 at 05:06
  • `stmt.Exec` <--- thanks for pointing this out. `postgres.queries.insertUserData.ExecContext(ctx, &c.ID, &c.Username, &c.Password)` giving now `sql: expected 8 arguments, got 3` So ExecContext should be the function to use. I am debugging :') thanks for the hint. – Pygirl Jun 06 '22 at 05:22
  • @Pygirl you should add other values, I just add three of them. you should add others in the `... other data` part. – ttrasn Jun 06 '22 at 05:25
  • 1
    Yes currently doing that. – Pygirl Jun 06 '22 at 05:26
1

I use this helper for inserts.

Works with query syntax as follows:

INSERT INTO checks (
    status) VALUES (:status)
    returning id;

Sample struct

    type Row struct {
        Status string `db:"status"`
    }
// Insert inserts row into table using query SQL command
// table used only for loging, actual table name defined in query
// function expects Query with named parameters
func Insert(ctx context.Context, row interface{}, query string, table string, tx *sqlx.Tx) (int64, error) {
    // convert named query to native parameters format
    query, args, err := tx.BindNamed(query, row)
    if err != nil {
        return 0, fmt.Errorf("cannot bind parameters for insert into %q: %w", table, err)
    }

    var id struct {
        Val int64 `db:"id"`
    }

    err = sqlx.GetContext(ctx, tx, &id, query, args...)
    if err != nil {
        return 0, fmt.Errorf("cannot insert into %q: %w", table, err)
    }

    return id.Val, nil
}
Dmitry Harnitski
  • 5,838
  • 1
  • 28
  • 43
  • Thanks for the sample code. +1. Let me try to debug my code by following the hints and the sample code provided by u all. – Pygirl Jun 06 '22 at 05:24