diff --git a/go.mod b/go.mod index 74ed10b753decc4f0740af947abb8019a84b7ba0..adab48cd8c270cdd95ace9d3d04a6331ba34b922 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/smartystreets/goconvey v1.7.2 github.com/stretchr/testify v1.7.5 - github.com/tidwall/btree v1.3.1 + github.com/tidwall/btree v1.4.3 github.com/yireyun/go-queue v0.0.0-20220725040158-a4dd64810e1e go.uber.org/zap v1.21.0 golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 diff --git a/go.sum b/go.sum index 82649e0a36daf08e4d1d56cc59e2b69694535fcb..2b72851565a17d12d3f98620e2120dd7ce5c036a 100644 --- a/go.sum +++ b/go.sum @@ -456,6 +456,8 @@ github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324 github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/tidwall/btree v1.3.1 h1:636+tdVDs8Hjcf35Di260W2xCW4KuoXOKyk9QWOvCpA= github.com/tidwall/btree v1.3.1/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= +github.com/tidwall/btree v1.4.3 h1:Lf5U/66bk0ftNppOBjVoy/AIPBrLMkheBp4NnSNiYOo= +github.com/tidwall/btree v1.4.3/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= diff --git a/pkg/txn/storage/txn/account.go b/pkg/txn/storage/txn/account.go index a1db7617783aa0600f7b6bd4985df0421563ed1b..44b1ccffcba9199e6d561debe80bb4658b72a73b 100644 --- a/pkg/txn/storage/txn/account.go +++ b/pkg/txn/storage/txn/account.go @@ -15,7 +15,6 @@ package txnstorage import ( - "github.com/google/uuid" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" ) @@ -39,8 +38,7 @@ func (m *MemHandler) ensureAccount( if len(keys) == 0 { // create one db := DatabaseRow{ - ID: uuid.NewString(), - NumberID: catalog.SystemDBID, + ID: txnengine.NewID(), AccountID: accessInfo.AccountID, Name: catalog.SystemDBName, } diff --git a/pkg/txn/storage/txn/batch.go b/pkg/txn/storage/txn/batch.go index b01f60e5b77e76858da023047b3f11bb3e21d3c0..8840d02af71b79d5dc16eedeca966115783fa9bc 100644 --- a/pkg/txn/storage/txn/batch.go +++ b/pkg/txn/storage/txn/batch.go @@ -343,7 +343,7 @@ func vectorAt(vec *vector.Vector, i int) (value Nullable) { } - panic(fmt.Errorf("unknown column type: %v", vec.Typ)) + panic(fmt.Sprintf("unknown column type: %v", vec.Typ)) } func vectorAppend(vec *vector.Vector, value Nullable, heap *mheap.Mheap) { diff --git a/pkg/txn/storage/txn/catalog.go b/pkg/txn/storage/txn/catalog.go index 49a4cc19b8fcda57179f128d2cfc95c7881390e6..f653256f933319920cf3ea5af9736cb8797424c2 100644 --- a/pkg/txn/storage/txn/catalog.go +++ b/pkg/txn/storage/txn/catalog.go @@ -18,6 +18,7 @@ import ( "fmt" "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/txn/storage/txn/memtable" "github.com/matrixorigin/matrixone/pkg/vm/engine" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" @@ -35,15 +36,24 @@ var ( index_RowID = Text("row id") ) +type ( + DatabaseRowIter = Iter[ID, DatabaseRow] + RelationRowIter = Iter[ID, RelationRow] + AttributeRowIter = Iter[ID, AttributeRow] +) + type DatabaseRow struct { - ID string - NumberID uint64 + ID ID AccountID uint32 // 0 is the sys account Name string } -func (d DatabaseRow) Key() Text { - return Text(d.ID) +func (d DatabaseRow) Key() ID { + return d.ID +} + +func (d DatabaseRow) Value() DatabaseRow { + return d } func (d DatabaseRow) Indexes() []Tuple { @@ -61,8 +71,8 @@ func (d DatabaseRow) AttrByName(tx *Transaction, name string) (ret Nullable, err if attr.Name != name { continue } - if !typeMatch(ret.Value, attr.Type.Oid) { - panic(fmt.Errorf("%s should be %v typed", name, attr.Type)) + if !memtable.TypeMatch(ret.Value, attr.Type.Oid) { + panic(fmt.Sprintf("%s should be %v typed", name, attr.Type)) } } }() @@ -74,17 +84,16 @@ func (d DatabaseRow) AttrByName(tx *Transaction, name string) (ret Nullable, err case catalog.SystemDBAttr_CreateSQL: ret.Value = "" case catalog.SystemDBAttr_ID: - ret.Value = d.NumberID + ret.Value = uint64(d.ID) default: - panic(fmt.Errorf("fixme: %s", name)) + panic(fmt.Sprintf("fixme: %s", name)) } return } type RelationRow struct { - ID string - NumberID uint64 - DatabaseID string + ID ID + DatabaseID ID Name string Type txnengine.RelationType Comments string @@ -95,14 +104,18 @@ type RelationRow struct { handler *MemHandler } -func (r RelationRow) Key() Text { - return Text(r.ID) +func (r RelationRow) Key() ID { + return r.ID +} + +func (r RelationRow) Value() RelationRow { + return r } func (r RelationRow) Indexes() []Tuple { return []Tuple{ - {index_DatabaseID, Text(r.DatabaseID)}, - {index_DatabaseID_Name, Text(r.DatabaseID), Text(r.Name)}, + {index_DatabaseID, r.DatabaseID}, + {index_DatabaseID_Name, r.DatabaseID, Text(r.Name)}, } } @@ -114,22 +127,22 @@ func (r RelationRow) AttrByName(tx *Transaction, name string) (ret Nullable, err if attr.Name != name { continue } - if !typeMatch(ret.Value, attr.Type.Oid) { - panic(fmt.Errorf("%s should be %v typed", name, attr.Type)) + if !memtable.TypeMatch(ret.Value, attr.Type.Oid) { + panic(fmt.Sprintf("%s should be %v typed", name, attr.Type)) } } }() switch name { case catalog.SystemRelAttr_ID: - ret.Value = r.NumberID + ret.Value = uint64(r.ID) case catalog.SystemRelAttr_Name: ret.Value = r.Name case catalog.SystemRelAttr_DBName: - if r.DatabaseID == "" { + if r.DatabaseID.IsEmpty() { ret.Value = "" return } - db, err := r.handler.databases.Get(tx, Text(r.DatabaseID)) + db, err := r.handler.databases.Get(tx, r.DatabaseID) if err != nil { return ret, err } @@ -143,24 +156,24 @@ func (r RelationRow) AttrByName(tx *Transaction, name string) (ret Nullable, err case catalog.SystemRelAttr_CreateSQL: ret.Value = "" case catalog.SystemRelAttr_DBID: - if r.DatabaseID == "" { + if r.DatabaseID.IsEmpty() { ret.Value = "" return } - db, err := r.handler.databases.Get(tx, Text(r.DatabaseID)) + db, err := r.handler.databases.Get(tx, r.DatabaseID) if err != nil { return ret, err } - ret.Value = db.NumberID + ret.Value = uint64(db.ID) default: - panic(fmt.Errorf("fixme: %s", name)) + panic(fmt.Sprintf("fixme: %s", name)) } return } type AttributeRow struct { - ID string - RelationID string + ID ID + RelationID ID Order int Nullable bool engine.Attribute @@ -168,16 +181,20 @@ type AttributeRow struct { handler *MemHandler } -func (a AttributeRow) Key() Text { - return Text(a.ID) +func (a AttributeRow) Key() ID { + return a.ID +} + +func (a AttributeRow) Value() AttributeRow { + return a } func (a AttributeRow) Indexes() []Tuple { return []Tuple{ - {index_RelationID, Text(a.RelationID)}, - {index_RelationID_Name, Text(a.RelationID), Text(a.Name)}, - {index_RelationID_IsPrimary, Text(a.RelationID), Bool(a.Primary)}, - {index_RelationID_IsHidden, Text(a.RelationID), Bool(a.IsHidden)}, + {index_RelationID, a.RelationID}, + {index_RelationID_Name, a.RelationID, Text(a.Name)}, + {index_RelationID_IsPrimary, a.RelationID, Bool(a.Primary)}, + {index_RelationID_IsHidden, a.RelationID, Bool(a.IsHidden)}, } } @@ -189,28 +206,28 @@ func (a AttributeRow) AttrByName(tx *Transaction, name string) (ret Nullable, er if attr.Name != name { continue } - if !typeMatch(ret.Value, attr.Type.Oid) { - panic(fmt.Errorf("%s should be %v typed", name, attr.Type)) + if !memtable.TypeMatch(ret.Value, attr.Type.Oid) { + panic(fmt.Sprintf("%s should be %v typed", name, attr.Type)) } } }() switch name { case catalog.SystemColAttr_DBName: - rel, err := a.handler.relations.Get(tx, Text(a.RelationID)) + rel, err := a.handler.relations.Get(tx, a.RelationID) if err != nil { return ret, err } - if rel.DatabaseID == "" { + if rel.DatabaseID.IsEmpty() { ret.Value = "" return ret, nil } - db, err := a.handler.databases.Get(tx, Text(rel.DatabaseID)) + db, err := a.handler.databases.Get(tx, rel.DatabaseID) if err != nil { return ret, err } ret.Value = db.Name case catalog.SystemColAttr_RelName: - rel, err := a.handler.relations.Get(tx, Text(a.RelationID)) + rel, err := a.handler.relations.Get(tx, a.RelationID) if err != nil { return ret, err } @@ -250,24 +267,28 @@ func (a AttributeRow) AttrByName(tx *Transaction, name string) (ret Nullable, er case catalog.SystemColAttr_IsHidden: ret.Value = boolToInt8(a.IsHidden) default: - panic(fmt.Errorf("fixme: %s", name)) + panic(fmt.Sprintf("fixme: %s", name)) } return } type IndexRow struct { - ID string - RelationID string + ID ID + RelationID ID engine.IndexTableDef } -func (i IndexRow) Key() Text { - return Text(i.ID) +func (i IndexRow) Key() ID { + return i.ID +} + +func (i IndexRow) Value() IndexRow { + return i } func (i IndexRow) Indexes() []Tuple { return []Tuple{ - {index_RelationID, Text(i.RelationID)}, - {index_RelationID_Name, Text(i.RelationID), Text(i.Name)}, + {index_RelationID, i.RelationID}, + {index_RelationID_Name, i.RelationID, Text(i.Name)}, } } diff --git a/pkg/txn/storage/txn/catalog_handler.go b/pkg/txn/storage/txn/catalog_handler.go index e4e284a61b80bfdb82697a022cc62b14e231bc3f..fdadec460ec8bf297b5380088ecdb48790875b36 100644 --- a/pkg/txn/storage/txn/catalog_handler.go +++ b/pkg/txn/storage/txn/catalog_handler.go @@ -20,11 +20,13 @@ import ( "sync" "github.com/google/uuid" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/nulls" "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/pb/timestamp" "github.com/matrixorigin/matrixone/pkg/pb/txn" + "github.com/matrixorigin/matrixone/pkg/txn/storage/txn/memtable" "github.com/matrixorigin/matrixone/pkg/vm/engine" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/moengine" @@ -36,11 +38,11 @@ import ( // CatalogHandler handles read-only requests for catalog type CatalogHandler struct { upstream *MemHandler - dbID string - sysRelationIDs map[string]string + dbID ID + sysRelationIDs map[ID]string iterators struct { sync.Mutex - Map map[string]any // id -> Iterator + Map map[ID]any // id -> Iterator } } @@ -50,16 +52,16 @@ func NewCatalogHandler(upstream *MemHandler) *CatalogHandler { handler := &CatalogHandler{ upstream: upstream, - sysRelationIDs: make(map[string]string), + sysRelationIDs: make(map[ID]string), } - handler.iterators.Map = make(map[string]any) + handler.iterators.Map = make(map[ID]any) now := Time{ Timestamp: timestamp.Timestamp{ PhysicalTime: math.MinInt, }, } - tx := NewTransaction(uuid.NewString(), now, SnapshotIsolation) + tx := memtable.NewTransaction(uuid.NewString(), now, memtable.SnapshotIsolation) defer func() { if err := tx.Commit(); err != nil { panic(err) @@ -68,8 +70,7 @@ func NewCatalogHandler(upstream *MemHandler) *CatalogHandler { // database db := DatabaseRow{ - ID: uuid.NewString(), - NumberID: catalog.SystemDBID, + ID: ID(catalog.SystemDBID), AccountID: 0, Name: catalog.SystemDBName, } @@ -80,8 +81,7 @@ func NewCatalogHandler(upstream *MemHandler) *CatalogHandler { // relations databasesRelRow := RelationRow{ - ID: uuid.NewString(), - NumberID: catalog.SystemTable_DB_ID, + ID: ID(catalog.SystemTable_DB_ID), DatabaseID: db.ID, Name: catalog.SystemTable_DB_Name, Type: txnengine.RelationTable, @@ -92,8 +92,7 @@ func NewCatalogHandler(upstream *MemHandler) *CatalogHandler { handler.sysRelationIDs[databasesRelRow.ID] = databasesRelRow.Name tablesRelRow := RelationRow{ - ID: uuid.NewString(), - NumberID: catalog.SystemTable_Table_ID, + ID: ID(catalog.SystemTable_Table_ID), DatabaseID: db.ID, Name: catalog.SystemTable_Table_Name, Type: txnengine.RelationTable, @@ -104,8 +103,7 @@ func NewCatalogHandler(upstream *MemHandler) *CatalogHandler { handler.sysRelationIDs[tablesRelRow.ID] = tablesRelRow.Name attributesRelRow := RelationRow{ - ID: uuid.NewString(), - NumberID: catalog.SystemTable_Columns_ID, + ID: ID(catalog.SystemTable_Columns_ID), DatabaseID: db.ID, Name: catalog.SystemTable_Columns_Name, Type: txnengine.RelationTable, @@ -127,7 +125,7 @@ func NewCatalogHandler(upstream *MemHandler) *CatalogHandler { continue } row := AttributeRow{ - ID: uuid.NewString(), + ID: txnengine.NewID(), RelationID: databasesRelRow.ID, Order: i, Nullable: true, @@ -148,7 +146,7 @@ func NewCatalogHandler(upstream *MemHandler) *CatalogHandler { continue } row := AttributeRow{ - ID: uuid.NewString(), + ID: txnengine.NewID(), RelationID: tablesRelRow.ID, Order: i, Nullable: true, @@ -169,7 +167,7 @@ func NewCatalogHandler(upstream *MemHandler) *CatalogHandler { continue } row := AttributeRow{ - ID: uuid.NewString(), + ID: txnengine.NewID(), RelationID: attributesRelRow.ID, Order: i, Nullable: true, @@ -184,10 +182,9 @@ func NewCatalogHandler(upstream *MemHandler) *CatalogHandler { } func (c *CatalogHandler) HandleAddTableDef(meta txn.TxnMeta, req txnengine.AddTableDefReq, resp *txnengine.AddTableDefResp) (err error) { - if name, ok := c.sysRelationIDs[req.TableID]; ok { + if _, ok := c.sysRelationIDs[req.TableID]; ok { defer logReq("catalog", req, meta, resp, &err)() - resp.ErrResp.Why = fmt.Sprintf("%s is system table", name) - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } return c.upstream.HandleAddTableDef(meta, req, resp) } @@ -204,20 +201,20 @@ func (c *CatalogHandler) HandleCloseTableIter(meta txn.TxnMeta, req txnengine.Cl if ok { defer logReq("catalog", req, meta, resp, &err)() switch v := v.(type) { - case *Iter[Text, DatabaseRow]: + case *DatabaseRowIter: if err := v.TableIter.Close(); err != nil { return err } - case *Iter[Text, RelationRow]: + case *RelationRowIter: if err := v.TableIter.Close(); err != nil { return err } - case *Iter[Text, AttributeRow]: + case *AttributeRowIter: if err := v.TableIter.Close(); err != nil { return err } default: - panic(fmt.Errorf("fixme: %T", v)) + panic(fmt.Sprintf("fixme: %T", v)) } c.iterators.Lock() delete(c.iterators.Map, req.IterID) @@ -246,8 +243,7 @@ func (c *CatalogHandler) HandleCreateDatabase(meta txn.TxnMeta, req txnengine.Cr if req.Name == catalog.SystemDBName { defer logReq("catalog", req, meta, resp, &err)() - resp.ErrResp.Why = req.Name + " is system database" - return nil + return moerr.NewDBAlreadyExists(req.Name) } return c.upstream.HandleCreateDatabase(meta, req, resp) } @@ -257,19 +253,17 @@ func (c *CatalogHandler) HandleCreateRelation(meta txn.TxnMeta, req txnengine.Cr } func (c *CatalogHandler) HandleDelTableDef(meta txn.TxnMeta, req txnengine.DelTableDefReq, resp *txnengine.DelTableDefResp) (err error) { - if name, ok := c.sysRelationIDs[req.TableID]; ok { + if _, ok := c.sysRelationIDs[req.TableID]; ok { defer logReq("catalog", req, meta, resp, &err)() - resp.ErrResp.Why = fmt.Sprintf("%s is system table", name) - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } return c.upstream.HandleDelTableDef(meta, req, resp) } func (c *CatalogHandler) HandleDelete(meta txn.TxnMeta, req txnengine.DeleteReq, resp *txnengine.DeleteResp) (err error) { - if name, ok := c.sysRelationIDs[req.TableID]; ok { + if _, ok := c.sysRelationIDs[req.TableID]; ok { defer logReq("catalog", req, meta, resp, &err)() - resp.ErrResp.Why = fmt.Sprintf("%s is system table", name) - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } return c.upstream.HandleDelete(meta, req, resp) } @@ -283,8 +277,7 @@ func (c *CatalogHandler) HandleDeleteDatabase(meta txn.TxnMeta, req txnengine.De if req.Name == catalog.SystemDBName { defer logReq("catalog", req, meta, resp, &err)() - resp.ErrResp.Why = fmt.Sprintf("%s is system database", req.Name) - return nil + return moerr.NewBadDB(req.Name) } return c.upstream.HandleDeleteDatabase(meta, req, resp) } @@ -294,8 +287,7 @@ func (c *CatalogHandler) HandleDeleteRelation(meta txn.TxnMeta, req txnengine.De for _, name := range c.sysRelationIDs { if req.Name == name { defer logReq("catalog", req, meta, resp, &err)() - resp.ErrResp.Why = "can't delete this system table" - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.Name) } } } @@ -344,7 +336,7 @@ func (c *CatalogHandler) HandleNewTableIter(meta txn.TxnMeta, req txnengine.NewT attrsMap := make(map[string]*AttributeRow) if err := c.upstream.iterRelationAttributes( tx, req.TableID, - func(_ Text, row *AttributeRow) error { + func(_ ID, row *AttributeRow) error { attrsMap[row.Name] = row return nil }, @@ -356,30 +348,30 @@ func (c *CatalogHandler) HandleNewTableIter(meta txn.TxnMeta, req txnengine.NewT switch name { case catalog.SystemTable_DB_Name: tableIter := c.upstream.databases.NewIter(tx) - iter = &Iter[Text, DatabaseRow]{ + iter = &DatabaseRowIter{ TableIter: tableIter, AttrsMap: attrsMap, nextFunc: tableIter.First, } case catalog.SystemTable_Table_Name: tableIter := c.upstream.relations.NewIter(tx) - iter = &Iter[Text, RelationRow]{ + iter = &RelationRowIter{ TableIter: tableIter, AttrsMap: attrsMap, nextFunc: tableIter.First, } case catalog.SystemTable_Columns_Name: tableIter := c.upstream.attributes.NewIter(tx) - iter = &Iter[Text, AttributeRow]{ + iter = &AttributeRowIter{ TableIter: tableIter, AttrsMap: attrsMap, nextFunc: tableIter.First, } default: - panic(fmt.Errorf("fixme: %s", name)) + panic(fmt.Sprintf("fixme: %s", name)) } - id := uuid.NewString() + id := txnengine.NewID() resp.IterID = id c.iterators.Lock() c.iterators.Map[id] = iter @@ -443,7 +435,7 @@ func (c *CatalogHandler) HandleRead(meta txn.TxnMeta, req txnengine.ReadReq, res switch iter := v.(type) { - case *Iter[Text, DatabaseRow]: + case *DatabaseRowIter: for i, name := range req.ColNames { b.Vecs[i] = vector.New(iter.AttrsMap[name].Type) } @@ -465,7 +457,7 @@ func (c *CatalogHandler) HandleRead(meta txn.TxnMeta, req txnengine.ReadReq, res } } - case *Iter[Text, RelationRow]: + case *RelationRowIter: for i, name := range req.ColNames { b.Vecs[i] = vector.New(iter.AttrsMap[name].Type) } @@ -488,7 +480,7 @@ func (c *CatalogHandler) HandleRead(meta txn.TxnMeta, req txnengine.ReadReq, res } } - case *Iter[Text, AttributeRow]: + case *AttributeRowIter: for i, name := range req.ColNames { b.Vecs[i] = vector.New(iter.AttrsMap[name].Type) } @@ -515,7 +507,7 @@ func (c *CatalogHandler) HandleRead(meta txn.TxnMeta, req txnengine.ReadReq, res } default: - panic(fmt.Errorf("fixme: %T", v)) + panic(fmt.Sprintf("fixme: %T", v)) } if rows > 0 { @@ -541,25 +533,25 @@ func (c *CatalogHandler) HandleStartRecovery(ch chan txn.TxnMeta) { } func (c *CatalogHandler) HandleTruncate(meta txn.TxnMeta, req txnengine.TruncateReq, resp *txnengine.TruncateResp) (err error) { - if name, ok := c.sysRelationIDs[req.TableID]; ok { + if _, ok := c.sysRelationIDs[req.TableID]; ok { defer logReq("catalog", req, meta, resp, &err)() - resp.ErrResp.Why = fmt.Sprintf("%s is system table", name) + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } return c.upstream.HandleTruncate(meta, req, resp) } func (c *CatalogHandler) HandleUpdate(meta txn.TxnMeta, req txnengine.UpdateReq, resp *txnengine.UpdateResp) (err error) { - if name, ok := c.sysRelationIDs[req.TableID]; ok { + if _, ok := c.sysRelationIDs[req.TableID]; ok { defer logReq("catalog", req, meta, resp, &err)() - resp.ErrResp.Why = fmt.Sprintf("%s is system table", name) + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } return c.upstream.HandleUpdate(meta, req, resp) } func (c *CatalogHandler) HandleWrite(meta txn.TxnMeta, req txnengine.WriteReq, resp *txnengine.WriteResp) (err error) { - if name, ok := c.sysRelationIDs[req.TableID]; ok { + if _, ok := c.sysRelationIDs[req.TableID]; ok { defer logReq("catalog", req, meta, resp, &err)() - resp.ErrResp.Why = fmt.Sprintf("%s is system table", name) + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } err = c.upstream.HandleWrite(meta, req, resp) return diff --git a/pkg/txn/storage/txn/catalog_handler_test.go b/pkg/txn/storage/txn/catalog_handler_test.go index 1e94309ef7e81d6b052f0e01d25717fd5b3d26e3..c848019173b1f508b290b344e2ac7e7f8d1ccda4 100644 --- a/pkg/txn/storage/txn/catalog_handler_test.go +++ b/pkg/txn/storage/txn/catalog_handler_test.go @@ -24,6 +24,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/pb/txn" "github.com/matrixorigin/matrixone/pkg/testutil" "github.com/matrixorigin/matrixone/pkg/txn/clock" + "github.com/matrixorigin/matrixone/pkg/txn/storage/txn/memtable" "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/catalog" txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" "github.com/stretchr/testify/assert" @@ -41,7 +42,7 @@ func TestCatalogHandler(t *testing.T) { NewCatalogHandler( NewMemHandler( testutil.NewMheap(), - Serializable, + memtable.Serializable, clock, ), ), @@ -60,7 +61,7 @@ func TestCatalogHandler(t *testing.T) { } // system db - var dbID string + var dbID ID { var resp txnengine.OpenDatabaseResp err = handler.HandleOpenDatabase(meta, txnengine.OpenDatabaseReq{ @@ -82,7 +83,7 @@ func TestCatalogHandler(t *testing.T) { } // mo_database - var tableID string + var tableID ID { var resp txnengine.OpenRelationResp err = handler.HandleOpenRelation(meta, txnengine.OpenRelationReq{ @@ -93,7 +94,7 @@ func TestCatalogHandler(t *testing.T) { tableID = resp.ID assert.NotEmpty(t, tableID) } - var iterID string + var iterID ID { var resp txnengine.NewTableIterResp err = handler.HandleNewTableIter(meta, txnengine.NewTableIterReq{ diff --git a/pkg/txn/storage/txn/data.go b/pkg/txn/storage/txn/data.go index f59989b0316ef8f8191e85fbabc664b7adebbc29..0769e4788462a5fabc759c324f014fd7492e5117 100644 --- a/pkg/txn/storage/txn/data.go +++ b/pkg/txn/storage/txn/data.go @@ -20,30 +20,37 @@ import ( ) type DataKey struct { - tableID string + tableID ID primaryKey Tuple } func (d DataKey) Less(than DataKey) bool { - if d.tableID < than.tableID { + if d.tableID.Less(than.tableID) { return true } - if d.tableID > than.tableID { + if than.tableID.Less(d.tableID) { return false } return d.primaryKey.Less(than.primaryKey) } +// use AttributeRow.Order as index +type DataValue = []Nullable + type DataRow struct { - key DataKey - indexes []Tuple - attributes map[string]Nullable // attribute id -> nullable value + key DataKey + value DataValue + indexes []Tuple } func (a DataRow) Key() DataKey { return a.key } +func (a DataRow) Value() DataValue { + return a.value +} + func (a DataRow) Indexes() []Tuple { return a.indexes } @@ -52,33 +59,32 @@ func (a *DataRow) String() string { buf := new(strings.Builder) buf.WriteString("DataRow{") buf.WriteString(fmt.Sprintf("key: %+v", a.key)) - for key, value := range a.attributes { - buf.WriteString(fmt.Sprintf(", %s: %+v", key, value)) + for _, attr := range a.value { + buf.WriteString(fmt.Sprintf(", %+v", attr)) } buf.WriteString("}") return buf.String() } func NewDataRow( - tableID string, + tableID ID, indexes []Tuple, ) *DataRow { return &DataRow{ key: DataKey{ tableID: tableID, }, - attributes: make(map[string]Nullable), - indexes: indexes, + indexes: indexes, } } type NamedDataRow struct { - Row *DataRow + Value DataValue AttrsMap map[string]*AttributeRow } var _ NamedRow = new(NamedDataRow) func (n *NamedDataRow) AttrByName(tx *Transaction, name string) (Nullable, error) { - return n.Row.attributes[n.AttrsMap[name].ID], nil + return n.Value[n.AttrsMap[name].Order], nil } diff --git a/pkg/txn/storage/txn/frontend_test.go b/pkg/txn/storage/txn/frontend_test.go index c39fd1342e48a3fc264bebe0d09acccabdddf674..f8c1740562238f9def450a4cd572a68bd0fcfbf7 100644 --- a/pkg/txn/storage/txn/frontend_test.go +++ b/pkg/txn/storage/txn/frontend_test.go @@ -26,6 +26,7 @@ import ( logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice" "github.com/matrixorigin/matrixone/pkg/testutil" "github.com/matrixorigin/matrixone/pkg/txn/clock" + "github.com/matrixorigin/matrixone/pkg/txn/storage/txn/memtable" txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" "github.com/matrixorigin/matrixone/pkg/vm/mempool" "github.com/matrixorigin/matrixone/pkg/vm/mheap" @@ -35,7 +36,6 @@ import ( ) func TestFrontend(t *testing.T) { - t.Skip("Skip because of error handling refactor work.") ctx, cancel := context.WithTimeout( context.Background(), time.Minute, @@ -85,7 +85,7 @@ func TestFrontend(t *testing.T) { }, math.MaxInt) storage, err := NewMemoryStorage( heap, - SnapshotIsolation, + memtable.SnapshotIsolation, clock, ) assert.Nil(t, err) diff --git a/pkg/txn/storage/txn/handler.go b/pkg/txn/storage/txn/handler.go index 2dae212dd9272be927f7b683361383d6b47836fd..2dd3d62d99ca7c585d3f61153224c870c01424ff 100644 --- a/pkg/txn/storage/txn/handler.go +++ b/pkg/txn/storage/txn/handler.go @@ -15,6 +15,7 @@ package txnstorage import ( + apipb "github.com/matrixorigin/matrixone/pkg/pb/api" "github.com/matrixorigin/matrixone/pkg/pb/timestamp" "github.com/matrixorigin/matrixone/pkg/pb/txn" txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" @@ -176,7 +177,7 @@ type Handler interface { HandleGetLogTail( meta txn.TxnMeta, - req txnengine.GetLogTailReq, - resp *txnengine.GetLogTailResp, + req apipb.SyncLogTailReq, + resp *apipb.SyncLogTailResp, ) error } diff --git a/pkg/txn/storage/txn/log_tail.go b/pkg/txn/storage/txn/log_tail.go index d3f80c652ae48bb7384bbda6d5d4424e7a46e044..c5782c5b54aa661ec86a682ac80e1bc97c4c189e 100644 --- a/pkg/txn/storage/txn/log_tail.go +++ b/pkg/txn/storage/txn/log_tail.go @@ -19,31 +19,32 @@ import ( "errors" "math" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/vector" apipb "github.com/matrixorigin/matrixone/pkg/pb/api" "github.com/matrixorigin/matrixone/pkg/pb/timestamp" "github.com/matrixorigin/matrixone/pkg/pb/txn" - txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" + "github.com/matrixorigin/matrixone/pkg/txn/storage/txn/memtable" ) type LogTailEntry = apipb.Entry -func (m *MemHandler) HandleGetLogTail(meta txn.TxnMeta, req txnengine.GetLogTailReq, resp *txnengine.GetLogTailResp) (err error) { +func (m *MemHandler) HandleGetLogTail(meta txn.TxnMeta, req apipb.SyncLogTailReq, resp *apipb.SyncLogTailResp) (err error) { + tableID := ID(req.Table.TbId) // tx tx := m.getTx(meta) // table and db infos - tableRow, err := m.relations.Get(tx, Text(req.TableID)) + tableRow, err := m.relations.Get(tx, tableID) if err != nil { if errors.Is(err, sql.ErrNoRows) { - resp.ErrRelationNotFound.ID = req.TableID - return nil + return moerr.NewInternalError("invalid relation id %v", tableID) } return err } - dbRow, err := m.databases.Get(tx, Text(tableRow.DatabaseID)) + dbRow, err := m.databases.Get(tx, tableRow.DatabaseID) if err != nil { return err } @@ -52,14 +53,14 @@ func (m *MemHandler) HandleGetLogTail(meta txn.TxnMeta, req txnengine.GetLogTail from := timestamp.Timestamp{ PhysicalTime: math.MinInt64, } - if req.Request.CnHave != nil { - from = *req.Request.CnHave + if req.CnHave != nil { + from = *req.CnHave } to := timestamp.Timestamp{ PhysicalTime: math.MaxInt64, } - if req.Request.CnWant != nil { - to = *req.Request.CnWant + if req.CnWant != nil { + to = *req.CnWant } fromTime := Time{ Timestamp: from, @@ -69,21 +70,21 @@ func (m *MemHandler) HandleGetLogTail(meta txn.TxnMeta, req txnengine.GetLogTail } // attributes - rows, err := m.attributes.IndexRows(tx, Tuple{ + attrs, err := m.attributes.Index(tx, Tuple{ index_RelationID, - Text(req.TableID), + tableID, }) if err != nil { return err } attrsMap := make(map[string]*AttributeRow) - insertNames := make([]string, 0, len(rows)) - deleteNames := make([]string, 0, len(rows)) - for _, row := range rows { - attrsMap[row.Name] = row - insertNames = append(insertNames, row.Name) - if row.Primary || row.IsRowId { - deleteNames = append(deleteNames, row.Name) + insertNames := make([]string, 0, len(attrs)) + deleteNames := make([]string, 0, len(attrs)) + for _, attr := range attrs { + attrsMap[attr.Value.Name] = attr.Value + insertNames = append(insertNames, attr.Value.Name) + if attr.Value.Primary || attr.Value.IsRowId { + deleteNames = append(deleteNames, attr.Value.Name) } } @@ -99,33 +100,32 @@ func (m *MemHandler) HandleGetLogTail(meta txn.TxnMeta, req txnengine.GetLogTail // iter // we don't use m.data.NewIter because we want to see deleted rows - iter := m.data.rows.Iter() - defer iter.Release() - tableKey := &PhysicalRow[DataKey, DataRow]{ + iter := m.data.NewPhysicalIter() + defer iter.Close() + tableKey := &memtable.PhysicalRow[DataKey, DataValue]{ Key: DataKey{ - tableID: req.TableID, + tableID: tableID, primaryKey: Tuple{}, }, } for ok := iter.Seek(tableKey); ok; ok = iter.Next() { physicalRow := iter.Item() - if physicalRow.Key.tableID != req.TableID { + if physicalRow.Key.tableID != tableID { break } - values := physicalRow.Values - values.RLock() - for i := len(values.Values) - 1; i >= 0; i-- { - value := values.Values[i] + physicalRow.Versions.RLock() + for i := len(physicalRow.Versions.List) - 1; i >= 0; i-- { + value := physicalRow.Versions.List[i] if value.LockTx != nil && - value.LockTx.State.Load() == Committed && + value.LockTx.State.Load() == memtable.Committed && value.LockTime.After(fromTime) && value.LockTime.Before(toTime) { // committed delete namedRow := &NamedDataRow{ - Row: value.Value, + Value: *value.Value, AttrsMap: attrsMap, } if err := appendNamedRow(tx, m.mheap, deleteBatch, namedRow); err != nil { @@ -133,12 +133,12 @@ func (m *MemHandler) HandleGetLogTail(meta txn.TxnMeta, req txnengine.GetLogTail } break - } else if value.BornTx.State.Load() == Committed && + } else if value.BornTx.State.Load() == memtable.Committed && value.BornTime.After(fromTime) && value.BornTime.Before(toTime) { // committed insert namedRow := &NamedDataRow{ - Row: value.Value, + Value: *value.Value, AttrsMap: attrsMap, } if err := appendNamedRow(tx, m.mheap, insertBatch, namedRow); err != nil { @@ -149,7 +149,7 @@ func (m *MemHandler) HandleGetLogTail(meta txn.TxnMeta, req txnengine.GetLogTail } } - values.RUnlock() + physicalRow.Versions.RUnlock() } @@ -158,27 +158,27 @@ func (m *MemHandler) HandleGetLogTail(meta txn.TxnMeta, req txnengine.GetLogTail { EntryType: apipb.Entry_Insert, Bat: toPBBatch(insertBatch), - TableId: tableRow.NumberID, + TableId: uint64(tableRow.ID), TableName: tableRow.Name, - DatabaseId: dbRow.NumberID, + DatabaseId: uint64(dbRow.ID), DatabaseName: dbRow.Name, }, { EntryType: apipb.Entry_Delete, Bat: toPBBatch(deleteBatch), - TableId: tableRow.NumberID, + TableId: uint64(tableRow.ID), TableName: tableRow.Name, - DatabaseId: dbRow.NumberID, + DatabaseId: uint64(dbRow.ID), DatabaseName: dbRow.Name, }, } - resp.Response.Commands = entries + resp.Commands = entries return nil } -func (c *CatalogHandler) HandleGetLogTail(meta txn.TxnMeta, req txnengine.GetLogTailReq, resp *txnengine.GetLogTailResp) (err error) { +func (c *CatalogHandler) HandleGetLogTail(meta txn.TxnMeta, req apipb.SyncLogTailReq, resp *apipb.SyncLogTailResp) (err error) { return c.upstream.HandleGetLogTail(meta, req, resp) } diff --git a/pkg/txn/storage/txn/log_tail_test.go b/pkg/txn/storage/txn/log_tail_test.go index de9c895664299109be5490a721ea830eeafee10e..a3d69b6d83834d1f45ef044398e7dfcdb3281603 100644 --- a/pkg/txn/storage/txn/log_tail_test.go +++ b/pkg/txn/storage/txn/log_tail_test.go @@ -18,6 +18,7 @@ import ( "context" "testing" + apipb "github.com/matrixorigin/matrixone/pkg/pb/api" "github.com/matrixorigin/matrixone/pkg/pb/timestamp" "github.com/matrixorigin/matrixone/pkg/pb/txn" txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" @@ -46,14 +47,16 @@ func testLogTail( // test get log tail { - resp := testRead[txnengine.GetLogTailResp]( + resp, err := testRead[apipb.SyncLogTailResp]( t, s, txnMeta, txnengine.OpGetLogTail, - txnengine.GetLogTailReq{ + apipb.SyncLogTailReq{ + Table: &apipb.TableID{}, //TODO args }, ) //TODO asserts + _ = err _ = resp } } diff --git a/pkg/txn/storage/txn/mem_handler.go b/pkg/txn/storage/txn/mem_handler.go index 90bd2e66595d7a1c8822b98c578844951eb0ffa6..ec8f5a5b38ee45e69051c61c7cc5c6713422f095 100644 --- a/pkg/txn/storage/txn/mem_handler.go +++ b/pkg/txn/storage/txn/mem_handler.go @@ -15,14 +15,15 @@ package txnstorage import ( + crand "crypto/rand" "database/sql" + "encoding/binary" "errors" "fmt" - "math/rand" "sort" "sync" - "github.com/google/uuid" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/nulls" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -31,6 +32,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/pb/timestamp" "github.com/matrixorigin/matrixone/pkg/pb/txn" "github.com/matrixorigin/matrixone/pkg/txn/clock" + "github.com/matrixorigin/matrixone/pkg/txn/storage/txn/memtable" "github.com/matrixorigin/matrixone/pkg/vm/engine" txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" "github.com/matrixorigin/matrixone/pkg/vm/mheap" @@ -39,13 +41,13 @@ import ( type MemHandler struct { // catalog - databases *Table[Text, DatabaseRow] - relations *Table[Text, RelationRow] - attributes *Table[Text, AttributeRow] - indexes *Table[Text, IndexRow] + databases *memtable.Table[ID, DatabaseRow, DatabaseRow] + relations *memtable.Table[ID, RelationRow, RelationRow] + attributes *memtable.Table[ID, AttributeRow, AttributeRow] + indexes *memtable.Table[ID, IndexRow, IndexRow] // data - data *Table[DataKey, DataRow] + data *memtable.Table[DataKey, DataValue, DataRow] // transactions transactions struct { @@ -58,7 +60,7 @@ type MemHandler struct { iterators struct { sync.Mutex // iterator id -> iterator - Map map[string]*Iter[DataKey, DataRow] + Map map[ID]*Iter[DataKey, DataValue] } // misc @@ -68,14 +70,16 @@ type MemHandler struct { } type Iter[ - K Ordered[K], - R Row[K], + K memtable.Ordered[K], + V any, ] struct { - TableIter *TableIter[K, R] - TableID string + TableIter *memtable.TableIter[K, V] + TableID ID AttrsMap map[string]*AttributeRow Expr *plan.Expr nextFunc func() bool + ReadTime Time + Tx *Transaction } func NewMemHandler( @@ -84,17 +88,17 @@ func NewMemHandler( clock clock.Clock, ) *MemHandler { h := &MemHandler{ - databases: NewTable[Text, DatabaseRow](), - relations: NewTable[Text, RelationRow](), - attributes: NewTable[Text, AttributeRow](), - data: NewTable[DataKey, DataRow](), - indexes: NewTable[Text, IndexRow](), + databases: memtable.NewTable[ID, DatabaseRow, DatabaseRow](), + relations: memtable.NewTable[ID, RelationRow, RelationRow](), + attributes: memtable.NewTable[ID, AttributeRow, AttributeRow](), + indexes: memtable.NewTable[ID, IndexRow, IndexRow](), + data: memtable.NewTable[DataKey, DataValue, DataRow](), mheap: mheap, defaultIsolationPolicy: defaultIsolationPolicy, clock: clock, } h.transactions.Map = make(map[string]*Transaction) - h.iterators.Map = make(map[string]*Iter[DataKey, DataRow]) + h.iterators.Map = make(map[ID]*Iter[DataKey, DataValue]) return h } @@ -106,7 +110,7 @@ func (m *MemHandler) HandleAddTableDef(meta txn.TxnMeta, req txnengine.AddTableD maxAttributeOrder := 0 if err := m.iterRelationAttributes( tx, req.TableID, - func(_ Text, row *AttributeRow) error { + func(_ ID, row *AttributeRow) error { if row.Order > maxAttributeOrder { maxAttributeOrder = row.Order } @@ -120,10 +124,9 @@ func (m *MemHandler) HandleAddTableDef(meta txn.TxnMeta, req txnengine.AddTableD case *engine.CommentDef: // update comments - row, err := m.relations.Get(tx, Text(req.TableID)) + row, err := m.relations.Get(tx, req.TableID) if errors.Is(err, sql.ErrNoRows) { - resp.ErrResp.ID = req.TableID - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } if err != nil { return err @@ -135,10 +138,9 @@ func (m *MemHandler) HandleAddTableDef(meta txn.TxnMeta, req txnengine.AddTableD case *engine.PartitionDef: // update - row, err := m.relations.Get(tx, Text(req.TableID)) + row, err := m.relations.Get(tx, req.TableID) if errors.Is(err, sql.ErrNoRows) { - resp.ErrResp.ID = req.TableID - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } if err != nil { return err @@ -150,10 +152,9 @@ func (m *MemHandler) HandleAddTableDef(meta txn.TxnMeta, req txnengine.AddTableD case *engine.ViewDef: // update - row, err := m.relations.Get(tx, Text(req.TableID)) + row, err := m.relations.Get(tx, req.TableID) if errors.Is(err, sql.ErrNoRows) { - resp.ErrResp.ID = req.TableID - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } if err != nil { return err @@ -166,21 +167,20 @@ func (m *MemHandler) HandleAddTableDef(meta txn.TxnMeta, req txnengine.AddTableD case *engine.AttributeDef: // add attribute // check existence - keys, err := m.attributes.Index(tx, Tuple{ + entries, err := m.attributes.Index(tx, Tuple{ index_RelationID_Name, - Text(req.TableID), + req.TableID, Text(def.Attr.Name), }) if err != nil { return err } - if len(keys) > 0 { - resp.ErrResp.ErrExisted = true - return nil + if len(entries) > 0 { + return moerr.NewDuplicate() } // insert attrRow := AttributeRow{ - ID: uuid.NewString(), + ID: txnengine.NewID(), RelationID: req.TableID, Order: maxAttributeOrder + 1, Nullable: true, //TODO fix @@ -193,21 +193,20 @@ func (m *MemHandler) HandleAddTableDef(meta txn.TxnMeta, req txnengine.AddTableD case *engine.IndexTableDef: // add index // check existence - keys, err := m.indexes.Index(tx, Tuple{ + entries, err := m.indexes.Index(tx, Tuple{ index_RelationID_Name, - Text(req.TableID), + req.TableID, Text(def.Name), }) if err != nil { return err } - if len(keys) > 0 { - resp.ErrResp.ErrExisted = true - return nil + if len(entries) > 0 { + return moerr.NewDuplicate() } // insert idxRow := IndexRow{ - ID: uuid.NewString(), + ID: txnengine.NewID(), RelationID: req.TableID, IndexTableDef: *def, } @@ -217,10 +216,9 @@ func (m *MemHandler) HandleAddTableDef(meta txn.TxnMeta, req txnengine.AddTableD case *engine.PropertiesDef: // update properties - row, err := m.relations.Get(tx, Text(req.TableID)) + row, err := m.relations.Get(tx, req.TableID) if errors.Is(err, sql.ErrNoRows) { - resp.ErrResp.ID = req.TableID - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } for _, prop := range def.Properties { row.Properties[prop.Key] = prop.Value @@ -233,7 +231,7 @@ func (m *MemHandler) HandleAddTableDef(meta txn.TxnMeta, req txnengine.AddTableD // set primary index if err := m.iterRelationAttributes( tx, req.TableID, - func(_ Text, row *AttributeRow) error { + func(_ ID, row *AttributeRow) error { isPrimary := false for _, name := range def.Names { if name == row.Name { @@ -255,7 +253,7 @@ func (m *MemHandler) HandleAddTableDef(meta txn.TxnMeta, req txnengine.AddTableD } default: - return fmt.Errorf("unknown table def: %T", req.Def) + panic(fmt.Sprintf("unknown table def: %T", req.Def)) } @@ -267,8 +265,7 @@ func (m *MemHandler) HandleCloseTableIter(meta txn.TxnMeta, req txnengine.CloseT defer m.iterators.Unlock() iter, ok := m.iterators.Map[req.IterID] if !ok { - resp.ErrResp.ID = req.IterID - return nil + return moerr.NewInternalError("no such iter: %v", req.IterID) } delete(m.iterators.Map, req.IterID) if err := iter.TableIter.Close(); err != nil { @@ -280,7 +277,7 @@ func (m *MemHandler) HandleCloseTableIter(meta txn.TxnMeta, req txnengine.CloseT func (m *MemHandler) HandleCreateDatabase(meta txn.TxnMeta, req txnengine.CreateDatabaseReq, resp *txnengine.CreateDatabaseResp) error { tx := m.getTx(meta) - keys, err := m.databases.Index(tx, Tuple{ + entries, err := m.databases.Index(tx, Tuple{ index_AccountID_Name, Uint(req.AccessInfo.AccountID), Text(req.Name), @@ -288,15 +285,13 @@ func (m *MemHandler) HandleCreateDatabase(meta txn.TxnMeta, req txnengine.Create if err != nil { return err } - if len(keys) > 0 { - resp.ErrResp.ErrExisted = true - return nil + if len(entries) > 0 { + return moerr.NewDBAlreadyExists(req.Name) } - id := uuid.NewString() + id := txnengine.NewID() err = m.databases.Insert(tx, DatabaseRow{ ID: id, - NumberID: rand.Uint64(), AccountID: req.AccessInfo.AccountID, Name: req.Name, }) @@ -312,11 +307,10 @@ func (m *MemHandler) HandleCreateRelation(meta txn.TxnMeta, req txnengine.Create tx := m.getTx(meta) // validate database id - if req.DatabaseID != "" { - _, err := m.databases.Get(tx, Text(req.DatabaseID)) + if !req.DatabaseID.IsEmpty() { + _, err := m.databases.Get(tx, req.DatabaseID) if errors.Is(err, sql.ErrNoRows) { - resp.ErrResp.ID = req.DatabaseID - return nil + return moerr.NewNoDB() } if err != nil { return err @@ -324,23 +318,21 @@ func (m *MemHandler) HandleCreateRelation(meta txn.TxnMeta, req txnengine.Create } // check existence - keys, err := m.relations.Index(tx, Tuple{ + entries, err := m.relations.Index(tx, Tuple{ index_DatabaseID_Name, - Text(req.DatabaseID), + req.DatabaseID, Text(req.Name), }) if err != nil { return err } - if len(keys) > 0 { - resp.ErrResp.ErrExisted = true - return nil + if len(entries) > 0 { + return moerr.NewTableAlreadyExists(req.Name) } // row row := RelationRow{ - ID: uuid.NewString(), - NumberID: rand.Uint64(), + ID: txnengine.NewID(), DatabaseID: req.DatabaseID, Name: req.Name, Type: req.Type, @@ -378,7 +370,7 @@ func (m *MemHandler) HandleCreateRelation(meta txn.TxnMeta, req txnengine.Create primaryColumnNames = def.Names default: - panic(fmt.Errorf("unknown table def: %T", def)) + panic(fmt.Sprintf("unknown table def: %T", def)) } } @@ -406,7 +398,7 @@ func (m *MemHandler) HandleCreateRelation(meta txn.TxnMeta, req txnengine.Create attr.Primary = isPrimary } attrRow := AttributeRow{ - ID: uuid.NewString(), + ID: txnengine.NewID(), RelationID: row.ID, Order: i + 1, Nullable: true, //TODO fix @@ -420,7 +412,7 @@ func (m *MemHandler) HandleCreateRelation(meta txn.TxnMeta, req txnengine.Create // insert relation indexes for _, idx := range relIndexes { idxRow := IndexRow{ - ID: uuid.NewString(), + ID: txnengine.NewID(), RelationID: row.ID, IndexTableDef: idx, } @@ -446,10 +438,9 @@ func (m *MemHandler) HandleDelTableDef(meta txn.TxnMeta, req txnengine.DelTableD case *engine.CommentDef: // del comments - row, err := m.relations.Get(tx, Text(req.TableID)) + row, err := m.relations.Get(tx, req.TableID) if errors.Is(err, sql.ErrNoRows) { - resp.ErrResp.ID = req.TableID - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } if err != nil { return err @@ -461,42 +452,42 @@ func (m *MemHandler) HandleDelTableDef(meta txn.TxnMeta, req txnengine.DelTableD case *engine.AttributeDef: // delete attribute - keys, err := m.attributes.Index(tx, Tuple{ + entries, err := m.attributes.Index(tx, Tuple{ index_RelationID_Name, - Text(req.TableID), + req.TableID, Text(def.Attr.Name), }) if err != nil { return err } - for _, key := range keys { - if err := m.attributes.Delete(tx, key); err != nil { + for _, entry := range entries { + if err := m.attributes.Delete(tx, entry.Key); err != nil { return err } + //TODO update DataValue } case *engine.IndexTableDef: // delete index - keys, err := m.indexes.Index(tx, Tuple{ + entries, err := m.indexes.Index(tx, Tuple{ index_RelationID_Name, - Text(req.TableID), + req.TableID, Text(def.Name), }) if err != nil { return err } - for _, key := range keys { - if err := m.indexes.Delete(tx, key); err != nil { + for _, entry := range entries { + if err := m.indexes.Delete(tx, entry.Key); err != nil { return err } } case *engine.PropertiesDef: // delete properties - row, err := m.relations.Get(tx, Text(req.TableID)) + row, err := m.relations.Get(tx, req.TableID) if errors.Is(err, sql.ErrNoRows) { - resp.ErrResp.ID = req.TableID - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } for _, prop := range def.Properties { delete(row.Properties, prop.Key) @@ -509,7 +500,7 @@ func (m *MemHandler) HandleDelTableDef(meta txn.TxnMeta, req txnengine.DelTableD // delete primary index if err := m.iterRelationAttributes( tx, req.TableID, - func(key Text, row *AttributeRow) error { + func(key ID, row *AttributeRow) error { if !row.Primary { return nil } @@ -524,7 +515,7 @@ func (m *MemHandler) HandleDelTableDef(meta txn.TxnMeta, req txnengine.DelTableD } default: - return fmt.Errorf("unknown table def: %T", req.Def) + panic(fmt.Sprintf("unknown table def: %T", req.Def)) } @@ -540,19 +531,19 @@ func (m *MemHandler) HandleDelete(meta txn.TxnMeta, req txnengine.DeleteReq, res for i := 0; i < reqVecLen; i++ { value := vectorAt(req.Vector, i) rowID := value.Value.(types.Rowid) - keys, err := m.data.Index(tx, Tuple{ - index_RowID, typeConv(rowID), + entries, err := m.data.Index(tx, Tuple{ + index_RowID, memtable.ToOrdered(rowID), }) if err != nil { return err } - if len(keys) == 0 { + if len(entries) == 0 { continue } - if len(keys) != 1 { + if len(entries) != 1 { panic("impossible") } - if err := m.data.Delete(tx, keys[0]); err != nil { + if err := m.data.Delete(tx, entries[0].Key); err != nil { return err } } @@ -560,21 +551,21 @@ func (m *MemHandler) HandleDelete(meta txn.TxnMeta, req txnengine.DeleteReq, res } // by primary keys - rows, err := m.attributes.IndexRows(tx, Tuple{ + entries, err := m.attributes.Index(tx, Tuple{ index_RelationID_IsPrimary, - Text(req.TableID), + req.TableID, Bool(true), }) if err != nil { return err } - if len(rows) == 1 && rows[0].Name == req.ColumnName { + if len(entries) == 1 && entries[0].Value.Name == req.ColumnName { // by primary key for i := 0; i < reqVecLen; i++ { value := vectorAt(req.Vector, i) key := DataKey{ tableID: req.TableID, - primaryKey: Tuple{typeConv(value.Value)}, + primaryKey: Tuple{memtable.ToOrdered(value.Value)}, } if err := m.data.Delete(tx, key); err != nil { return err @@ -584,26 +575,25 @@ func (m *MemHandler) HandleDelete(meta txn.TxnMeta, req txnengine.DeleteReq, res } // by non-primary key, slow but works - rows, err = m.attributes.IndexRows(tx, Tuple{ + entries, err = m.attributes.Index(tx, Tuple{ index_RelationID_Name, - Text(req.TableID), + req.TableID, Text(req.ColumnName), }) if err != nil { return err } - if len(rows) != 1 { - resp.ErrResp.Name = req.ColumnName - return nil + if len(entries) != 1 { + return moerr.NewInternalError("wrong column name: %s", req.ColumnName) } - attrID := rows[0].ID + attrIndex := entries[0].Value.Order iter := m.data.NewIter(tx) defer iter.Close() tableKey := DataKey{ tableID: req.TableID, } for ok := iter.Seek(tableKey); ok; ok = iter.Next() { - key, row, err := iter.Read() + key, dataValue, err := iter.Read() if err != nil { return err } @@ -612,11 +602,11 @@ func (m *MemHandler) HandleDelete(meta txn.TxnMeta, req txnengine.DeleteReq, res } for i := 0; i < reqVecLen; i++ { value := vectorAt(req.Vector, i) - attrInRow, ok := (*row).attributes[attrID] - if !ok { + if attrIndex >= len(*dataValue) { // attr not in row continue } + attrInRow := (*dataValue)[attrIndex] if value.Equal(attrInRow) { if err := m.data.Delete(tx, key); err != nil { return err @@ -631,7 +621,7 @@ func (m *MemHandler) HandleDelete(meta txn.TxnMeta, req txnengine.DeleteReq, res func (m *MemHandler) HandleDeleteDatabase(meta txn.TxnMeta, req txnengine.DeleteDatabaseReq, resp *txnengine.DeleteDatabaseResp) error { tx := m.getTx(meta) - rows, err := m.databases.IndexRows(tx, Tuple{ + entries, err := m.databases.Index(tx, Tuple{ index_AccountID_Name, Uint(req.AccessInfo.AccountID), Text(req.Name), @@ -639,53 +629,53 @@ func (m *MemHandler) HandleDeleteDatabase(meta txn.TxnMeta, req txnengine.Delete if err != nil { return err } - if len(rows) == 0 { - resp.ErrResp.Name = req.Name - return nil + if len(entries) == 0 { + return moerr.NewNoDB() } - for _, row := range rows { - if err := m.databases.Delete(tx, row.Key()); err != nil { + for _, entry := range entries { + if err := m.databases.Delete(tx, entry.Key); err != nil { return err } - if err := m.deleteRelationsByDBID(tx, row.ID); err != nil { + if err := m.deleteRelationsByDBID(tx, entry.Value.ID); err != nil { return err } - resp.ID = row.ID + resp.ID = entry.Value.ID } return nil } -func (m *MemHandler) deleteRelationsByDBID(tx *Transaction, dbID string) error { - rows, err := m.relations.IndexRows(tx, Tuple{ +func (m *MemHandler) deleteRelationsByDBID(tx *Transaction, dbID ID) error { + entries, err := m.relations.Index(tx, Tuple{ index_DatabaseID, - Text(dbID), + dbID, }) if err != nil { return err } - for _, row := range rows { - if err := m.relations.Delete(tx, row.Key()); err != nil { + for _, entry := range entries { + if err := m.relations.Delete(tx, entry.Key); err != nil { return err } - if err := m.deleteAttributesByRelationID(tx, row.ID); err != nil { + if err := m.deleteAttributesByRelationID(tx, entry.Value.ID); err != nil { return err } + //TODO delete data } return nil } -func (m *MemHandler) deleteAttributesByRelationID(tx *Transaction, relationID string) error { - keys, err := m.attributes.Index(tx, Tuple{ +func (m *MemHandler) deleteAttributesByRelationID(tx *Transaction, relationID ID) error { + entries, err := m.attributes.Index(tx, Tuple{ index_RelationID, - Text(relationID), + relationID, }) if err != nil { return err } - for _, key := range keys { - if err := m.attributes.Delete(tx, key); err != nil { + for _, entry := range entries { + if err := m.attributes.Delete(tx, entry.Key); err != nil { return err } } @@ -694,37 +684,37 @@ func (m *MemHandler) deleteAttributesByRelationID(tx *Transaction, relationID st func (m *MemHandler) HandleDeleteRelation(meta txn.TxnMeta, req txnengine.DeleteRelationReq, resp *txnengine.DeleteRelationResp) error { tx := m.getTx(meta) - rows, err := m.relations.IndexRows(tx, Tuple{ + entries, err := m.relations.Index(tx, Tuple{ index_DatabaseID_Name, - Text(req.DatabaseID), + req.DatabaseID, Text(req.Name), }) if err != nil { return err } - if len(rows) == 0 { + if len(entries) == 0 { // the caller expects no error if table not exist //resp.ErrNotFound.Name = req.Name return nil } - if len(rows) != 1 { + if len(entries) != 1 { panic("impossible") } - row := rows[0] - if err := m.relations.Delete(tx, row.Key()); err != nil { + entry := entries[0] + if err := m.relations.Delete(tx, entry.Key); err != nil { return err } - if err := m.deleteAttributesByRelationID(tx, row.ID); err != nil { + if err := m.deleteAttributesByRelationID(tx, entry.Value.ID); err != nil { return err } - resp.ID = row.ID + resp.ID = entry.Value.ID return nil } func (m *MemHandler) HandleGetDatabases(meta txn.TxnMeta, req txnengine.GetDatabasesReq, resp *txnengine.GetDatabasesResp) error { tx := m.getTx(meta) - rows, err := m.databases.IndexRows(tx, Tuple{ + entries, err := m.databases.Index(tx, Tuple{ index_AccountID, Uint(req.AccessInfo.AccountID), }) @@ -732,8 +722,8 @@ func (m *MemHandler) HandleGetDatabases(meta txn.TxnMeta, req txnengine.GetDatab return err } - for _, row := range rows { - resp.Names = append(resp.Names, row.Name) + for _, entry := range entries { + resp.Names = append(resp.Names, entry.Value.Name) } return nil @@ -741,31 +731,31 @@ func (m *MemHandler) HandleGetDatabases(meta txn.TxnMeta, req txnengine.GetDatab func (m *MemHandler) HandleGetPrimaryKeys(meta txn.TxnMeta, req txnengine.GetPrimaryKeysReq, resp *txnengine.GetPrimaryKeysResp) error { tx := m.getTx(meta) - rows, err := m.attributes.IndexRows(tx, Tuple{ + entries, err := m.attributes.Index(tx, Tuple{ index_RelationID_IsPrimary, - Text(req.TableID), + req.TableID, Bool(true), }) if err != nil { return err } - for _, row := range rows { - resp.Attrs = append(resp.Attrs, &row.Attribute) + for _, entry := range entries { + resp.Attrs = append(resp.Attrs, &entry.Value.Attribute) } return nil } func (m *MemHandler) HandleGetRelations(meta txn.TxnMeta, req txnengine.GetRelationsReq, resp *txnengine.GetRelationsResp) error { tx := m.getTx(meta) - rows, err := m.relations.IndexRows(tx, Tuple{ + entries, err := m.relations.Index(tx, Tuple{ index_DatabaseID, - Text(req.DatabaseID), + req.DatabaseID, }) if err != nil { return err } - for _, row := range rows { - resp.Names = append(resp.Names, row.Name) + for _, entry := range entries { + resp.Names = append(resp.Names, entry.Value.Name) } return nil } @@ -773,7 +763,7 @@ func (m *MemHandler) HandleGetRelations(meta txn.TxnMeta, req txnengine.GetRelat func (m *MemHandler) HandleGetTableDefs(meta txn.TxnMeta, req txnengine.GetTableDefsReq, resp *txnengine.GetTableDefsResp) error { tx := m.getTx(meta) - relRow, err := m.relations.Get(tx, Text(req.TableID)) + relRow, err := m.relations.Get(tx, req.TableID) if errors.Is(err, sql.ErrNoRows) { // the caller expects no error if table not exist //resp.ErrTableNotFound.ID = req.TableID @@ -810,7 +800,7 @@ func (m *MemHandler) HandleGetTableDefs(meta txn.TxnMeta, req txnengine.GetTable var attrRows []*AttributeRow if err := m.iterRelationAttributes( tx, req.TableID, - func(key Text, row *AttributeRow) error { + func(key ID, row *AttributeRow) error { if row.IsHidden { return nil } @@ -842,14 +832,14 @@ func (m *MemHandler) HandleGetTableDefs(meta txn.TxnMeta, req txnengine.GetTable // indexes { - rows, err := m.indexes.IndexRows(tx, Tuple{ - index_RelationID, Text(req.TableID), + entries, err := m.indexes.Index(tx, Tuple{ + index_RelationID, req.TableID, }) if err != nil { return err } - for _, row := range rows { - resp.Defs = append(resp.Defs, &row.IndexTableDef) + for _, entry := range entries { + resp.Defs = append(resp.Defs, &entry.Value.IndexTableDef) } } @@ -870,16 +860,16 @@ func (m *MemHandler) HandleGetTableDefs(meta txn.TxnMeta, req txnengine.GetTable func (m *MemHandler) HandleGetHiddenKeys(meta txn.TxnMeta, req txnengine.GetHiddenKeysReq, resp *txnengine.GetHiddenKeysResp) error { tx := m.getTx(meta) - rows, err := m.attributes.IndexRows(tx, Tuple{ + entries, err := m.attributes.Index(tx, Tuple{ index_RelationID_IsHidden, - Text(req.TableID), + req.TableID, Bool(true), }) if err != nil { return err } - for _, row := range rows { - resp.Attrs = append(resp.Attrs, &row.Attribute) + for _, entry := range entries { + resp.Attrs = append(resp.Attrs, &entry.Value.Attribute) } return nil } @@ -891,7 +881,7 @@ func (m *MemHandler) HandleNewTableIter(meta txn.TxnMeta, req txnengine.NewTable attrsMap := make(map[string]*AttributeRow) if err := m.iterRelationAttributes( tx, req.TableID, - func(_ Text, row *AttributeRow) error { + func(_ ID, row *AttributeRow) error { attrsMap[row.Name] = row return nil }, @@ -899,7 +889,7 @@ func (m *MemHandler) HandleNewTableIter(meta txn.TxnMeta, req txnengine.NewTable return err } - iter := &Iter[DataKey, DataRow]{ + iter := &Iter[DataKey, DataValue]{ TableIter: tableIter, TableID: req.TableID, AttrsMap: attrsMap, @@ -910,11 +900,13 @@ func (m *MemHandler) HandleNewTableIter(meta txn.TxnMeta, req txnengine.NewTable } return tableIter.Seek(tableKey) }, + ReadTime: tx.Time, + Tx: tx, } m.iterators.Lock() defer m.iterators.Unlock() - id := uuid.NewString() + id := txnengine.NewID() resp.IterID = id m.iterators.Map[id] = iter @@ -924,7 +916,7 @@ func (m *MemHandler) HandleNewTableIter(meta txn.TxnMeta, req txnengine.NewTable func (m *MemHandler) HandleOpenDatabase(meta txn.TxnMeta, req txnengine.OpenDatabaseReq, resp *txnengine.OpenDatabaseResp) error { tx := m.getTx(meta) - rows, err := m.databases.IndexRows(tx, Tuple{ + entries, err := m.databases.Index(tx, Tuple{ index_AccountID_Name, Uint(req.AccessInfo.AccountID), Text(req.Name), @@ -933,31 +925,37 @@ func (m *MemHandler) HandleOpenDatabase(meta txn.TxnMeta, req txnengine.OpenData return err } - for _, row := range rows { - resp.ID = row.ID + for _, entry := range entries { + resp.ID = entry.Value.ID + resp.Name = entry.Value.Name return nil } - resp.ErrResp.Name = req.Name - return nil + return moerr.NewNoDB() } func (m *MemHandler) HandleOpenRelation(meta txn.TxnMeta, req txnengine.OpenRelationReq, resp *txnengine.OpenRelationResp) error { tx := m.getTx(meta) - rows, err := m.relations.IndexRows(tx, Tuple{ + entries, err := m.relations.Index(tx, Tuple{ index_DatabaseID_Name, - Text(req.DatabaseID), + req.DatabaseID, Text(req.Name), }) if err != nil { return err } - for _, row := range rows { - resp.ID = row.ID - resp.Type = row.Type - return nil + if len(entries) == 0 { + return moerr.NewNoSuchTable(req.DatabaseName, req.Name) + } + entry := entries[0] + resp.ID = entry.Value.ID + resp.Type = entry.Value.Type + resp.RelationName = entry.Value.Name + db, err := m.databases.Get(tx, entry.Value.DatabaseID) + if err != nil { + return err } - resp.ErrResp.Name = req.Name + resp.DatabaseName = db.Name return nil } @@ -968,8 +966,7 @@ func (m *MemHandler) HandleRead(meta txn.TxnMeta, req txnengine.ReadReq, resp *t iter, ok := m.iterators.Map[req.IterID] if !ok { m.iterators.Unlock() - resp.ErrResp.ID = req.IterID - return nil + return moerr.NewInternalError("no such iter: %v", req.IterID) } m.iterators.Unlock() @@ -987,18 +984,18 @@ func (m *MemHandler) HandleRead(meta txn.TxnMeta, req txnengine.ReadReq, resp *t maxRows := 4096 type Row struct { - Value *DataRow - PhysicalRow *PhysicalRow[DataKey, DataRow] + Value *DataValue + PhysicalRow *memtable.PhysicalRow[DataKey, DataValue] } var rows []Row for ok := fn(); ok; ok = iter.TableIter.Next() { item := iter.TableIter.Item() - row, err := item.Values.Read(iter.TableIter.readTime, iter.TableIter.tx) + value, err := item.Read(iter.ReadTime, iter.Tx) if err != nil { return err } - if row.key.tableID != iter.TableID { + if item.Key.tableID != iter.TableID { break } @@ -1008,7 +1005,7 @@ func (m *MemHandler) HandleRead(meta txn.TxnMeta, req txnengine.ReadReq, resp *t } rows = append(rows, Row{ - Value: row, + Value: value, PhysicalRow: item, }) if len(rows) >= maxRows { @@ -1026,7 +1023,7 @@ func (m *MemHandler) HandleRead(meta txn.TxnMeta, req txnengine.ReadReq, resp *t tx := m.getTx(meta) for _, row := range rows { namedRow := &NamedDataRow{ - Row: row.Value, + Value: *row.Value, AttrsMap: iter.AttrsMap, } if err := appendNamedRow(tx, m.mheap, b, namedRow); err != nil { @@ -1047,10 +1044,9 @@ func (m *MemHandler) HandleRead(meta txn.TxnMeta, req txnengine.ReadReq, resp *t func (m *MemHandler) HandleTruncate(meta txn.TxnMeta, req txnengine.TruncateReq, resp *txnengine.TruncateResp) error { tx := m.getTx(meta) - _, err := m.relations.Get(tx, Text(req.TableID)) + _, err := m.relations.Get(tx, req.TableID) if errors.Is(err, sql.ErrNoRows) { - resp.ErrResp.ID = req.TableID - return nil + return moerr.NewNoSuchTable(req.DatabaseName, req.TableName) } iter := m.data.NewIter(tx) defer iter.Close() @@ -1078,8 +1074,9 @@ func (m *MemHandler) HandleUpdate(meta txn.TxnMeta, req txnengine.UpdateReq, res if err := m.rangeBatchPhysicalRows( tx, req.TableID, + req.DatabaseName, + req.TableName, req.Batch, - &resp.ErrResp, func( row *DataRow, rowID types.Rowid, @@ -1102,8 +1099,9 @@ func (m *MemHandler) HandleWrite(meta txn.TxnMeta, req txnengine.WriteReq, resp if err := m.rangeBatchPhysicalRows( tx, req.TableID, + req.DatabaseName, + req.TableName, req.Batch, - &resp.ErrResp, func( row *DataRow, rowID types.Rowid, @@ -1122,9 +1120,10 @@ func (m *MemHandler) HandleWrite(meta txn.TxnMeta, req txnengine.WriteReq, resp func (m *MemHandler) rangeBatchPhysicalRows( tx *Transaction, - tableID string, + tableID ID, + dbName string, + tableName string, b *batch.Batch, - resp *txnengine.ErrorResp, fn func( *DataRow, types.Rowid, @@ -1135,7 +1134,7 @@ func (m *MemHandler) rangeBatchPhysicalRows( nameToAttrs := make(map[string]*AttributeRow) if err := m.iterRelationAttributes( tx, tableID, - func(_ Text, row *AttributeRow) error { + func(_ ID, row *AttributeRow) error { nameToAttrs[row.Name] = row return nil }, @@ -1144,8 +1143,7 @@ func (m *MemHandler) rangeBatchPhysicalRows( } if len(nameToAttrs) == 0 { - resp.ID = tableID - return nil + return moerr.NewNoSuchTable(dbName, tableName) } // iter @@ -1160,10 +1158,15 @@ func (m *MemHandler) rangeBatchPhysicalRows( physicalRow := NewDataRow( tableID, []Tuple{ - {index_RowID, typeConv(rowID)}, + {index_RowID, memtable.ToOrdered(rowID)}, }, ) - physicalRow.attributes[nameToAttrs[rowIDColumnName].ID] = Nullable{ + physicalRow.value = make(DataValue, 0, len(nameToAttrs)) + idx := nameToAttrs[rowIDColumnName].Order + for idx >= len(physicalRow.value) { + physicalRow.value = append(physicalRow.value, Nullable{}) + } + physicalRow.value[idx] = Nullable{ Value: rowID, } @@ -1172,19 +1175,29 @@ func (m *MemHandler) rangeBatchPhysicalRows( attr, ok := nameToAttrs[name] if !ok { - return fmt.Errorf("unknown attr: %s", name) + panic(fmt.Sprintf("unknown attr: %s", name)) } if attr.Primary { - physicalRow.key.primaryKey = append(physicalRow.key.primaryKey, typeConv(col.Value)) + physicalRow.key.primaryKey = append( + physicalRow.key.primaryKey, + memtable.ToOrdered(col.Value), + ) } - physicalRow.attributes[attr.ID] = col + idx := attr.Order + for idx >= len(physicalRow.value) { + physicalRow.value = append(physicalRow.value, Nullable{}) + } + physicalRow.value[idx] = col } // use row id as primary key if no primary key is provided if len(physicalRow.key.primaryKey) == 0 { - physicalRow.key.primaryKey = append(physicalRow.key.primaryKey, typeConv(rowID)) + physicalRow.key.primaryKey = append( + physicalRow.key.primaryKey, + memtable.ToOrdered(rowID), + ) } if err := fn(physicalRow, rowID); err != nil { @@ -1202,7 +1215,7 @@ func (m *MemHandler) getTx(meta txn.TxnMeta) *Transaction { defer m.transactions.Unlock() tx, ok := m.transactions.Map[id] if !ok { - tx = NewTransaction( + tx = memtable.NewTransaction( id, Time{ Timestamp: meta.SnapshotTS, @@ -1253,18 +1266,18 @@ func (m *MemHandler) HandleStartRecovery(ch chan txn.TxnMeta) { func (m *MemHandler) iterRelationAttributes( tx *Transaction, - relationID string, - fn func(key Text, row *AttributeRow) error, + relationID ID, + fn func(key ID, row *AttributeRow) error, ) error { - rows, err := m.attributes.IndexRows(tx, Tuple{ + entries, err := m.attributes.Index(tx, Tuple{ index_RelationID, - Text(relationID), + relationID, }) if err != nil { return err } - for _, row := range rows { - if err := fn(row.Key(), row); err != nil { + for _, entry := range entries { + if err := fn(entry.Key, entry.Value); err != nil { return err } } @@ -1295,3 +1308,12 @@ func (m *MemHandler) HandleTableStats(meta txn.TxnMeta, req txnengine.TableStats return nil } + +func newRowID() types.Rowid { + var rowid types.Rowid + err := binary.Read(crand.Reader, binary.LittleEndian, &rowid) + if err != nil { + panic(err) + } + return rowid +} diff --git a/pkg/txn/storage/txn/mem_handler_test.go b/pkg/txn/storage/txn/mem_handler_test.go index 7ebd346f8e66934db37eccd7f73338f85b0a8b26..3c48055eeb5c09f7f09998dcdd574d2c59c06cd9 100644 --- a/pkg/txn/storage/txn/mem_handler_test.go +++ b/pkg/txn/storage/txn/mem_handler_test.go @@ -21,6 +21,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/testutil" "github.com/matrixorigin/matrixone/pkg/txn/clock" + "github.com/matrixorigin/matrixone/pkg/txn/storage/txn/memtable" ) func TestMemHandler(t *testing.T) { @@ -28,7 +29,7 @@ func TestMemHandler(t *testing.T) { return New( NewMemHandler( testutil.NewMheap(), - Serializable, + memtable.Serializable, clock.NewHLCClock(func() int64 { return time.Now().UnixNano() }, math.MaxInt64), diff --git a/pkg/txn/storage/txn/atomic.go b/pkg/txn/storage/txn/memtable/atomic.go similarity index 97% rename from pkg/txn/storage/txn/atomic.go rename to pkg/txn/storage/txn/memtable/atomic.go index 92cac6fd416bd7b918badb4664ae743abe338bea..7d7a3ec035037149b19f5ff8e2d54976a2da75a0 100644 --- a/pkg/txn/storage/txn/atomic.go +++ b/pkg/txn/storage/txn/memtable/atomic.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package txnstorage +package memtable import "sync/atomic" diff --git a/pkg/txn/storage/txn/memtable/bench_test.go b/pkg/txn/storage/txn/memtable/bench_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a0a9524f6ee4a2c0671f397b24a7b4abfd2ca683 --- /dev/null +++ b/pkg/txn/storage/txn/memtable/bench_test.go @@ -0,0 +1,59 @@ +// Copyright 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package memtable + +import ( + "testing" + + "github.com/google/uuid" +) + +func BenchmarkTable(b *testing.B) { + tx := NewTransaction(uuid.NewString(), Time{}, SnapshotIsolation) + table := NewTable[Int, int, TestRow]() + b.ResetTimer() + for i := 0; i < b.N; i++ { + key := Int(i) + tx.Time.Timestamp.PhysicalTime++ + if err := table.Delete(tx, key); err != nil { + b.Fatal(err) + } + row := TestRow{ + key: key, + value: i, + } + tx.Time.Timestamp.PhysicalTime++ + if err := table.Insert(tx, row); err != nil { + b.Fatal(err) + } + tx.Time.Timestamp.PhysicalTime++ + p, err := table.Get(tx, key) + if err != nil { + b.Fatal(err) + } + if *p != i { + b.Fatal() + } + entries, err := table.Index(tx, Tuple{ + Text("foo"), Int(i), + }) + if err != nil { + b.Fatal(err) + } + if len(entries) != 1 { + b.Fatal() + } + } +} diff --git a/pkg/txn/storage/txn/isolation.go b/pkg/txn/storage/txn/memtable/isolation.go similarity index 97% rename from pkg/txn/storage/txn/isolation.go rename to pkg/txn/storage/txn/memtable/isolation.go index f789ab571f6871d6b583c72293766482b63e52f0..54d78e8ec9ac352e927eefb0ad0183d332123ad6 100644 --- a/pkg/txn/storage/txn/isolation.go +++ b/pkg/txn/storage/txn/memtable/isolation.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package txnstorage +package memtable type IsolationPolicy struct { Read ReadPolicy diff --git a/pkg/txn/storage/txn/nullable.go b/pkg/txn/storage/txn/memtable/nullable.go similarity index 91% rename from pkg/txn/storage/txn/nullable.go rename to pkg/txn/storage/txn/memtable/nullable.go index a786033d8c5015483436a43a8fe9715b2791bb50..bd19a78c5a144bd0fe440e062bcd771ac497f962 100644 --- a/pkg/txn/storage/txn/nullable.go +++ b/pkg/txn/storage/txn/memtable/nullable.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package txnstorage +package memtable import ( "bytes" @@ -34,7 +34,7 @@ func (n Nullable) Equal(n2 Nullable) bool { if ok { return bytes.Equal(bsA, bsB) } - panic(fmt.Errorf("type not the same: %T %T", n.Value, n2.Value)) + panic(fmt.Sprintf("type not the same: %T %T", n.Value, n2.Value)) } return n.Value == n2.Value } diff --git a/pkg/txn/storage/txn/memtable/physical_iter.go b/pkg/txn/storage/txn/memtable/physical_iter.go new file mode 100644 index 0000000000000000000000000000000000000000..cac18516f1d7eff079c063947b8ea297a424b24a --- /dev/null +++ b/pkg/txn/storage/txn/memtable/physical_iter.go @@ -0,0 +1,35 @@ +// Copyright 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package memtable + +import "github.com/tidwall/btree" + +type TablePhysicalIter[ + K Ordered[K], + V any, +] struct { + btree.GenericIter[*PhysicalRow[K, V]] +} + +func (t *Table[K, V, R]) NewPhysicalIter() *TablePhysicalIter[K, V] { + return &TablePhysicalIter[K, V]{ + GenericIter: t.rows.Iter(), + } +} + +func (t *TablePhysicalIter[K, V]) Close() error { + t.GenericIter.Release() + return nil +} diff --git a/pkg/txn/storage/txn/mvcc.go b/pkg/txn/storage/txn/memtable/physical_row.go similarity index 60% rename from pkg/txn/storage/txn/mvcc.go rename to pkg/txn/storage/txn/memtable/physical_row.go index 5af7448a744cb5ae0374aae9160a40d672be8163..4cbaeee4b082679c7741e05f84ff43a2ca9272c9 100644 --- a/pkg/txn/storage/txn/mvcc.go +++ b/pkg/txn/storage/txn/memtable/physical_row.go @@ -12,24 +12,34 @@ // See the License for the specific language governing permissions and // limitations under the License. -package txnstorage +package memtable import ( "database/sql" "fmt" "io" "sync" + "time" "github.com/matrixorigin/matrixone/pkg/common/moerr" + txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" ) -type MVCC[T any] struct { - //TODO use lock-free linked list - sync.RWMutex - Values []*MVCCValue[T] +type PhysicalRow[ + K Ordered[K], + V any, +] struct { + Key K + LastUpdate *Atomic[time.Time] + Versions struct { + sync.RWMutex + List []*Version[V] + //TODO version GC + } } -type MVCCValue[T any] struct { +type Version[T any] struct { + ID ID BornTx *Transaction BornTime Time LockTx *Transaction @@ -39,15 +49,23 @@ type MVCCValue[T any] struct { // Read reads the visible value from Values // readTime's logical time should be monotonically increasing in one transaction to reflect commands order -func (m *MVCC[T]) Read(now Time, tx *Transaction) (*T, error) { +func (p *PhysicalRow[K, V]) Read(now Time, tx *Transaction) (value *V, err error) { + version, err := p.readVersion(now, tx) + if version != nil { + value = version.Value + } + return +} + +func (p *PhysicalRow[K, V]) readVersion(now Time, tx *Transaction) (*Version[V], error) { if tx.State.Load() != Active { panic("should not call Read") } - m.RLock() - defer m.RUnlock() - for i := len(m.Values) - 1; i >= 0; i-- { - value := m.Values[i] + p.Versions.RLock() + defer p.Versions.RUnlock() + for i := len(p.Versions.List) - 1; i >= 0; i-- { + value := p.Versions.List[i] if value.Visible(now, tx.ID) { switch tx.IsolationPolicy.Read { case ReadCommitted: @@ -59,10 +77,10 @@ func (m *MVCC[T]) Read(now Time, tx *Transaction) (*T, error) { case ReadNoStale: // BornTx must be committed to be visible here if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) { - return value.Value, moerr.NewTxnReadConflict("%s %s", tx.ID, value.BornTx.ID) + return value, moerr.NewTxnReadConflict("%s %s", tx.ID, value.BornTx.ID) } } - return value.Value, nil + return value, nil } } @@ -70,15 +88,15 @@ func (m *MVCC[T]) Read(now Time, tx *Transaction) (*T, error) { } // ReadVisible reads a committed value despite the tx's isolation policy -func (m *MVCC[T]) ReadVisible(now Time, tx *Transaction) (*MVCCValue[T], error) { +func (p *PhysicalRow[K, V]) ReadVisible(now Time, tx *Transaction) (*Version[V], error) { if tx.State.Load() != Active { panic("should not call Read") } - m.RLock() - defer m.RUnlock() - for i := len(m.Values) - 1; i >= 0; i-- { - value := m.Values[i] + p.Versions.RLock() + defer p.Versions.RUnlock() + for i := len(p.Versions.List) - 1; i >= 0; i-- { + value := p.Versions.List[i] if value.Visible(now, tx.ID) { return value, nil } @@ -87,39 +105,39 @@ func (m *MVCC[T]) ReadVisible(now Time, tx *Transaction) (*MVCCValue[T], error) return nil, sql.ErrNoRows } -func (m *MVCCValue[T]) Visible(now Time, txID string) bool { +func (v *Version[T]) Visible(now Time, txID string) bool { // the following algorithm is from https://momjian.us/main/writings/pgsql/mvcc.pdf // "[Mike Olson] says 17 march 1993: the tests in this routine are correct; if you think they’re not, you’re wrongand you should think about it again. i know, it happened to me." // inserted by current tx - if m.BornTx.ID == txID { + if v.BornTx.ID == txID { // inserted before the read time - if m.BornTime.Before(now) { + if v.BornTime.Before(now) { // not been deleted - if m.LockTx == nil { + if v.LockTx == nil { return true } // deleted by current tx after the read time - if m.LockTx.ID == txID && m.LockTime.After(now) { + if v.LockTx.ID == txID && v.LockTime.After(now) { return true } } } // inserted by a committed tx - if m.BornTx.State.Load() == Committed { + if v.BornTx.State.Load() == Committed { // not been deleted - if m.LockTx == nil { + if v.LockTx == nil { // for isolation levels stricter than read-committed, instead of checking timestamps here, let the caller do it. return true } // being deleted by current tx after the read time - if m.LockTx.ID == txID && m.LockTime.After(now) { + if v.LockTx.ID == txID && v.LockTime.After(now) { return true } // deleted by another tx but not committed - if m.LockTx.ID != txID && m.LockTx.State.Load() != Committed { + if v.LockTx.ID != txID && v.LockTx.State.Load() != Committed { return true } } @@ -127,16 +145,16 @@ func (m *MVCCValue[T]) Visible(now Time, txID string) bool { return false } -func (m *MVCC[T]) Insert(now Time, tx *Transaction, value *T) error { +func (p *PhysicalRow[K, V]) Insert(now Time, tx *Transaction, value *V, callbacks ...any) error { if tx.State.Load() != Active { panic("should not call Insert") } - m.Lock() - defer m.Unlock() + p.Versions.Lock() + defer p.Versions.Unlock() - for i := len(m.Values) - 1; i >= 0; i-- { - value := m.Values[i] + for i := len(p.Versions.List) - 1; i >= 0; i-- { + value := p.Versions.List[i] if value.Visible(now, tx.ID) { if value.LockTx != nil && value.LockTx.State.Load() != Aborted { // locked by active or committed tx @@ -148,25 +166,36 @@ func (m *MVCC[T]) Insert(now Time, tx *Transaction, value *T) error { } } - m.Values = append(m.Values, &MVCCValue[T]{ + id := txnengine.NewID() + p.Versions.List = append(p.Versions.List, &Version[V]{ + ID: id, BornTx: tx, BornTime: now, Value: value, }) + for _, callback := range callbacks { + switch callback := callback.(type) { + case func(ID): // version id + callback(id) + default: + panic(fmt.Sprintf("unknown type: %T", callback)) + } + } + return nil } -func (m *MVCC[T]) Delete(now Time, tx *Transaction) error { +func (p *PhysicalRow[K, V]) Delete(now Time, tx *Transaction) error { if tx.State.Load() != Active { panic("should not call Delete") } - m.Lock() - defer m.Unlock() + p.Versions.Lock() + defer p.Versions.Unlock() - for i := len(m.Values) - 1; i >= 0; i-- { - value := m.Values[i] + for i := len(p.Versions.List) - 1; i >= 0; i-- { + value := p.Versions.List[i] if value.Visible(now, tx.ID) { if value.LockTx != nil && value.LockTx.State.Load() != Aborted { return moerr.NewTxnWriteConflict("%s %s", tx.ID, value.LockTx.ID) @@ -183,30 +212,45 @@ func (m *MVCC[T]) Delete(now Time, tx *Transaction) error { return sql.ErrNoRows } -func (m *MVCC[T]) Update(now Time, tx *Transaction, newValue *T) error { +func (p *PhysicalRow[K, V]) Update(now Time, tx *Transaction, newValue *V, callbacks ...any) error { if tx.State.Load() != Active { panic("should not call Update") } - m.Lock() - defer m.Unlock() + p.Versions.Lock() + defer p.Versions.Unlock() - for i := len(m.Values) - 1; i >= 0; i-- { - value := m.Values[i] + for i := len(p.Versions.List) - 1; i >= 0; i-- { + value := p.Versions.List[i] if value.Visible(now, tx.ID) { + if value.LockTx != nil && value.LockTx.State.Load() != Aborted { return moerr.NewTxnWriteConflict("%s %s", tx.ID, value.LockTx.ID) } + if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) { return moerr.NewTxnWriteConflict("%s %s", tx.ID, value.BornTx.ID) } + value.LockTx = tx value.LockTime = now - m.Values = append(m.Values, &MVCCValue[T]{ + id := txnengine.NewID() + p.Versions.List = append(p.Versions.List, &Version[V]{ + ID: id, BornTx: tx, BornTime: now, Value: newValue, }) + + for _, callback := range callbacks { + switch callback := callback.(type) { + case func(ID): // version id + callback(id) + default: + panic(fmt.Sprintf("unknown type: %T", callback)) + } + } + return nil } } @@ -214,8 +258,10 @@ func (m *MVCC[T]) Update(now Time, tx *Transaction, newValue *T) error { return sql.ErrNoRows } -func (m *MVCC[T]) dump(w io.Writer) { - for _, value := range m.Values { +func (p *PhysicalRow[K, V]) dump(w io.Writer) { + p.Versions.RLock() + defer p.Versions.RUnlock() + for _, value := range p.Versions.List { fmt.Fprintf(w, "born tx %s, born time %s, value %v", value.BornTx.ID, value.BornTime.String(), diff --git a/pkg/txn/storage/txn/mvcc_test.go b/pkg/txn/storage/txn/memtable/physical_row_test.go similarity index 68% rename from pkg/txn/storage/txn/mvcc_test.go rename to pkg/txn/storage/txn/memtable/physical_row_test.go index 075e8f5aca81f74a10e0bdc347133625e0eec0df..237be2648e35f8f24fce49526a995bfb283b8f14 100644 --- a/pkg/txn/storage/txn/mvcc_test.go +++ b/pkg/txn/storage/txn/memtable/physical_row_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package txnstorage +package memtable import ( "database/sql" @@ -25,13 +25,13 @@ import ( "github.com/stretchr/testify/assert" ) -func testMVCC( +func testPhysicalRow( t *testing.T, isolationPolicy IsolationPolicy, ) { // new - m := new(MVCC[int]) + m := new(PhysicalRow[Int, int]) m.dump(io.Discard) // time @@ -53,20 +53,20 @@ func testMVCC( n := 1 err := m.Insert(now, tx1, &n) assert.Nil(t, err) - assert.Equal(t, 1, len(m.Values)) - assert.Equal(t, tx1, m.Values[0].BornTx) - assert.Equal(t, now, m.Values[0].BornTime) - assert.Nil(t, m.Values[0].LockTx) - assert.True(t, m.Values[0].LockTime.IsZero()) + assert.Equal(t, 1, len(m.Versions.List)) + assert.Equal(t, tx1, m.Versions.List[0].BornTx) + assert.Equal(t, now, m.Versions.List[0].BornTime) + assert.Nil(t, m.Versions.List[0].LockTx) + assert.True(t, m.Versions.List[0].LockTime.IsZero()) n2 := 2 err = m.Insert(now, tx2, &n2) assert.Nil(t, err) - assert.Equal(t, 2, len(m.Values)) - assert.Equal(t, tx2, m.Values[1].BornTx) - assert.Equal(t, now, m.Values[1].BornTime) - assert.Nil(t, m.Values[1].LockTx) - assert.True(t, m.Values[1].LockTime.IsZero()) + assert.Equal(t, 2, len(m.Versions.List)) + assert.Equal(t, tx2, m.Versions.List[1].BornTx) + assert.Equal(t, now, m.Versions.List[1].BornTime) + assert.Nil(t, m.Versions.List[1].LockTx) + assert.True(t, m.Versions.List[1].LockTime.IsZero()) // not readable now res, err := m.Read(now, tx1) @@ -93,9 +93,9 @@ func testMVCC( // delete err = m.Delete(now, tx1) assert.Nil(t, err) - assert.Equal(t, 2, len(m.Values)) - assert.Equal(t, tx1, m.Values[0].LockTx) - assert.Equal(t, now, m.Values[0].LockTime) + assert.Equal(t, 2, len(m.Versions.List)) + assert.Equal(t, tx1, m.Versions.List[0].LockTx) + assert.Equal(t, now, m.Versions.List[0].LockTime) // not readable now by current tx res, err = m.Read(now, tx1) @@ -110,9 +110,9 @@ func testMVCC( err = m.Delete(now, tx2) assert.Nil(t, err) - assert.Equal(t, 2, len(m.Values)) - assert.Equal(t, tx2, m.Values[1].LockTx) - assert.Equal(t, now, m.Values[1].LockTime) + assert.Equal(t, 2, len(m.Versions.List)) + assert.Equal(t, tx2, m.Versions.List[1].LockTx) + assert.Equal(t, now, m.Versions.List[1].LockTime) res, err = m.Read(now, tx2) assert.Equal(t, sql.ErrNoRows, err) @@ -124,20 +124,20 @@ func testMVCC( n3 := 3 err = m.Insert(now, tx1, &n3) assert.Nil(t, err) - assert.Equal(t, 3, len(m.Values)) - assert.Equal(t, tx1, m.Values[2].BornTx) - assert.Equal(t, now, m.Values[2].BornTime) - assert.Nil(t, m.Values[2].LockTx) - assert.True(t, m.Values[2].LockTime.IsZero()) + assert.Equal(t, 3, len(m.Versions.List)) + assert.Equal(t, tx1, m.Versions.List[2].BornTx) + assert.Equal(t, now, m.Versions.List[2].BornTime) + assert.Nil(t, m.Versions.List[2].LockTx) + assert.True(t, m.Versions.List[2].LockTime.IsZero()) n4 := 4 err = m.Insert(now, tx2, &n4) assert.Nil(t, err) - assert.Equal(t, 4, len(m.Values)) - assert.Equal(t, tx2, m.Values[3].BornTx) - assert.Equal(t, now, m.Values[3].BornTime) - assert.Nil(t, m.Values[3].LockTx) - assert.True(t, m.Values[3].LockTime.IsZero()) + assert.Equal(t, 4, len(m.Versions.List)) + assert.Equal(t, tx2, m.Versions.List[3].BornTx) + assert.Equal(t, now, m.Versions.List[3].BornTime) + assert.Nil(t, m.Versions.List[3].LockTx) + assert.True(t, m.Versions.List[3].LockTime.IsZero()) tick() @@ -145,13 +145,13 @@ func testMVCC( n5 := 5 err = m.Update(now, tx1, &n5) assert.Nil(t, err) - assert.Equal(t, 5, len(m.Values)) - assert.Equal(t, tx1, m.Values[2].LockTx) - assert.Equal(t, now, m.Values[2].LockTime) - assert.Equal(t, tx1, m.Values[4].BornTx) - assert.Equal(t, now, m.Values[4].BornTime) - assert.Nil(t, m.Values[4].LockTx) - assert.True(t, m.Values[4].LockTime.IsZero()) + assert.Equal(t, 5, len(m.Versions.List)) + assert.Equal(t, tx1, m.Versions.List[2].LockTx) + assert.Equal(t, now, m.Versions.List[2].LockTime) + assert.Equal(t, tx1, m.Versions.List[4].BornTx) + assert.Equal(t, now, m.Versions.List[4].BornTime) + assert.Nil(t, m.Versions.List[4].LockTx) + assert.True(t, m.Versions.List[4].LockTime.IsZero()) // commit tx1 err = tx1.Commit() @@ -176,7 +176,7 @@ func testMVCC( assert.Equal(t, 5, *res) assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnReadConflict)) default: - panic(fmt.Errorf("not handle: %v", isolationPolicy.Read)) + panic(fmt.Sprintf("not handle: %v", isolationPolicy.Read)) } // write stale conflict @@ -217,18 +217,18 @@ func testMVCC( assert.True(t, moerr.IsMoErrCode(err, moerr.ErrTxnWriteConflict)) } -func TestMVCC(t *testing.T) { +func TestPhysicalRow(t *testing.T) { t.Run("read committed", func(t *testing.T) { - testMVCC(t, IsolationPolicy{ + testPhysicalRow(t, IsolationPolicy{ Read: ReadCommitted, }) }) t.Run("snapshot isolation", func(t *testing.T) { - testMVCC(t, SnapshotIsolation) + testPhysicalRow(t, SnapshotIsolation) }) t.Run("serializable", func(t *testing.T) { - testMVCC(t, Serializable) + testPhysicalRow(t, Serializable) }) } diff --git a/pkg/txn/storage/txn/memtable/table.go b/pkg/txn/storage/txn/memtable/table.go new file mode 100644 index 0000000000000000000000000000000000000000..2ab532be30fd5e0a3e4752255c74f2fe3c267281 --- /dev/null +++ b/pkg/txn/storage/txn/memtable/table.go @@ -0,0 +1,331 @@ +// Copyright 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package memtable + +import ( + "database/sql" + "errors" + "sync" + "time" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/tidwall/btree" +) + +type Table[ + K Ordered[K], + V any, + R Row[K, V], +] struct { + sync.Mutex + rows *btree.BTreeG[*PhysicalRow[K, V]] + rowsHint *btree.PathHint + index *btree.BTreeG[*IndexEntry[K, V]] + indexHint *btree.PathHint + writeSets map[*Transaction]map[*PhysicalRow[K, V]]struct{} +} + +type Row[K any, V any] interface { + Key() K + Value() V + Indexes() []Tuple +} + +type NamedRow interface { + AttrByName(tx *Transaction, name string) (Nullable, error) +} + +type Ordered[To any] interface { + Less(to To) bool +} + +type IndexEntry[ + K Ordered[K], + V any, +] struct { + Index Tuple + Key K + VersionID ID + Value *V +} + +func NewTable[ + K Ordered[K], + V any, + R Row[K, V], +]() *Table[K, V, R] { + return &Table[K, V, R]{ + rows: btree.NewBTreeG(func(a, b *PhysicalRow[K, V]) bool { + return a.Key.Less(b.Key) + }), + rowsHint: new(btree.PathHint), + index: btree.NewBTreeG(func(a, b *IndexEntry[K, V]) bool { + if a.Index.Less(b.Index) { + return true + } + if b.Index.Less(a.Index) { + return false + } + if a.Key.Less(b.Key) { + return true + } + if b.Key.Less(a.Key) { + return false + } + return a.VersionID.Less(b.VersionID) + }), + indexHint: new(btree.PathHint), + writeSets: make(map[*Transaction]map[*PhysicalRow[K, V]]struct{}), + } +} + +func (t *Table[K, V, R]) Insert( + tx *Transaction, + row R, +) error { + key := row.Key() + physicalRow := t.getOrSetRowByKey(key) + + existed, err := physicalRow.ReadVisible(tx.Time, tx) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + if err != nil { + return err + } + if existed != nil { + return moerr.NewPrimaryKeyDuplicated(key) + } + + value := row.Value() + if err := physicalRow.Insert( + tx.Time, tx, &value, + func(versionID ID) { + for _, index := range row.Indexes() { + t.index.SetHint(&IndexEntry[K, V]{ + Index: index, + Key: key, + VersionID: versionID, + Value: &value, + }, t.indexHint) + } + }, + ); err != nil { + return err + } + physicalRow.LastUpdate.Store(time.Now()) + + t.setCommitter(tx, physicalRow) + + tx.Time.Tick() + return nil +} + +func (t *Table[K, V, R]) Update( + tx *Transaction, + row R, +) error { + key := row.Key() + physicalRow := t.getOrSetRowByKey(key) + + value := row.Value() + if err := physicalRow.Update( + tx.Time, tx, &value, + func(versionID ID) { + for _, index := range row.Indexes() { + t.index.SetHint(&IndexEntry[K, V]{ + Index: index, + Key: key, + VersionID: versionID, + Value: &value, + }, t.indexHint) + } + }, + ); err != nil { + return err + } + physicalRow.LastUpdate.Store(time.Now()) + + t.setCommitter(tx, physicalRow) + + tx.Time.Tick() + return nil +} + +func (t *Table[K, V, R]) Delete( + tx *Transaction, + key K, +) error { + physicalRow := t.getRowByKey(key) + + if physicalRow == nil { + return nil + } + if err := physicalRow.Delete(tx.Time, tx); err != nil { + return err + } + physicalRow.LastUpdate.Store(time.Now()) + + t.setCommitter(tx, physicalRow) + + tx.Time.Tick() + return nil +} + +func (t *Table[K, V, R]) Get( + tx *Transaction, + key K, +) ( + value *V, + err error, +) { + physicalRow := t.getRowByKey(key) + if physicalRow == nil { + err = sql.ErrNoRows + return + } + value, err = physicalRow.Read(tx.Time, tx) + if err != nil { + return + } + return +} + +func (t *Table[K, V, R]) getRowByKey(key K) *PhysicalRow[K, V] { + pivot := &PhysicalRow[K, V]{ + Key: key, + } + row, _ := t.rows.GetHint(pivot, t.rowsHint) + return row +} + +func (t *Table[K, V, R]) getOrSetRowByKey(key K) *PhysicalRow[K, V] { + pivot := &PhysicalRow[K, V]{ + Key: key, + } + if row, _ := t.rows.GetHint(pivot, t.rowsHint); row != nil { + return row + } + return t.getOrSetRowByKeySlow(pivot) +} + +func (t *Table[K, V, R]) getOrSetRowByKeySlow(pivot *PhysicalRow[K, V]) *PhysicalRow[K, V] { + t.Lock() + defer t.Unlock() + if row, _ := t.rows.GetHint(pivot, t.rowsHint); row != nil { + return row + } + pivot.LastUpdate = NewAtomic(time.Now()) + t.rows.SetHint(pivot, t.rowsHint) + return pivot +} + +func (t *Table[K, V, R]) Index(tx *Transaction, index Tuple) (entries []*IndexEntry[K, V], err error) { + pivot := &IndexEntry[K, V]{ + Index: index, + } + iter := t.index.Copy().Iter() + defer iter.Release() + for ok := iter.Seek(pivot); ok; ok = iter.Next() { + item := iter.Item() + if index.Less(item.Index) { + break + } + if item.Index.Less(index) { + break + } + + physicalRow := t.getRowByKey(item.Key) + if physicalRow == nil { + continue + } + currentVersion, err := physicalRow.readVersion(tx.Time, tx) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + continue + } + return nil, err + } + if currentVersion.ID == item.VersionID { + entries = append(entries, item) + } + } + return +} + +func (t *Table[K, V, R]) setCommitter(tx *Transaction, row *PhysicalRow[K, V]) { + tx.committers[t] = struct{}{} + t.Lock() + defer t.Unlock() + set, ok := t.writeSets[tx] + if !ok { + set = make(map[*PhysicalRow[K, V]]struct{}) + t.writeSets[tx] = set + } + set[row] = struct{}{} +} + +func (t *Table[K, V, R]) CommitTx(tx *Transaction) error { + t.Lock() + set := t.writeSets[tx] + t.Unlock() + defer func() { + t.Lock() + delete(t.writeSets, tx) + t.Unlock() + }() + + for physicalRow := range set { + + // verify the latest committed operation is done by the tx + var err error + physicalRow.Versions.RLock() + for i := len(physicalRow.Versions.List) - 1; i >= 0; i-- { + version := physicalRow.Versions.List[i] + + // locked by another committed tx after tx begin + if version.LockTx != nil && + version.LockTx.State.Load() == Committed && + version.LockTx.ID != tx.ID && + version.LockTime.After(tx.BeginTime) { + err = moerr.NewPrimaryKeyDuplicated(physicalRow.Key) + break + } + + // born in another committed tx after tx begin + if version.BornTx.State.Load() == Committed && + version.BornTx.ID != tx.ID && + version.BornTime.After(tx.BeginTime) { + err = moerr.NewPrimaryKeyDuplicated(physicalRow.Key) + break + } + + } + physicalRow.Versions.RUnlock() + + if err != nil { + return err + } + + } + + return nil +} + +func (t *Table[K, V, R]) AbortTx(tx *Transaction) { + t.Lock() + delete(t.writeSets, tx) + t.Unlock() +} diff --git a/pkg/txn/storage/txn/table_iter.go b/pkg/txn/storage/txn/memtable/table_iter.go similarity index 68% rename from pkg/txn/storage/txn/table_iter.go rename to pkg/txn/storage/txn/memtable/table_iter.go index 50a2ba2b2017827239ca87472ddc110a4933e08d..68e5d2bfa3a379c88ba2bcae9dbb5162956d9ca0 100644 --- a/pkg/txn/storage/txn/table_iter.go +++ b/pkg/txn/storage/txn/memtable/table_iter.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package txnstorage +package memtable import ( "github.com/tidwall/btree" @@ -20,19 +20,19 @@ import ( type TableIter[ K Ordered[K], - R Row[K], + V any, ] struct { tx *Transaction - iter btree.GenericIter[*PhysicalRow[K, R]] + iter btree.GenericIter[*PhysicalRow[K, V]] readTime Time } -func (t *Table[K, R]) NewIter( +func (t *Table[K, V, R]) NewIter( tx *Transaction, ) ( - iter *TableIter[K, R], + iter *TableIter[K, V], ) { - iter = &TableIter[K, R]{ + iter = &TableIter[K, V]{ tx: tx, iter: t.rows.Copy().Iter(), readTime: tx.Time, @@ -40,27 +40,27 @@ func (t *Table[K, R]) NewIter( return } -func (t *TableIter[K, R]) Read() (key K, row *R, err error) { +func (t *TableIter[K, V]) Read() (key K, value *V, err error) { physicalRow := t.iter.Item() key = physicalRow.Key - row, err = physicalRow.Values.Read(t.readTime, t.tx) + value, err = physicalRow.Read(t.readTime, t.tx) if err != nil { return } return } -func (t *TableIter[K, R]) Item() (row *PhysicalRow[K, R]) { +func (t *TableIter[K, V]) Item() (row *PhysicalRow[K, V]) { return t.iter.Item() } -func (t *TableIter[K, R]) Next() bool { +func (t *TableIter[K, V]) Next() bool { for { if ok := t.iter.Next(); !ok { return false } // skip unreadable values - value, _ := t.iter.Item().Values.Read(t.readTime, t.tx) + value, _ := t.iter.Item().Read(t.readTime, t.tx) if value == nil { continue } @@ -68,13 +68,13 @@ func (t *TableIter[K, R]) Next() bool { } } -func (t *TableIter[K, R]) First() bool { +func (t *TableIter[K, V]) First() bool { if ok := t.iter.First(); !ok { return false } for { // skip unreadable values - value, _ := t.iter.Item().Values.Read(t.readTime, t.tx) + value, _ := t.iter.Item().Read(t.readTime, t.tx) if value == nil { if ok := t.iter.Next(); !ok { return false @@ -85,8 +85,8 @@ func (t *TableIter[K, R]) First() bool { } } -func (t *TableIter[K, R]) Seek(key K) bool { - pivot := &PhysicalRow[K, R]{ +func (t *TableIter[K, V]) Seek(key K) bool { + pivot := &PhysicalRow[K, V]{ Key: key, } if ok := t.iter.Seek(pivot); !ok { @@ -94,7 +94,7 @@ func (t *TableIter[K, R]) Seek(key K) bool { } for { // skip unreadable values - value, _ := t.iter.Item().Values.Read(t.readTime, t.tx) + value, _ := t.iter.Item().Read(t.readTime, t.tx) if value == nil { if ok := t.iter.Next(); !ok { return false @@ -105,7 +105,7 @@ func (t *TableIter[K, R]) Seek(key K) bool { } } -func (t *TableIter[K, R]) Close() error { +func (t *TableIter[K, V]) Close() error { t.iter.Release() return nil } diff --git a/pkg/txn/storage/txn/table_test.go b/pkg/txn/storage/txn/memtable/table_test.go similarity index 85% rename from pkg/txn/storage/txn/table_test.go rename to pkg/txn/storage/txn/memtable/table_test.go index c6445dbec07640dbbd21ae1a21e4bcc54610a922..bda5cbcabfa963da059100c87fec10c2a0f4099f 100644 --- a/pkg/txn/storage/txn/table_test.go +++ b/pkg/txn/storage/txn/memtable/table_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package txnstorage +package memtable import ( "testing" @@ -31,6 +31,10 @@ func (t TestRow) Key() Int { return t.key } +func (t TestRow) Value() int { + return t.value +} + func (t TestRow) Indexes() []Tuple { return []Tuple{ {Text("foo"), Int(t.value)}, @@ -39,7 +43,7 @@ func (t TestRow) Indexes() []Tuple { func TestTable(t *testing.T) { - table := NewTable[Int, TestRow]() + table := NewTable[Int, int, TestRow]() tx := NewTransaction("1", Time{}, Serializable) row := TestRow{key: 42, value: 1} @@ -50,7 +54,7 @@ func TestTable(t *testing.T) { // get r, err := table.Get(tx, Int(42)) assert.Nil(t, err) - assert.Equal(t, &row, r) + assert.Equal(t, 1, *r) // update row.value = 2 @@ -59,20 +63,21 @@ func TestTable(t *testing.T) { r, err = table.Get(tx, Int(42)) assert.Nil(t, err) - assert.Equal(t, &row, r) + assert.Equal(t, 2, *r) // index - keys, err := table.Index(tx, Tuple{ + entries, err := table.Index(tx, Tuple{ Text("foo"), Int(1), }) assert.Nil(t, err) - assert.Equal(t, 0, len(keys)) - keys, err = table.Index(tx, Tuple{ + assert.Equal(t, 0, len(entries)) + entries, err = table.Index(tx, Tuple{ Text("foo"), Int(2), }) assert.Nil(t, err) - assert.Equal(t, 1, len(keys)) - assert.Equal(t, Int(42), keys[0]) + assert.Equal(t, 1, len(entries)) + assert.Equal(t, Int(42), entries[0].Key) + assert.Equal(t, 2, *entries[0].Value) // delete err = table.Delete(tx, Int(42)) @@ -82,7 +87,7 @@ func TestTable(t *testing.T) { func TestTableIsolation(t *testing.T) { - table := NewTable[Int, TestRow]() + table := NewTable[Int, int, TestRow]() tx1 := NewTransaction("1", Time{ Timestamp: timestamp.Timestamp{ diff --git a/pkg/txn/storage/txn/time.go b/pkg/txn/storage/txn/memtable/time.go similarity index 98% rename from pkg/txn/storage/txn/time.go rename to pkg/txn/storage/txn/memtable/time.go index 1bdb32a158a0b5885e588fce90d525d0a198fd04..4331669c909a88b0986f48ceacfc76e7475e6c51 100644 --- a/pkg/txn/storage/txn/time.go +++ b/pkg/txn/storage/txn/memtable/time.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package txnstorage +package memtable import ( "fmt" diff --git a/pkg/txn/storage/txn/transaction.go b/pkg/txn/storage/txn/memtable/transaction.go similarity index 98% rename from pkg/txn/storage/txn/transaction.go rename to pkg/txn/storage/txn/memtable/transaction.go index 5d3f72ced798f56c81529b0baed753f9ccab47a8..8058c8ac9317936e605a2fef077d3946bb38b6f7 100644 --- a/pkg/txn/storage/txn/transaction.go +++ b/pkg/txn/storage/txn/memtable/transaction.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package txnstorage +package memtable type Transaction struct { ID string diff --git a/pkg/txn/storage/txn/tuple.go b/pkg/txn/storage/txn/memtable/tuple.go similarity index 89% rename from pkg/txn/storage/txn/tuple.go rename to pkg/txn/storage/txn/memtable/tuple.go index 7668350a8aa1eb39fca33adc3cff98812b7b6f68..1cd5540f223ed8085a0d6cd864da4f1a5787950c 100644 --- a/pkg/txn/storage/txn/tuple.go +++ b/pkg/txn/storage/txn/memtable/tuple.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package txnstorage +package memtable import "fmt" @@ -78,8 +78,16 @@ func (t Tuple) Less(than Tuple) bool { return false } + case ID: + key2 := than[i].(ID) + if key.Less(key2) { + return true + } else if key2.Less(key) { + return false + } + default: - panic(fmt.Errorf("unknown key type: %T", key)) + panic(fmt.Sprintf("unknown key type: %T", key)) } } // equal diff --git a/pkg/txn/storage/txn/memtable/types.go b/pkg/txn/storage/txn/memtable/types.go new file mode 100644 index 0000000000000000000000000000000000000000..62b15967cc72f4f952e091112171b7e33532338b --- /dev/null +++ b/pkg/txn/storage/txn/memtable/types.go @@ -0,0 +1,175 @@ +// Copyright 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package memtable + +import ( + "bytes" + "fmt" + + "github.com/matrixorigin/matrixone/pkg/container/types" + txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" +) + +type ID = txnengine.ID + +type Text string + +func (t Text) Less(than Text) bool { + return t < than +} + +type Bool bool + +func (b Bool) Less(than Bool) bool { + return bool(!b && than) +} + +type Int int64 + +func (i Int) Less(than Int) bool { + return i < than +} + +type Uint int64 + +func (i Uint) Less(than Uint) bool { + return i < than +} + +type Float float64 + +func (f Float) Less(than Float) bool { + return f < than +} + +type Bytes []byte + +func (b Bytes) Less(than Bytes) bool { + return bytes.Compare(b, than) < 0 +} + +func ToOrdered(v any) any { + if v == nil { + panic("should not be nil") + } + switch v := v.(type) { + case bool: + return Bool(v) + case int: + return Int(v) + case int8: + return Int(v) + case int16: + return Int(v) + case int32: + return Int(v) + case int64: + return Int(v) + case uint: + return Uint(v) + case uint8: + return Uint(v) + case uint16: + return Uint(v) + case uint32: + return Uint(v) + case uint64: + return Uint(v) + case float32: + return Float(v) + case float64: + return Float(v) + case []byte: + return Bytes(v) + case types.Date: + return Int(v) + case types.Datetime: + return Int(v) + case types.Timestamp: + return Int(v) + case types.Decimal64: + return Bytes(v[:]) + case types.Decimal128: + return Bytes(v[:]) + case types.TS: + return Bytes(v[:]) + case types.Rowid: + return Bytes(v[:]) + case types.Uuid: + return Bytes(v[:]) + case ID: + return v + default: + panic(fmt.Sprintf("unknown type: %T", v)) + } +} + +func TypeMatch(v any, typ types.T) bool { + if v == nil { + panic("should not be nil") + } + var ok bool + switch typ { + case types.T_bool: + _, ok = v.(bool) + case types.T_int8: + _, ok = v.(int8) + case types.T_int16: + _, ok = v.(int16) + case types.T_int32: + _, ok = v.(int32) + case types.T_int64: + _, ok = v.(int64) + case types.T_uint8: + _, ok = v.(uint8) + case types.T_uint16: + _, ok = v.(uint16) + case types.T_uint32: + _, ok = v.(uint32) + case types.T_uint64: + _, ok = v.(uint64) + case types.T_float32: + _, ok = v.(float32) + case types.T_float64: + _, ok = v.(float64) + case types.T_decimal64: + _, ok = v.(types.Decimal64) + case types.T_decimal128: + _, ok = v.(types.Decimal128) + case types.T_date: + _, ok = v.(types.Date) + case types.T_time: + _, ok = v.(types.TimeType) + case types.T_datetime: + _, ok = v.(types.Datetime) + case types.T_timestamp: + _, ok = v.(types.Timestamp) + case types.T_interval: + _, ok = v.(types.IntervalType) + case types.T_char: + _, ok = v.(string) + case types.T_varchar: + _, ok = v.(string) + case types.T_json: + _, ok = v.(string) + case types.T_blob: + _, ok = v.(string) + case types.T_uuid: + _, ok = v.(string) + default: + panic(fmt.Sprintf("fixme: %v", typ)) + } + return ok +} diff --git a/pkg/txn/storage/txn/storage_test.go b/pkg/txn/storage/txn/storage_test.go index 7c6dc0b012dcac89ed04a6b8425eba8812b00f45..e1774f035a367baeeadf03109f98c3bf031b3e48 100644 --- a/pkg/txn/storage/txn/storage_test.go +++ b/pkg/txn/storage/txn/storage_test.go @@ -20,6 +20,7 @@ import ( "encoding/gob" "testing" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" @@ -54,73 +55,75 @@ func testDatabase( // open database { - resp := testRead[txnengine.OpenDatabaseResp]( + _, err := testRead[txnengine.OpenDatabaseResp]( t, s, txnMeta, txnengine.OpOpenDatabase, txnengine.OpenDatabaseReq{ Name: "foo", }, ) - assert.Equal(t, "foo", resp.ErrResp.Name) + assert.True(t, moerr.IsMoErrCode(err, moerr.ErrNoDB)) } // create database { - resp := testWrite[txnengine.CreateDatabaseResp]( + resp, err := testWrite[txnengine.CreateDatabaseResp]( t, s, txnMeta, txnengine.OpCreateDatabase, txnengine.CreateDatabaseReq{ Name: "foo", }, ) - assert.Equal(t, false, resp.ErrResp.ErrExisted) + assert.Nil(t, err) assert.NotEmpty(t, resp.ID) } // get databases { - resp := testRead[txnengine.GetDatabasesResp]( + resp, err := testRead[txnengine.GetDatabasesResp]( t, s, txnMeta, txnengine.OpGetDatabases, txnengine.GetDatabasesReq{}, ) + assert.Nil(t, err) assert.Equal(t, 1, len(resp.Names)) assert.Equal(t, "foo", resp.Names[0]) } // open database - var dbID string + var dbID ID { - resp := testRead[txnengine.OpenDatabaseResp]( + resp, err := testRead[txnengine.OpenDatabaseResp]( t, s, txnMeta, txnengine.OpOpenDatabase, txnengine.OpenDatabaseReq{ Name: "foo", }, ) - assert.Equal(t, "", resp.ErrResp.Name) + assert.Nil(t, err) assert.NotNil(t, resp.ID) dbID = resp.ID // delete database defer func() { { - resp := testWrite[txnengine.DeleteDatabaseResp]( + resp, err := testWrite[txnengine.DeleteDatabaseResp]( t, s, txnMeta, txnengine.OpDeleteDatabase, txnengine.DeleteDatabaseReq{ Name: "foo", }, ) - assert.Equal(t, "", resp.ErrResp.Name) + assert.Nil(t, err) assert.NotEmpty(t, resp.ID) } { - resp := testRead[txnengine.GetDatabasesResp]( + resp, err := testRead[txnengine.GetDatabasesResp]( t, s, txnMeta, txnengine.OpGetDatabases, txnengine.GetDatabasesReq{}, ) + assert.Nil(t, err) assert.Equal(t, 0, len(resp.Names)) } }() @@ -128,7 +131,7 @@ func testDatabase( // open relation { - resp := testRead[txnengine.OpenRelationResp]( + _, err := testRead[txnengine.OpenRelationResp]( t, s, txnMeta, txnengine.OpOpenRelation, txnengine.OpenRelationReq{ @@ -136,12 +139,12 @@ func testDatabase( Name: "table", }, ) - assert.Equal(t, "table", resp.ErrResp.Name) + assert.True(t, moerr.IsMoErrCode(err, moerr.ErrNoSuchTable)) } // create relation { - resp := testWrite[txnengine.CreateRelationResp]( + resp, err := testWrite[txnengine.CreateRelationResp]( t, s, txnMeta, txnengine.OpCreateRelation, txnengine.CreateRelationReq{ @@ -166,27 +169,28 @@ func testDatabase( }, }, ) - assert.Equal(t, false, resp.ErrResp.ErrExisted) + assert.Nil(t, err) assert.NotEmpty(t, resp.ID) } // get relations { - resp := testRead[txnengine.GetRelationsResp]( + resp, err := testRead[txnengine.GetRelationsResp]( t, s, txnMeta, txnengine.OpGetRelations, txnengine.GetRelationsReq{ DatabaseID: dbID, }, ) + assert.Nil(t, err) assert.Equal(t, 1, len(resp.Names)) assert.Equal(t, "table", resp.Names[0]) } // open relation - var relID string + var relID ID { - resp := testRead[txnengine.OpenRelationResp]( + resp, err := testRead[txnengine.OpenRelationResp]( t, s, txnMeta, txnengine.OpOpenRelation, txnengine.OpenRelationReq{ @@ -194,7 +198,7 @@ func testDatabase( Name: "table", }, ) - assert.Equal(t, "", resp.ErrResp.Name) + assert.Nil(t, err) assert.NotNil(t, resp.ID) relID = resp.ID assert.Equal(t, txnengine.RelationTable, resp.Type) @@ -203,15 +207,14 @@ func testDatabase( // get relation defs { - resp := testRead[txnengine.GetTableDefsResp]( + resp, err := testRead[txnengine.GetTableDefsResp]( t, s, txnMeta, txnengine.OpGetTableDefs, txnengine.GetTableDefsReq{ TableID: relID, }, ) - assert.Empty(t, resp.ErrResp.ID) - assert.Empty(t, resp.ErrResp.Name) + assert.Nil(t, err) assert.Equal(t, 3, len(resp.Defs)) } @@ -239,7 +242,7 @@ func testDatabase( bat.Vecs[0] = colA bat.Vecs[1] = colB bat.InitZsOne(5) - resp := testWrite[txnengine.WriteResp]( + _, err := testWrite[txnengine.WriteResp]( t, s, txnMeta, txnengine.OpWrite, txnengine.WriteReq{ @@ -247,26 +250,25 @@ func testDatabase( Batch: bat, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) } // read - var iterID string + var iterID ID { - resp := testRead[txnengine.NewTableIterResp]( + resp, err := testRead[txnengine.NewTableIterResp]( t, s, txnMeta, txnengine.OpNewTableIter, txnengine.NewTableIterReq{ TableID: relID, }, ) + assert.Nil(t, err) assert.NotEmpty(t, resp.IterID) - assert.Empty(t, resp.ErrResp) iterID = resp.IterID } { - resp := testRead[txnengine.ReadResp]( + resp, err := testRead[txnengine.ReadResp]( t, s, txnMeta, txnengine.OpRead, txnengine.ReadReq{ @@ -274,8 +276,7 @@ func testDatabase( ColNames: []string{"a", "b"}, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) assert.NotNil(t, resp.Batch) assert.Equal(t, 5, resp.Batch.Length()) } @@ -291,7 +292,7 @@ func testDatabase( 1, }, ) - resp := testWrite[txnengine.DeleteResp]( + _, err := testWrite[txnengine.DeleteResp]( t, s, txnMeta, txnengine.OpDelete, txnengine.DeleteReq{ @@ -300,25 +301,24 @@ func testDatabase( Vector: colA, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) } // read after delete { - resp := testRead[txnengine.NewTableIterResp]( + resp, err := testRead[txnengine.NewTableIterResp]( t, s, txnMeta, txnengine.OpNewTableIter, txnengine.NewTableIterReq{ TableID: relID, }, ) + assert.Nil(t, err) assert.NotEmpty(t, resp.IterID) - assert.Empty(t, resp.ErrResp) iterID = resp.IterID } { - resp := testRead[txnengine.ReadResp]( + resp, err := testRead[txnengine.ReadResp]( t, s, txnMeta, txnengine.OpRead, txnengine.ReadReq{ @@ -326,8 +326,7 @@ func testDatabase( ColNames: []string{"a", "b"}, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) assert.NotNil(t, resp.Batch) assert.Equal(t, 4, resp.Batch.Length()) } @@ -343,7 +342,7 @@ func testDatabase( 8, }, ) - resp := testWrite[txnengine.DeleteResp]( + _, err := testWrite[txnengine.DeleteResp]( t, s, txnMeta, txnengine.OpDelete, txnengine.DeleteReq{ @@ -352,25 +351,24 @@ func testDatabase( Vector: colB, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) } // read after delete { - resp := testRead[txnengine.NewTableIterResp]( + resp, err := testRead[txnengine.NewTableIterResp]( t, s, txnMeta, txnengine.OpNewTableIter, txnengine.NewTableIterReq{ TableID: relID, }, ) + assert.Nil(t, err) assert.NotEmpty(t, resp.IterID) - assert.Empty(t, resp.ErrResp) iterID = resp.IterID } { - resp := testRead[txnengine.ReadResp]( + resp, err := testRead[txnengine.ReadResp]( t, s, txnMeta, txnengine.OpRead, txnengine.ReadReq{ @@ -378,8 +376,7 @@ func testDatabase( ColNames: []string{"a", "b"}, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) assert.NotNil(t, resp.Batch) assert.Equal(t, 3, resp.Batch.Length()) } @@ -408,7 +405,7 @@ func testDatabase( bat.Vecs[0] = colA bat.Vecs[1] = colB bat.InitZsOne(1) - resp := testWrite[txnengine.WriteResp]( + _, err := testWrite[txnengine.WriteResp]( t, s, txnMeta, txnengine.OpWrite, txnengine.WriteReq{ @@ -416,13 +413,12 @@ func testDatabase( Batch: bat, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) } // delete relation { - resp := testWrite[txnengine.DeleteRelationResp]( + resp, err := testWrite[txnengine.DeleteRelationResp]( t, s, txnMeta, txnengine.OpDeleteRelation, txnengine.DeleteRelationReq{ @@ -430,23 +426,24 @@ func testDatabase( Name: "table", }, ) - assert.Equal(t, "", resp.ErrResp.Name) + assert.Nil(t, err) assert.NotEmpty(t, resp.ID) } { - resp := testRead[txnengine.GetRelationsResp]( + resp, err := testRead[txnengine.GetRelationsResp]( t, s, txnMeta, txnengine.OpGetRelations, txnengine.GetRelationsReq{ DatabaseID: dbID, }, ) + assert.Nil(t, err) assert.Equal(t, 0, len(resp.Names)) } // new relation without primary key { - resp := testWrite[txnengine.CreateRelationResp]( + resp, err := testWrite[txnengine.CreateRelationResp]( t, s, txnMeta, txnengine.OpCreateRelation, txnengine.CreateRelationReq{ @@ -469,7 +466,7 @@ func testDatabase( }, }, ) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) assert.NotEmpty(t, resp.ID) relID = resp.ID } @@ -498,7 +495,7 @@ func testDatabase( bat.Vecs[0] = colA bat.Vecs[1] = colB bat.InitZsOne(5) - resp := testWrite[txnengine.WriteResp]( + _, err := testWrite[txnengine.WriteResp]( t, s, txnMeta, txnengine.OpWrite, txnengine.WriteReq{ @@ -506,8 +503,7 @@ func testDatabase( Batch: bat, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) } // delete by primary key @@ -521,7 +517,7 @@ func testDatabase( 1, }, ) - resp := testWrite[txnengine.DeleteResp]( + _, err := testWrite[txnengine.DeleteResp]( t, s, txnMeta, txnengine.OpDelete, txnengine.DeleteReq{ @@ -530,25 +526,24 @@ func testDatabase( Vector: colA, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) } // read after delete { - resp := testRead[txnengine.NewTableIterResp]( + resp, err := testRead[txnengine.NewTableIterResp]( t, s, txnMeta, txnengine.OpNewTableIter, txnengine.NewTableIterReq{ TableID: relID, }, ) + assert.Nil(t, err) assert.NotEmpty(t, resp.IterID) - assert.Empty(t, resp.ErrResp) iterID = resp.IterID } { - resp := testRead[txnengine.ReadResp]( + resp, err := testRead[txnengine.ReadResp]( t, s, txnMeta, txnengine.OpRead, txnengine.ReadReq{ @@ -556,8 +551,7 @@ func testDatabase( ColNames: []string{"a", "b"}, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) assert.NotNil(t, resp.Batch) assert.Equal(t, 4, resp.Batch.Length()) } @@ -573,7 +567,7 @@ func testDatabase( 8, }, ) - resp := testWrite[txnengine.DeleteResp]( + _, err := testWrite[txnengine.DeleteResp]( t, s, txnMeta, txnengine.OpDelete, txnengine.DeleteReq{ @@ -582,26 +576,25 @@ func testDatabase( Vector: colB, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) } // read after delete { - resp := testRead[txnengine.NewTableIterResp]( + resp, err := testRead[txnengine.NewTableIterResp]( t, s, txnMeta, txnengine.OpNewTableIter, txnengine.NewTableIterReq{ TableID: relID, }, ) + assert.Nil(t, err) assert.NotEmpty(t, resp.IterID) - assert.Empty(t, resp.ErrResp) iterID = resp.IterID } var rowIDs *vector.Vector { - resp := testRead[txnengine.ReadResp]( + resp, err := testRead[txnengine.ReadResp]( t, s, txnMeta, txnengine.OpRead, txnengine.ReadReq{ @@ -609,8 +602,7 @@ func testDatabase( ColNames: []string{"a", "b", rowIDColumnName}, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) assert.NotNil(t, resp.Batch) assert.Equal(t, 3, resp.Batch.Length()) rowIDs = resp.Batch.Vecs[2] @@ -618,7 +610,7 @@ func testDatabase( // delete by row id { - resp := testWrite[txnengine.DeleteResp]( + _, err := testWrite[txnengine.DeleteResp]( t, s, txnMeta, txnengine.OpDelete, txnengine.DeleteReq{ @@ -627,25 +619,24 @@ func testDatabase( Vector: rowIDs, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) } // read after delete { - resp := testRead[txnengine.NewTableIterResp]( + resp, err := testRead[txnengine.NewTableIterResp]( t, s, txnMeta, txnengine.OpNewTableIter, txnengine.NewTableIterReq{ TableID: relID, }, ) + assert.Nil(t, err) assert.NotEmpty(t, resp.IterID) - assert.Empty(t, resp.ErrResp) iterID = resp.IterID } { - resp := testRead[txnengine.ReadResp]( + resp, err := testRead[txnengine.ReadResp]( t, s, txnMeta, txnengine.OpRead, txnengine.ReadReq{ @@ -653,8 +644,7 @@ func testDatabase( ColNames: []string{"a", "b", rowIDColumnName}, }, ) - assert.Empty(t, resp.ErrResp) - assert.Empty(t, resp.ErrResp) + assert.Nil(t, err) assert.Nil(t, resp.Batch) } @@ -675,14 +665,17 @@ func testRead[ req Req, ) ( resp Resp, + err error, ) { buf := new(bytes.Buffer) - err := gob.NewEncoder(buf).Encode(req) + err = gob.NewEncoder(buf).Encode(req) assert.Nil(t, err) res, err := s.Read(context.TODO(), txnMeta, op, buf.Bytes()) - assert.Nil(t, err) + if err != nil { + return + } data, err := res.Read() assert.Nil(t, err) @@ -703,14 +696,17 @@ func testWrite[ req Req, ) ( resp Resp, + err error, ) { buf := new(bytes.Buffer) - err := gob.NewEncoder(buf).Encode(req) + err = gob.NewEncoder(buf).Encode(req) assert.Nil(t, err) data, err := s.Write(context.TODO(), txnMeta, op, buf.Bytes()) - assert.Nil(t, err) + if err != nil { + return + } err = gob.NewDecoder(bytes.NewReader(data)).Decode(&resp) assert.Nil(t, err) diff --git a/pkg/txn/storage/txn/table.go b/pkg/txn/storage/txn/table.go deleted file mode 100644 index d328ed980c6fd64e732b0a9d514b5d700bbe62eb..0000000000000000000000000000000000000000 --- a/pkg/txn/storage/txn/table.go +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2022 Matrix Origin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package txnstorage - -import ( - "database/sql" - "errors" - "sync" - "time" - - "github.com/matrixorigin/matrixone/pkg/common/moerr" - "github.com/tidwall/btree" -) - -type Table[ - K Ordered[K], - R Row[K], -] struct { - sync.Mutex - rows *btree.Generic[*PhysicalRow[K, R]] - index *btree.Generic[IndexEntry[K, R]] - writeSets map[*Transaction]map[*PhysicalRow[K, R]]struct{} -} - -type Row[K any] interface { - Key() K - Indexes() []Tuple -} - -type NamedRow interface { - AttrByName(tx *Transaction, name string) (Nullable, error) -} - -type PhysicalRow[ - K Ordered[K], - R Row[K], -] struct { - Key K - LastUpdate *Atomic[time.Time] - Values *MVCC[R] -} - -type Ordered[To any] interface { - Less(to To) bool -} - -type IndexEntry[K Ordered[K], R Row[K]] struct { - Index Tuple - Key K - Row *R -} - -func NewTable[ - K Ordered[K], - R Row[K], -]() *Table[K, R] { - return &Table[K, R]{ - rows: btree.NewGeneric(func(a, b *PhysicalRow[K, R]) bool { - return a.Key.Less(b.Key) - }), - index: btree.NewGeneric(func(a, b IndexEntry[K, R]) bool { - if a.Index.Less(b.Index) { - return true - } - if b.Index.Less(a.Index) { - return false - } - return a.Key.Less(b.Key) - }), - writeSets: make(map[*Transaction]map[*PhysicalRow[K, R]]struct{}), - } -} - -func (t *Table[K, R]) Insert( - tx *Transaction, - row R, -) error { - key := row.Key() - t.Lock() - physicalRow := t.getOrSetRowByKey(key) - t.Unlock() - - existed, err := physicalRow.Values.ReadVisible(tx.Time, tx) - if errors.Is(err, sql.ErrNoRows) { - err = nil - } - if err != nil { - return err - } - if existed != nil { - return moerr.NewPrimaryKeyDuplicated(key) - } - - if err := physicalRow.Values.Insert(tx.Time, tx, &row); err != nil { - return err - } - physicalRow.LastUpdate.Store(time.Now()) - for _, index := range row.Indexes() { - t.index.Set(IndexEntry[K, R]{ - Index: index, - Key: key, - Row: &row, - }) - } - - t.setCommitter(tx, physicalRow) - - tx.Time.Tick() - return nil -} - -func (t *Table[K, R]) Update( - tx *Transaction, - row R, -) error { - key := row.Key() - t.Lock() - physicalRow := t.getOrSetRowByKey(key) - t.Unlock() - - if err := physicalRow.Values.Update(tx.Time, tx, &row); err != nil { - return err - } - physicalRow.LastUpdate.Store(time.Now()) - for _, index := range row.Indexes() { - t.index.Set(IndexEntry[K, R]{ - Index: index, - Key: key, - Row: &row, - }) - } - - //TODO - //t.setCommitter(tx, physicalRow) - - tx.Time.Tick() - return nil -} - -func (t *Table[K, R]) Delete( - tx *Transaction, - key K, -) error { - t.Lock() - physicalRow := t.getRowByKey(key) - t.Unlock() - - if physicalRow == nil { - return nil - } - if err := physicalRow.Values.Delete(tx.Time, tx); err != nil { - return err - } - physicalRow.LastUpdate.Store(time.Now()) - - //TODO - //t.setCommitter(tx, physicalRow) - - tx.Time.Tick() - return nil -} - -func (t *Table[K, R]) Get( - tx *Transaction, - key K, -) ( - row *R, - err error, -) { - t.Lock() - physicalRow := t.getRowByKey(key) - t.Unlock() - if physicalRow == nil { - err = sql.ErrNoRows - return - } - mvccValues := physicalRow.Values - row, err = mvccValues.Read(tx.Time, tx) - if err != nil { - return - } - return -} - -func (t *Table[K, R]) getRowByKey(key K) *PhysicalRow[K, R] { - pivot := &PhysicalRow[K, R]{ - Key: key, - } - row, _ := t.rows.Get(pivot) - return row -} - -func (t *Table[K, R]) getOrSetRowByKey(key K) *PhysicalRow[K, R] { - pivot := &PhysicalRow[K, R]{ - Key: key, - } - row, ok := t.rows.Get(pivot) - if !ok { - row = pivot - row.Values = new(MVCC[R]) - row.LastUpdate = NewAtomic(time.Now()) - t.rows.Set(row) - } - return row -} - -func (t *Table[K, R]) Index(tx *Transaction, index Tuple) (keys []K, err error) { - pivot := IndexEntry[K, R]{ - Index: index, - } - iter := t.index.Copy().Iter() - defer iter.Release() - for ok := iter.Seek(pivot); ok; ok = iter.Next() { - item := iter.Item() - if index.Less(item.Index) { - break - } - if item.Index.Less(index) { - break - } - cur, err := t.Get(tx, item.Key) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - continue - } - return nil, err - } - if cur == item.Row { - keys = append(keys, item.Key) - } - } - return -} - -func (t *Table[K, R]) IndexRows(tx *Transaction, index Tuple) (rows []*R, err error) { - pivot := IndexEntry[K, R]{ - Index: index, - } - iter := t.index.Copy().Iter() - defer iter.Release() - for ok := iter.Seek(pivot); ok; ok = iter.Next() { - item := iter.Item() - if index.Less(item.Index) { - break - } - if item.Index.Less(index) { - break - } - cur, err := t.Get(tx, item.Key) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - continue - } - return nil, err - } - if cur == item.Row { - rows = append(rows, cur) - } - } - return -} - -func (t *Table[K, R]) setCommitter(tx *Transaction, row *PhysicalRow[K, R]) { - tx.committers[t] = struct{}{} - t.Lock() - set, ok := t.writeSets[tx] - if !ok { - set = make(map[*PhysicalRow[K, R]]struct{}) - t.writeSets[tx] = set - } - set[row] = struct{}{} - t.Unlock() -} - -func (t *Table[K, R]) CommitTx(tx *Transaction) error { - t.Lock() - set := t.writeSets[tx] - t.Unlock() - defer func() { - t.Lock() - delete(t.writeSets, tx) - t.Unlock() - }() - - for physicalRow := range set { - values := physicalRow.Values - - var err error - values.RLock() - for i := len(values.Values) - 1; i >= 0; i-- { - value := values.Values[i] - - if value.Visible(tx.Time, tx.ID) && - value.BornTx.ID != tx.ID && - value.BornTime.After(tx.BeginTime) { - err = moerr.NewPrimaryKeyDuplicated(physicalRow.Key) - break - } - - } - values.RUnlock() - - if err != nil { - return err - } - - } - - return nil -} - -func (t *Table[K, R]) AbortTx(tx *Transaction) { - t.Lock() - delete(t.writeSets, tx) - t.Unlock() -} diff --git a/pkg/txn/storage/txn/types.go b/pkg/txn/storage/txn/types.go index c8c48b2117e4ca46c69d04bbce00e7efc8853a17..ae783147176c177667e14c12f8809f9f35ce000e 100644 --- a/pkg/txn/storage/txn/types.go +++ b/pkg/txn/storage/txn/types.go @@ -15,161 +15,27 @@ package txnstorage import ( - "bytes" - "crypto/rand" - "encoding/binary" - "fmt" - - "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/txn/storage/txn/memtable" + txnengine "github.com/matrixorigin/matrixone/pkg/vm/engine/txn" ) -type Text string - -func (t Text) Less(than Text) bool { - return t < than -} - -type Bool bool - -func (b Bool) Less(than Bool) bool { - return bool(!b && than) -} - -type Int int64 - -func (i Int) Less(than Int) bool { - return i < than -} - -type Uint int64 - -func (i Uint) Less(than Uint) bool { - return i < than -} - -type Float float64 - -func (f Float) Less(than Float) bool { - return f < than -} - -type Bytes []byte - -func (b Bytes) Less(than Bytes) bool { - return bytes.Compare(b, than) < 0 -} - -func typeConv(v any) any { - if v == nil { - panic("should not be nil") - } - switch v := v.(type) { - case bool: - return Bool(v) - case int: - return Int(v) - case int8: - return Int(v) - case int16: - return Int(v) - case int32: - return Int(v) - case int64: - return Int(v) - case uint: - return Uint(v) - case uint8: - return Uint(v) - case uint16: - return Uint(v) - case uint32: - return Uint(v) - case uint64: - return Uint(v) - case float32: - return Float(v) - case float64: - return Float(v) - case []byte: - return Bytes(v) - case types.Date: - return Int(v) - case types.Datetime: - return Int(v) - case types.Timestamp: - return Int(v) - case types.Decimal64: - return Bytes(v[:]) - case types.Decimal128: - return Bytes(v[:]) - case types.TS: - return Bytes(v[:]) - case types.Rowid: - return Bytes(v[:]) - case types.Uuid: - return Bytes(v[:]) - default: - panic(fmt.Errorf("unknown type: %T", v)) - } -} +type ( + ID = txnengine.ID + IsolationPolicy = memtable.IsolationPolicy + Nullable = memtable.Nullable + Transaction = memtable.Transaction + Tuple = memtable.Tuple + Text = memtable.Text + Uint = memtable.Uint + Bool = memtable.Bool + Time = memtable.Time + NamedRow = memtable.NamedRow +) -func typeMatch(v any, typ types.T) bool { - if v == nil { - panic("should not be nil") - } - var ok bool - switch typ { - case types.T_bool: - _, ok = v.(bool) - case types.T_int8: - _, ok = v.(int8) - case types.T_int16: - _, ok = v.(int16) - case types.T_int32: - _, ok = v.(int32) - case types.T_int64: - _, ok = v.(int64) - case types.T_uint8: - _, ok = v.(uint8) - case types.T_uint16: - _, ok = v.(uint16) - case types.T_uint32: - _, ok = v.(uint32) - case types.T_uint64: - _, ok = v.(uint64) - case types.T_float32: - _, ok = v.(float32) - case types.T_float64: - _, ok = v.(float64) - case types.T_decimal64: - _, ok = v.(types.Decimal64) - case types.T_decimal128: - _, ok = v.(types.Decimal128) - case types.T_date: - _, ok = v.(types.Date) - case types.T_time: - _, ok = v.(types.TimeType) - case types.T_datetime: - _, ok = v.(types.Datetime) - case types.T_timestamp: - _, ok = v.(types.Timestamp) - case types.T_interval: - _, ok = v.(types.IntervalType) - case types.T_char: - _, ok = v.(string) - case types.T_varchar: - _, ok = v.(string) - case types.T_json: - _, ok = v.(string) - case types.T_blob: - _, ok = v.(string) - case types.T_uuid: - _, ok = v.(string) - default: - panic(fmt.Errorf("fixme: %v", typ)) - } - return ok -} +var ( + SnapshotIsolation = memtable.SnapshotIsolation + Serializable = memtable.Serializable +) func boolToInt8(b bool) int8 { if b { @@ -177,12 +43,3 @@ func boolToInt8(b bool) int8 { } return 0 } - -func newRowID() types.Rowid { - var rowid types.Rowid - err := binary.Read(rand.Reader, binary.LittleEndian, &rowid) - if err != nil { - panic(err) - } - return rowid -} diff --git a/pkg/vm/engine/txn/database.go b/pkg/vm/engine/txn/database.go index 7b6c125e763dd92f8334f5b043572c8f26ce72ad..e70c0844099d5db8348d79265f6d9a523196fbde 100644 --- a/pkg/vm/engine/txn/database.go +++ b/pkg/vm/engine/txn/database.go @@ -24,10 +24,10 @@ import ( ) type Database struct { + id ID + name string engine *Engine txnOperator client.TxnOperator - - id string } var _ engine.Database = new(Database) @@ -41,10 +41,11 @@ func (d *Database) Create(ctx context.Context, relName string, defs []engine.Tab d.engine.allNodesShards, OpCreateRelation, CreateRelationReq{ - DatabaseID: d.id, - Type: RelationTable, - Name: strings.ToLower(relName), - Defs: defs, + DatabaseID: d.id, + DatabaseName: d.name, + Type: RelationTable, + Name: strings.ToLower(relName), + Defs: defs, }, ) if err != nil { @@ -63,8 +64,9 @@ func (d *Database) Delete(ctx context.Context, relName string) error { d.engine.allNodesShards, OpDeleteRelation, DeleteRelationReq{ - DatabaseID: d.id, - Name: strings.ToLower(relName), + DatabaseID: d.id, + DatabaseName: d.name, + Name: strings.ToLower(relName), }, ) if err != nil { @@ -87,8 +89,9 @@ func (d *Database) Relation(ctx context.Context, relName string) (engine.Relatio d.engine.firstNodeShard, OpOpenRelation, OpenRelationReq{ - DatabaseID: d.id, - Name: strings.ToLower(relName), + DatabaseID: d.id, + DatabaseName: d.name, + Name: strings.ToLower(relName), }, ) if err != nil { @@ -101,9 +104,11 @@ func (d *Database) Relation(ctx context.Context, relName string) (engine.Relatio case RelationTable, RelationView: table := &Table{ - engine: d.engine, - txnOperator: d.txnOperator, - id: resp.ID, + engine: d.engine, + txnOperator: d.txnOperator, + id: resp.ID, + databaseName: resp.DatabaseName, + tableName: resp.RelationName, } return table, nil diff --git a/pkg/vm/engine/txn/engine.go b/pkg/vm/engine/txn/engine.go index c3d956a8c496d2890e356304f9105e92c0ea1237..ddbb1653c7ff2ce1bd0b68fe5ada9c2b639a0f58 100644 --- a/pkg/vm/engine/txn/engine.go +++ b/pkg/vm/engine/txn/engine.go @@ -102,6 +102,7 @@ func (e *Engine) Database(ctx context.Context, dbName string, txnOperator client engine: e, txnOperator: txnOperator, id: resp.ID, + name: resp.Name, } return db, nil diff --git a/pkg/vm/engine/txn/id.go b/pkg/vm/engine/txn/id.go new file mode 100644 index 0000000000000000000000000000000000000000..c4a6698df18cbcee4d7f8b6c104df757e1fb7f0f --- /dev/null +++ b/pkg/vm/engine/txn/id.go @@ -0,0 +1,47 @@ +// Copyright 2022 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package txnengine + +import ( + crand "crypto/rand" + "encoding/binary" + "math/rand" +) + +type ID int64 + +func init() { + var seed int64 + binary.Read(crand.Reader, binary.LittleEndian, &seed) + rand.Seed(seed) +} + +func NewID() (id ID) { + //TODO will use an id generate service + id = ID(rand.Int63()) + return +} + +func (i ID) Less(than ID) bool { + return i < than +} + +func (i ID) IsEmpty() bool { + return i == emptyID +} + +var ( + emptyID ID +) diff --git a/pkg/vm/engine/txn/log_tail.go b/pkg/vm/engine/txn/log_tail.go index 94df60425411ccfd08c73eed1d6f6ae54e0ce1a7..2969eec677e0d5e636120a53f304c5e7e0989c32 100644 --- a/pkg/vm/engine/txn/log_tail.go +++ b/pkg/vm/engine/txn/log_tail.go @@ -32,7 +32,7 @@ func (t *Table) GetLogTail( err error, ) { - resps, err := DoTxnRequest[GetLogTailResp]( + resps, err := DoTxnRequest[apipb.SyncLogTailResp]( ctx, t.engine, t.txnOperator.Read, @@ -40,12 +40,11 @@ func (t *Table) GetLogTail( return t.engine.shardPolicy.Stores([]logservicepb.DNStore{targetStore}) }, OpGetLogTail, - GetLogTailReq{ - TableID: t.id, - Request: apipb.SyncLogTailReq{ - CnHave: from, - CnWant: to, - Table: &apipb.TableID{}, // we dont use this + apipb.SyncLogTailReq{ + CnHave: from, + CnWant: to, + Table: &apipb.TableID{ + TbId: uint64(t.id), }, }, ) @@ -53,5 +52,5 @@ func (t *Table) GetLogTail( return nil, err } - return &resps[0].Response, nil + return &resps[0], nil } diff --git a/pkg/vm/engine/txn/operations.go b/pkg/vm/engine/txn/operations.go index b3fd9404de13e6ab5e2a04e68d9067e0c43f08e5..0e83b8010f88a1285c720328daba5a0b72a888d9 100644 --- a/pkg/vm/engine/txn/operations.go +++ b/pkg/vm/engine/txn/operations.go @@ -17,7 +17,6 @@ package txnengine import ( "encoding/gob" - "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" @@ -109,11 +108,54 @@ func init() { } -type ErrorResp struct { - ErrExisted bool - ID string - Name string - Why string +type Request interface { + CreateDatabaseReq | + OpenDatabaseReq | + GetDatabasesReq | + DeleteDatabaseReq | + CreateRelationReq | + DeleteRelationReq | + OpenRelationReq | + GetRelationsReq | + AddTableDefReq | + DelTableDefReq | + DeleteReq | + GetPrimaryKeysReq | + GetTableDefsReq | + GetHiddenKeysReq | + TruncateReq | + UpdateReq | + WriteReq | + NewTableIterReq | + ReadReq | + CloseTableIterReq | + TableStatsReq | + apipb.SyncLogTailReq +} + +type Response interface { + CreateDatabaseResp | + OpenDatabaseResp | + GetDatabasesResp | + DeleteDatabaseResp | + CreateRelationResp | + DeleteRelationResp | + OpenRelationResp | + GetRelationsResp | + AddTableDefResp | + DelTableDefResp | + DeleteResp | + GetPrimaryKeysResp | + GetTableDefsResp | + GetHiddenKeysResp | + TruncateResp | + UpdateResp | + WriteResp | + NewTableIterResp | + ReadResp | + CloseTableIterResp | + TableStatsResp | + apipb.SyncLogTailResp } type CreateDatabaseReq struct { @@ -122,8 +164,7 @@ type CreateDatabaseReq struct { } type CreateDatabaseResp struct { - ID string - ErrResp ErrorResp + ID ID } type OpenDatabaseReq struct { @@ -132,8 +173,8 @@ type OpenDatabaseReq struct { } type OpenDatabaseResp struct { - ID string - ErrResp ErrorResp + ID ID + Name string } type GetDatabasesReq struct { @@ -150,45 +191,46 @@ type DeleteDatabaseReq struct { } type DeleteDatabaseResp struct { - ID string - ErrResp ErrorResp + ID ID } type CreateRelationReq struct { - DatabaseID string - Name string - Type RelationType - Defs []engine.TableDef + DatabaseID ID + DatabaseName string + Name string + Type RelationType + Defs []engine.TableDef } type CreateRelationResp struct { - ID string - ErrResp ErrorResp + ID ID } type DeleteRelationReq struct { - DatabaseID string - Name string + DatabaseID ID + DatabaseName string + Name string } type DeleteRelationResp struct { - ID string - ErrResp ErrorResp + ID ID } type OpenRelationReq struct { - DatabaseID string - Name string + DatabaseID ID + DatabaseName string + Name string } type OpenRelationResp struct { - ID string - Type RelationType - ErrResp ErrorResp + ID ID + Type RelationType + DatabaseName string + RelationName string } type GetRelationsReq struct { - DatabaseID string + DatabaseID ID } type GetRelationsResp struct { @@ -196,107 +238,108 @@ type GetRelationsResp struct { } type AddTableDefReq struct { - TableID string + TableID ID Def engine.TableDef + + DatabaseName string + TableName string } type AddTableDefResp struct { - ErrResp ErrorResp } type DelTableDefReq struct { - TableID string - Def engine.TableDef + TableID ID + DatabaseName string + TableName string + Def engine.TableDef } type DelTableDefResp struct { - ErrResp ErrorResp } type DeleteReq struct { - TableID string - ColumnName string - Vector *vector.Vector + TableID ID + DatabaseName string + TableName string + ColumnName string + Vector *vector.Vector } type DeleteResp struct { - ErrResp ErrorResp } type GetPrimaryKeysReq struct { - TableID string + TableID ID } type GetPrimaryKeysResp struct { - Attrs []*engine.Attribute - ErrResp ErrorResp + Attrs []*engine.Attribute } type GetTableDefsReq struct { - TableID string + TableID ID } type GetTableDefsResp struct { - Defs []engine.TableDef - ErrResp ErrorResp + Defs []engine.TableDef } type GetHiddenKeysReq struct { - TableID string + TableID ID } type GetHiddenKeysResp struct { - Attrs []*engine.Attribute - ErrResp ErrorResp + Attrs []*engine.Attribute } type TruncateReq struct { - TableID string + TableID ID + DatabaseName string + TableName string } type TruncateResp struct { AffectedRows int64 - ErrResp ErrorResp } type UpdateReq struct { - TableID string - Batch *batch.Batch + TableID ID + DatabaseName string + TableName string + Batch *batch.Batch } type UpdateResp struct { - ErrReadOnly moerr.Error - ErrResp ErrorResp } type WriteReq struct { - TableID string - Batch *batch.Batch + TableID ID + DatabaseName string + TableName string + Batch *batch.Batch } type WriteResp struct { - ErrResp ErrorResp } type NewTableIterReq struct { - TableID string + TableID ID Expr *plan.Expr Shards [][]byte } type NewTableIterResp struct { - IterID string - ErrResp ErrorResp + IterID ID } type ReadReq struct { - IterID string + IterID ID ColNames []string } type ReadResp struct { - Batch *batch.Batch - ErrResp ErrorResp + Batch *batch.Batch heap *mheap.Mheap } @@ -313,28 +356,16 @@ func (r *ReadResp) SetHeap(heap *mheap.Mheap) { } type CloseTableIterReq struct { - IterID string + IterID ID } type CloseTableIterResp struct { - ErrResp ErrorResp } type TableStatsReq struct { - TableID string + TableID ID } type TableStatsResp struct { - Rows int - ErrResp ErrorResp -} - -type GetLogTailReq struct { - TableID string - Request apipb.SyncLogTailReq -} - -type GetLogTailResp struct { - ErrRelationNotFound ErrorResp - Response apipb.SyncLogTailResp + Rows int } diff --git a/pkg/vm/engine/txn/request.go b/pkg/vm/engine/txn/request.go index 4a6e3a925cce1f785d941589a4888ffd3a7ccb1f..9aaab02784f414e086009a884a1a23fa3f9f920b 100644 --- a/pkg/vm/engine/txn/request.go +++ b/pkg/vm/engine/txn/request.go @@ -18,6 +18,7 @@ import ( "bytes" "context" "encoding/gob" + "fmt" "time" "github.com/matrixorigin/matrixone/pkg/common/moerr" @@ -27,8 +28,8 @@ import ( ) func DoTxnRequest[ - Resp any, - Req any, + Resp Response, + Req Request, ]( ctx context.Context, e engine.Engine, @@ -79,10 +80,10 @@ func DoTxnRequest[ if err != nil { return } - for _, resp := range result.Responses { if resp.TxnError != nil { - err = moerr.NewTxnError("resp txnError %s", resp.TxnError.Message) + //TODO no way to construct moerr.Error by code and message now + err = fmt.Errorf("code %v, message %v", resp.TxnError.Code, resp.TxnError.Message) return } } @@ -92,17 +93,6 @@ func DoTxnRequest[ if err = gob.NewDecoder(bytes.NewReader(res.CNOpResponse.Payload)).Decode(&resp); err != nil { return } - - // XXX This code is beyond me. Why do you need to use reflects and - // type implements for RPC code. - // respValue := reflect.ValueOf(resp) - // for i := 0; i < respValue.NumField(); i++ { - // field := respValue.Field(i) - // if field.Type().Implements(errorType) && !field.IsZero() { - // err = moerr.NewInternalError("txn request error %d req. This error handling code is messed up.") - // return - // } - // } resps = append(resps, resp) } diff --git a/pkg/vm/engine/txn/shard.go b/pkg/vm/engine/txn/shard.go index 9a833972a01c4a99bc3456ece0cf2ae877912e05..ed95d463484118aa733322c07eec9135d3e502ae 100644 --- a/pkg/vm/engine/txn/shard.go +++ b/pkg/vm/engine/txn/shard.go @@ -40,7 +40,7 @@ func NewDefaultShardPolicy(heap *mheap.Mheap) ShardPolicy { type ShardPolicy interface { Vector( ctx context.Context, - tableID string, + tableID ID, getDefs getDefsFunc, colName string, vec *vector.Vector, @@ -52,7 +52,7 @@ type ShardPolicy interface { Batch( ctx context.Context, - tableID string, + tableID ID, getDefs getDefsFunc, batch *batch.Batch, nodes []logservicepb.DNStore, @@ -132,7 +132,7 @@ var _ ShardPolicy = new(NoShard) func (s *NoShard) Vector( ctx context.Context, - tableID string, + tableID ID, getDefs getDefsFunc, colName string, vec *vector.Vector, @@ -151,7 +151,7 @@ func (s *NoShard) Vector( func (s *NoShard) Batch( ctx context.Context, - tableID string, + tableID ID, getDefs getDefsFunc, bat *batch.Batch, nodes []logservicepb.DNStore, diff --git a/pkg/vm/engine/txn/shard_fallback.go b/pkg/vm/engine/txn/shard_fallback.go index fd8f4f14a41fc714936d7d8600fbb5aa0c8d300f..9e59f7150b3ba19f06eb9ca3689e03bb677184cc 100644 --- a/pkg/vm/engine/txn/shard_fallback.go +++ b/pkg/vm/engine/txn/shard_fallback.go @@ -27,7 +27,7 @@ type FallbackShard []ShardPolicy var _ ShardPolicy = FallbackShard{} -func (f FallbackShard) Batch(ctx context.Context, tableID string, getDefs func(context.Context) ([]engine.TableDef, error), batch *batch.Batch, nodes []logservicepb.DNStore) (sharded []*ShardedBatch, err error) { +func (f FallbackShard) Batch(ctx context.Context, tableID ID, getDefs func(context.Context) ([]engine.TableDef, error), batch *batch.Batch, nodes []logservicepb.DNStore) (sharded []*ShardedBatch, err error) { for _, policy := range f { sharded, err := policy.Batch(ctx, tableID, getDefs, batch, nodes) if err != nil { @@ -41,7 +41,7 @@ func (f FallbackShard) Batch(ctx context.Context, tableID string, getDefs func(c panic("all shard policy failed") } -func (f FallbackShard) Vector(ctx context.Context, tableID string, getDefs func(context.Context) ([]engine.TableDef, error), colName string, vec *vector.Vector, nodes []logservicepb.DNStore) (sharded []*ShardedVector, err error) { +func (f FallbackShard) Vector(ctx context.Context, tableID ID, getDefs func(context.Context) ([]engine.TableDef, error), colName string, vec *vector.Vector, nodes []logservicepb.DNStore) (sharded []*ShardedVector, err error) { for _, policy := range f { sharded, err := policy.Vector(ctx, tableID, getDefs, colName, vec, nodes) if err != nil { diff --git a/pkg/vm/engine/txn/shard_hash.go b/pkg/vm/engine/txn/shard_hash.go index 522f09fab58d3cad1ce9dcb9a2334f9e07784254..4558cce9cbc12d008e09da15d80ea2112ad58529 100644 --- a/pkg/vm/engine/txn/shard_hash.go +++ b/pkg/vm/engine/txn/shard_hash.go @@ -43,7 +43,7 @@ func NewHashShard(heap *mheap.Mheap) *HashShard { func (*HashShard) Batch( ctx context.Context, - tableID string, + tableID ID, getDefs getDefsFunc, bat *batch.Batch, nodes []logservicepb.DNStore, @@ -158,7 +158,7 @@ func (*HashShard) Batch( func (h *HashShard) Vector( ctx context.Context, - tableID string, + tableID ID, getDefs getDefsFunc, colName string, vec *vector.Vector, @@ -583,7 +583,7 @@ func getNullableValueFromVector(vec *vector.Vector, i int) (value Nullable) { } - panic(fmt.Errorf("unknown column type: %v", vec.Typ)) + panic(fmt.Sprintf("unknown column type: %v", vec.Typ)) } func appendNullableValueToVector(vec *vector.Vector, value Nullable, heap *mheap.Mheap) { diff --git a/pkg/vm/engine/txn/table.go b/pkg/vm/engine/txn/table.go index b513f6623d725e3221ca77f70685a44c8d953837..3aa28aace73c6ffd99e778d1aa0f09a9884ea174 100644 --- a/pkg/vm/engine/txn/table.go +++ b/pkg/vm/engine/txn/table.go @@ -16,6 +16,7 @@ package txnengine import ( "context" + "fmt" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/vector" @@ -24,9 +25,11 @@ import ( ) type Table struct { - engine *Engine - txnOperator client.TxnOperator - id string + id ID + engine *Engine + txnOperator client.TxnOperator + databaseName string + tableName string } var _ engine.Relation = new(Table) @@ -65,8 +68,10 @@ func (t *Table) AddTableDef(ctx context.Context, def engine.TableDef) error { t.engine.allNodesShards, OpAddTableDef, AddTableDefReq{ - TableID: t.id, - Def: def, + TableID: t.id, + Def: def, + DatabaseName: t.databaseName, + TableName: t.tableName, }, ) if err != nil { @@ -85,8 +90,10 @@ func (t *Table) DelTableDef(ctx context.Context, def engine.TableDef) error { t.engine.allNodesShards, OpDelTableDef, DelTableDefReq{ - TableID: t.id, - Def: def, + TableID: t.id, + DatabaseName: t.databaseName, + TableName: t.tableName, + Def: def, }, ) if err != nil { @@ -122,9 +129,11 @@ func (t *Table) Delete(ctx context.Context, vec *vector.Vector, colName string) thisShard(shard.Shard), OpDelete, DeleteReq{ - TableID: t.id, - ColumnName: colName, - Vector: shard.Vector, + TableID: t.id, + DatabaseName: t.databaseName, + TableName: t.tableName, + ColumnName: colName, + Vector: shard.Vector, }, ) if err != nil { @@ -207,7 +216,9 @@ func (t *Table) Truncate(ctx context.Context) (uint64, error) { t.engine.allNodesShards, OpTruncate, TruncateReq{ - TableID: t.id, + TableID: t.id, + DatabaseName: t.databaseName, + TableName: t.tableName, }, ) if err != nil { @@ -250,8 +261,10 @@ func (t *Table) Update(ctx context.Context, data *batch.Batch) error { thisShard(shard.Shard), OpUpdate, UpdateReq{ - TableID: t.id, - Batch: shard.Batch, + TableID: t.id, + DatabaseName: t.databaseName, + TableName: t.tableName, + Batch: shard.Batch, }, ) if err != nil { @@ -290,8 +303,10 @@ func (t *Table) Write(ctx context.Context, data *batch.Batch) error { thisShard(shard.Shard), OpWrite, WriteReq{ - TableID: t.id, - Batch: shard.Batch, + TableID: t.id, + DatabaseName: t.databaseName, + TableName: t.tableName, + Batch: shard.Batch, }, ) if err != nil { @@ -323,5 +338,5 @@ func (t *Table) GetHideKeys(ctx context.Context) (attrs []*engine.Attribute, err } func (t *Table) GetTableID(ctx context.Context) string { - return t.id + return fmt.Sprintf("%x", t.id) } diff --git a/pkg/vm/engine/txn/table_reader.go b/pkg/vm/engine/txn/table_reader.go index 77bb3993e575a02b29f1026afee4397a695eedd6..9099be7fd558f15b5c6ab4fc9008b4454e568649 100644 --- a/pkg/vm/engine/txn/table_reader.go +++ b/pkg/vm/engine/txn/table_reader.go @@ -33,7 +33,7 @@ type TableReader struct { type IterInfo struct { Shard Shard - IterID string + IterID ID } func (t *Table) NewReader( @@ -88,10 +88,10 @@ func (t *Table) NewReader( return nil, err } - iterIDSets := make([][]string, parallel) + iterIDSets := make([][]ID, parallel) i := 0 for _, resp := range resps { - if resp.IterID != "" { + if resp.IterID != emptyID { iterIDSets[i] = append(iterIDSets[i], resp.IterID) i++ if i >= parallel {