20260729231854
This commit is contained in:
1 parent
1c839b707a
commit
84673b3d3d
39 files changed
+4683
-281
No files matched your search
@@ -0,0 +1,633 @@
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Reads the legacy SQL Server catalog and emits reviewed, schema-only FMS migrations.
|
||||
* The generated SQL never connects back to the legacy database and never contains data.
|
||||
*/
|
||||
public final class GenerateBusinessMigrations {
|
||||
private static final Set<String> EXCLUDED_TABLES = lowerSet(
|
||||
"b_contact", "b_contact_type", "b_othercompany", "b_othercompany_bmfl",
|
||||
"b_user", "b_user_power", "b_user_qx", "b_user_record",
|
||||
"dtproperties", "log_datas"
|
||||
);
|
||||
private static final Set<String> TREE_TABLES = lowerSet(
|
||||
"b_other_bmfl", "bc_pricelist_bmfl", "oa_title_bmfl"
|
||||
);
|
||||
private static final Set<String> EXCLUDED_VIEWS = lowerSet(
|
||||
"v_autoidrule", "v_b_contact", "v_b_employee", "v_b_user_record"
|
||||
);
|
||||
private static final Set<String> BOOLEAN_COLUMNS = lowerSet(
|
||||
"b_black", "b_canuse", "b_visible", "b_used", "b_default", "b_isdefault",
|
||||
"b_finish", "b_sh", "b_bill", "b_zx", "b_toall", "b_reminder",
|
||||
"b_othermachinelogin", "b_salesman", "b_docman", "b_dealman",
|
||||
"b_visible_customer", "b_visible_supply", "b_levelvisible", "b_fromcustomer",
|
||||
"b_logintime", "b_checkxy", "b_loginfee", "mx_finish", "mx_sh"
|
||||
);
|
||||
private static final Set<String> TRUE_DEFAULT_COLUMNS = lowerSet(
|
||||
"b_canuse", "b_visible", "b_used"
|
||||
);
|
||||
private static final Set<String> UNICODE_HINTS = lowerSet(
|
||||
"name", "memo", "bz", "addr", "address", "subject", "content", "info",
|
||||
"desc", "caption", "department", "duty", "bank", "goods", "mark",
|
||||
"remark", "taste", "habit", "situation", "place", "body"
|
||||
);
|
||||
private static final Map<String, String> BUSINESS_CODE_FIELDS = Map.ofEntries(
|
||||
Map.entry("bc_khht", "b_htno"),
|
||||
Map.entry("bs_business", "b_ywno"),
|
||||
Map.entry("cw_account", "b_account"),
|
||||
Map.entry("cw_bill", "b_ywno"),
|
||||
Map.entry("cw_invoice", "b_ywno"),
|
||||
Map.entry("cw_sfd", "b_ywno")
|
||||
);
|
||||
private static final Map<String, String> CHILD_PARENTS = Map.ofEntries(
|
||||
Map.entry("b_othercompany_accountbank", "b_othercompany"),
|
||||
Map.entry("bc_pricelist_sub", "bc_pricelist"),
|
||||
Map.entry("bs_business_container", "bs_business"),
|
||||
Map.entry("bs_business_cw", "bs_business"),
|
||||
Map.entry("bs_business_hdbl", "bs_business"),
|
||||
Map.entry("bs_business_transport", "bs_business"),
|
||||
Map.entry("cw_billsub", "cw_bill"),
|
||||
Map.entry("cw_invoicesub", "cw_invoice"),
|
||||
Map.entry("cw_sfdsub", "cw_sfd"),
|
||||
Map.entry("oa_notices_attach", "oa_notices"),
|
||||
Map.entry("oa_noticessub", "oa_notices")
|
||||
);
|
||||
private static final Map<String, String> RENAMED_COLUMNS = Map.ofEntries(
|
||||
Map.entry("bs_business_cw.mx_type_id", "mx_direction"),
|
||||
Map.entry("bs_business_transport.mx_hstype_id", "mx_source_type"),
|
||||
Map.entry("cw_bill.b_module_id", "b_direction"),
|
||||
Map.entry("cw_bill.b_type_id", "b_document_type"),
|
||||
Map.entry("cw_invoice.b_module_id", "b_direction"),
|
||||
Map.entry("cw_invoice.b_type_id", "b_document_type"),
|
||||
Map.entry("cw_sfd.b_module_id", "b_direction"),
|
||||
Map.entry("cw_sfd.b_type_id", "b_document_type")
|
||||
);
|
||||
|
||||
private record Column(
|
||||
String name,
|
||||
String type,
|
||||
int maxLength,
|
||||
int precision,
|
||||
int scale,
|
||||
boolean nullable,
|
||||
int ordinal
|
||||
) {}
|
||||
|
||||
private record Table(String name, List<Column> columns) {}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Path apiRoot = Path.of("").toAbsolutePath().normalize();
|
||||
Path sourceConfig = apiRoot.resolve("config/dbconfigs/G3HY2025.properties");
|
||||
Path migrations = apiRoot.resolve("config/migrations");
|
||||
|
||||
Properties properties = new Properties();
|
||||
try (InputStream input = Files.newInputStream(sourceConfig)) {
|
||||
properties.load(input);
|
||||
}
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(
|
||||
properties.getProperty("url"),
|
||||
properties.getProperty("username"),
|
||||
properties.getProperty("password")
|
||||
)) {
|
||||
Map<String, Table> tables = loadTables(connection);
|
||||
writeDomain(migrations.resolve("016_create_base_tables.sql"), "基础资料", tables, "base");
|
||||
writeDomain(
|
||||
migrations.resolve("017_create_contract_file_tables.sql"),
|
||||
"合同和文件资料",
|
||||
tables,
|
||||
"contract-file"
|
||||
);
|
||||
writeDomain(migrations.resolve("018_create_business_tables.sql"), "业务单据", tables, "business");
|
||||
writeDomain(migrations.resolve("019_create_finance_tables.sql"), "财务", tables, "finance");
|
||||
writeDomain(migrations.resolve("020_create_oa_tables.sql"), "OA", tables, "oa");
|
||||
writeViews(migrations.resolve("021_create_business_views.sql"), connection);
|
||||
writeVerification(migrations.resolve("verify_business_schema.sql"), tables, connection);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Table> loadTables(Connection connection) throws Exception {
|
||||
String sql = """
|
||||
SELECT t.name AS table_name, c.name AS column_name, ty.name AS type_name,
|
||||
c.max_length, c.precision, c.scale, c.is_nullable, c.column_id
|
||||
FROM sys.tables AS t
|
||||
INNER JOIN sys.schemas AS s ON s.schema_id = t.schema_id
|
||||
INNER JOIN sys.columns AS c ON c.object_id = t.object_id
|
||||
INNER JOIN sys.types AS ty ON ty.user_type_id = c.user_type_id
|
||||
WHERE s.name = N'dbo' AND t.is_ms_shipped = 0
|
||||
ORDER BY t.name, c.column_id
|
||||
""";
|
||||
Map<String, List<Column>> columnsByTable = new LinkedHashMap<>();
|
||||
try (Statement statement = connection.createStatement(); ResultSet rows = statement.executeQuery(sql)) {
|
||||
while (rows.next()) {
|
||||
String tableName = rows.getString("table_name");
|
||||
columnsByTable.computeIfAbsent(tableName, ignored -> new ArrayList<>()).add(new Column(
|
||||
rows.getString("column_name"),
|
||||
rows.getString("type_name"),
|
||||
rows.getInt("max_length"),
|
||||
rows.getInt("precision"),
|
||||
rows.getInt("scale"),
|
||||
rows.getBoolean("is_nullable"),
|
||||
rows.getInt("column_id")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Table> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, List<Column>> entry : columnsByTable.entrySet()) {
|
||||
if (!includeTable(entry.getKey())) continue;
|
||||
result.put(entry.getKey().toLowerCase(Locale.ROOT), new Table(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean includeTable(String tableName) {
|
||||
String name = tableName.toLowerCase(Locale.ROOT);
|
||||
if (EXCLUDED_TABLES.contains(name)) return false;
|
||||
return name.startsWith("b_")
|
||||
|| name.startsWith("bc_")
|
||||
|| name.startsWith("bf_")
|
||||
|| name.startsWith("bs_")
|
||||
|| name.startsWith("cw_")
|
||||
|| name.startsWith("oa_");
|
||||
}
|
||||
|
||||
private static String domainOf(String tableName) {
|
||||
String name = tableName.toLowerCase(Locale.ROOT);
|
||||
if (name.startsWith("bc_") || name.startsWith("bf_")) return "contract-file";
|
||||
if (name.startsWith("bs_")) return "business";
|
||||
if (name.startsWith("cw_")) return "finance";
|
||||
if (name.startsWith("oa_")) return "oa";
|
||||
return "base";
|
||||
}
|
||||
|
||||
private static void writeDomain(
|
||||
Path target,
|
||||
String title,
|
||||
Map<String, Table> tables,
|
||||
String domain
|
||||
) throws Exception {
|
||||
StringBuilder sql = new StringBuilder(header(title));
|
||||
tables.values().stream()
|
||||
.filter(table -> domainOf(table.name()).equals(domain))
|
||||
.sorted(Comparator.comparing(Table::name, String.CASE_INSENSITIVE_ORDER))
|
||||
.forEach(table -> sql.append(createTable(table)));
|
||||
if (domain.equals("finance")) appendFinanceRelations(sql);
|
||||
sql.append("\nCOMMIT TRANSACTION;\n");
|
||||
Files.writeString(target, sql.toString(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static String header(String title) {
|
||||
return """
|
||||
/* Generated schema-only migration: %s. No legacy data is copied. */
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
IF DB_NAME() <> N'FMS'
|
||||
THROW 51501, N'本迁移只能在 FMS 目标数据库执行。', 1;
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
""".formatted(title);
|
||||
}
|
||||
|
||||
private static String createTable(Table table) {
|
||||
String lowerName = table.name().toLowerCase(Locale.ROOT);
|
||||
if (TREE_TABLES.contains(lowerName)) return createTreeTable(table.name());
|
||||
|
||||
boolean child = hasColumn(table, "subid");
|
||||
String primaryKey = child ? originalColumnName(table, "subid") : "b_id";
|
||||
boolean sourceHasStringId = !child && table.columns().stream().anyMatch(column ->
|
||||
column.name().equalsIgnoreCase("b_id") && isTextType(column.type())
|
||||
);
|
||||
String businessCode = businessCodeField(table, sourceHasStringId);
|
||||
|
||||
List<String> definitions = new ArrayList<>();
|
||||
if (!child && !hasColumn(table, "b_id")) {
|
||||
definitions.add(" b_id bigint NOT NULL");
|
||||
}
|
||||
for (Column column : table.columns()) {
|
||||
if (isPluralIdColumn(column.name())) continue;
|
||||
String targetName = targetColumnName(table, column.name());
|
||||
String definition = " " + quote(targetName) + " " + targetType(table, column);
|
||||
boolean requiredCode = businessCode != null && column.name().equalsIgnoreCase(businessCode);
|
||||
boolean requiredKey = column.name().equalsIgnoreCase(primaryKey)
|
||||
|| (!child && column.name().equalsIgnoreCase("b_id"));
|
||||
if (BOOLEAN_COLUMNS.contains(column.name().toLowerCase(Locale.ROOT))) {
|
||||
int defaultValue = TRUE_DEFAULT_COLUMNS.contains(column.name().toLowerCase(Locale.ROOT)) ? 1 : 0;
|
||||
definition += " NOT NULL CONSTRAINT "
|
||||
+ quote("DF_" + safeName(table.name()) + "_" + safeName(column.name()))
|
||||
+ " DEFAULT (" + defaultValue + ")";
|
||||
} else {
|
||||
definition += requiredCode || requiredKey ? " NOT NULL" : column.nullable() ? " NULL" : " NOT NULL";
|
||||
}
|
||||
definitions.add(definition);
|
||||
}
|
||||
if (sourceHasStringId && businessCode != null && !hasColumn(table, businessCode)) {
|
||||
definitions.add(" " + quote(businessCode) + " varchar(50) NOT NULL");
|
||||
}
|
||||
definitions.add(" CONSTRAINT " + quote("PK_" + safeName(table.name()))
|
||||
+ " PRIMARY KEY (" + quote(primaryKey) + ")");
|
||||
if (businessCode != null) {
|
||||
definitions.add(" CONSTRAINT " + quote("UQ_" + safeName(table.name()) + "_" + businessCode)
|
||||
+ " UNIQUE (" + quote(businessCode) + ")");
|
||||
}
|
||||
String parentTable = CHILD_PARENTS.get(lowerName);
|
||||
if (child && parentTable != null && hasColumn(table, "father")) {
|
||||
definitions.add(" CONSTRAINT " + quote("FK_" + safeName(table.name()) + "_father")
|
||||
+ " FOREIGN KEY (" + quote(originalColumnName(table, "father")) + ") REFERENCES dbo."
|
||||
+ quote(parentTable) + " (b_id)");
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(" IF OBJECT_ID(N'dbo.").append(escape(table.name())).append("', N'U') IS NULL\n");
|
||||
result.append(" BEGIN\n");
|
||||
result.append(" CREATE TABLE dbo.").append(quote(table.name())).append(" (\n");
|
||||
result.append(String.join(",\n", definitions)).append("\n );\n");
|
||||
appendIndexes(result, table, primaryKey, businessCode);
|
||||
result.append(" END;\n\n");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static String createTreeTable(String tableName) {
|
||||
String safe = safeName(tableName);
|
||||
return """
|
||||
IF OBJECT_ID(N'dbo.%s', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.%s (
|
||||
b_id bigint NOT NULL,
|
||||
b_parent_id bigint NULL,
|
||||
b_name nvarchar(150) NOT NULL,
|
||||
b_level int NOT NULL CONSTRAINT %s DEFAULT (1),
|
||||
b_path varchar(1000) NOT NULL,
|
||||
b_xh int NOT NULL CONSTRAINT %s DEFAULT (0),
|
||||
b_canuse int NOT NULL CONSTRAINT %s DEFAULT (1),
|
||||
b_inputuser_id bigint NULL,
|
||||
b_inputdatetime datetime2(0) NOT NULL CONSTRAINT %s DEFAULT (SYSDATETIME()),
|
||||
b_updateuser_id bigint NULL,
|
||||
b_updatedatetime datetime2(0) NULL,
|
||||
CONSTRAINT %s PRIMARY KEY (b_id),
|
||||
CONSTRAINT %s CHECK (b_parent_id IS NULL OR b_parent_id <> b_id),
|
||||
CONSTRAINT %s FOREIGN KEY (b_parent_id) REFERENCES dbo.%s (b_id)
|
||||
);
|
||||
CREATE INDEX %s ON dbo.%s (b_parent_id, b_xh, b_id);
|
||||
CREATE INDEX %s ON dbo.%s (b_path, b_xh, b_id);
|
||||
END;
|
||||
|
||||
""".formatted(
|
||||
escape(tableName), quote(tableName),
|
||||
quote("DF_" + safe + "_level"), quote("DF_" + safe + "_xh"),
|
||||
quote("DF_" + safe + "_canuse"), quote("DF_" + safe + "_inputdatetime"),
|
||||
quote("PK_" + safe), quote("CK_" + safe + "_parent"), quote("FK_" + safe + "_parent"),
|
||||
quote(tableName), quote("IX_" + safe + "_parent"), quote(tableName),
|
||||
quote("IX_" + safe + "_path"), quote(tableName)
|
||||
);
|
||||
}
|
||||
|
||||
private static void appendIndexes(
|
||||
StringBuilder sql,
|
||||
Table table,
|
||||
String primaryKey,
|
||||
String businessCode
|
||||
) {
|
||||
Set<String> indexed = new LinkedHashSet<>();
|
||||
for (Column column : table.columns()) {
|
||||
String targetName = targetColumnName(table, column.name());
|
||||
String lower = targetName.toLowerCase(Locale.ROOT);
|
||||
if (lower.equals(primaryKey.toLowerCase(Locale.ROOT))) continue;
|
||||
if (businessCode != null && lower.equals(businessCode.toLowerCase(Locale.ROOT))) continue;
|
||||
if (lower.equals("father") || (lower.endsWith("_id") && !isAuditUserColumn(lower))) {
|
||||
if (!indexed.add(lower)) continue;
|
||||
String indexName = "IX_" + safeName(table.name()) + "_" + safeName(column.name());
|
||||
sql.append(" CREATE INDEX ").append(quote(indexName)).append(" ON dbo.")
|
||||
.append(quote(table.name())).append(" (").append(quote(targetName))
|
||||
.append(", ").append(quote(primaryKey)).append(");\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String businessCodeField(Table table, boolean sourceHasStringId) {
|
||||
String lowerName = table.name().toLowerCase(Locale.ROOT);
|
||||
String configured = BUSINESS_CODE_FIELDS.get(lowerName);
|
||||
if (configured != null) return configured;
|
||||
if (hasColumn(table, "b_code")) return originalColumnName(table, "b_code");
|
||||
return sourceHasStringId ? "b_code" : null;
|
||||
}
|
||||
|
||||
private static String targetType(Table table, Column column) {
|
||||
String name = column.name().toLowerCase(Locale.ROOT);
|
||||
String sourceType = column.type().toLowerCase(Locale.ROOT);
|
||||
String renameKey = table.name().toLowerCase(Locale.ROOT) + "." + name;
|
||||
if (renameKey.equals("bs_business_cw.mx_type_id")
|
||||
|| renameKey.equals("cw_bill.b_module_id")
|
||||
|| renameKey.equals("cw_invoice.b_module_id")
|
||||
|| renameKey.equals("cw_sfd.b_module_id")) {
|
||||
return "varchar(20)";
|
||||
}
|
||||
if (renameKey.equals("bs_business_transport.mx_hstype_id")
|
||||
|| renameKey.endsWith(".b_type_id")) {
|
||||
return "varchar(50)";
|
||||
}
|
||||
if (name.equals("b_id") || name.equals("subid") || name.equals("father") || isReferenceId(name)) {
|
||||
return "bigint";
|
||||
}
|
||||
if (BOOLEAN_COLUMNS.contains(name) || sourceType.equals("bit")) return "int";
|
||||
if (name.endsWith("datetime") || name.endsWith("_datetime") || name.equals("b_inputdate")) {
|
||||
return "datetime2(0)";
|
||||
}
|
||||
if (name.equals("b_inputtime")) return "time(0)";
|
||||
if (name.equals("b_birthday") || name.endsWith("begindate") || name.endsWith("enddate")
|
||||
|| name.endsWith("expiredate")) {
|
||||
return "date";
|
||||
}
|
||||
if (sourceType.equals("text") || sourceType.equals("ntext")) return "nvarchar(max)";
|
||||
if (sourceType.equals("image")) return "varbinary(max)";
|
||||
if (sourceType.equals("datetime") || sourceType.equals("smalldatetime")) return "datetime2(0)";
|
||||
if (sourceType.equals("float") || sourceType.equals("real")) return "decimal(18,6)";
|
||||
if (sourceType.equals("money") || sourceType.equals("smallmoney")) return "decimal(19,4)";
|
||||
if (sourceType.equals("decimal") || sourceType.equals("numeric")) {
|
||||
return sourceType + "(" + column.precision() + "," + column.scale() + ")";
|
||||
}
|
||||
if (sourceType.equals("varchar") || sourceType.equals("char")
|
||||
|| sourceType.equals("nvarchar") || sourceType.equals("nchar")) {
|
||||
boolean unicode = sourceType.startsWith("n") || hasUnicodeHint(name);
|
||||
String base = unicode ? "nvarchar" : "varchar";
|
||||
int bytesPerChar = sourceType.startsWith("n") ? 2 : 1;
|
||||
String length = column.maxLength() < 0 ? "max" : String.valueOf(column.maxLength() / bytesPerChar);
|
||||
return base + "(" + length + ")";
|
||||
}
|
||||
if (sourceType.equals("binary") || sourceType.equals("varbinary")) {
|
||||
String length = column.maxLength() < 0 ? "max" : String.valueOf(column.maxLength());
|
||||
return sourceType + "(" + length + ")";
|
||||
}
|
||||
if (sourceType.equals("timestamp") || sourceType.equals("rowversion")) return "binary(8)";
|
||||
if (sourceType.equals("uniqueidentifier")) return "bigint";
|
||||
return sourceType;
|
||||
}
|
||||
|
||||
private static boolean isReferenceId(String name) {
|
||||
return name.endsWith("_id")
|
||||
|| name.equals("userid")
|
||||
|| name.equals("moduleid")
|
||||
|| name.endsWith("userid")
|
||||
|| name.endsWith("moduleid");
|
||||
}
|
||||
|
||||
private static boolean isAuditUserColumn(String name) {
|
||||
return name.equals("b_inputuser_id")
|
||||
|| name.equals("b_updateuser_id")
|
||||
|| name.equals("mx_inputuser_id")
|
||||
|| name.equals("mx_updateuser_id");
|
||||
}
|
||||
|
||||
private static boolean isPluralIdColumn(String name) {
|
||||
String lower = name.toLowerCase(Locale.ROOT);
|
||||
return lower.endsWith("_ids") || lower.equals("b_yw_ids") || lower.equals("b_bill_ids")
|
||||
|| lower.equals("b_inv_ids");
|
||||
}
|
||||
|
||||
private static boolean hasUnicodeHint(String name) {
|
||||
for (String hint : UNICODE_HINTS) {
|
||||
if (name.contains(hint)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isTextType(String type) {
|
||||
String lower = type.toLowerCase(Locale.ROOT);
|
||||
return lower.equals("varchar") || lower.equals("nvarchar") || lower.equals("char")
|
||||
|| lower.equals("nchar") || lower.equals("text") || lower.equals("ntext");
|
||||
}
|
||||
|
||||
private static boolean hasColumn(Table table, String name) {
|
||||
return table.columns().stream().anyMatch(column -> column.name().equalsIgnoreCase(name));
|
||||
}
|
||||
|
||||
private static String originalColumnName(Table table, String name) {
|
||||
return table.columns().stream()
|
||||
.filter(column -> column.name().equalsIgnoreCase(name))
|
||||
.findFirst()
|
||||
.map(Column::name)
|
||||
.orElse(name);
|
||||
}
|
||||
|
||||
private static void appendFinanceRelations(StringBuilder sql) {
|
||||
appendRelation(sql, "cw_bill_business", "b_bill_id", "cw_bill", "b_business_id", "bs_business");
|
||||
appendRelation(sql, "cw_invoice_bill", "b_invoice_id", "cw_invoice", "b_bill_id", "cw_bill");
|
||||
appendRelation(sql, "cw_invoice_business", "b_invoice_id", "cw_invoice", "b_business_id", "bs_business");
|
||||
appendRelation(sql, "cw_sfd_business", "b_sfd_id", "cw_sfd", "b_business_id", "bs_business");
|
||||
appendRelation(sql, "cw_sfd_invoice", "b_sfd_id", "cw_sfd", "b_invoice_id", "cw_invoice");
|
||||
appendRelation(sql, "cw_sfd_bill", "b_sfd_id", "cw_sfd", "b_bill_id", "cw_bill");
|
||||
}
|
||||
|
||||
private static void appendRelation(
|
||||
StringBuilder sql,
|
||||
String table,
|
||||
String leftColumn,
|
||||
String leftTable,
|
||||
String rightColumn,
|
||||
String rightTable
|
||||
) {
|
||||
String safe = safeName(table);
|
||||
sql.append(" IF OBJECT_ID(N'dbo.").append(table).append("', N'U') IS NULL\n")
|
||||
.append(" BEGIN\n")
|
||||
.append(" CREATE TABLE dbo.").append(quote(table)).append(" (\n")
|
||||
.append(" subid bigint NOT NULL,\n")
|
||||
.append(" ").append(quote(leftColumn)).append(" bigint NOT NULL,\n")
|
||||
.append(" ").append(quote(rightColumn)).append(" bigint NOT NULL,\n")
|
||||
.append(" b_xh int NOT NULL CONSTRAINT ").append(quote("DF_" + safe + "_xh"))
|
||||
.append(" DEFAULT (0),\n")
|
||||
.append(" CONSTRAINT ").append(quote("PK_" + safe)).append(" PRIMARY KEY (subid),\n")
|
||||
.append(" CONSTRAINT ").append(quote("UQ_" + safe)).append(" UNIQUE (")
|
||||
.append(quote(leftColumn)).append(", ").append(quote(rightColumn)).append("),\n")
|
||||
.append(" CONSTRAINT ").append(quote("FK_" + safe + "_left")).append(" FOREIGN KEY (")
|
||||
.append(quote(leftColumn)).append(") REFERENCES dbo.").append(quote(leftTable)).append(" (b_id),\n")
|
||||
.append(" CONSTRAINT ").append(quote("FK_" + safe + "_right")).append(" FOREIGN KEY (")
|
||||
.append(quote(rightColumn)).append(") REFERENCES dbo.").append(quote(rightTable)).append(" (b_id)\n")
|
||||
.append(" );\n")
|
||||
.append(" END;\n\n");
|
||||
}
|
||||
|
||||
private static void writeViews(Path target, Connection connection) throws Exception {
|
||||
String query = """
|
||||
SELECT v.name, sm.definition
|
||||
FROM sys.views AS v
|
||||
INNER JOIN sys.schemas AS s ON s.schema_id = v.schema_id
|
||||
INNER JOIN sys.sql_modules AS sm ON sm.object_id = v.object_id
|
||||
WHERE s.name = N'dbo'
|
||||
ORDER BY v.name
|
||||
""";
|
||||
StringBuilder sql = new StringBuilder(header("业务视图"));
|
||||
try (Statement statement = connection.createStatement(); ResultSet rows = statement.executeQuery(query)) {
|
||||
while (rows.next()) {
|
||||
String name = rows.getString("name");
|
||||
if (!includeView(name)) continue;
|
||||
String definition = rows.getString("definition");
|
||||
if (name.equalsIgnoreCase("v_b_othercompany_bmfl")) {
|
||||
definition = "CREATE VIEW dbo.v_b_othercompany_bmfl AS "
|
||||
+ "SELECT b_id, b_name FROM dbo.b_othercompany_bmfl";
|
||||
} else if (name.equalsIgnoreCase("v_b_feetype")) {
|
||||
definition = "CREATE VIEW dbo.v_b_feetype AS SELECT "
|
||||
+ "fee.b_id, fee.b_name, fee.b_name_e, fee.b_bz, fee.b_default, "
|
||||
+ "fee.b_amount, fee.b_tax, fee.b_money_id, fee.b_inputuser_id, "
|
||||
+ "fee.b_inputdatetime, fee.b_updateuser_id, fee.b_updatedatetime, money.b_hl "
|
||||
+ "FROM dbo.b_feetype AS fee "
|
||||
+ "LEFT JOIN dbo.b_money AS money ON money.b_id = fee.b_money_id";
|
||||
}
|
||||
definition = removeLegacyPluralIds(definition);
|
||||
definition = renameLegacySemanticIds(name, definition);
|
||||
sql.append(" IF OBJECT_ID(N'dbo.").append(escape(name)).append("', N'V') IS NOT NULL\n")
|
||||
.append(" DROP VIEW dbo.").append(quote(name)).append(";\n")
|
||||
.append(" EXEC sys.sp_executesql N'")
|
||||
.append(escape(definition))
|
||||
.append("';\n\n");
|
||||
}
|
||||
}
|
||||
sql.append("COMMIT TRANSACTION;\n");
|
||||
Files.writeString(target, sql.toString(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static boolean includeView(String viewName) {
|
||||
String name = viewName.toLowerCase(Locale.ROOT);
|
||||
if (EXCLUDED_VIEWS.contains(name)) return false;
|
||||
return name.startsWith("v_b_") || name.startsWith("v_bc_")
|
||||
|| name.startsWith("v_bs_") || name.startsWith("v_cw_")
|
||||
|| name.equals("v_feetype");
|
||||
}
|
||||
|
||||
private static String removeLegacyPluralIds(String definition) {
|
||||
String result = definition;
|
||||
for (String field : List.of("b_yw_ids", "b_bill_ids", "b_inv_ids")) {
|
||||
result = result.replaceAll(
|
||||
"(?i)(?:dbo\\.)?[A-Za-z0-9_]+\\." + field + "\\s*,\\s*",
|
||||
""
|
||||
);
|
||||
result = result.replaceAll(
|
||||
"(?i),\\s*(?:dbo\\.)?[A-Za-z0-9_]+\\." + field + "(?=\\s+FROM)",
|
||||
""
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String renameLegacySemanticIds(String viewName, String definition) {
|
||||
String result = definition;
|
||||
result = replaceIgnoreCase(result, "dbo.bs_business_cw.mx_type_id", "dbo.bs_business_cw.mx_direction");
|
||||
result = replaceIgnoreCase(result, "dbo.bs_business_transport.mx_hstype_id", "dbo.bs_business_transport.mx_source_type");
|
||||
result = replaceIgnoreCase(result, "dbo.cw_bill.b_module_id", "dbo.cw_bill.b_direction");
|
||||
result = replaceIgnoreCase(result, "dbo.cw_bill.b_type_id", "dbo.cw_bill.b_document_type");
|
||||
result = replaceIgnoreCase(result, "dbo.cw_invoice.b_module_id", "dbo.cw_invoice.b_direction");
|
||||
result = replaceIgnoreCase(result, "dbo.cw_invoice.b_type_id", "dbo.cw_invoice.b_document_type");
|
||||
result = replaceIgnoreCase(result, "dbo.cw_sfd.b_module_id", "dbo.cw_sfd.b_direction");
|
||||
result = replaceIgnoreCase(result, "dbo.cw_sfd.b_type_id", "dbo.cw_sfd.b_document_type");
|
||||
if (viewName.equalsIgnoreCase("v_bs_business_cw")
|
||||
|| viewName.equalsIgnoreCase("v_bs_business_cw_yf")
|
||||
|| viewName.equalsIgnoreCase("v_bs_business_cw_ys")) {
|
||||
result = replaceIgnoreCase(result, "mx_type_id", "mx_direction");
|
||||
}
|
||||
if (viewName.equalsIgnoreCase("v_cw_account")) {
|
||||
result = replaceIgnoreCase(result, "b_type_id", "b_direction");
|
||||
result = replaceIgnoreCase(
|
||||
result,
|
||||
"a.b_account_id = dbo.cw_account.b_account",
|
||||
"a.b_account_id = dbo.cw_account.b_id"
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String replaceIgnoreCase(String value, String source, String replacement) {
|
||||
return value.replaceAll("(?i)" + java.util.regex.Pattern.quote(source), replacement);
|
||||
}
|
||||
|
||||
private static String targetColumnName(Table table, String sourceName) {
|
||||
String key = table.name().toLowerCase(Locale.ROOT) + "." + sourceName.toLowerCase(Locale.ROOT);
|
||||
return RENAMED_COLUMNS.getOrDefault(key, sourceName);
|
||||
}
|
||||
|
||||
private static void writeVerification(
|
||||
Path target,
|
||||
Map<String, Table> tables,
|
||||
Connection connection
|
||||
) throws Exception {
|
||||
int expectedTables = tables.size() + 6 + 5;
|
||||
int expectedViews = 0;
|
||||
try (Statement statement = connection.createStatement(); ResultSet rows = statement.executeQuery(
|
||||
"SELECT name FROM sys.views WHERE is_ms_shipped = 0"
|
||||
)) {
|
||||
while (rows.next()) if (includeView(rows.getString(1))) expectedViews++;
|
||||
}
|
||||
String sql = """
|
||||
SET NOCOUNT ON;
|
||||
|
||||
IF DB_NAME() <> N'FMS'
|
||||
THROW 51510, N'验证脚本只能在 FMS 目标数据库执行。', 1;
|
||||
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM sys.tables WHERE is_ms_shipped = 0) AS table_count,
|
||||
(SELECT COUNT(*) FROM sys.views WHERE is_ms_shipped = 0) AS view_count,
|
||||
%d AS generated_business_table_minimum,
|
||||
%d AS generated_business_view_count;
|
||||
|
||||
SELECT OBJECT_SCHEMA_NAME(c.object_id) AS schema_name,
|
||||
OBJECT_NAME(c.object_id) AS object_name,
|
||||
c.name AS column_name,
|
||||
TYPE_NAME(c.user_type_id) AS type_name
|
||||
FROM sys.columns AS c
|
||||
INNER JOIN sys.tables AS t ON t.object_id = c.object_id
|
||||
WHERE t.is_ms_shipped = 0
|
||||
AND (c.name = N'b_id' OR c.name = N'subid' OR c.name = N'father' OR c.name LIKE N'%%[_]id')
|
||||
AND TYPE_NAME(c.user_type_id) <> N'bigint'
|
||||
ORDER BY schema_name, object_name, c.column_id;
|
||||
|
||||
SELECT s.name AS schema_name, v.name AS view_name
|
||||
FROM sys.views AS v
|
||||
INNER JOIN sys.schemas AS s ON s.schema_id = v.schema_id
|
||||
WHERE v.is_ms_shipped = 0 AND OBJECTPROPERTY(v.object_id, 'IsSchemaBound') IN (0, 1)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM sys.sql_expression_dependencies AS d
|
||||
WHERE d.referencing_id = v.object_id AND d.referenced_id IS NULL
|
||||
AND d.referenced_entity_name IS NOT NULL
|
||||
)
|
||||
ORDER BY s.name, v.name;
|
||||
""".formatted(expectedTables, expectedViews);
|
||||
Files.writeString(target, sql, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static String quote(String identifier) {
|
||||
return "[" + identifier.replace("]", "]]" ) + "]";
|
||||
}
|
||||
|
||||
private static String escape(String value) {
|
||||
return value.replace("'", "''");
|
||||
}
|
||||
|
||||
private static String safeName(String value) {
|
||||
return value.replaceAll("[^A-Za-z0-9_]", "_");
|
||||
}
|
||||
|
||||
private static Set<String> lowerSet(String... values) {
|
||||
Set<String> result = new HashSet<>();
|
||||
for (String value : values) result.add(value.toLowerCase(Locale.ROOT));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/** Executes only the reviewed schema-only business migrations against the FMS database. */
|
||||
public final class RunBusinessMigrations {
|
||||
private static final List<String> MIGRATIONS = List.of(
|
||||
"015_normalize_existing_business_tables.sql",
|
||||
"016_create_base_tables.sql",
|
||||
"017_create_contract_file_tables.sql",
|
||||
"018_create_business_tables.sql",
|
||||
"019_create_finance_tables.sql",
|
||||
"020_create_oa_tables.sql",
|
||||
"021_create_business_views.sql"
|
||||
);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Path apiRoot = Path.of("").toAbsolutePath().normalize();
|
||||
Path configPath = apiRoot.resolve("config/dbconfigs/G3HD.properties");
|
||||
Path migrationDirectory = apiRoot.resolve("config/migrations");
|
||||
|
||||
Properties properties = new Properties();
|
||||
try (InputStream input = Files.newInputStream(configPath)) {
|
||||
properties.load(input);
|
||||
}
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(
|
||||
properties.getProperty("url"),
|
||||
properties.getProperty("username"),
|
||||
properties.getProperty("password")
|
||||
)) {
|
||||
if (!"FMS".equalsIgnoreCase(connection.getCatalog())) {
|
||||
throw new IllegalStateException("Refusing to migrate database: " + connection.getCatalog());
|
||||
}
|
||||
for (String migration : MIGRATIONS) {
|
||||
String sql = Files.readString(migrationDirectory.resolve(migration), StandardCharsets.UTF_8);
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.execute(sql);
|
||||
}
|
||||
System.out.println("APPLIED|" + migration);
|
||||
}
|
||||
try (Statement statement = connection.createStatement(); ResultSet rows = statement.executeQuery(
|
||||
"SELECT (SELECT COUNT(*) FROM sys.tables WHERE is_ms_shipped = 0), "
|
||||
+ "(SELECT COUNT(*) FROM sys.views WHERE is_ms_shipped = 0)"
|
||||
)) {
|
||||
rows.next();
|
||||
System.out.println("CATALOG|tables=" + rows.getInt(1) + "|views=" + rows.getInt(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** Performs read-only checks after the business schema migrations have run. */
|
||||
public final class VerifyBusinessMigration {
|
||||
private static final List<String> TABLE_MIGRATIONS = List.of(
|
||||
"016_create_base_tables.sql",
|
||||
"017_create_contract_file_tables.sql",
|
||||
"018_create_business_tables.sql",
|
||||
"019_create_finance_tables.sql",
|
||||
"020_create_oa_tables.sql"
|
||||
);
|
||||
private static final Pattern CREATE_TABLE = Pattern.compile(
|
||||
"(?i)CREATE\\s+TABLE\\s+dbo\\.(?:\\[([^]]+)]|([A-Za-z0-9_]+))"
|
||||
);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Path apiRoot = Path.of("").toAbsolutePath().normalize();
|
||||
Path migrationDirectory = apiRoot.resolve("config/migrations");
|
||||
Set<String> generatedTables = loadGeneratedTables(migrationDirectory);
|
||||
|
||||
Properties properties = new Properties();
|
||||
try (InputStream input = Files.newInputStream(apiRoot.resolve("config/dbconfigs/G3HD.properties"))) {
|
||||
properties.load(input);
|
||||
}
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(
|
||||
properties.getProperty("url"),
|
||||
properties.getProperty("username"),
|
||||
properties.getProperty("password")
|
||||
)) {
|
||||
if (!"FMS".equalsIgnoreCase(connection.getCatalog())) {
|
||||
throw new IllegalStateException("Refusing to verify database: " + connection.getCatalog());
|
||||
}
|
||||
|
||||
verifyGeneratedTables(connection, generatedTables);
|
||||
verifyCoreTables(connection);
|
||||
verifyViews(connection);
|
||||
printCatalogCounts(connection, generatedTables.size());
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> loadGeneratedTables(Path migrationDirectory) throws Exception {
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
for (String migration : TABLE_MIGRATIONS) {
|
||||
String sql = Files.readString(migrationDirectory.resolve(migration), StandardCharsets.UTF_8);
|
||||
Matcher matcher = CREATE_TABLE.matcher(sql);
|
||||
while (matcher.find()) {
|
||||
result.add(matcher.group(1) != null ? matcher.group(1) : matcher.group(2));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void verifyGeneratedTables(Connection connection, Set<String> tables) throws Exception {
|
||||
for (String table : tables) {
|
||||
if (!tableExists(connection, table)) {
|
||||
throw new IllegalStateException("Missing generated table: dbo." + table);
|
||||
}
|
||||
if (rowCount(connection, table) != 0) {
|
||||
throw new IllegalStateException("Generated table is not empty: dbo." + table);
|
||||
}
|
||||
|
||||
String primaryKey = columnExists(connection, table, "subid") ? "subid" : "b_id";
|
||||
if (!primaryKeyMatches(connection, table, primaryKey)) {
|
||||
throw new IllegalStateException("Unexpected primary key on dbo." + table + "; expected " + primaryKey);
|
||||
}
|
||||
verifyIdTypes(connection, table);
|
||||
verifyNoLegacyTypes(connection, table);
|
||||
verifyRemovedPluralIds(connection, table);
|
||||
}
|
||||
System.out.println("PASS|generated_tables=" + tables.size() + "|empty=true|keys=true|types=true");
|
||||
}
|
||||
|
||||
private static void verifyCoreTables(Connection connection) throws Exception {
|
||||
for (String table : List.of("b_othercompany", "b_contact", "b_othercompany_bmfl")) {
|
||||
if (!tableExists(connection, table)) {
|
||||
throw new IllegalStateException("Missing core business table: dbo." + table);
|
||||
}
|
||||
verifyIdTypes(connection, table);
|
||||
}
|
||||
|
||||
long users = scalar(connection, "SELECT COUNT_BIG(*) FROM dbo.b_user");
|
||||
long modules = scalar(connection, "SELECT COUNT_BIG(*) FROM dbo.s_module");
|
||||
long fields = scalar(connection, "SELECT COUNT_BIG(*) FROM dbo.s_module_field");
|
||||
long autoCodes = scalar(connection, "SELECT COUNT_BIG(*) FROM dbo.s_module_auto_code");
|
||||
if (users == 0 || modules == 0 || fields == 0) {
|
||||
throw new IllegalStateException("Existing user or module metadata is missing");
|
||||
}
|
||||
System.out.println("PASS|preserved|users=" + users + "|modules=" + modules
|
||||
+ "|fields=" + fields + "|auto_codes=" + autoCodes);
|
||||
}
|
||||
|
||||
private static void verifyViews(Connection connection) throws Exception {
|
||||
List<String> viewNames = new ArrayList<>();
|
||||
try (Statement statement = connection.createStatement(); ResultSet rows = statement.executeQuery(
|
||||
"SELECT QUOTENAME(SCHEMA_NAME(schema_id)) + N'.' + QUOTENAME(name) "
|
||||
+ "FROM sys.views WHERE is_ms_shipped = 0 ORDER BY name"
|
||||
)) {
|
||||
while (rows.next()) {
|
||||
viewNames.add(rows.getString(1));
|
||||
}
|
||||
}
|
||||
for (String viewName : viewNames) {
|
||||
try (Statement refresh = connection.createStatement()) {
|
||||
refresh.execute("EXEC sys.sp_refreshview N'" + viewName.replace("'", "''") + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("PASS|views_refreshed=" + viewNames.size());
|
||||
}
|
||||
|
||||
private static void printCatalogCounts(Connection connection, int generatedTableCount) throws Exception {
|
||||
try (Statement statement = connection.createStatement(); ResultSet rows = statement.executeQuery(
|
||||
"SELECT (SELECT COUNT(*) FROM sys.tables WHERE is_ms_shipped = 0), "
|
||||
+ "(SELECT COUNT(*) FROM sys.views WHERE is_ms_shipped = 0)"
|
||||
)) {
|
||||
rows.next();
|
||||
System.out.println("PASS|catalog|tables=" + rows.getInt(1) + "|views=" + rows.getInt(2)
|
||||
+ "|generated_tables=" + generatedTableCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean tableExists(Connection connection, String table) throws Exception {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT COUNT(*) FROM sys.tables WHERE schema_id = SCHEMA_ID(N'dbo') AND name = ?"
|
||||
)) {
|
||||
statement.setString(1, table);
|
||||
try (ResultSet rows = statement.executeQuery()) {
|
||||
rows.next();
|
||||
return rows.getInt(1) == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean columnExists(Connection connection, String table, String column) throws Exception {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT COUNT(*) FROM sys.columns WHERE object_id = OBJECT_ID(?) AND name = ?"
|
||||
)) {
|
||||
statement.setString(1, "dbo." + table);
|
||||
statement.setString(2, column);
|
||||
try (ResultSet rows = statement.executeQuery()) {
|
||||
rows.next();
|
||||
return rows.getInt(1) == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean primaryKeyMatches(Connection connection, String table, String expected) throws Exception {
|
||||
String sql = """
|
||||
SELECT c.name
|
||||
FROM sys.indexes AS i
|
||||
INNER JOIN sys.index_columns AS ic
|
||||
ON ic.object_id = i.object_id AND ic.index_id = i.index_id
|
||||
INNER JOIN sys.columns AS c
|
||||
ON c.object_id = ic.object_id AND c.column_id = ic.column_id
|
||||
WHERE i.object_id = OBJECT_ID(?) AND i.is_primary_key = 1
|
||||
ORDER BY ic.key_ordinal
|
||||
""";
|
||||
try (PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
statement.setString(1, "dbo." + table);
|
||||
try (ResultSet rows = statement.executeQuery()) {
|
||||
return rows.next()
|
||||
&& rows.getString(1).equalsIgnoreCase(expected)
|
||||
&& !rows.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyIdTypes(Connection connection, String table) throws Exception {
|
||||
String sql = """
|
||||
SELECT c.name, TYPE_NAME(c.user_type_id)
|
||||
FROM sys.columns AS c
|
||||
WHERE c.object_id = OBJECT_ID(?)
|
||||
AND (LOWER(c.name) IN (N'b_id', N'subid', N'father') OR LOWER(c.name) LIKE N'%[_]id')
|
||||
AND TYPE_NAME(c.user_type_id) <> N'bigint'
|
||||
""";
|
||||
try (PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
statement.setString(1, "dbo." + table);
|
||||
try (ResultSet rows = statement.executeQuery()) {
|
||||
if (rows.next()) {
|
||||
throw new IllegalStateException("Non-bigint ID column: dbo." + table + "."
|
||||
+ rows.getString(1) + " (" + rows.getString(2).toLowerCase(Locale.ROOT) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyNoLegacyTypes(Connection connection, String table) throws Exception {
|
||||
String sql = """
|
||||
SELECT c.name, TYPE_NAME(c.user_type_id)
|
||||
FROM sys.columns AS c
|
||||
WHERE c.object_id = OBJECT_ID(?)
|
||||
AND TYPE_NAME(c.user_type_id) IN (N'text', N'ntext', N'image', N'float', N'real', N'uniqueidentifier')
|
||||
""";
|
||||
try (PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
statement.setString(1, "dbo." + table);
|
||||
try (ResultSet rows = statement.executeQuery()) {
|
||||
if (rows.next()) {
|
||||
throw new IllegalStateException("Legacy type remains: dbo." + table + "." + rows.getString(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void verifyRemovedPluralIds(Connection connection, String table) throws Exception {
|
||||
for (String column : List.of("b_yw_ids", "b_bill_ids", "b_inv_ids")) {
|
||||
if (columnExists(connection, table, column)) {
|
||||
throw new IllegalStateException("Legacy plural-ID column remains: dbo." + table + "." + column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static long rowCount(Connection connection, String table) throws Exception {
|
||||
return scalar(connection, "SELECT COUNT_BIG(*) FROM dbo.[" + table.replace("]", "]]" ) + "]");
|
||||
}
|
||||
|
||||
private static long scalar(Connection connection, String sql) throws Exception {
|
||||
try (Statement statement = connection.createStatement(); ResultSet rows = statement.executeQuery(sql)) {
|
||||
rows.next();
|
||||
return rows.getLong(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user