go-mysql-server
go-mysql-server copied to clipboard
MemoryDB: UPDATE succeeds but is not reflected in subsequent SELECT
Have 5 tables with the following relationships:
graph TD;
table1-->table2;
table1-->table3;
table3-->table2;
table4-->table3;
table5-->table3;
table5-->table4;
Updating a row in table3 will show as succeeded with rowsAffected == 1, however a subsequent select will not reflect the change in the value.
Removing the table2 constraint from table1 will restore the expected behavior. Removing the table4 constraint from table5 also restores the expected behavior.
Something about the combination of these relationships results in updates not being reflected.
Here is a reproduction in go:
package blue_test
import (
"context"
"database/sql"
"fmt"
"net"
"os"
"testing"
"time"
sqle "github.com/dolthub/go-mysql-server"
gmsSql "github.com/dolthub/go-mysql-server/sql"
vsql "github.com/dolthub/vitess/go/mysql"
"google.golang.org/grpc/test/bufconn"
"gotest.tools/assert"
"github.com/dolthub/go-mysql-server/memory"
"github.com/dolthub/go-mysql-server/server"
"github.com/go-sql-driver/mysql"
_ "github.com/go-sql-driver/mysql"
)
func newMysql(t *testing.T) *sql.DB {
dbName := "blue"
listener := bufconn.Listen(1024)
memdb := memory.NewDatabase(dbName)
memdb.EnablePrimaryKeyIndexes()
pro := memory.NewDBProvider(memdb)
engine := sqle.NewDefault(pro)
cfg := server.Config{Listener: listener}
sessionBuilder := func(ctx context.Context, conn *vsql.Conn, addr string) (gmsSql.Session, error) {
host := ""
user := ""
mysqlConnectionUser, ok := conn.UserData.(gmsSql.MysqlConnectionUser)
if ok {
host = mysqlConnectionUser.Host
user = mysqlConnectionUser.User
}
client := gmsSql.Client{Address: host, User: user, Capabilities: conn.Capabilities}
baseSession := gmsSql.NewBaseSessionWithClientServer(addr, client, conn.ConnectionID)
return memory.NewSession(baseSession, pro), nil
}
s, err := server.NewServer(cfg, engine, sessionBuilder, nil)
assert.NilError(t, err)
networkName := "testNetwork"
mysql.RegisterDialContext(networkName, func(ctx context.Context, addr string) (net.Conn, error) {
return listener.DialContext(ctx)
})
driver, err := mysql.NewConnector(&mysql.Config{
DBName: dbName,
Addr: "bufconn",
Net: networkName,
Passwd: "",
User: "root",
AllowNativePasswords: true,
MultiStatements: true,
ParseTime: true,
Loc: time.UTC,
})
assert.NilError(t, err)
go func() {
err := s.Start()
if err != nil {
panic(err)
}
}()
db := sql.OpenDB(driver)
var pingErr error
for i := 0; i < 3; i++ {
if pingErr = db.Ping(); pingErr == nil {
break
}
time.Sleep(time.Millisecond * 5)
}
assert.NilError(t, pingErr)
t.Cleanup(func() {
s.Close()
db.Close()
engine.Close()
})
return db
}
func TestBlue(t *testing.T) {
db := newMysql(t)
dat, err := os.ReadFile("schema.sql")
assert.NilError(t, err)
_, err = db.Exec(string(dat))
assert.NilError(t, err)
table2Insert, err := db.Exec("INSERT INTO table2 (name) VALUES (\"abcd\")")
assert.NilError(t, err)
table2Id, err := table2Insert.LastInsertId()
assert.NilError(t, err)
table3Insert, err := db.Exec(fmt.Sprintf("INSERT INTO table3 (t2Id) VALUES (%d)", table2Id))
assert.NilError(t, err)
table3Id, err := table3Insert.LastInsertId()
assert.NilError(t, err)
table3Update, err := db.Exec(fmt.Sprintf("UPDATE table3 SET flag=1 WHERE id = %d", table3Id))
assert.NilError(t, err)
rowsAffected, err := table3Update.RowsAffected()
assert.NilError(t, err)
assert.Equal(t, int(rowsAffected), 1)
var t2Id int64
var flag int
row := db.QueryRow(fmt.Sprintf("SELECT t2Id, flag FROM table3 WHERE id = %d", table3Id))
err = row.Scan(&t2Id, &flag)
assert.NilError(t, err)
assert.Equal(t, t2Id, table2Id)
assert.Equal(t, flag, 1)
_, err = db.Exec(fmt.Sprintf("UPDATE table3 SET flag=0 WHERE id = %d", table3Id))
assert.NilError(t, err)
var t2Id2 int64
var flag2 int
row = db.QueryRow(fmt.Sprintf("SELECT t2Id, flag FROM table3 WHERE id = %d", table3Id))
err = row.Scan(&t2Id2, &flag2)
assert.NilError(t, err)
assert.Equal(t, t2Id2, table2Id)
assert.Equal(t, flag2, 0)
}
schema.sql
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `table1`
--
DROP TABLE IF EXISTS `table1`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `table1` (
`id` int NOT NULL AUTO_INCREMENT,
`t3Id` int NOT NULL,
`t2Id` int NOT NULL,
PRIMARY KEY (`id`),
KEY `t3Id` (`t3Id`),
KEY `t2Id` (`t2Id`),
CONSTRAINT `table1_ibfk_1` FOREIGN KEY (`t3Id`) REFERENCES `table3` (`id`),
CONSTRAINT `table1_ibfk_2` FOREIGN KEY (`t2Id`) REFERENCES `table2` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `table1`
--
LOCK TABLES `table1` WRITE;
/*!40000 ALTER TABLE `table1` DISABLE KEYS */;
/*!40000 ALTER TABLE `table1` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `table2`
--
DROP TABLE IF EXISTS `table2`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `table2` (
`id` int NOT NULL AUTO_INCREMENT,
`name` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `table2`
--
LOCK TABLES `table2` WRITE;
/*!40000 ALTER TABLE `table2` DISABLE KEYS */;
/*!40000 ALTER TABLE `table2` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `table3`
--
DROP TABLE IF EXISTS `table3`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `table3` (
`id` int NOT NULL AUTO_INCREMENT,
`t2Id` int NOT NULL,
`flag` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `t2Id` (`t2Id`),
CONSTRAINT `table3_ibfk_1` FOREIGN KEY (`t2Id`) REFERENCES `table2` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `table3`
--
LOCK TABLES `table3` WRITE;
/*!40000 ALTER TABLE `table3` DISABLE KEYS */;
/*!40000 ALTER TABLE `table3` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `table4`
--
DROP TABLE IF EXISTS `table4`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `table4` (
`id` int NOT NULL AUTO_INCREMENT,
`t3Id` int DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `table4_t3Id_foreign_idx` (`t3Id`),
CONSTRAINT `table4_t3Id_foreign_idx` FOREIGN KEY (`t3Id`) REFERENCES `table3` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `table4`
--
LOCK TABLES `table4` WRITE;
/*!40000 ALTER TABLE `table4` DISABLE KEYS */;
/*!40000 ALTER TABLE `table4` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `table5`
--
DROP TABLE IF EXISTS `table5`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `table5` (
`id` int NOT NULL AUTO_INCREMENT,
`t4Id` int DEFAULT NULL,
`t3Id` int NOT NULL,
PRIMARY KEY (`id`),
KEY `table5_t3Id_foreign_idx` (`t3Id`),
KEY `table5_t4Id_foreign_idx` (`t4Id`),
CONSTRAINT `table5_t4Id_foreign_idx` FOREIGN KEY (`t4Id`) REFERENCES `table4` (`id`),
CONSTRAINT `table5_t3Id_foreign_idx` FOREIGN KEY (`t3Id`) REFERENCES `table3` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `table5`
--
LOCK TABLES `table5` WRITE;
/*!40000 ALTER TABLE `table5` DISABLE KEYS */;
/*!40000 ALTER TABLE `table5` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;