func manageCollaborators(w *web) { var form struct { ProjectName string Collaborators []string } w.decode(&form) proj := getProject(w.user, w.user.Id, form.ProjectName) // We need to do a delete followed by an insert, which means we need // exclusion for this project to prevent race conditions. lockKey := fmt.Sprintf("%s-%s", proj.Name, proj.Owner.Id) locker.Lock(lockKey) defer locker.Unlock(lockKey) csql.Tx(db, func(tx *sql.Tx) { csql.Exec(tx, ` DELETE FROM collaborator WHERE project_owner = $1 AND project_name = $2 `, proj.Owner.Id, proj.Name) for _, collaborator := range form.Collaborators { u := findUserById(collaborator) csql.Exec(tx, ` INSERT INTO collaborator (project_owner, project_name, userid) VALUES ($1, $2, $3) `, proj.Owner.Id, proj.Name, u.Id) } }) w.json(w.r.PostForm) }
// Set associates the plain text password given with the user that is uniquely // identified by id. The password is hashed with bcrypt. If there is a problem // with hashing or with storing the password, an error is returned. // // This may be called on a new user. func (s *Store) Set(id, password string) (cerr error) { defer csql.Safe(&cerr) hash, err := bcrypt.GenerateFromPassword( []byte(password), bcrypt.DefaultCost) if err != nil { return err } // This lock can be avoided if we use some sort of upsert. // It's possible with Postgres, but this is just way easier. locker.Lock(id) defer locker.Unlock(id) n := csql.Count(s, ` SELECT COUNT(*) FROM `+SqlTableName+` WHERE id = $1 `, id) if n == 0 { csql.Exec(s, ` INSERT INTO `+SqlTableName+` (id, hash) VALUES ($1, $2) `, id, hash) } else { csql.Exec(s, ` UPDATE `+SqlTableName+` SET id = $1, hash = $2 WHERE id = $1 `, id, hash) } return nil }
func insertDocument( creator *lcmUser, proj *project, display string, recorded time.Time, categories []string, content string, ) (*document, error) { d := &document{ Project: proj, Display: display, Name: displayToName(display), Recorded: recorded, Categories: categories, Content: content, CreatedBy: creator, Created: time.Now().UTC(), Modified: time.Now().UTC(), } if err := d.validate(); err != nil { return nil, err } csql.Exec(db, ` INSERT INTO document ( project_owner, project_name, name, recorded, categories, content, created_by, created, modified ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) `, d.Project.Owner.Id, d.Project.Name, d.Name, d.Recorded, d.Categories, d.Content, d.CreatedBy.Id, d.Created, d.Modified) return d, nil }
func doIndices( db *DB, getSql func(index, *DB) string, tables ...string, ) (err error) { defer csql.Safe(&err) trgmEnabled := db.IsFuzzyEnabled() var q string var ok bool for _, idx := range indices { if idx.isFulltext() && !trgmEnabled { // Only show the error message if we're on PostgreSQL. if db.Driver == "postgres" { log.Printf("Skipping fulltext index '%s' since "+ "the pg_trgm extension is not enabled.", idx.sqlName()) } continue } if len(tables) == 0 || fun.In(idx.table, tables) { q += getSql(idx, db) + "; " ok = true } } if ok { csql.Exec(db, q) } return }
func connect(conf configPgsql) *lcmDB { conns := fmt.Sprintf( "user=%s password=%s host=%s port=%d dbname=%s sslmode=disable", conf.User, conf.Password, conf.Host, conf.Port, conf.Database) pgsqlDB, err := migration.Open("postgres", conns, schemaMigrations) if err != nil { log.Fatalf("Could not connect to PostgreSQL (%s@%s/%s): %s", conf.User, conf.Host, conf.Database, err) } // All time operations in the database are done in UTC. // Times for the user (in their timezone) are mostly handled in template // helper functions. csql.Exec(pgsqlDB, "SET timezone = UTC") return &lcmDB{pgsqlDB, conf} }
// insertProject will add the details given as a project to the database. // An error is returned if the data doesn't validate. func insertProject(owner *lcmUser, displayName string) (*project, error) { proj := &project{ Owner: owner, Name: displayToName(displayName), Display: displayName, Added: time.Now().UTC(), } if err := proj.validate(); err != nil { return nil, err } csql.Exec(db, ` INSERT INTO project (owner, name, created) VALUES ($1, $2, $3) `, proj.Owner.Id, proj.Name, proj.Added) return proj, nil }
// Results executes the parameters of the search and returns the results. func (s *Searcher) Results() (rs []Result, err error) { defer csql.Safe(&err) // Set the similarity threshold first. if s.db.IsFuzzyEnabled() { csql.Exec(s.db, "SELECT set_limit($1)", s.similarThreshold) } if s.subTvshow != nil { if err := s.subTvshow.choose(s, s.chooser); err != nil { return nil, err } } if s.subCredits != nil { if err := s.subCredits.choose(s, s.chooser); err != nil { return nil, err } } if s.subCast != nil { if err := s.subCast.choose(s, s.chooser); err != nil { return nil, err } } var rows *sql.Rows if len(s.name) == 0 { rows = csql.Query(s.db, s.sql()) } else { rows = csql.Query(s.db, s.sql(), strings.Join(s.name, " ")) } csql.ForRow(rows, func(scanner csql.RowScanner) { var r Result var ent string csql.Scan(scanner, &ent, &r.Id, &r.Name, &r.Year, &r.Similarity, &r.Attrs, &r.Rank.Votes, &r.Rank.Rank, &r.Credit.ActorId, &r.Credit.MediaId, &r.Credit.Character, &r.Credit.Position, &r.Credit.Attrs) r.Entity = imdb.Entities[ent] rs = append(rs, r) }) return }
// delete will delete the project from the database. This includes all // attached collaborators and documents. func (proj *project) delete() { csql.Exec(db, ` DELETE FROM project WHERE owner = $1 AND name = $2 `, proj.Owner.Id, proj.Name) }