fix: support database collation in shadow scripts

This commit is contained in:
zgw
2026-09-04 11:47:01 +08:00
parent 4de09dd693
commit b940bf0eb1
3 changed files with 25 additions and 8 deletions

View File

@@ -7,8 +7,8 @@ DECLARE @expected_objects TABLE (object_name sysname NOT NULL PRIMARY KEY, objec
INSERT INTO @expected_objects VALUES
(N'shadow_package_manifest','U'),(N'warehouse_policy','U'),(N'project_policy','U'),(N'space_role','U'),(N'space_topology','U'),(N'inventory_item_snapshot','U'),(N'space_inventory_summary','U'),(N'lane_state','U'),(N'reservation','U'),(N'deep_workflow_lock','U'),(N'decision_log','U'),(N'shadow_snapshot_state','U'),(N'cargo_space_snapshot','U'),(N'active_job_snapshot','U'),(N'shadow_inbound_request_snapshot','U'),(N'shadow_outbound_request_snapshot','U'),(N'shadow_runtime_metric','U'),(N'shadow_instance_heartbeat','U'),
(N'v_inventory_item','V'),(N'v_active_job','V'),(N'v_space_state','V'),(N'usp_rebuild_space_topology','P'),(N'usp_sync_inventory_snapshot','P'),(N'usp_compute_lane_semantic_fingerprint','P'),(N'usp_rebuild_lane_state','P'),(N'usp_refresh_dirty_lanes','P'),(N'usp_claim_shadow_instance','P'),(N'usp_refresh_shadow_snapshot','P');
IF EXISTS (SELECT 1 FROM sys.objects object LEFT JOIN @expected_objects expected ON expected.object_name = object.name AND expected.object_type = object.type WHERE object.schema_id = SCHEMA_ID(N'alg') AND object.is_ms_shipped = 0 AND object.parent_object_id = 0 AND expected.object_name IS NULL) THROW 51013, 'FT546 shadow uninstall refused: unknown object exists in alg schema', 1;
IF EXISTS (SELECT 1 FROM @expected_objects expected LEFT JOIN sys.objects object ON object.schema_id=SCHEMA_ID(N'alg') AND object.name=expected.object_name AND object.type=expected.object_type WHERE object.object_id IS NULL) THROW 51014, 'FT546 shadow uninstall refused: expected object is missing or has wrong type', 1;
IF EXISTS (SELECT 1 FROM sys.objects object LEFT JOIN @expected_objects expected ON expected.object_name = object.name AND expected.object_type = object.type COLLATE DATABASE_DEFAULT WHERE object.schema_id = SCHEMA_ID(N'alg') AND object.is_ms_shipped = 0 AND object.parent_object_id = 0 AND expected.object_name IS NULL) THROW 51013, 'FT546 shadow uninstall refused: unknown object exists in alg schema', 1;
IF EXISTS (SELECT 1 FROM @expected_objects expected LEFT JOIN sys.objects object ON object.schema_id=SCHEMA_ID(N'alg') AND object.name=expected.object_name AND object.type COLLATE DATABASE_DEFAULT=expected.object_type WHERE object.object_id IS NULL) THROW 51014, 'FT546 shadow uninstall refused: expected object is missing or has wrong type', 1;
BEGIN TRY
BEGIN TRANSACTION;
DROP PROCEDURE alg.usp_refresh_shadow_snapshot; DROP PROCEDURE alg.usp_claim_shadow_instance; DROP PROCEDURE alg.usp_refresh_dirty_lanes; DROP PROCEDURE alg.usp_rebuild_lane_state; DROP PROCEDURE alg.usp_compute_lane_semantic_fingerprint; DROP PROCEDURE alg.usp_sync_inventory_snapshot; DROP PROCEDURE alg.usp_rebuild_space_topology;

View File

@@ -81,7 +81,7 @@ BEGIN
FROM sys.objects object
LEFT JOIN @expected_v3 expected
ON expected.object_name = N'alg.' + object.name
AND expected.object_type = object.type
AND expected.object_type = object.type COLLATE DATABASE_DEFAULT
WHERE object.schema_id = SCHEMA_ID(N'alg')
AND object.is_ms_shipped = 0
AND object.parent_object_id = 0
@@ -95,7 +95,7 @@ BEGIN
LEFT JOIN sys.objects object
ON object.schema_id = SCHEMA_ID(N'alg')
AND N'alg.' + object.name = expected.object_name
AND object.type = expected.object_type
AND object.type COLLATE DATABASE_DEFAULT = expected.object_type
WHERE object.object_id IS NULL
)
THROW 51033, 'FT546 shadow upgrade refused: expected object is missing or has wrong type', 1;
@@ -114,7 +114,7 @@ IF EXISTS (
FROM sys.objects object
LEFT JOIN @expected_v2 expected
ON expected.object_name = N'alg.' + object.name
AND expected.object_type = object.type
AND expected.object_type = object.type COLLATE DATABASE_DEFAULT
WHERE object.schema_id = SCHEMA_ID(N'alg')
AND object.is_ms_shipped = 0
AND object.parent_object_id = 0
@@ -128,7 +128,7 @@ IF EXISTS (
LEFT JOIN sys.objects object
ON object.schema_id = SCHEMA_ID(N'alg')
AND N'alg.' + object.name = expected.object_name
AND object.type = expected.object_type
AND object.type COLLATE DATABASE_DEFAULT = expected.object_type
WHERE object.object_id IS NULL
)
THROW 51036, 'FT546 shadow upgrade refused: expected object is missing or has wrong type', 1;
@@ -1332,7 +1332,7 @@ BEGIN TRY
FROM sys.objects object
LEFT JOIN @expected_v3_post expected
ON expected.object_name = N'alg.' + object.name
AND expected.object_type = object.type
AND expected.object_type = object.type COLLATE DATABASE_DEFAULT
WHERE object.schema_id = SCHEMA_ID(N'alg')
AND object.is_ms_shipped = 0
AND object.parent_object_id = 0
@@ -1346,7 +1346,7 @@ BEGIN TRY
LEFT JOIN sys.objects object
ON object.schema_id = SCHEMA_ID(N'alg')
AND N'alg.' + object.name = expected.object_name
AND object.type = expected.object_type
AND object.type COLLATE DATABASE_DEFAULT = expected.object_type
WHERE object.object_id IS NULL
)
THROW 51033, 'FT546 shadow upgrade refused: expected object is missing or has wrong type', 1;

View File

@@ -175,6 +175,19 @@ class ShadowSqlServerMigrationScriptTest {
"expected object is missing or has wrong type");
}
@Test
void metadataTypeAllowListsUseDatabaseCollationAcrossUpgradeAndUninstall() throws IOException {
String upgrade = normalize(resource("db/sqlserver/upgrade-shadow-v2-to-v3.sql"));
String uninstall = normalize(resource("db/sqlserver/uninstall-shadow.sql"));
assertThat(matches(upgrade, "object\\.type\\s+collate\\s+database_default"))
.as("all six upgrade metadata type comparisons")
.isEqualTo(6);
assertThat(matches(uninstall, "object\\.type\\s+collate\\s+database_default"))
.as("both uninstall metadata type comparisons")
.isEqualTo(2);
}
@Test
void refreshCopiesInputsChecksSourceStabilityAndPublishesInsideOneTransaction() throws IOException {
String install = normalize(resource("db/sqlserver/install-shadow.sql"));
@@ -350,6 +363,10 @@ class ShadowSqlServerMigrationScriptTest {
return count;
}
private long matches(String value, String regex) {
return Pattern.compile(regex).matcher(value).results().count();
}
private Set<String> objects(String sql, Pattern pattern) {
Matcher matcher = pattern.matcher(sql);
Set<String> objects = new LinkedHashSet<>();