| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- package executor
- import (
- "fmt"
- "strings"
- "testing"
- "github.com/danfragoso/pizzasql-next/pkg/storage"
- "github.com/danfragoso/pizzasql-next/pkg/testkv"
- )
- func TestWarmUniqueValidationDoesNotScan(t *testing.T) {
- kv := testkv.New(t)
- pool := kv.Pool(8)
- defer pool.Close()
- schemas := storage.NewSchemaManager(pool, "unique_cache")
- tables := storage.NewTableManager(pool, schemas, "unique_cache")
- e := newExec(schemas, tables)
- execMust(t, e, "CREATE TABLE t (id INTEGER PRIMARY KEY, v INTEGER UNIQUE)")
- var values []string
- for i := 1; i <= 100; i++ {
- values = append(values, fmt.Sprintf("(%d,%d)", i, i))
- }
- execMust(t, e, "INSERT INTO t VALUES "+strings.Join(values, ","))
- for _, sql := range []string{
- "INSERT INTO t VALUES (101,101)",
- "UPDATE t SET v=102 WHERE id=101",
- "DELETE FROM t WHERE id=101",
- "INSERT INTO t VALUES (102,102)",
- "BEGIN", "INSERT INTO t VALUES (103,103)", "ROLLBACK",
- "INSERT INTO t VALUES (103,103)",
- "BEGIN", "UPDATE t SET v=501 WHERE id=1", "UPDATE t SET v=1 WHERE id=2", "UPDATE t SET v=2 WHERE id=1", "COMMIT",
- "INSERT INTO t VALUES (104,NULL),(105,NULL)",
- } {
- before := kv.RequestCount(10)
- execMust(t, e, sql)
- if n := kv.RequestCount(10) - before; n != 0 {
- t.Fatalf("%s: unique validation scanned %d pages", sql, n)
- }
- }
- for _, sql := range []string{"INSERT INTO t VALUES (200,2)", "UPDATE t SET v=2 WHERE id=103"} {
- if _, err := execSQL(e, sql); err == nil || !strings.Contains(err.Error(), "UNIQUE") {
- t.Fatalf("%s: %v", sql, err)
- }
- }
- execMust(t, e, "UPDATE t SET id=1000 WHERE id=1")
- if _, err := execSQL(e, "INSERT INTO t VALUES (200,2)"); err == nil {
- t.Fatal("PK move released an owned unique value")
- }
- execMust(t, e, "DELETE FROM t WHERE id=1000")
- execMust(t, e, "INSERT INTO t VALUES (200,2)")
- // A new manager models restart: durable rows rebuild the cache once, not on
- // every mutation. Managers are shared per database in normal operation.
- schemas = storage.NewSchemaManager(pool, "unique_cache")
- tables = storage.NewTableManager(pool, schemas, "unique_cache")
- e = newExec(schemas, tables)
- before := kv.RequestCount(10)
- execMust(t, e, "INSERT INTO t VALUES (201,201)")
- if kv.RequestCount(10) == before {
- t.Fatal("cold UNIQUE cache did not read durable state")
- }
- before = kv.RequestCount(10)
- execMust(t, e, "INSERT INTO t VALUES (202,202)")
- if kv.RequestCount(10) != before {
- t.Fatal("warm UNIQUE cache rebuilt after restart")
- }
- if _, err := execSQL(e, "INSERT INTO t VALUES (203,201)"); err == nil {
- t.Fatal("restarted cache lost an existing value")
- }
- }
- func TestUniqueCacheDropRecreateAndFailedTransaction(t *testing.T) {
- _, schemas, tables := newTestDB(t)
- e := newExec(schemas, tables)
- execMust(t, e, "CREATE TABLE t (id INTEGER PRIMARY KEY, v INTEGER)")
- execMust(t, e, "CREATE UNIQUE INDEX MixedCase ON t(v)")
- execMust(t, e, "INSERT INTO t VALUES (1,1)")
- execMust(t, e, "BEGIN")
- execMust(t, e, "INSERT INTO t VALUES (2,2),(3,2)")
- if _, err := execSQL(e, "COMMIT"); err == nil {
- t.Fatal("duplicate pending values committed")
- }
- execMust(t, e, "INSERT INTO t VALUES (2,2)")
- execMust(t, e, "DROP INDEX MixedCase")
- execMust(t, e, "INSERT INTO t VALUES (3,2)")
- if _, err := execSQL(e, "CREATE UNIQUE INDEX MixedCase ON t(v)"); err == nil {
- t.Fatal("recreated unique index ignored durable duplicate")
- }
- execMust(t, e, "DELETE FROM t WHERE id=3")
- execMust(t, e, "CREATE UNIQUE INDEX MixedCase ON t(v)")
- if _, err := execSQL(e, "INSERT INTO t VALUES (3,2)"); err == nil {
- t.Fatal("recreated cache lost owner")
- }
- }
- func TestUniqueCacheAmbiguousWriteOutcome(t *testing.T) {
- for _, operation := range []string{"transaction", "insert", "update", "delete"} {
- t.Run(operation, func(t *testing.T) {
- kv := testkv.New(t)
- pool := kv.Pool(8)
- defer pool.Close()
- schemas := storage.NewSchemaManager(pool, "ambiguous")
- tables := storage.NewTableManager(pool, schemas, "ambiguous")
- e := newExec(schemas, tables)
- execMust(t, e, "CREATE TABLE t (id INTEGER PRIMARY KEY, v INTEGER UNIQUE)")
- execMust(t, e, "INSERT INTO t VALUES (1,1)")
- kv.DropNextCompareReply()
- var err error
- switch operation {
- case "transaction":
- _, err = execSQL(e, "INSERT INTO t VALUES (2,2)")
- case "insert":
- err = tables.Insert("t", storage.Row{"id": int64(2), "v": int64(2)})
- case "update":
- _, _, err = tables.UpdateByPK("t", "1", func(storage.Row) (storage.Row, error) { return storage.Row{"v": int64(2)}, nil })
- case "delete":
- _, _, err = tables.DeleteByPK("t", "1")
- }
- if err == nil {
- t.Fatal("dropped reply unexpectedly succeeded")
- }
- if operation == "delete" {
- execMust(t, e, "INSERT INTO t VALUES (3,1)")
- } else {
- if _, err := execSQL(e, "INSERT INTO t VALUES (3,2)"); err == nil {
- t.Fatal("unknown but durable unique owner was ignored")
- }
- if operation == "update" {
- execMust(t, e, "INSERT INTO t VALUES (4,1)")
- }
- }
- })
- }
- }
|