ApplyLobsterBindingIdMigration.java 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import java.sql.*;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * Add lobster_workflow_instance.binding_id on tenant DB(s).
  6. * Usage:
  7. * java -cp ".;mysql-connector-j.jar" ApplyLobsterBindingIdMigration all
  8. * java -cp ".;mysql-connector-j.jar" ApplyLobsterBindingIdMigration 33 32
  9. */
  10. public class ApplyLobsterBindingIdMigration {
  11. private static final String MASTER_HOST = getenv("DB_HOST", "cq-cdb-8fjmemkb.sql.tencentcdb.com");
  12. private static final int MASTER_PORT = Integer.parseInt(getenv("DB_PORT", "27220"));
  13. private static final String MASTER_USER = getenv("DB_USER", "root");
  14. private static final String MASTER_PASSWORD = getenv("DB_PASSWORD", "Ylrz_1q2w3e4r5t6y");
  15. private static final String MASTER_DB = getenv("DB_NAME", "ylrz_saas");
  16. public static void main(String[] args) throws Exception {
  17. List<Long> tenantIds = resolveTenantIds(args);
  18. if (tenantIds.isEmpty()) {
  19. System.err.println("Usage: ApplyLobsterBindingIdMigration all | tenantId ...");
  20. System.exit(1);
  21. }
  22. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD)) {
  23. for (Long tenantId : tenantIds) {
  24. TenantDb tenant = loadTenant(master, tenantId);
  25. if (tenant == null) {
  26. System.err.println("[SKIP] tenant " + tenantId + " not found");
  27. continue;
  28. }
  29. System.out.println("[RUN] tenant " + tenantId + " -> " + tenant.host + ":" + tenant.port + "/" + tenant.database);
  30. try (Connection tenantConn = open(tenant.host, tenant.port, tenant.database, tenant.user, tenant.password)) {
  31. applyBindingIdColumn(tenantConn);
  32. }
  33. System.out.println("[OK] tenant " + tenantId);
  34. }
  35. }
  36. }
  37. private static List<Long> resolveTenantIds(String[] args) throws SQLException {
  38. List<Long> ids = new ArrayList<>();
  39. if (args.length == 0) {
  40. ids.add(33L);
  41. return ids;
  42. }
  43. if ("all".equalsIgnoreCase(args[0].trim())) {
  44. try (Connection master = open(MASTER_HOST, MASTER_PORT, MASTER_DB, MASTER_USER, MASTER_PASSWORD);
  45. Statement st = master.createStatement();
  46. ResultSet rs = st.executeQuery("SELECT id FROM tenant_info WHERE status = 1 ORDER BY id")) {
  47. while (rs.next()) {
  48. ids.add(rs.getLong(1));
  49. }
  50. }
  51. return ids;
  52. }
  53. for (String arg : args) {
  54. ids.add(Long.parseLong(arg.trim()));
  55. }
  56. return ids;
  57. }
  58. private static void applyBindingIdColumn(Connection conn) throws SQLException {
  59. if (!tableExists(conn, "lobster_workflow_instance")) {
  60. System.out.println(" skip: table lobster_workflow_instance not found");
  61. return;
  62. }
  63. addColumnIfMissing(conn, "lobster_workflow_instance", "binding_id",
  64. "ALTER TABLE lobster_workflow_instance ADD COLUMN binding_id BIGINT NULL "
  65. + "COMMENT 'tag binding id' AFTER workflow_id");
  66. addIndexIfMissing(conn, "lobster_workflow_instance", "idx_lobster_instance_binding",
  67. "CREATE INDEX idx_lobster_instance_binding "
  68. + "ON lobster_workflow_instance (company_id, binding_id, del_flag)");
  69. }
  70. private static void addColumnIfMissing(Connection conn, String table, String column, String ddl) throws SQLException {
  71. if (columnExists(conn, table, column)) {
  72. System.out.println(" column exists: " + table + "." + column);
  73. return;
  74. }
  75. try (Statement st = conn.createStatement()) {
  76. st.execute(ddl);
  77. System.out.println(" added column: " + table + "." + column);
  78. }
  79. }
  80. private static void addIndexIfMissing(Connection conn, String table, String indexName, String ddl) throws SQLException {
  81. if (indexExists(conn, table, indexName)) {
  82. System.out.println(" index exists: " + table + "." + indexName);
  83. return;
  84. }
  85. try (Statement st = conn.createStatement()) {
  86. st.execute(ddl);
  87. System.out.println(" added index: " + table + "." + indexName);
  88. }
  89. }
  90. private static boolean tableExists(Connection conn, String table) throws SQLException {
  91. String sql = "SELECT COUNT(*) FROM information_schema.TABLES "
  92. + "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?";
  93. try (PreparedStatement ps = conn.prepareStatement(sql)) {
  94. ps.setString(1, table);
  95. try (ResultSet rs = ps.executeQuery()) {
  96. return rs.next() && rs.getInt(1) > 0;
  97. }
  98. }
  99. }
  100. private static boolean columnExists(Connection conn, String table, String column) throws SQLException {
  101. String sql = "SELECT COUNT(*) FROM information_schema.COLUMNS "
  102. + "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?";
  103. try (PreparedStatement ps = conn.prepareStatement(sql)) {
  104. ps.setString(1, table);
  105. ps.setString(2, column);
  106. try (ResultSet rs = ps.executeQuery()) {
  107. return rs.next() && rs.getInt(1) > 0;
  108. }
  109. }
  110. }
  111. private static boolean indexExists(Connection conn, String table, String indexName) throws SQLException {
  112. String sql = "SELECT COUNT(*) FROM information_schema.STATISTICS "
  113. + "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND INDEX_NAME = ?";
  114. try (PreparedStatement ps = conn.prepareStatement(sql)) {
  115. ps.setString(1, table);
  116. ps.setString(2, indexName);
  117. try (ResultSet rs = ps.executeQuery()) {
  118. return rs.next() && rs.getInt(1) > 0;
  119. }
  120. }
  121. }
  122. private static TenantDb loadTenant(Connection master, long tenantId) throws SQLException {
  123. String sql = "SELECT db_url, db_account, db_pwd FROM tenant_info WHERE id = ? AND status = 1 LIMIT 1";
  124. try (PreparedStatement ps = master.prepareStatement(sql)) {
  125. ps.setLong(1, tenantId);
  126. try (ResultSet rs = ps.executeQuery()) {
  127. if (!rs.next()) {
  128. return null;
  129. }
  130. return parseJdbcUrl(rs.getString("db_url"), rs.getString("db_account"), rs.getString("db_pwd"));
  131. }
  132. }
  133. }
  134. private static TenantDb parseJdbcUrl(String jdbcUrl, String user, String password) {
  135. if (jdbcUrl == null || !jdbcUrl.startsWith("jdbc:mysql://")) {
  136. throw new IllegalArgumentException("Unsupported db_url: " + jdbcUrl);
  137. }
  138. String body = jdbcUrl.substring("jdbc:mysql://".length());
  139. int slash = body.indexOf('/');
  140. if (slash < 0) {
  141. throw new IllegalArgumentException("Invalid db_url: " + jdbcUrl);
  142. }
  143. String hostPort = body.substring(0, slash);
  144. String rest = body.substring(slash + 1);
  145. int q = rest.indexOf('?');
  146. String database = q >= 0 ? rest.substring(0, q) : rest;
  147. String host;
  148. int port;
  149. int colon = hostPort.indexOf(':');
  150. if (colon >= 0) {
  151. host = hostPort.substring(0, colon);
  152. port = Integer.parseInt(hostPort.substring(colon + 1));
  153. } else {
  154. host = hostPort;
  155. port = 3306;
  156. }
  157. TenantDb t = new TenantDb();
  158. t.host = host;
  159. t.port = port;
  160. t.database = database;
  161. t.user = user != null ? user : MASTER_USER;
  162. t.password = password != null ? password : MASTER_PASSWORD;
  163. return t;
  164. }
  165. private static Connection open(String host, int port, String database, String user, String password)
  166. throws SQLException {
  167. String url = "jdbc:mysql://" + host + ":" + port + "/" + database
  168. + "?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&allowMultiQueries=true";
  169. return DriverManager.getConnection(url, user, password);
  170. }
  171. private static String getenv(String key, String defaultValue) {
  172. String v = System.getenv(key);
  173. return v != null && !v.isEmpty() ? v : defaultValue;
  174. }
  175. private static final class TenantDb {
  176. String host;
  177. int port;
  178. String database;
  179. String user;
  180. String password;
  181. }
  182. }