@@ -986,3 +986,178 @@ TEST_F(DistributedDmlTest, PlanCacheSeesUpdatedShardMap) {
986986 EXPECT_EQ (mock_executor.get_executed_sqls (s).size (), 0u ) << s;
987987 }
988988}
989+
990+ TEST_F (DistributedDmlTest, UpdateQualifiedNonKeyDoesNotMove) {
991+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (3, 'Carol', 17)" );
992+ const char * home = backend_for_id (3 );
993+ auto result = execute_distributed_dml (" UPDATE users SET users.age = 40 WHERE id = 3" );
994+ EXPECT_TRUE (result.success ) << result.error_message ;
995+ EXPECT_EQ (row_count_on (home, " users" ), 1u );
996+ EXPECT_EQ (mock_executor.total_row_count (" users" ), 1u );
997+ }
998+
999+ TEST_F (DistributedDmlTest, UpdateShardKeyToSameValue) {
1000+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (3, 'Carol', 17)" );
1001+ auto result = execute_distributed_dml (" UPDATE users SET id = 3 WHERE id = 3" );
1002+ EXPECT_TRUE (result.success ) << result.error_message ;
1003+ EXPECT_EQ (row_count_on (backend_for_id (3 ), " users" ), 1u );
1004+ EXPECT_EQ (mock_executor.total_row_count (" users" ), 1u );
1005+ }
1006+
1007+ TEST_F (DistributedDmlTest, UpdateMovesThenPointSelectAndDelete) {
1008+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (3, 'Carol', 17)" );
1009+ ASSERT_STRNE (backend_for_id (3 ), backend_for_id (9 ));
1010+ EXPECT_TRUE (execute_distributed_dml (" UPDATE users SET id = 9 WHERE id = 3" ).success );
1011+ EXPECT_EQ (execute_distributed_select (" SELECT name FROM users WHERE id = 3" ).row_count (), 0u );
1012+ EXPECT_EQ (execute_distributed_select (" SELECT name FROM users WHERE id = 9" ).row_count (), 1u );
1013+ EXPECT_TRUE (execute_distributed_dml (" DELETE FROM users WHERE id = 9" ).success );
1014+ EXPECT_EQ (mock_executor.total_row_count (" users" ), 0u );
1015+ }
1016+
1017+ TEST_F (DistributedDmlTest, UpdateMovesMultipleRowsToOneShard) {
1018+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (3, 'A', 1)" );
1019+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (4, 'B', 2)" );
1020+ EXPECT_TRUE (execute_distributed_dml (" UPDATE users SET id = 9 WHERE id IN (3, 4)" ).success );
1021+ EXPECT_EQ (row_count_on (backend_for_id (9 ), " users" ), 2u );
1022+ EXPECT_EQ (mock_executor.total_row_count (" users" ), 2u );
1023+ }
1024+
1025+ TEST_F (DistributedDmlTest, InsertNegativeStringInt) {
1026+ auto ins = execute_distributed_dml (
1027+ " INSERT INTO users (id, name, age) VALUES ('-7', 'Neg', 1)" );
1028+ EXPECT_TRUE (ins.success ) << ins.error_message ;
1029+ EXPECT_EQ (row_count_on (backend_for_id (-7 ), " users" ), 1u );
1030+ EXPECT_EQ (execute_distributed_select (" SELECT name FROM users WHERE id = -7" ).row_count (), 1u );
1031+ }
1032+
1033+ TEST_F (DistributedDmlTest, InsertLeadingZeroStringInt) {
1034+ auto ins = execute_distributed_dml (
1035+ " INSERT INTO users (id, name, age) VALUES ('03', 'Zed', 1)" );
1036+ EXPECT_TRUE (ins.success ) << ins.error_message ;
1037+ EXPECT_EQ (row_count_on (backend_for_id (3 ), " users" ), 1u );
1038+ EXPECT_EQ (execute_distributed_select (" SELECT name FROM users WHERE id = 3" ).row_count (), 1u );
1039+ }
1040+
1041+ TEST_F (DistributedDmlTest, EmptyInSubqueryDeletesNothing) {
1042+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (3, 'Carol', 17)" );
1043+ mock_executor.clear_sql_logs ();
1044+ auto result = execute_distributed_dml (
1045+ " DELETE FROM users WHERE id IN (SELECT user_id FROM orders)" );
1046+ EXPECT_TRUE (result.success ) << result.error_message ;
1047+ EXPECT_EQ (mock_executor.total_row_count (" users" ), 1u );
1048+ }
1049+
1050+ TEST_F (DistributedDmlTest, EmptyInSubqueryUpdateTouchesNothing) {
1051+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (3, 'Carol', 17)" );
1052+ auto result = execute_distributed_dml (
1053+ " UPDATE users SET age = 99 WHERE id IN (SELECT user_id FROM orders)" );
1054+ EXPECT_TRUE (result.success ) << result.error_message ;
1055+ auto got = execute_distributed_select (" SELECT age FROM users WHERE id = 3" );
1056+ ASSERT_EQ (got.row_count (), 1u );
1057+ EXPECT_EQ (got.rows [0 ].get (0 ).int_val , 17 );
1058+ }
1059+
1060+ TEST_F (DistributedDmlTest, InSubqueryStringNames) {
1061+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (3, 'Carol', 17)" );
1062+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (4, 'Dave', 18)" );
1063+ auto result = execute_distributed_dml (
1064+ " DELETE FROM users WHERE name IN (SELECT name FROM users WHERE id = 3)" );
1065+ EXPECT_TRUE (result.success ) << result.error_message ;
1066+ EXPECT_EQ (mock_executor.total_row_count (" users" ), 1u );
1067+ EXPECT_EQ (execute_distributed_select (" SELECT name FROM users WHERE id = 4" ).row_count (), 1u );
1068+ }
1069+
1070+ TEST_F (DistributedDmlTest, InsertSelectEmptySourceIsNoop) {
1071+ auto result = execute_distributed_dml (
1072+ " INSERT INTO users (id, name, age) SELECT order_id, 'x', 1 FROM orders WHERE order_id = 999" );
1073+ EXPECT_TRUE (result.success ) << result.error_message ;
1074+ EXPECT_EQ (mock_executor.total_row_count (" users" ), 0u );
1075+ }
1076+
1077+ TEST_F (DistributedDmlTest, UnknownTableSelectIsEmpty) {
1078+ catalog.add_table (" " , " ghost" , {{" id" , SqlType::make_int (), false }});
1079+ Parser<Dialect::MySQL> parser;
1080+ auto pr = parser.parse (" SELECT * FROM ghost" , 19 );
1081+ ASSERT_EQ (pr.status , ParseResult::OK );
1082+ PlanBuilder<Dialect::MySQL> builder (catalog, parser.arena ());
1083+ PlanNode* plan = builder.build (pr.ast );
1084+ DistributedPlanner<Dialect::MySQL> dist (shard_map, catalog, parser.arena (),
1085+ &mock_executor, &functions);
1086+ EXPECT_EQ (dist.distribute (plan), nullptr );
1087+ ASSERT_NE (dist.last_error (), nullptr );
1088+ }
1089+
1090+ TEST_F (DistributedDmlTest, UpdateUnknownTableErrors) {
1091+ catalog.add_table (" " , " ghost" , {{" id" , SqlType::make_int (), false }});
1092+ auto result = execute_distributed_dml (" UPDATE ghost SET id = 1" );
1093+ EXPECT_FALSE (result.success );
1094+ EXPECT_NE (result.error_message .find (" shard map" ), std::string::npos);
1095+ }
1096+
1097+ TEST_F (DistributedDmlTest, DeleteUnknownTableErrors) {
1098+ catalog.add_table (" " , " ghost" , {{" id" , SqlType::make_int (), false }});
1099+ auto result = execute_distributed_dml (" DELETE FROM ghost" );
1100+ EXPECT_FALSE (result.success );
1101+ EXPECT_NE (result.error_message .find (" shard map" ), std::string::npos);
1102+ }
1103+
1104+ TEST_F (DistributedDmlTest, PlanCacheCountTwice) {
1105+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (3, 'Carol', 17)" );
1106+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (4, 'Dave', 18)" );
1107+ LocalTransactionManager txn (data_arena);
1108+ Session<Dialect::MySQL> session (catalog, txn);
1109+ session.set_remote_executor (&mock_executor);
1110+ session.set_shard_map (&shard_map);
1111+ const char * sql = " SELECT COUNT(*) FROM users" ;
1112+ auto a = session.execute_query (sql);
1113+ auto b = session.execute_query (sql);
1114+ ASSERT_EQ (a.row_count (), 1u );
1115+ ASSERT_EQ (b.row_count (), 1u );
1116+ EXPECT_EQ (a.rows [0 ].get (0 ).tag , b.rows [0 ].get (0 ).tag );
1117+ EXPECT_EQ (a.rows [0 ].get (0 ).to_int64 (), 2 );
1118+ EXPECT_EQ (b.rows [0 ].get (0 ).to_int64 (), 2 );
1119+ EXPECT_EQ (session.plan_cache_size (), 1u );
1120+ }
1121+
1122+ TEST_F (DistributedDmlTest, PlanCacheSumAndGroupByTwice) {
1123+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (3, 'Carol', 17)" );
1124+ execute_distributed_dml (" INSERT INTO users (id, name, age) VALUES (4, 'Dave', 18)" );
1125+ LocalTransactionManager txn (data_arena);
1126+ Session<Dialect::MySQL> session (catalog, txn);
1127+ session.set_remote_executor (&mock_executor);
1128+ session.set_shard_map (&shard_map);
1129+ const char * sql = " SELECT SUM(age) FROM users" ;
1130+ auto a = session.execute_query (sql);
1131+ auto b = session.execute_query (sql);
1132+ ASSERT_EQ (a.row_count (), 1u );
1133+ ASSERT_EQ (b.row_count (), 1u );
1134+ EXPECT_EQ (a.rows [0 ].get (0 ).to_int64 (), 35 );
1135+ EXPECT_EQ (b.rows [0 ].get (0 ).to_int64 (), 35 );
1136+ }
1137+
1138+ TEST_F (DistributedDmlTest, CompositeQualifiedShardKeyMove) {
1139+ catalog.add_table (" " , " kv" , {
1140+ {" tenant_id" , SqlType::make_int (), false },
1141+ {" id" , SqlType::make_int (), false },
1142+ {" name" , SqlType::make_varchar (255 ), true },
1143+ });
1144+ TableShardConfig cfg;
1145+ cfg.table_name = " kv" ;
1146+ cfg.shard_key = " tenant_id+id" ;
1147+ cfg.shards = {{" shard0" }, {" shard1" }, {" shard2" }};
1148+ shard_map.add_table (cfg);
1149+ mock_executor.add_table_to_all (" kv" , {
1150+ {" tenant_id" , SqlType::make_int (), false },
1151+ {" id" , SqlType::make_int (), false },
1152+ {" name" , SqlType::make_varchar (255 ), true },
1153+ });
1154+ EXPECT_TRUE (execute_distributed_dml (
1155+ " INSERT INTO kv (tenant_id, id, name) VALUES (1, 3, 'A')" ).success );
1156+ auto result = execute_distributed_dml (
1157+ " UPDATE kv SET kv.id = 9 WHERE tenant_id = 1 AND id = 3" );
1158+ EXPECT_TRUE (result.success ) << result.error_message ;
1159+ EXPECT_EQ (execute_distributed_select (
1160+ " SELECT name FROM kv WHERE tenant_id = 1 AND id = 9" ).row_count (), 1u );
1161+ EXPECT_EQ (execute_distributed_select (
1162+ " SELECT name FROM kv WHERE tenant_id = 1 AND id = 3" ).row_count (), 0u );
1163+ }
0 commit comments