From aa623a39850633239bbd61b77d639758739f517e Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Wed, 9 Dec 2020 23:49:21 +0000 Subject: [PATCH] Ensure indexes are updated properly when deleting records --- db/define_test.go | 120 ++++++++++++++++++++++++++++++++++++++++++++++ db/document.go | 14 +++--- 2 files changed, 128 insertions(+), 6 deletions(-) diff --git a/db/define_test.go b/db/define_test.go index 24f4b7f4..b0d9338b 100644 --- a/db/define_test.go +++ b/db/define_test.go @@ -1147,6 +1147,47 @@ func TestDefine(t *testing.T) { }) + Convey("Define a single-field unique index on a table, and ensure it prevents duplicate record values, after deleting records", t, func() { + + setupDB(workerCount) + + txt := ` + USE NS test DB test; + DEFINE INDEX test ON person COLUMNS email UNIQUE; + UPDATE person:one SET account="one", email="info@demo.com"; + UPDATE person:one SET account="demo", email="info@demo.com"; + UPDATE person:one SET account="demo", email="test@demo.com"; + UPDATE person:two SET account="demo", email="info@demo.com"; + DELETE person:one; + UPDATE person:tre SET account="demo", email="info@demo.com"; + UPDATE person:tre SET account="demo", email="test@demo.com"; + SELECT * FROM person ORDER BY meta.id; + ` + + res, err := Execute(permsKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 10) + So(res[1].Status, ShouldEqual, "OK") + So(res[2].Result, ShouldHaveLength, 1) + So(res[2].Status, ShouldEqual, "OK") + So(res[3].Result, ShouldHaveLength, 1) + So(res[3].Status, ShouldEqual, "OK") + So(res[4].Result, ShouldHaveLength, 1) + So(res[4].Status, ShouldEqual, "OK") + So(res[5].Result, ShouldHaveLength, 1) + So(res[5].Status, ShouldEqual, "OK") + So(res[6].Result, ShouldHaveLength, 0) + So(res[6].Status, ShouldEqual, "OK") + So(res[7].Result, ShouldHaveLength, 0) + So(res[7].Status, ShouldEqual, "ERR_IX") + So(res[8].Result, ShouldHaveLength, 1) + So(res[8].Status, ShouldEqual, "OK") + So(res[9].Result, ShouldHaveLength, 2) + So(data.Consume(res[9].Result[0]).Get("id").Data(), ShouldResemble, &sql.Thing{"person", "tre"}) + So(data.Consume(res[9].Result[1]).Get("id").Data(), ShouldResemble, &sql.Thing{"person", "two"}) + + }) + Convey("Define a multiple-field unique index on a table, and ensure it prevents duplicate record values", t, func() { setupDB(workerCount) @@ -1183,6 +1224,85 @@ func TestDefine(t *testing.T) { }) + Convey("Define a multiple-field unique index on a table, and ensure it prevents duplicate record values, after updating records", t, func() { + + setupDB(workerCount) + + txt := ` + USE NS test DB test; + DEFINE INDEX test ON person COLUMNS account, email UNIQUE; + UPDATE person:one SET account="one", email="info@demo.com"; + UPDATE person:one SET account="demo", email="info@demo.com"; + UPDATE person:one SET account="demo", email="test@demo.com"; + UPDATE person:two SET account="demo", email="info@demo.com"; + UPDATE person:tre SET account="demo", email="info@demo.com"; + UPDATE person:tre SET account="demo", email="test@demo.com"; + SELECT * FROM person ORDER BY meta.id; + ` + + res, err := Execute(permsKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 9) + So(res[1].Status, ShouldEqual, "OK") + So(res[2].Result, ShouldHaveLength, 1) + So(res[2].Status, ShouldEqual, "OK") + So(res[3].Result, ShouldHaveLength, 1) + So(res[3].Status, ShouldEqual, "OK") + So(res[4].Result, ShouldHaveLength, 1) + So(res[4].Status, ShouldEqual, "OK") + So(res[5].Result, ShouldHaveLength, 1) + So(res[5].Status, ShouldEqual, "OK") + So(res[6].Result, ShouldHaveLength, 0) + So(res[6].Status, ShouldEqual, "ERR_IX") + So(res[7].Result, ShouldHaveLength, 0) + So(res[7].Status, ShouldEqual, "ERR_IX") + So(res[8].Result, ShouldHaveLength, 2) + So(data.Consume(res[8].Result[0]).Get("id").Data(), ShouldResemble, &sql.Thing{"person", "one"}) + So(data.Consume(res[8].Result[1]).Get("id").Data(), ShouldResemble, &sql.Thing{"person", "two"}) + + }) + + Convey("Define a multiple-field unique index on a table, and ensure it prevents duplicate record values, after deleting records", t, func() { + + setupDB(workerCount) + + txt := ` + USE NS test DB test; + DEFINE INDEX test ON person COLUMNS account, email UNIQUE; + UPDATE person:one SET account="one", email="info@demo.com"; + UPDATE person:one SET account="demo", email="info@demo.com"; + UPDATE person:one SET account="demo", email="test@demo.com"; + UPDATE person:two SET account="demo", email="info@demo.com"; + DELETE person:one; + UPDATE person:tre SET account="demo", email="info@demo.com"; + UPDATE person:tre SET account="demo", email="test@demo.com"; + SELECT * FROM person ORDER BY meta.id; + ` + + res, err := Execute(permsKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 10) + So(res[1].Status, ShouldEqual, "OK") + So(res[2].Result, ShouldHaveLength, 1) + So(res[2].Status, ShouldEqual, "OK") + So(res[3].Result, ShouldHaveLength, 1) + So(res[3].Status, ShouldEqual, "OK") + So(res[4].Result, ShouldHaveLength, 1) + So(res[4].Status, ShouldEqual, "OK") + So(res[5].Result, ShouldHaveLength, 1) + So(res[5].Status, ShouldEqual, "OK") + So(res[6].Result, ShouldHaveLength, 0) + So(res[6].Status, ShouldEqual, "OK") + So(res[7].Result, ShouldHaveLength, 0) + So(res[7].Status, ShouldEqual, "ERR_IX") + So(res[8].Result, ShouldHaveLength, 1) + So(res[8].Status, ShouldEqual, "OK") + So(res[9].Result, ShouldHaveLength, 2) + So(data.Consume(res[9].Result[0]).Get("id").Data(), ShouldResemble, &sql.Thing{"person", "tre"}) + So(data.Consume(res[9].Result[1]).Get("id").Data(), ShouldResemble, &sql.Thing{"person", "two"}) + + }) + Convey("Define a multiple-field foreign record unique index on a table, and ensure it prevents duplicate record values", t, func() { setupDB(workerCount) diff --git a/db/document.go b/db/document.go index a03cbfd3..8bac218a 100644 --- a/db/document.go +++ b/db/document.go @@ -422,16 +422,18 @@ func (d *document) purgeIndex(ctx context.Context) (err error) { del := indx.Build(ix.Cols, d.initial) if ix.Uniq == true { - for _, v := range del { - key := &keys.Index{KV: d.key.KV, NS: d.key.NS, DB: d.key.DB, TB: d.key.TB, IX: ix.Name.VA, FD: v} - d.i.e.tx.DelC(ctx, 0, key.Encode(), d.id.Bytes()) + for _, f := range del { + enfd := data.Consume(f).Encode() + didx := &keys.Index{KV: d.key.KV, NS: d.key.NS, DB: d.key.DB, TB: d.key.TB, IX: ix.Name.VA, FD: enfd} + d.i.e.tx.DelC(ctx, 0, didx.Encode(), d.id.Bytes()) } } if ix.Uniq == false { - for _, v := range del { - key := &keys.Point{KV: d.key.KV, NS: d.key.NS, DB: d.key.DB, TB: d.key.TB, IX: ix.Name.VA, FD: v, ID: d.key.ID} - d.i.e.tx.DelC(ctx, 0, key.Encode(), d.id.Bytes()) + for _, f := range del { + enfd := data.Consume(f).Encode() + aidx := &keys.Point{KV: d.key.KV, NS: d.key.NS, DB: d.key.DB, TB: d.key.TB, IX: ix.Name.VA, FD: enfd, ID: d.key.ID} + d.i.e.tx.DelC(ctx, 0, aidx.Encode(), d.id.Bytes()) } }