Spanner: Add trivial sequence number generator. (#101777)
* Add trivial sequence number generator.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type sequenceGenerator struct {
|
||||
db *sql.DB
|
||||
sequencesTable string
|
||||
}
|
||||
|
||||
func newSequenceGenerator(db *sql.DB) *sequenceGenerator {
|
||||
return &sequenceGenerator{
|
||||
db: db,
|
||||
sequencesTable: "autoincrement_sequences",
|
||||
}
|
||||
}
|
||||
|
||||
func (sg *sequenceGenerator) Next(ctx context.Context, table, column string) (int64, error) {
|
||||
// Current implementation fetches new value for each Next call.
|
||||
key := fmt.Sprintf("%s:%s", table, column)
|
||||
|
||||
tx, err := sg.db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// TODO: "FOR UPDATE". (Somehow this doesn't seem to be supported in Spanner emulator?)
|
||||
r, err := tx.QueryContext(ctx, "SELECT next_value FROM "+sg.sequencesTable+" WHERE name = ?", key)
|
||||
if err != nil {
|
||||
err2 := tx.Rollback()
|
||||
return 0, errors.Join(err, err2)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
// Sequence doesn't exist yet. Return 1, and put 2 into the table.
|
||||
if !r.Next() {
|
||||
if err := r.Err(); err != nil {
|
||||
return 0, errors.Join(err, tx.Rollback())
|
||||
}
|
||||
val := int64(1)
|
||||
|
||||
_, err := tx.ExecContext(ctx, "INSERT INTO "+sg.sequencesTable+" (name, next_value) VALUES(?, ?)", key, val+1)
|
||||
if err != nil {
|
||||
return 0, errors.Join(err, tx.Rollback())
|
||||
}
|
||||
|
||||
return val, tx.Commit()
|
||||
}
|
||||
|
||||
var val int64
|
||||
if err := r.Scan(&val); err != nil {
|
||||
return 0, errors.Join(err, tx.Rollback())
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, "UPDATE "+sg.sequencesTable+" SET next_value = ? WHERE name = ?", val+1, key)
|
||||
if err != nil {
|
||||
return 0, errors.Join(err, tx.Rollback())
|
||||
}
|
||||
|
||||
return val, tx.Commit()
|
||||
}
|
||||
|
||||
func (sg *sequenceGenerator) close() {
|
||||
// Nothing to do just yet.
|
||||
}
|
||||
Reference in New Issue
Block a user