Files
metapi/src/server/db/sharedIndexSchemaCompatibility.ts
T
ciat-777 b15da138d1 feat: enhance MySQL upgrade generation with index prefix length control
- Added support for specifying prefix lengths for live text-backed columns in MySQL upgrade SQL generation.
- Introduced `mysqlIndexPrefixRequirements` option to `generateUpgradeSql` function.
- Updated tests to validate the new functionality for MySQL upgrade generation.
- Removed legacy shared index compatibility specifications to avoid duplication with runtime schema bootstrap.
- Improved error handling and response structure in account token coverage refresh logic.
- Added tests for coverage refresh failure scenarios to ensure proper handling of token persistence.

# Conflicts:
#	src/server/db/sharedIndexSchemaCompatibility.test.ts
#	src/server/db/sharedIndexSchemaCompatibility.ts
#	src/server/index.ts
2026-03-15 23:13:37 +08:00

60 lines
2.0 KiB
TypeScript

export type SharedIndexSchemaDialect = 'sqlite' | 'mysql' | 'postgres';
export interface SharedIndexSchemaInspector {
dialect: SharedIndexSchemaDialect;
tableExists(table: string): Promise<boolean>;
execute(sqlText: string): Promise<void>;
}
type SharedIndexCompatibilitySpec = {
table: string;
indexName: string;
createSql: Record<SharedIndexSchemaDialect, string>;
};
// Contract-defined indexes are owned by generated schema artifacts and
// runtimeSchemaBootstrap. This legacy hook is intentionally empty so startup
// does not maintain a second static source of index SQL.
export const SHARED_INDEX_COMPATIBILITY_SPECS: SharedIndexCompatibilitySpec[] = [];
function normalizeSchemaErrorMessage(error: unknown): string {
if (typeof error === 'object' && error && 'message' in error) {
return String((error as { message?: unknown }).message || '');
}
return String(error || '');
}
function isDuplicateSchemaError(error: unknown): boolean {
const lowered = normalizeSchemaErrorMessage(error).toLowerCase();
return lowered.includes('already exists')
|| lowered.includes('duplicate')
|| lowered.includes('relation') && lowered.includes('already exists');
}
async function executeIgnoreDuplicate(inspector: SharedIndexSchemaInspector, sqlText: string): Promise<void> {
try {
await inspector.execute(sqlText);
} catch (error) {
if (!isDuplicateSchemaError(error)) {
throw error;
}
}
}
export async function ensureSharedIndexSchemaCompatibility(inspector: SharedIndexSchemaInspector): Promise<void> {
const tableExistsCache = new Map<string, boolean>();
for (const spec of SHARED_INDEX_COMPATIBILITY_SPECS) {
let hasTable = tableExistsCache.get(spec.table);
if (hasTable === undefined) {
hasTable = await inspector.tableExists(spec.table);
tableExistsCache.set(spec.table, hasTable);
}
if (!hasTable) {
continue;
}
await executeIgnoreDuplicate(inspector, spec.createSql[inspector.dialect]);
}
}