Skip to content

Commit

Permalink
https://github.com/go-sql-driver/mysql/issues/420
Browse files Browse the repository at this point in the history
  • Loading branch information
akihikodaki committed Jan 9, 2017
1 parent b007242 commit 257aeb2
Show file tree
Hide file tree
Showing 61 changed files with 1,716 additions and 1,351 deletions.
91 changes: 48 additions & 43 deletions db/club.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

package db

import "github.com/kagucho/tsubonesystem3/chanjson"
import (
"github.com/kagucho/tsubonesystem3/chanjson"
"log"
)

// Chief is a structure to hold the information about a member.
type Chief struct {
Expand All @@ -35,6 +38,11 @@ type Club struct {
Name string `json:"name"`
}

type ClubResult struct {
Error error
Value Club
}

type clubMemberResult struct {
Error error
Value struct {
Expand Down Expand Up @@ -66,66 +74,63 @@ type clubResult struct {
}

// QueryClub returns db.Club corresponding with the given ID.
func (db DB) QueryClub(id string) (Club, error) {
var chiefID uint16
var clubID uint8
var club Club

if scanError := db.sql.QueryRow(
`SELECT chief,id,name FROM clubs WHERE display_id=?`,
id).Scan(&chiefID, &clubID, &club.Name); scanError != nil {
return Club{}, scanError
}

if scanError := db.sql.QueryRow(
`SELECT display_id,mail,nickname,realname,tel FROM members WHERE id=?`,
chiefID).Scan(
&club.Chief.ID, &club.Chief.Mail,
&club.Chief.Nickname, &club.Chief.Realname,
&club.Chief.Tel); scanError != nil {
return Club{}, scanError
}

members := make(chan clubMemberResult)
func (db DB) QueryClub(id string) <-chan ClubResult {
clubChan := make(chan ClubResult)

go func() {
defer close(members)
var club ClubResult

rows, queryError := db.sql.Query(
`SELECT member FROM club_member WHERE club=?`, clubID)
rows, queryError := db.stmts[stmtBriefClub].Query(id)
if queryError != nil {
members <- clubMemberResult{Error: queryError}
clubChan <- ClubResult{Error: queryError}
close(clubChan)
return
}

defer rows.Close()

for rows.Next() {
var memberID uint16
rows.Next()
club.Error = rows.Scan(&club.Value.Name)
if club.Error != nil {
clubChan <- club
close(clubChan)
return
}

if scanError := rows.Scan(&memberID); scanError != nil {
members <- clubMemberResult{Error: scanError}
return
}
rows.Next()
club.Error = rows.Scan(
&club.Value.Chief.ID, &club.Value.Chief.Mail,
&club.Value.Chief.Nickname, &club.Value.Chief.Realname,
&club.Value.Chief.Tel)
if club.Error != nil {
log.Println("chief")
clubChan <- club
close(clubChan)
return
}

var result clubMemberResult
result.Error = db.sql.QueryRow(
`SELECT entrance,display_id,nickname,realname FROM members WHERE id=?`,
memberID).Scan(
&result.Value.Entrance, &result.Value.ID,
&result.Value.Nickname, &result.Value.Realname)
members := make(chan clubMemberResult)
defer close(members)

members <- result
club.Value.Members = chanjson.New(members)
clubChan <- club
close(clubChan)

if result.Error != nil {
for rows.Next() {
var member clubMemberResult
member.Error = rows.Scan(
&member.Value.Entrance, &member.Value.ID,
&member.Value.Nickname, &member.Value.Realname)

members <- member

if member.Error != nil {
return
}
}
}()

club.Members = chanjson.New(members)

return club, nil
return clubChan
}

// QueryClubName returns the name of the club identified with the given ID.
Expand Down
34 changes: 30 additions & 4 deletions db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,41 @@ import (
"github.com/kagucho/tsubonesystem3/configuration"
)

const (
stmtBriefClub = iota

stmtNumber
)

var stmtQueries = [...]string{
stmtBriefClub: `CALL brief_club(?)`,
}

// DB is a structure to keep the connection to the database.
type DB struct {
sql *sql.DB
stmts [stmtNumber]*sql.Stmt
}

// Open returns a new connection to the database.
func Open() (DB, error) {
sql, openError := sql.Open(`mysql`, configuration.DBDSN)
return DB{sql}, openError
// Prepare prepares the database.
func Prepare() (DB, error) {
var db DB
var prepareError error

db.sql, prepareError = sql.Open(`mysql`, configuration.DBDSN)
if prepareError != nil {
return db, prepareError
}

for index, query := range stmtQueries {
db.stmts[index], prepareError = db.sql.Prepare(query)
if prepareError != nil {
db.sql.Close()
return db, prepareError
}
}

return db, nil
}

// Close closes the connection to the database.
Expand Down
163 changes: 160 additions & 3 deletions db/member.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type Member struct {
}

type member struct {
Affiliation string `json:"affiliation"`
Entrance uint16 `json:"entrance"`
ID string `json:"id"`
Nickname string `json:"nickname"`
Expand Down Expand Up @@ -199,7 +200,7 @@ func (db DB) QueryMembers() chanjson.ChanJSON {
defer close(resultChan)

rows, queryError := db.sql.Query(
`SELECT entrance,display_id,nickname,ob,realname FROM members`)
`SELECT affiliation,entrance,display_id,nickname,ob,realname FROM members`)
if queryError != nil {
resultChan <- memberResult{Error: queryError}
return
Expand All @@ -211,8 +212,11 @@ func (db DB) QueryMembers() chanjson.ChanJSON {
var result memberResult

result.Error = rows.Scan(
&result.Value.Entrance, &result.Value.ID,
&result.Value.Nickname, &result.Value.OB,
&result.Value.Affiliation,
&result.Value.Entrance,
&result.Value.ID,
&result.Value.Nickname,
&result.Value.OB,
&result.Value.Realname)

resultChan <- result
Expand Down Expand Up @@ -281,6 +285,159 @@ func (db DB) QueryMembersCount(entrance int, nickname string, realname string,
return count, scanError
}

func memberQueryClubIDs(db DB, clubs []string) (map[uint8]struct{}, error) {
clubInterfaces := make([]interface{}, len(clubs))
clubIDs := make(map[uint8]struct{}, len(clubs))

for index, value := range clubs {
clubInterfaces[index] = value
}

rows, queryError := db.sql.Query(
strings.Join([]string{
`SELECT id FROM clubs WHERE display_id IN(?`,
strings.Repeat(`,?`, len(clubs) - 1), `)`,
}, ``),
clubInterfaces...)
if queryError != nil {
return nil, queryError
}

defer rows.Close()

for rows.Next() {
var id uint8
if scanError := rows.Scan(&id); scanError != nil {
return nil, scanError
}

clubIDs[id] = struct{}{}
}

return clubIDs, nil
}

func memberDiffClubs(db DB, member uint16, clubs map[uint8]struct{}) ([]interface{}, error) {
deleted := make([]interface{}, 0)
registered, queryError := db.sql.Query(`SELECT id,club FROM club_member WHERE member=?`, member)
if queryError != nil {
return nil, queryError
}

defer registered.Close()

for registered.Next() {
var id uint16
var club uint8
if scanError := registered.Scan(&id, &club); scanError != nil {
return nil, scanError
}

if _, present := clubs[club]; !present {
deleted = append(deleted, id)
} else {
delete(clubs, club)
}
}

return deleted, nil
}

func (db DB) UpdateMember(id string, affiliation string, clubs []string, entrance int, gender, mail, nickname, realname string) error {
expressions := make([]string, 0, 5)
arguments := make([]interface{}, 0, 5)

if affiliation != `` {
expressions = append(expressions, `affiliation=?`)
arguments = append(arguments, affiliation)
}

if entrance != 0 {
expressions = append(expressions, `entrance=?`)
arguments = append(arguments, entrance)
}

if gender != `` {
expressions = append(expressions, `gender=?`)
arguments = append(arguments, gender)
}

if mail != `` {
expressions = append(expressions, `mail=?`)
arguments = append(arguments, mail)
}

if nickname != `` {
expressions = append(expressions, `nickname=?`)
arguments = append(arguments, nickname)
}

if realname != `` {
expressions = append(expressions, `realname=?`)
arguments = append(arguments, realname)
}

if len(expressions) > 0 {
arguments = append(arguments, id)

_, execError := db.sql.Exec(
strings.Join([]string{`UPDATE members SET`, strings.Join(expressions, `,`), `WHERE id=?`}, ` `),
arguments...)
if execError != nil {
return execError
}
}

if len(clubs) > 0 {
var dbID uint16
if scanError := db.sql.QueryRow(`SELECT id FROM members WHERE display_id=?`, id).Scan(&dbID); scanError != nil {
return scanError
}

pendings, pendingsError := memberQueryClubIDs(db, clubs)
if pendingsError != nil {
return pendingsError
}

toDelete, diffError := memberDiffClubs(db, dbID, pendings)
if diffError != nil {
return diffError
}

if len(toDelete) > 0 {
if _, execError := db.sql.Exec(
strings.Join([]string{
`DELETE FROM club_member WHERE id IN(?`,
strings.Repeat(`,?`, len(toDelete) - 1), `)`,
}, ``),
toDelete...);
execError != nil {
return execError
}
}

if len(pendings) > 0 {
arguments = make([]interface{}, 0, len(pendings) * 2);

for club := range pendings {
arguments = append(arguments, club, dbID)
}

if _, execError := db.sql.Exec(
strings.Join([]string{
`INSERT INTO club_member(club,member)VALUES(?,?)`,
strings.Repeat(`,(?,?)`, len(pendings) - 1),
}, ``),
arguments...);
execError != nil {
return execError
}
}
}

return nil
}

// ValidateMemberEntrance returns whether the given entrance year is valid.
func ValidateMemberEntrance(entrance int) bool {
return entrance >= 1901 && entrance <= 2155
Expand Down
8 changes: 4 additions & 4 deletions handler/apiv0/club/detail.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ func DetailServeHTTP(writer http.ResponseWriter, request *http.Request,
defer common.Recover(writer)

id := request.FormValue(`id`)
detail, queryError := dbInstance.QueryClub(id)
result := <-dbInstance.QueryClub(id)

switch queryError {
switch result.Error {
case nil:
return func() {
common.ServeJSON(writer, detail, http.StatusOK)
common.ServeJSON(writer, result.Value, http.StatusOK)
}

case sql.ErrNoRows:
Expand All @@ -50,7 +50,7 @@ func DetailServeHTTP(writer http.ResponseWriter, request *http.Request,
}

default:
panic(queryError)
panic(result.Error)
}
}()

Expand Down
Loading

0 comments on commit 257aeb2

Please sign in to comment.