package com.alibaba.datax.plugin.rdbms.reader.util; import com.alibaba.datax.common.exception.DataXException; import com.alibaba.datax.common.util.Configuration; import com.alibaba.datax.common.util.ListUtil; import com.alibaba.datax.plugin.rdbms.reader.Constant; import com.alibaba.datax.plugin.rdbms.reader.Key; import com.alibaba.datax.plugin.rdbms.util.DBUtil; import com.alibaba.datax.plugin.rdbms.util.DBUtilErrorCode; import com.alibaba.datax.plugin.rdbms.util.DataBaseType; import com.alibaba.datax.plugin.rdbms.util.TableExpandUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; public final class OriginalConfPretreatmentUtil { private static final Logger LOG = LoggerFactory .getLogger(OriginalConfPretreatmentUtil.class); public static DataBaseType DATABASE_TYPE; public static void doPretreatment(Configuration originalConfig) { // 检查 username/password 配置(必填) originalConfig.getNecessaryValue(Key.USERNAME, DBUtilErrorCode.REQUIRED_VALUE); originalConfig.getNecessaryValue(Key.PASSWORD, DBUtilErrorCode.REQUIRED_VALUE); dealWhere(originalConfig); simplifyConf(originalConfig); } public static void dealWhere(Configuration originalConfig) { String where = originalConfig.getString(Key.WHERE, null); if(StringUtils.isNotBlank(where)) { String whereImprove = where.trim(); if(whereImprove.endsWith(";") || whereImprove.endsWith(";")) { whereImprove = whereImprove.substring(0,whereImprove.length()-1); } originalConfig.set(Key.WHERE, whereImprove); } } /** * 对配置进行初步处理: * <ol> * <li>处理同一个数据库配置了多个jdbcUrl的情况</li> * <li>识别并标记是采用querySql 模式还是 table 模式</li> * <li>对 table 模式,确定分表个数,并处理 column 转 *事项</li> * </ol> */ private static void simplifyConf(Configuration originalConfig) { boolean isTableMode = recognizeTableOrQuerySqlMode(originalConfig); originalConfig.set(Constant.IS_TABLE_MODE, isTableMode); dealJdbcAndTable(originalConfig); dealColumnConf(originalConfig); } private static void dealJdbcAndTable(Configuration originalConfig) { String username = originalConfig.getString(Key.USERNAME); String password = originalConfig.getString(Key.PASSWORD); boolean checkSlave = originalConfig.getBool(Key.CHECK_SLAVE, false); boolean isTableMode = originalConfig.getBool(Constant.IS_TABLE_MODE); boolean isPreCheck = originalConfig.getBool(Key.DRYRUN,false); List<Object> conns = originalConfig.getList(Constant.CONN_MARK, Object.class); List<String> preSql = originalConfig.getList(Key.PRE_SQL, String.class); int tableNum = 0; for (int i = 0, len = conns.size(); i < len; i++) { Configuration connConf = Configuration .from(conns.get(i).toString()); connConf.getNecessaryValue(Key.JDBC_URL, DBUtilErrorCode.REQUIRED_VALUE); List<String> jdbcUrls = connConf .getList(Key.JDBC_URL, String.class); String jdbcUrl; if (isPreCheck) { jdbcUrl = DBUtil.chooseJdbcUrlWithoutRetry(DATABASE_TYPE, jdbcUrls, username, password, preSql, checkSlave); } else { jdbcUrl = DBUtil.chooseJdbcUrl(DATABASE_TYPE, jdbcUrls, username, password, preSql, checkSlave); } jdbcUrl = DATABASE_TYPE.appendJDBCSuffixForReader(jdbcUrl); // 回写到connection[i].jdbcUrl originalConfig.set(String.format("%s[%d].%s", Constant.CONN_MARK, i, Key.JDBC_URL), jdbcUrl); LOG.info("Available jdbcUrl:{}.",jdbcUrl); if (isTableMode) { // table 方式 // 对每一个connection 上配置的table 项进行解析(已对表名称进行了 ` 处理的) List<String> tables = connConf.getList(Key.TABLE, String.class); List<String> expandedTables = TableExpandUtil.expandTableConf( DATABASE_TYPE, tables); if (null == expandedTables || expandedTables.isEmpty()) { throw DataXException.asDataXException( DBUtilErrorCode.ILLEGAL_VALUE, String.format("您所配置的读取数据库表:%s 不正确. 因为DataX根据您的配置找不到这张表. 请检查您的配置并作出修改." + "请先了解 DataX 配置.", StringUtils.join(tables, ","))); } tableNum += expandedTables.size(); originalConfig.set(String.format("%s[%d].%s", Constant.CONN_MARK, i, Key.TABLE), expandedTables); } else { // 说明是配置的 querySql 方式,不做处理. } } originalConfig.set(Constant.TABLE_NUMBER_MARK, tableNum); } private static void dealColumnConf(Configuration originalConfig) { boolean isTableMode = originalConfig.getBool(Constant.IS_TABLE_MODE); List<String> userConfiguredColumns = originalConfig.getList(Key.COLUMN, String.class); if (isTableMode) { if (null == userConfiguredColumns || userConfiguredColumns.isEmpty()) { throw DataXException.asDataXException(DBUtilErrorCode.REQUIRED_VALUE, "您未配置读取数据库表的列信息. " + "正确的配置方式是给 column 配置上您需要读取的列名称,用英文逗号分隔. 例如: \"column\": [\"id\", \"name\"],请参考上述配置并作出修改."); } else { String splitPk = originalConfig.getString(Key.SPLIT_PK, null); if (1 == userConfiguredColumns.size() && "*".equals(userConfiguredColumns.get(0))) { LOG.warn("您的配置文件中的列配置存在一定的风险. 因为您未配置读取数据库表的列,当您的表字段个数、类型有变动时,可能影响任务正确性甚至会运行出错。请检查您的配置并作出修改."); // 回填其值,需要以 String 的方式转交后续处理 originalConfig.set(Key.COLUMN, "*"); } else { String jdbcUrl = originalConfig.getString(String.format( "%s[0].%s", Constant.CONN_MARK, Key.JDBC_URL)); String username = originalConfig.getString(Key.USERNAME); String password = originalConfig.getString(Key.PASSWORD); String tableName = originalConfig.getString(String.format( "%s[0].%s[0]", Constant.CONN_MARK, Key.TABLE)); List<String> allColumns = DBUtil.getTableColumns( DATABASE_TYPE, jdbcUrl, username, password, tableName); LOG.info("table:[{}] has columns:[{}].", tableName, StringUtils.join(allColumns, ",")); // warn:注意mysql表名区分大小写 allColumns = ListUtil.valueToLowerCase(allColumns); List<String> quotedColumns = new ArrayList<String>(); for (String column : userConfiguredColumns) { if ("*".equals(column)) { throw DataXException.asDataXException( DBUtilErrorCode.ILLEGAL_VALUE, "您的配置文件中的列配置信息有误. 因为根据您的配置,数据库表的列中存在多个*. 请检查您的配置并作出修改. "); } quotedColumns.add(column); //以下判断没有任何意义 // if (null == column) { // quotedColumns.add(null); // } else { // if (allColumns.contains(column.toLowerCase())) { // quotedColumns.add(column); // } else { // // 可能是由于用户填写为函数,或者自己对字段进行了`处理或者常量 // quotedColumns.add(column); // } // } } originalConfig.set(Key.COLUMN_LIST, quotedColumns); originalConfig.set(Key.COLUMN, StringUtils.join(quotedColumns, ",")); if (StringUtils.isNotBlank(splitPk)) { if (!allColumns.contains(splitPk.toLowerCase())) { throw DataXException.asDataXException(DBUtilErrorCode.ILLEGAL_SPLIT_PK, String.format("您的配置文件中的列配置信息有误. 因为根据您的配置,您读取的数据库表:%s 中没有主键名为:%s. 请检查您的配置并作出修改.", tableName, splitPk)); } } } } } else { // querySql模式,不希望配制 column,那样是混淆不清晰的 if (null != userConfiguredColumns && userConfiguredColumns.size() > 0) { LOG.warn("您的配置有误. 由于您读取数据库表采用了querySql的方式, 所以您不需要再配置 column. 如果您不想看到这条提醒,请移除您源头表中配置中的 column."); originalConfig.remove(Key.COLUMN); } // querySql模式,不希望配制 where,那样是混淆不清晰的 String where = originalConfig.getString(Key.WHERE, null); if (StringUtils.isNotBlank(where)) { LOG.warn("您的配置有误. 由于您读取数据库表采用了querySql的方式, 所以您不需要再配置 where. 如果您不想看到这条提醒,请移除您源头表中配置中的 where."); originalConfig.remove(Key.WHERE); } // querySql模式,不希望配制 splitPk,那样是混淆不清晰的 String splitPk = originalConfig.getString(Key.SPLIT_PK, null); if (StringUtils.isNotBlank(splitPk)) { LOG.warn("您的配置有误. 由于您读取数据库表采用了querySql的方式, 所以您不需要再配置 splitPk. 如果您不想看到这条提醒,请移除您源头表中配置中的 splitPk."); originalConfig.remove(Key.SPLIT_PK); } } } private static boolean recognizeTableOrQuerySqlMode( Configuration originalConfig) { List<Object> conns = originalConfig.getList(Constant.CONN_MARK, Object.class); List<Boolean> tableModeFlags = new ArrayList<Boolean>(); List<Boolean> querySqlModeFlags = new ArrayList<Boolean>(); String table = null; String querySql = null; boolean isTableMode = false; boolean isQuerySqlMode = false; for (int i = 0, len = conns.size(); i < len; i++) { Configuration connConf = Configuration .from(conns.get(i).toString()); table = connConf.getString(Key.TABLE, null); querySql = connConf.getString(Key.QUERY_SQL, null); isTableMode = StringUtils.isNotBlank(table); tableModeFlags.add(isTableMode); isQuerySqlMode = StringUtils.isNotBlank(querySql); querySqlModeFlags.add(isQuerySqlMode); if (false == isTableMode && false == isQuerySqlMode) { // table 和 querySql 二者均未配制 throw DataXException.asDataXException( DBUtilErrorCode.TABLE_QUERYSQL_MISSING, "您的配置有误. 因为table和querySql应该配置并且只能配置一个. 请检查您的配置并作出修改."); } else if (true == isTableMode && true == isQuerySqlMode) { // table 和 querySql 二者均配置 throw DataXException.asDataXException(DBUtilErrorCode.TABLE_QUERYSQL_MIXED, "您的配置凌乱了. 因为datax不能同时既配置table又配置querySql.请检查您的配置并作出修改."); } } // 混合配制 table 和 querySql if (!ListUtil.checkIfValueSame(tableModeFlags) || !ListUtil.checkIfValueSame(tableModeFlags)) { throw DataXException.asDataXException(DBUtilErrorCode.TABLE_QUERYSQL_MIXED, "您配置凌乱了. 不能同时既配置table又配置querySql. 请检查您的配置并作出修改."); } return tableModeFlags.get(0); } }