Add LOCK table support.
This commit is contained in:
parent
dd9b815dbb
commit
c9561ecc37
8 changed files with 142 additions and 116 deletions
|
|
@ -29,11 +29,6 @@ func newInsertStatement(t WritableTable, columns ...Column) InsertStatement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type columnAssignment struct {
|
|
||||||
col Column
|
|
||||||
expr Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
type insertStatementImpl struct {
|
type insertStatementImpl struct {
|
||||||
table WritableTable
|
table WritableTable
|
||||||
columns []Column
|
columns []Column
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ func Literal(value interface{}) *literalExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l literalExpression) Serialize(out *queryData, options ...serializeOption) error {
|
func (l literalExpression) Serialize(out *queryData, options ...serializeOption) error {
|
||||||
//sqltypes.Value(c.value).EncodeSql(out)
|
|
||||||
|
|
||||||
out.InsertArgument(l.value)
|
out.InsertArgument(l.value)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
95
sqlbuilder/lock_statement.go
Normal file
95
sqlbuilder/lock_statement.go
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
package sqlbuilder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sub0zero/go-sqlbuilder/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lockMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LOCK_ACCESS_SHARE = "ACCESS SHARE"
|
||||||
|
LOCK_ROW_SHARE = "ROW SHARE"
|
||||||
|
LOCK_ROW_EXCLUSIVE = "ROW EXCLUSIVE"
|
||||||
|
LOCK_SHARE_UPDATE_EXCLUSIVE = "SHARE UPDATE EXCLUSIVE"
|
||||||
|
LOCK_SHARE = "SHARE"
|
||||||
|
LOCK_SHARE_ROW_EXCLUSIVE = "SHARE ROW EXCLUSIVE"
|
||||||
|
LOCK_EXCLUSIVE = "EXCLUSIVE"
|
||||||
|
LOCK_ACCESS_EXCLUSIVE = "ACCESS EXCLUSIVE"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lockStatement interface {
|
||||||
|
Statement
|
||||||
|
|
||||||
|
IN(lockMode lockMode) lockStatement
|
||||||
|
NOWAIT() lockStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
type lockStatementImpl struct {
|
||||||
|
tables []tableInterface
|
||||||
|
lockMode lockMode
|
||||||
|
nowait bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func LOCK(tables ...tableInterface) lockStatement {
|
||||||
|
return &lockStatementImpl{
|
||||||
|
tables: tables,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lockStatementImpl) IN(lockMode lockMode) lockStatement {
|
||||||
|
l.lockMode = lockMode
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lockStatementImpl) NOWAIT() lockStatement {
|
||||||
|
l.nowait = true
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lockStatementImpl) Sql() (query string, args []interface{}, err error) {
|
||||||
|
if l == nil {
|
||||||
|
return "", nil, errors.New("nil statement.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(l.tables) == 0 {
|
||||||
|
return "", nil, errors.New("There is no table selected to be locked. ")
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &queryData{}
|
||||||
|
|
||||||
|
out.WriteString("LOCK TABLE ")
|
||||||
|
|
||||||
|
for i, table := range l.tables {
|
||||||
|
if i > 0 {
|
||||||
|
out.WriteString(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := table.SerializeSql(out)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.lockMode != "" {
|
||||||
|
out.WriteString(" IN ")
|
||||||
|
out.WriteString(string(l.lockMode))
|
||||||
|
out.WriteString(" MODE")
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.nowait {
|
||||||
|
out.WriteString(" NOWAIT")
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.buff.String(), out.args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lockStatementImpl) Query(db types.Db, destination interface{}) error {
|
||||||
|
return Query(l, db, destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lockStatementImpl) Execute(db types.Db) (sql.Result, error) {
|
||||||
|
return Execute(l, db)
|
||||||
|
}
|
||||||
24
sqlbuilder/lock_statement_test.go
Normal file
24
sqlbuilder/lock_statement_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
package sqlbuilder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gotest.tools/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLockSingleTable(t *testing.T) {
|
||||||
|
lock := table1.LOCK().IN(LOCK_ROW_SHARE)
|
||||||
|
|
||||||
|
queryStr, _, err := lock.Sql()
|
||||||
|
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, queryStr, `LOCK TABLE db.table1 IN ROW SHARE MODE`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLockMultipleTable(t *testing.T) {
|
||||||
|
lock := LOCK(table2, table1).IN(LOCK_ACCESS_EXCLUSIVE).NOWAIT()
|
||||||
|
|
||||||
|
queryStr, _, err := lock.Sql()
|
||||||
|
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, queryStr, `LOCK TABLE db.table2, db.table1 IN ACCESS EXCLUSIVE MODE NOWAIT`)
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package sqlbuilder
|
package sqlbuilder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
@ -15,14 +14,11 @@ func TestUnionNoSelect(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnionOneSelect(t *testing.T) {
|
func TestUnionOneSelect(t *testing.T) {
|
||||||
query, args, err := UNION(
|
_, _, err := UNION(
|
||||||
table1.SELECT(table1Col1),
|
table1.SELECT(table1Col1),
|
||||||
).Sql()
|
).Sql()
|
||||||
|
|
||||||
assert.Assert(t, err != nil)
|
assert.Assert(t, err != nil)
|
||||||
fmt.Println(err.Error())
|
|
||||||
fmt.Println(query)
|
|
||||||
fmt.Println(args)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnionTwoSelect(t *testing.T) {
|
func TestUnionTwoSelect(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -13,115 +13,12 @@ type Statement interface {
|
||||||
Execute(db types.Db) (sql.Result, error)
|
Execute(db types.Db) (sql.Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LockStatement is used to take Read/Write lock on tables.
|
|
||||||
// See http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
|
|
||||||
//type LockStatement interface {
|
|
||||||
// Statement
|
|
||||||
//
|
|
||||||
// AddReadLock(table *Table) LockStatement
|
|
||||||
// AddWriteLock(table *Table) LockStatement
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// UnlockStatement can be used to release tableName locks taken using LockStatement.
|
|
||||||
//// NOTE: You can not selectively release a lock and continue to hold lock on
|
|
||||||
//// another tableName. UnlockStatement releases all the lock held in the current
|
|
||||||
//// session.
|
|
||||||
//type UnlockStatement interface {
|
|
||||||
// Statement
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// SetGtidNextStatement returns a SQL statement that can be used to explicitly set the next GTID.
|
//// SetGtidNextStatement returns a SQL statement that can be used to explicitly set the next GTID.
|
||||||
//type GtidNextStatement interface {
|
//type GtidNextStatement interface {
|
||||||
// Statement
|
// Statement
|
||||||
//}
|
//}
|
||||||
//
|
//
|
||||||
////
|
|
||||||
//// UNION SELECT Statement ======================================================
|
|
||||||
////
|
|
||||||
////
|
|
||||||
//// LOCK statement ===========================================================
|
|
||||||
////
|
|
||||||
//
|
|
||||||
//// NewLockStatement returns a SQL representing empty set of locks. You need to use
|
|
||||||
//// AddReadLock/AddWriteLock to add tables that need to be locked.
|
|
||||||
//// NOTE: You need at least one lock in the set for it to be a valid statement.
|
|
||||||
//func NewLockStatement() LockStatement {
|
|
||||||
// return &lockStatementImpl{}
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//type lockStatementImpl struct {
|
|
||||||
// locks []tableLock
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//type tableLock struct {
|
|
||||||
// t *Table
|
|
||||||
// w bool
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (l *lockStatementImpl) Execute(db *sql.DB, data interface{}) error {
|
|
||||||
// return nil
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// AddReadLock takes read lock on the tableName.
|
|
||||||
//func (s *lockStatementImpl) AddReadLock(t *Table) LockStatement {
|
|
||||||
// s.locks = append(s.locks, tableLock{t: t, w: false})
|
|
||||||
// return s
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// AddWriteLock takes write lock on the tableName.
|
|
||||||
//func (s *lockStatementImpl) AddWriteLock(t *Table) LockStatement {
|
|
||||||
// s.locks = append(s.locks, tableLock{t: t, w: true})
|
|
||||||
// return s
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (s *lockStatementImpl) String() (sql string, err error) {
|
|
||||||
// if len(s.locks) == 0 {
|
|
||||||
// return "", errors.New("No locks added")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// buf := new(bytes.Buffer)
|
|
||||||
// _, _ = buf.WriteString("LOCK TABLES ")
|
|
||||||
//
|
|
||||||
// for idx, lock := range s.locks {
|
|
||||||
// if lock.t == nil {
|
|
||||||
// return "", errors.Newf("nil tableName.", buf.String())
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if err = lock.t.Serialize(buf); err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if lock.w {
|
|
||||||
// _, _ = buf.WriteString(" WRITE")
|
|
||||||
// } else {
|
|
||||||
// _, _ = buf.WriteString(" READ")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if idx != len(s.locks)-1 {
|
|
||||||
// _, _ = buf.WriteString(", ")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return buf.String(), nil
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// NewUnlockStatement returns SQL statement that can be used to release tableName locks
|
|
||||||
//// grabbed by the current session.
|
|
||||||
//func NewUnlockStatement() UnlockStatement {
|
|
||||||
// return &unlockStatementImpl{}
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//type unlockStatementImpl struct {
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (u *unlockStatementImpl) Execute(db *sql.DB, data interface{}) error {
|
|
||||||
// return nil
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (s *unlockStatementImpl) String() (sql string, err error) {
|
|
||||||
// return "UNLOCK TABLES", nil
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// SET GTID_NEXT statement returns a SQL statement that can be used to explicitly set the next GTID.
|
//// SET GTID_NEXT statement returns a SQL statement that can be used to explicitly set the next GTID.
|
||||||
//func NewGtidNextStatement(sid []byte, gno uint64) GtidNextStatement {
|
//func NewGtidNextStatement(sid []byte, gno uint64) GtidNextStatement {
|
||||||
// return >idNextStatementImpl{
|
// return >idNextStatementImpl{
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ type WritableTable interface {
|
||||||
INSERT(columns ...Column) InsertStatement
|
INSERT(columns ...Column) InsertStatement
|
||||||
UPDATE(columns ...Column) UpdateStatement
|
UPDATE(columns ...Column) UpdateStatement
|
||||||
DELETE() DeleteStatement
|
DELETE() DeleteStatement
|
||||||
|
|
||||||
|
LOCK() lockStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defines a physical tableName in the database that is both readable and writable.
|
// Defines a physical tableName in the database that is both readable and writable.
|
||||||
|
|
@ -172,6 +174,10 @@ func (t *Table) DELETE() DeleteStatement {
|
||||||
return newDeleteStatement(t)
|
return newDeleteStatement(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Table) LOCK() lockStatement {
|
||||||
|
return LOCK(t)
|
||||||
|
}
|
||||||
|
|
||||||
type joinType int
|
type joinType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
|
|
@ -694,6 +694,21 @@ func TestSelectWithCase(t *testing.T) {
|
||||||
assert.Equal(t, dest[1].StaffIdNum, "ONE")
|
assert.Equal(t, dest[1].StaffIdNum, "ONE")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLockTable(t *testing.T) {
|
||||||
|
query := Address.LOCK().IN(sqlbuilder.LOCK_EXCLUSIVE).NOWAIT()
|
||||||
|
|
||||||
|
queryStr, _, err := query.Sql()
|
||||||
|
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.Equal(t, queryStr, `LOCK TABLE dvds.address IN EXCLUSIVE MODE NOWAIT`)
|
||||||
|
|
||||||
|
tx, _ := db.Begin()
|
||||||
|
|
||||||
|
_, err = query.Execute(tx)
|
||||||
|
|
||||||
|
assert.NilError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func int16Ptr(i int16) *int16 {
|
func int16Ptr(i int16) *int16 {
|
||||||
return &i
|
return &i
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue