Controlling a Pointer in Go - sql

I am trying to iterate through rows after it does a query to a database but I'm having trouble iterating through it a second time to find the females.
I believe this is because of the fact that after it iterates through rows printing out the men, the pointer is left at the end and can't go back.
rows2, rowErr :=db.Query("SELECT GIVENNAME,gender, count(givenname) as Frequency from people group by givenname order by givenname asc")
for rows2.Next() {
nextErr := rows2.Scan(&givenName,&gender, &frequency)
if nextErr != nil{
log.Fatal(nextErr.Error())
}
if gender == "male" {
fmt.Println(givenName, gender, frequency)
}
}
fmt.Println("")
for rows2.Next() {
nextErr := rows2.Scan(&givenName,&gender, &frequency)
if nextErr != nil{
log.Fatal(nextErr.Error())
}
if gender == "female" {
fmt.Println(givenName, gender, frequency)
}
}
How can I put the pointer at the beginning again? Or will I have to do another query?

You can't 'rollback' a pointer to a previous value, hence you have to use another (temporary) var which you use to iterate.
rows2, rowErr := db.Query("SELECT GIVENNAME, gender, count(givenname) as Frequency from people group by givenname order by givenname asc")
var list1, list2 MyPeopleStruct
temp := rows2
for temp.Next() {
nextErr := rows2.Scan(&givenName,&gender, &frequency)
if nextErr != nil{
log.Fatal(nextErr.Error())
}
if gender == "male" {
fmt.Println(givenName, gender, frequency)
}
}
fmt.Println("")
temp = rows2 // reinitialise it to the actual value you need
for temp.Next() {
nextErr := rows2.Scan(&givenName,&gender, &frequency)
if nextErr != nil{
log.Fatal(nextErr.Error())
}
if gender == "female" {
fmt.Println(givenName, gender, frequency)
}
}
Anyway, why do you even need to make two different for loops?
Can't you do everything using only one? like:
type MyPeopleStruct struct{
Gender string
Name string
Frequency int
}
rows2, rowErr := db.Query("SELECT GIVENNAME, gender, count(givenname) as Frequency from people group by givenname order by givenname asc")
var listM, listF []MyPeopleStruct
temp := rows2
for temp.Next() {
nextErr := rows2.Scan(&givenName,&gender, &frequency)
if nextErr != nil{
log.Fatal(nextErr.Error())
}
if gender == "male" {
fmt.Println(givenName, gender, frequency)
listM = append(listM, MyPeopleStruct{gender,givenName,frequency})
}
if gender == "female" {
fmt.Println(givenName, gender, frequency)
listF = append(listF, MyPeopleStruct{gender,givenName,frequency})
}
}
// Use listM and listF

Related

What is it CHECK constraint failed: subject <> ''?

What is it CHECK constraint failed: subject <> '' ?
I get an error with stmt, err_execontext := s.db.ExecContext(ctx, insert, subject, description).
How can I solve this?
type TODOService struct {
db *sql.DB
}
// NewTODOService returns new TODOService.
func NewTODOService(db *sql.DB) *TODOService {
return &TODOService{
db: db,
}
}
// CreateTODO creates a TODO on DB.
func (s *TODOService) CreateTODO(ctx context.Context, subject, description string) (*model.TODO, error) {
const (
insert = `INSERT INTO todos(subject, description) VALUES(?, ?)`
confirm = `SELECT subject, description, created_at, updated_at FROM todos WHERE id = ?`
)
stmt, err_execontext := s.db.ExecContext(ctx, insert, subject, description)
if err_execontext != nil {
log.Fatal("server/todo.go 31行目", err_execontext)
}
created_id, err_iserid := stmt.LastInsertId()
if err_iserid != nil {
log.Fatal("server/todo.go 35行目", err_iserid)
}
var todo model.TODO
err := s.db.QueryRowContext(ctx, confirm, created_id).Scan(&todo.Subject, &todo.Description, &todo.CreatedAt, &todo.UpdatedAt)
switch {
case err == sql.ErrNoRows:
log.Fatalf("no todo with id %d", created_id)
case err != nil:
log.Fatal(err)
}
return &todo, err
}
type TODOHandler struct {
svc *service.TODOService
}
// NewTODOHandler returns TODOHandler based http.Handler.
func NewTODOHandler(svc *service.TODOService) *TODOHandler {
return &TODOHandler{
svc: svc,
}
}
// Create handles the endpoint that creates the TODO.
func (h *TODOHandler) Create(ctx context.Context, req *model.CreateTODORequest) (*model.CreateTODOResponse, error) {
created_todo, err := h.svc.CreateTODO(ctx, req.Subject, req.Description)
if err != nil {
log.Fatal("Failed to", err)
}
return &model.CreateTODOResponse{TODO: *created_todo}, nil
}
type (
// A TODO expresses ...
TODO struct{
ID int `json:"id"`
Subject string `json:"subject"`
Description string `json:"description"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"UpdatedAt"`
}
// A CreateTODORequest expresses ...
CreateTODORequest struct{
Subject string `json:"subject"`
Description string `json:"description"`
}
// A CreateTODOResponse expresses ...
CreateTODOResponse struct{
TODO TODO `json:"todo"`
}
)
What is it CHECK constraint failed: subject <> '' ?
I get an error with stmt, err_execontext := s.db.ExecContext(ctx, insert, subject, description).
How can I solve this?
CREATE TABLE IF NOT EXISTS todos (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
subject TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
created_at DATETIME NOT NULL DEFAULT (DATETIME('now')),
updated_at DATETIME NOT NULL DEFAULT (DATETIME('now')),
CHECK(subject <> '')
);
CREATE TRIGGER IF NOT EXISTS trigger_todos_updated_at AFTER UPDATE ON todos
BEGIN
UPDATE todos SET updated_at = DATETIME('now') WHERE id == NEW.id;
END;

Not in condition using gorm in golang - dynamic not in condition for select statement

I have two databases Booking and Room. Booking has roomid as one of its field. I wrote a select statement which saves the rows retrieved in result variable as stated below.
var result models.Booking
rows, err := utils.DB.Model(&currRequest).Where("check_in BETWEEN ? AND ? AND check_out BETWEEN ? AND ?", currRequest.CheckIn, currRequest.CheckOut, currRequest.CheckIn, currRequest.CheckOut).Select("room_id").Rows()
for rows.Next() {
utils.DB.ScanRows(rows, &result)
fmt.Println(result.RoomID)
}
Now my result.roomid has values of roomids that satisfy the select statement from the bookings table
My result variable may have multiple room id values. I am able to retrieve the roomid values by looping through the result variable. Now I have to check in my main room database called Room and get those room ids that are not in the result struct. By using the below statement, I am only able to access the first value in result.roomid so the not in condition only considers the first values in result.roomid. How do I do the not in condition for all the values in result.roomid?
rows, err := utils.DB.Model(&models.Room{}).Not(result.RoomID).Select("room_id").Rows()
Full code:
package handlers
import (
"encoding/json"
"fmt"
"net/http"
"server/models"
"server/utils"
"strings"
)
func AvailableRoomsHandler(w http.ResponseWriter, r *http.Request) {
currRequest := &models.Booking{}
err := json.NewDecoder(r.Body).Decode(currRequest)
//check if a valid request has been sent from front end
if err != nil {
//fmt.Println(err)
var resp = map[string]interface{}{"status": false, "message": "Invalid json request"}
json.NewEncoder(w).Encode(resp)
return
}
noOfRoomsOccupied := 0
var notinrooms string
// Use GORM API build SQL
//check if any rooms are available which havent been booked yet in the requested check-in and check-out dates
var result models.Booking
rows, err := utils.DB.Model(&currRequest).Where("check_in BETWEEN ? AND ? AND check_out BETWEEN ? AND ?", currRequest.CheckIn, currRequest.CheckOut, currRequest.CheckIn, currRequest.CheckOut).Select("room_id").Rows()
if err != nil {
json.NewEncoder(w).Encode(err)
fmt.Print("error occured in select statement")
return
} else {
defer rows.Close()
for rows.Next() {
noOfRoomsOccupied = noOfRoomsOccupied + 1
utils.DB.ScanRows(rows, &result)
fmt.Println(result.RoomID)
notinrooms = notinrooms + result.RoomID + ","
}
notinrooms = strings.TrimRight(notinrooms, ",")
fmt.Println(notinrooms)
//calculate the number of rooms in the database
//rows, err := utils.DB.Model(&models.Room{}).Select("room_id").Rows()
res := utils.DB.Find(&models.Room{})
rowcount := res.RowsAffected
fmt.Println(rowcount)
if noOfRoomsOccupied == int(rowcount) {
var resp = map[string]interface{}{"status": false, "message": "no rooms available in the specified time period"}
json.NewEncoder(w).Encode(resp)
return
} else {
noOfRooms := (currRequest.NoOfGuests + currRequest.NoOfChildren) / 2
if (currRequest.NoOfGuests+currRequest.NoOfChildren)%2 == 1 {
noOfRooms = noOfRooms + 1
}
if int(noOfRooms) < int(rowcount)-noOfRoomsOccupied {
fmt.Println("number of rooms to book : ", noOfRooms)
//assign rooms if available
var roomids models.Room
//rows, err := utils.DB.Model(&models.Room{}).Not(result.RoomID).Select("room_id").Rows()
fmt.Println("rooms that can be booked")
rows, err := utils.DB.Model(&models.Room{}).Not(result.RoomID).Select("room_id").Rows()
//rows, err := utils.DB.Model(&models.Room{}).Not([]string{notinrooms}).Select("room_id").Rows()
//map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}
if err != nil {
json.NewEncoder(w).Encode(err)
fmt.Print("error occured in select statement to get room ids to assign")
return
} else {
defer rows.Close()
for rows.Next() {
noOfRoomsOccupied = noOfRoomsOccupied + 1
utils.DB.ScanRows(rows, &roomids)
fmt.Println(roomids.RoomID)
}
}
var success = map[string]interface{}{"message": "Select statement worked well"}
json.NewEncoder(w).Encode(success)
return
}
}
}
}
When I do result.roomid, it only gives the first room id and eliminates only that room id in the above select statement. How do I eliminate all the room ids I found in the booking table in the rooms table data?
I tried splitting the result.roomid values and tried to form a string and gave it in the select statement but that didn't work. I tried looping through every result.roomid and ran the not in a statement but that will not make any sense.
With this code:
var result models.Booking
rows, err := utils.DB.Model(&currRequest).Where("check_in BETWEEN ? AND ? AND check_out BETWEEN ? AND ?", currRequest.CheckIn, currRequest.CheckOut, currRequest.CheckIn, currRequest.CheckOut).Select("room_id").Rows()
if err != nil {
json.NewEncoder(w).Encode(err)
fmt.Print("error occured in select statement")
return
} else {
defer rows.Close()
for rows.Next() {
noOfRoomsOccupied = noOfRoomsOccupied + 1
utils.DB.ScanRows(rows, &result)
//rest of the code
}
}
you only get one row of potentially many rows from the result set. To get all the rows and extract their values, you should use []models.Booking.
result := []models.Booking{}
rows, err := utils.DB.Model(&currRequest).Where("check_in BETWEEN ? AND ? AND check_out BETWEEN ? AND ?", currRequest.CheckIn, currRequest.CheckOut, currRequest.CheckIn, currRequest.CheckOut).Select("room_id").Rows()
if err != nil {
json.NewEncoder(w).Encode(err)
fmt.Print("error occured in select statement")
return
} else {
defer rows.Close()
for rows.Next() {
var b models.Booking
noOfRoomsOccupied = noOfRoomsOccupied + 1
utils.DB.ScanRows(rows, &b)
result = append(result, b)
//rest of the code
}
}
However, since you only need roomid anyway, you could make it easier by using []uint (assuming roomid is of type uint).
result := []uint{}
rows, err := utils.DB.Model(&currRequest).Where("check_in BETWEEN ? AND ? AND check_out BETWEEN ? AND ?", currRequest.CheckIn, currRequest.CheckOut, currRequest.CheckIn, currRequest.CheckOut).Select("room_id").Rows()
if err != nil {
json.NewEncoder(w).Encode(err)
fmt.Print("error occured in select statement")
return
} else {
defer rows.Close()
for rows.Next() {
var rid uint
noOfRoomsOccupied = noOfRoomsOccupied + 1
utils.DB.ScanRows(rows, &rid)
result = append(result, rid)
//rest of the code
}
}
With the result being of type []uint, it would be easier to use it with the Not function (per documentation):
rows, err := utils.DB.Model(&models.Room{}).Not(result).Select("room_id").Rows()

Filtering and sorting an SQL query to recreate a nested struct

I am new to Go and I'm trying to populate a struct called Reliefworker from an SQL query which I can send as a JSON payload.
Essentially I have a Reliefworker who may be assigned to many communities and the community can consist of multiple regions.
I suspect there is a clever way of doing this other than what I intend which is a primitive solution that will add a sort to the SQL (by Community) and create a function that detects if the community being added is different to the previous one, in which case I would create a new community struct type object to be appended.
type Reliefworker struct {
Name string `json:"name"`
Communities []Community `json:"community"`
Deployment_id string `json:"deployment_id"`
}
type Community struct{
Name string `json:"name"`
community_id string `json:"community_id"`
Regions []Region `json:"regions"`
}
type Region struct{
Name string `json:"name"`
Region_id string `json:"region_id"`
Reconstruction_grant string `json:"reconstruction_grant"`
Currency string `json:"currency"`
}
Currently I have created a struct that reflects what I am actually getting from SQL whilst pondering my next move. Perhaps this might be a good stepping stone instead of attempting an on-the-fly transformation ?
type ReliefWorker_community_region struct {
Deployment_id string
Community_title int
Region_name string
Reconstruction_grant int
}
func GetReliefWorkers(deployment_id string) []Reliefworker {
fmt.Printf("Confirm I have a deployment id:%v\n", deployment_id)
rows, err := middleware.Db.Query("select deployment_id, community_title, region_name, reconstruction_grant WHERE Deployment_id=$1", brand_id)
if err != nil {
return
}
for rows.Next() {
reliefworker := Reliefworker{}
err = rows.Scan(&deployment_id, &community_title, &region_name, &reconstruction_grant)
if err != nil {
return
}
}
rows.Close()
return
}
I think a sort makes a lot of sense, primitive solutions can be the most efficient:
func GetReliefWorkers(deployment_id string) []Reliefworker {
// Added sort to query
q := "select worker_name, community_title, region_name, reconstruction_grant WHERE deployment_id=? ORDER BY community_title"
rows, err := middleware.Db.Query(q, deployment_id)
if err != nil {
return
}
defer rows.Close() // So rows get closed even on an error
c := Community{} // To keep track of the current community
cmatrix := [][]string{[]string{}} // Matrix of communities and workers
communities := []Community{} // List of communities
workers := make(map[string]Reliefworker) // Map of workers
var ccount int // Index of community in lists
for rows.Next() {
w := Reliefworker{Deployment_id: deployment_id}
r := Region{}
var ctitle string // For comparison later
err = rows.Scan(&w.Name, &ctitle, &r.Name, &r.Reconstruction_grant)
if err != nil {
return
}
if ctitle != c.Name {
communities = append(communities, c)
c = Community{}
c.Name = ctitle
ccount++
cmatrix = append(cmatrix, []string{})
}
c.Regions = append(c.Regions, r)
cmatrix[ccount] = append(cmatrix[ccount], w.Name)
workers[w.Name] = w
}
for i, c := range communities {
for _, id := range cmatrix[i] {
w := workers[id] // To avoid error
w.Communities = append(w.Communities, c)
workers[id] = w
}
}
out := []Reliefworker{}
for _, w := range workers {
out = append(out, w)
}
return out
}
Though it might make even more sense to create seperate tables for communities, regions, and workers, then query them all with a JOIN: https://www.w3schools.com/sql/sql_join_inner.asp
UPDATE: Since you only want to retrieve one Reliefworker, would something like this work?
type ReliefWorker struct {
Name string `json:"name"`
Communities []Community `json:"community"`
}
type Community struct {
Name string `json:"name"`
Regions []Region `json:"regions"`
}
type Region struct {
Name string `json:"name"`
Region_id string `json:"region_id"`
Reconstruction_grant int `json:"reconstruction_grant"`
Currency string `json:"currency"`
}
func GetReliefWorkers(deployment_id string) Reliefworker {
reliefworker := Reliefworker{}
communities := make(map[string]Community)
rows, err := middleware.Db.Query("select name, community_title, region_name, region_id, reconstruction_grant WHERE Deployment_id=$1", deployment_id)
if err != nil {
if err == sql.ErrNoRows {
fmt.Printf("No records for ReliefWorker:%v\n", deployment_id)
}
panic(err)
}
defer rows.Close()
for rows.Next() {
c := Community{}
r := Region{}
err = rows.Scan(&reliefworker.Name, &c.Name, &r.Name, &r.Region_id, &r.Reconstruction_grant)
if err != nil {
panic(err)
}
if _, ok := communities[c.Name]; ok {
c = communities[c.Name]
}
c.Regions = append(c.Regions, r)
communities[c.Name] = c
}
for _, c := range commmunities {
reliefworker.Communities = append(reliefworker.Communities, c)
}
return reliefworker
}
Ok, my crude solution doesn't contain a shred of the intelligence #Absentbird demonstrates but I'm here to learn.
#Absentbird I love your use of maps and multidimensional arrays to hold a matrix of communities and workers. I will focus on making this part of my arsenal over the weekend.
I can accept and adapt #Absentbird's solution once I have a solution to why it gives the error "cannot assign to struct field workers[id].Communities in mapcompilerUnaddressableFieldAssign" for the line workers[id].Communities = append(workers[id].Communities, c)
Firstly apologies as I had to correct two things. Firstly I only needed to return ReliefWorkers (not an array of ReliefWorkers). Secondly ReliefWorker struct did not need to contain the Deployment_id since I already knew it.
I am new to Go so I'd really appreciate feedback on what I can do to better leverage the language and write more concise code.
My structs and solution is currently as follows:
type ReliefWorker struct {
Name string `json:"name"`
Communities []Community `json:"community"`
}
type Community struct {
Name string `json:"name"`
Regions []Region `json:"regions"`
}
type Region struct {
Name string `json:"name"`
Region_id string `json:"region_id"`
Reconstruction_grant int `json:"reconstruction_grant"`
Currency string `json:"currency"`
}
type ReliefWorker_community_region struct {
Name string
Community_title string
Region_name string
Reconstruction_grant int
}
func GetReliefWorkers(deployment_id string) Reliefworker {
var reliefworker Reliefworker
var communitiesOnly []string
var name string
var allReliefWorkerData []ReliefWorker_community_region
rows, err := middleware.Db.Query("select name, community_title, region_name, reconstruction_grant WHERE Deployment_id=$1", deployment_id)
for rows.Next() {
reliefWorker_community_region := ReliefWorker_community_region{}
err = rows.Scan(&reliefWorker_community_region.Name, &reliefWorker_community_region.Community_title, &reliefWorker_community_region.Region_name, &reliefWorker_community_region.Reconstruction_grant)
if err != nil {
panic(err)
}
name = reliefWorker_community_region.Name
allReliefWorkerData = append(allReliefWorkerData, reliefWorker_community_region)
communitiesOnly = append(communitiesOnly, reliefWorker_community_region.Community_title) //All communities go in here, even duplicates, will will create a unique set later
}
rows.Close()
if err != nil {
if err == sql.ErrNoRows {
fmt.Printf("No records for ReliefWorker:%v\n", deployment_id)
}
panic(err)
}
var unique []string //Use this to create a unique index of communities
for _, v := range communitiesOnly {
skip := false
for _, u := range unique {
if v == u {
skip = true
break
}
}
if !skip {
unique = append(unique, v)
}
}
fmt.Println(unique)
reliefworker.Name = name
var community Community
var communities []Community
for _, v := range unique {
community.Name = v
communities = append(communities, community)
}
// Go through each record from the database held within allReliefWorkerData and grab every region belonging to a Community, when done append it to Communities and finally append that to ReliefWorker
for j, u := range communities {
var regions []Region
for i, v := range allReliefWorkerData {
if v.Community_title == u.Name {
var region Region
region.Name = v.Region_name
region.Reconstruction_grant = v.Reconstruction_grant
regions = append(regions, region)
}
}
communities[j].Regions = regions
}
reliefworker.Communities = communities
return reliefworker
}

beego api post insert data

First of all. I use postman to POST data. How to take data from the postman key.
It works by using this way (in my code I set it by myself).
I want to get firstname, lastname, email in form-data.
I want to use as below.
func InsertOneUser(user User) *User {
o := orm.NewOrm()
qs := o.QueryTable(new(User))
i, _ := qs.PrepareInsert()
var u User
user.FirstName = "firstname" <----- this
user.LastName = "lastname" <----- this
user.Email = "something#yahoo.com" <----- this
user.Password, _ = hashPassword(user.Password)
user.RegDate = time.Now()
id, err := i.Insert(&user)
return &u
}
You need beego.Controller in func. if func has beego.Controller, there are two ways(key, parseform).
package main
import (
"fmt"
"github.com/astaxie/beego"
)
type User struct {
Firstname string `form:"firstname"`
Lastname string `form:"lastname"`
Email string `form:"email"`
}
type MainController struct {
beego.Controller
}
func (this *MainController) Post() {
// using key
firstname := this.GetString("firstname")
lastname := this.GetString("lastname")
email := this.GetString("email")
// using Parseform
u := User{}
if err := this.ParseForm(&u); err != nil {
fmt.Println(err)
}
fmt.Println(u)
this.Ctx.WriteString(fmt.Sprintf("%s %s %s", firstname, lastname, email))
}
func main() {
beego.Router("/api/v1/user", &MainController{})
beego.Run()
}

sql: scan row(s) with unknown number of columns (select * from ...)

I have a table t containing a lot of columns, and my sql is like this: select * from t. Now I only want to scan one column or two from the wide returned row set. However, the sql.Scan accepts dest ...interface{} as arguments. Does it mean I have to scan everything and use only the column I needed?
I know I could change the sql from select * to select my_favorite_rows, however, in this case, I have no way to change the sql.
You can make use of Rows.Columns, e.g.
package main
import (
"database/sql"
"fmt"
"github.com/lib/pq"
)
type Vehicle struct {
Id int
Name string
Wheels int
}
// VehicleCol returns a reference for a column of a Vehicle
func VehicleCol(colname string, vh *Vehicle) interface{} {
switch colname {
case "id":
return &vh.Id
case "name":
return &vh.Name
case "wheels":
return &vh.Wheels
default:
panic("unknown column " + colname)
}
}
func panicOnErr(err error) {
if err != nil {
panic(err.Error())
}
}
func main() {
conn, err := pq.ParseURL(`postgres://docker:docker#172.17.0.2:5432/pgsqltest?schema=public`)
panicOnErr(err)
var db *sql.DB
db, err = sql.Open("postgres", conn)
panicOnErr(err)
var rows *sql.Rows
rows, err = db.Query("select * from vehicle")
panicOnErr(err)
// get the column names from the query
var columns []string
columns, err = rows.Columns()
panicOnErr(err)
colNum := len(columns)
all := []Vehicle{}
for rows.Next() {
vh := Vehicle{}
// make references for the cols with the aid of VehicleCol
cols := make([]interface{}, colNum)
for i := 0; i < colNum; i++ {
cols[i] = VehicleCol(columns[i], &vh)
}
err = rows.Scan(cols...)
panicOnErr(err)
all = append(all, vh)
}
fmt.Printf("%#v\n", all)
}
For unknown length of columns but if you're sure about their type,
cols, err := rows.Columns()
if err != nil {
log.Fatal(err.Error())
}
colLen := len(cols)
vals := make([]interface{}, colLen)
for rows.Next() {
for i := 0; i < len(colLen); i++ {
vals[i] = new(string)
}
err := rows.Scan(vals...)
if err != nil {
log.Fatal(err.Error()) // if wrong type
}
fmt.Printf("Column 1: %s\n", *(vals[0].(*string))) // will panic if wrong type
}
PS: Not recommended for prod