nextProjectID returns the next per-project auto-increment ID for the given table. Must be called within a transaction. Locks the project row to serialize concurrent inserts. Returns at least idMinValue (101) for new projects.
(ctx context.Context, tx *sql.Tx, table, projectID string)
| 18 | // Must be called within a transaction. Locks the project row to serialize concurrent inserts. |
| 19 | // Returns at least idMinValue (101) for new projects. |
| 20 | func nextProjectID(ctx context.Context, tx *sql.Tx, table, projectID string) (int64, error) { |
| 21 | if _, err := tx.ExecContext(ctx, |
| 22 | "SELECT 1 FROM project WHERE resource_id = $1 FOR UPDATE", projectID); err != nil { |
| 23 | return 0, errors.Wrapf(err, "failed to lock project %s", projectID) |
| 24 | } |
| 25 | var maxID int64 |
| 26 | |
| 27 | q := qb.Q() |
| 28 | q.Space(` |
| 29 | SELECT COALESCE(MAX(id), 0) FROM ? WHERE project = ? |
| 30 | `, qb.Q().Space(table), projectID) |
| 31 | query, args, err := q.ToSQL() |
| 32 | if err != nil { |
| 33 | return 0, errors.Wrapf(err, "failed to build sql") |
| 34 | } |
| 35 | |
| 36 | if err := tx.QueryRowContext(ctx, query, args...).Scan(&maxID); err != nil { |
| 37 | return 0, errors.Wrapf(err, "failed to get max id for %s", table) |
| 38 | } |
| 39 | nextID := maxID + 1 |
| 40 | if nextID < idMinValue { |
| 41 | nextID = idMinValue |
| 42 | } |
| 43 | return nextID, nil |
| 44 | } |
no test coverage detected