| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 9f64b61 commit e521cf4
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -86,7 +86,7 @@ public abstract class AbstractSQLConfig<T, M extends Map<String, Object>, L exte | |||
| 86 | 86 | * 表名映射,隐藏真实表名,对安全要求很高的表可以这么做 | |
| 87 | 87 | */ | |
| 88 | 88 | public static Map<String, String> TABLE_KEY_MAP; | |
| 89 | - public static Map<String, String> UPSERT_TABLE_MAP; | ||
| 89 | + public static Map<String, Object> UPSERT_TABLE_MAP; | ||
| 90 | 90 | /** | |
| 91 | 91 | * 字段名映射,隐藏真实字段名,对安全要求很高的表可以这么做,另外可以配置 name_tag:(name,tag) 来实现多字段 IN,length_tag:length(tag) 来实现 SQL 函数复杂条件 | |
| 92 | 92 | */ | |
@@ -1190,23 +1190,23 @@ public boolean isKingBase() { | |||
| 1190 | 1190 | public static boolean isKingBase(String db) { | |
| 1191 | 1191 | return KingbaseSQLDialect.from(db).isKingbase(); | |
| 1192 | 1192 | } | |
| 1193 | - | ||
| 1193 | + | ||
| 1194 | 1194 | @Override | |
| 1195 | 1195 | public boolean isKingBaseMySQL() { | |
| 1196 | 1196 | return isKingBaseMySQL(gainSQLDatabase()); | |
| 1197 | 1197 | } | |
| 1198 | 1198 | public static boolean isKingBaseMySQL(String db) { | |
| 1199 | 1199 | return KingbaseSQLDialect.from(db).isMySQL(); | |
| 1200 | 1200 | } | |
| 1201 | - | ||
| 1201 | + | ||
| 1202 | 1202 | @Override | |
| 1203 | 1203 | public boolean isKingBaseOracle() { | |
| 1204 | 1204 | return isKingBaseOracle(gainSQLDatabase()); | |
| 1205 | 1205 | } | |
| 1206 | 1206 | public static boolean isKingBaseOracle(String db) { | |
| 1207 | 1207 | return KingbaseSQLDialect.from(db).isOracle(); | |
| 1208 | 1208 | } | |
| 1209 | - | ||
| 1209 | + | ||
| 1210 | 1210 | @Override | |
| 1211 | 1211 | public boolean isKingBaseSQLServer() { | |
| 1212 | 1212 | return isKingBaseSQLServer(gainSQLDatabase()); | |
@@ -2898,7 +2898,7 @@ else if (ck.contains("`") || ck.contains("'") || origin.startsWith("_") || origi | |||
| 2898 | 2898 | public List<List<Object>> getValues() { | |
| 2899 | 2899 | return values; | |
| 2900 | 2900 | } | |
| 2901 | - public String getValuesString() { | ||
| 2901 | + public String gainValuesString() { | ||
| 2902 | 2902 | String s = ""; | |
| 2903 | 2903 | if (values != null && values.size() > 0) { | |
| 2904 | 2904 | Object[] items = new Object[values.size()]; | |
@@ -5036,70 +5036,198 @@ public static <T, M extends Map<String, Object>, L extends List<Object>> String | |||
| 5036 | 5036 | String cSql; | |
| 5037 | 5037 | switch (method) { | |
| 5038 | 5038 | case POST: | |
| 5039 | - String s = UPSERT_TABLE_MAP.get(table); | ||
| 5040 | - // 暂不支持,UNIQUE KEY 应该比较固定不会频繁改动,而且改了也是同步改后端配置,和前端传参关系不大 | ||
| 5041 | - // Object obj = UPSERT_TABLE_MAP.get(table); | ||
| 5042 | - // if (obj instanceof String) { | ||
| 5043 | - // s += obj; | ||
| 5044 | - // } else if (obj instanceof Map<?, ?>) { // { "a+": 1, "b-": 1 } | ||
| 5045 | - // Map<String, Object> setObj = (Map<String, Object>) obj; | ||
| 5046 | - // AbstractSQLConfig<T, M, L> cfg = gainParser().createSQLConfig(); | ||
| 5047 | - // s = cfg.gainSetString(); | ||
| 5048 | - // } | ||
| 5049 | - | ||
| 5050 | - return "INSERT INTO " + tablePath + config.gainColumnString() + " VALUES" + config.getValuesString() | ||
| 5051 | - + (StringUtil.isEmpty(s) ? "" : " ON DUPLICATE KEY UPDATE " + s); | ||
| 5039 | + return config.gainUpsertSQL(); | ||
| 5052 | 5040 | case PUT: | |
| 5053 | - if(config.isClickHouse()){ | ||
| 5054 | - return "ALTER TABLE " + tablePath + " UPDATE" + config.gainSetString() + config.gainWhereString(true); | ||
| 5055 | - } | ||
| 5056 | - cSql = "UPDATE " + tablePath + config.gainSetString() + config.gainWhereString(true) | ||
| 5057 | - + (config.isMySQL() || KingbaseSQLDialect.from(config.gainSQLDatabase()).supportsDmlLimit() ? config.gainLimitString() : ""); | ||
| 5058 | - cSql = buildWithAsExprSql(config, cSql); | ||
| 5059 | - return cSql; | ||
| 5041 | + return config.gainUpdateSQL(); | ||
| 5060 | 5042 | case DELETE: | |
| 5061 | - if(config.isClickHouse()){ | ||
| 5062 | - return "ALTER TABLE " + tablePath + " DELETE" + config.gainWhereString(true); | ||
| 5063 | - } | ||
| 5064 | - cSql = "DELETE FROM " + tablePath + config.gainWhereString(true) | ||
| 5065 | - + (config.isMySQL() || KingbaseSQLDialect.from(config.gainSQLDatabase()).supportsDmlLimit() ? config.gainLimitString() : ""); // PostgreSQL 不允许 LIMIT | ||
| 5066 | - cSql = buildWithAsExprSql(config, cSql); | ||
| 5067 | - return cSql; | ||
| 5043 | + return config.gainDeleteSQL(); | ||
| 5068 | 5044 | default: | |
| 5069 | - KingbaseSQLDialect kingbaseDialect = KingbaseSQLDialect.from(config.gainSQLDatabase()); | ||
| 5070 | - String kingbaseExplain = kingbaseDialect.getExplainPrefix(); | ||
| 5071 | - String explain = config.isExplain() ? (kingbaseExplain != null ? kingbaseExplain | ||
| 5072 | - : (config.isSQLServer() ? "SET STATISTICS PROFILE ON " | ||
| 5073 | - : (config.isOracle() || config.isDameng() ? "EXPLAIN PLAN FOR " : "EXPLAIN "))) : ""; | ||
| 5074 | - if (config.isTest() && RequestMethod.isGetMethod(config.getMethod(), true)) { // FIXME 为啥是 code 而不是 count ? | ||
| 5075 | - String q = config.getQuote(); // 生成 SELECT ( (24 >=0 AND 24 <3) ) AS `code` LIMIT 1 OFFSET 0 | ||
| 5076 | - return explain + "SELECT " + config.gainWhereString(false) | ||
| 5077 | - + config.gainAs() + q + JSONResponse.KEY_COUNT + q + config.gainLimitString(); | ||
| 5078 | - } | ||
| 5079 | - | ||
| 5080 | - config.setPreparedValueList(new ArrayList<Object>()); | ||
| 5081 | - String column = config.gainColumnString(); | ||
| 5082 | - if (config.isOracle() || config.isDameng() || config.isKingBaseOracle()) { | ||
| 5083 | - //When config's database is oracle,Using subquery since Oracle12 below does not support OFFSET FETCH paging syntax. | ||
| 5084 | - //针对oracle分组后条数的统计 | ||
| 5085 | - if (StringUtil.isNotEmpty(config.getGroup(),true) && RequestMethod.isHeadMethod(config.getMethod(), true)){ | ||
| 5086 | - return explain + "SELECT count(*) FROM (SELECT " + (config.getCache() == JSONMap.CACHE_RAM | ||
| 5087 | - ? "SQL_NO_CACHE " : "") + column + " FROM " + gainConditionString(tablePath, config) + ") " + config.gainLimitString(); | ||
| 5045 | + return config.gainSelectSQL(); | ||
| 5046 | + } | ||
| 5047 | + } | ||
| 5048 | + | ||
| 5049 | + public static String gainInsertSQL(String tablePath, String columns, String values) { | ||
| 5050 | + return "INSERT INTO " + tablePath + columns + " VALUES" + values; | ||
| 5051 | + } | ||
| 5052 | + | ||
| 5053 | + public String gainUpsertSQL() throws Exception { | ||
| 5054 | + String table = getTable(); | ||
| 5055 | + Object obj = UPSERT_TABLE_MAP.get(table); | ||
| 5056 | + String tablePath = gainTablePath(); | ||
| 5057 | + if (obj == null) { | ||
| 5058 | + return gainInsertSQL(tablePath, gainColumnString(), gainValuesString()); | ||
| 5059 | + } | ||
| 5060 | + | ||
| 5061 | + String database = gainSQLDatabase(); | ||
| 5062 | + String q = getQuote(); | ||
| 5063 | + | ||
| 5064 | + String key = ""; | ||
| 5065 | + String setStr = ""; | ||
| 5066 | + if (obj instanceof String) { // MySQL/PostgreSQL 简单写 version = version + 1 | ||
| 5067 | + setStr = (String) obj; | ||
| 5068 | + } else if (obj instanceof Map<?, ?>) { | ||
| 5069 | + // PostgreSQL 写 { "": "excluded.version = Method.version" } | ||
| 5070 | + // 或 自定义 CONFLICT key 写 { "package,method": "excluded.version = Method.version + 1" } | ||
| 5071 | + // 或多数据库写 { "Oracle": { "method,tag": "excluded.version = Request.version + 1" }, "DB2": {...} } 等 | ||
| 5072 | + | ||
| 5073 | + Map<String, Object> setObj = (Map<String, Object>) obj; | ||
| 5074 | + Object dbSetObj = setObj.get(database); | ||
| 5075 | + if (dbSetObj instanceof String) { | ||
| 5076 | + setStr = (String) dbSetObj; | ||
| 5077 | + } else { | ||
| 5078 | + boolean isDBSet = dbSetObj instanceof Map<?, ?>; | ||
| 5079 | + if (isDBSet) { | ||
| 5080 | + setObj = (Map<String, Object>) dbSetObj; | ||
| 5081 | + } else if (dbSetObj != null) { | ||
| 5082 | + throw new IllegalArgumentException("UPSERT_TABLE_MAP 中 " + table + ": {" + database + ": value} 中 value 不合法,必须是 String 或 Map<String, Object> 类型!"); | ||
| 5083 | + } | ||
| 5084 | + | ||
| 5085 | + Set<Entry<String, Object>> set = setObj.entrySet(); | ||
| 5086 | + boolean first = true; | ||
| 5087 | + for (Entry<String, Object> entry : set) { | ||
| 5088 | + String k = entry == null ? null : entry.getKey(); | ||
| 5089 | + Object v = k == null ? null : entry.getValue(); | ||
| 5090 | + if (v == null || (isDBSet == false && DATABASE_LIST.contains(k))) { | ||
| 5091 | + continue; | ||
| 5088 | 5092 | } | |
| 5089 | 5093 | ||
| 5090 | - String sql = "SELECT " + (config.getCache() == JSONMap.CACHE_RAM | ||
| 5091 | - ? "SQL_NO_CACHE " : "") + column + " FROM " + gainConditionString(tablePath, config); | ||
| 5092 | - return explain + config.gainOraclePageSQL(sql); | ||
| 5094 | + if (StringUtil.isEmpty(k)) { | ||
| 5095 | + k = q + getIdKey() + q; | ||
| 5096 | + } else if (!(k.contains(",") || (k.startsWith("`") && k.endsWith("`")) || (k.startsWith("\"") && k.endsWith("\"")))) { | ||
| 5097 | + k = q + k + q; | ||
| 5098 | + } | ||
| 5099 | + | ||
| 5100 | + key += (first ? "" : ", ") + k; | ||
| 5101 | + setStr += (first ? "" : ", ") + v; // 自定义表达式不好自动处理,后端配置正确即可 gainValue(k, k, v); | ||
| 5102 | + first = false; | ||
| 5093 | 5103 | } | |
| 5104 | + } | ||
| 5105 | + } else { | ||
| 5106 | + throw new IllegalArgumentException("UPSERT_TABLE_MAP 中 " + table + ": value 中 value 不合法,必须是 String 或 Map<String, Object> 类型!"); | ||
| 5107 | + } | ||
| 5108 | + | ||
| 5109 | + if (StringUtil.isEmpty(setStr)) { | ||
| 5110 | + return gainInsertSQL(tablePath, gainColumnString(), gainValuesString()); | ||
| 5111 | + } | ||
| 5112 | + | ||
| 5113 | + String columns = gainColumnString(); | ||
| 5114 | + | ||
| 5115 | + if (isPostgreSQL(database) || isSQLite(database) || isCockroachDB(database) || isTimescaleDB(database)) { | ||
| 5116 | + if (StringUtil.isEmpty(key)) { | ||
| 5117 | + key = q + getIdKey() + q; | ||
| 5118 | + } | ||
| 5119 | + return gainInsertSQL(tablePath, columns, gainValuesString()) + " ON CONFLICT(" + key + ") DO UPDATE SET " + setStr; | ||
| 5120 | + } | ||
| 5121 | + | ||
| 5122 | + boolean isDB2 = isDb2(database); | ||
| 5123 | + boolean isOracle = isDB2 == false && isOracle(database); | ||
| 5124 | + boolean isDameng = isDB2 == false && isOracle == false && isDameng(database); | ||
| 5125 | + if (isDB2 || isOracle || isDameng || isSQLServer(database)) { | ||
| 5126 | + String alias = gainSQLAlias(); | ||
| 5127 | + String idKey = getIdKey(); | ||
| 5128 | + | ||
| 5129 | + String vs; | ||
| 5130 | + String cvs = ""; | ||
| 5094 | 5131 | ||
| 5095 | - cSql = "SELECT " + (config.getCache() == JSONMap.CACHE_RAM ? "SQL_NO_CACHE " : "") | ||
| 5096 | - + column + " FROM " + gainConditionString(tablePath, config) + config.gainLimitString(); | ||
| 5097 | - cSql = buildWithAsExprSql(config, cSql); | ||
| 5098 | - if(config.isElasticsearch()) { // elasticSearch 不支持 explain | ||
| 5099 | - return cSql; | ||
| 5132 | + vs = ""; | ||
| 5133 | + List<String> cols = getColumn(); | ||
| 5134 | + List<List<Object>> valss = cols == null || cols.isEmpty() ? null : getValues(); | ||
| 5135 | + if (valss == null || valss.size() != 1) { | ||
| 5136 | + throw new IllegalArgumentException("POST " + table + " 不合法,UPSERT 模式下 VALUES 必须是 1 条记录!"); | ||
| 5137 | + } | ||
| 5138 | + | ||
| 5139 | + List<Object> vals = valss.get(0); | ||
| 5140 | + boolean first = true; | ||
| 5141 | + for (int i = 0; i < cols.size(); i++) { | ||
| 5142 | + String c = cols.get(i); | ||
| 5143 | + if (c == null) { // FIXME 带上 id 是否有问题? || idKey.equals(c)) { | ||
| 5144 | + continue; | ||
| 5100 | 5145 | } | |
| 5101 | - return explain + cSql; | ||
| 5146 | + | ||
| 5147 | + Object v = vals.get(i); | ||
| 5148 | + String s = gainValue(c, c, v) + (isDB2 ? "" : " " + q + c + q); | ||
| 5149 | + | ||
| 5150 | + vs += (first ? "" : ", ") + s; | ||
| 5151 | + cvs += (first ? "" : ", ") + "excluded." + q + c + q; | ||
| 5152 | + first = false; | ||
| 5153 | + } | ||
| 5154 | + | ||
| 5155 | + if (StringUtil.isEmpty(vs)) { | ||
| 5156 | + throw new IllegalArgumentException("POST " + table + " 不合法,UPSERT 模式下 VALUES 必须是 1 条记录!"); | ||
| 5157 | + } | ||
| 5158 | + | ||
| 5159 | + vs = isDB2 ? "VALUES(" + vs + ")" : "SELECT " + vs + (isOracle || isDameng ? " FROM dual" : ""); | ||
| 5160 | + | ||
| 5161 | + idKey = q + idKey + q; | ||
| 5162 | + return "MERGE INTO " + tablePath + " " + alias + " USING (\n " + vs + "\n) excluded" | ||
| 5163 | + + (isDB2 ? columns : "") + " ON " + alias + "." + idKey + " = excluded." + idKey + " \nWHEN MATCHED THEN \n UPDATE SET " | ||
| 5164 | + + setStr + " \nWHEN NOT MATCHED THEN \n INSERT" + columns + " VALUES(" + cvs + ")"; | ||
| 5165 | + } | ||
| 5166 | + | ||
| 5167 | + return gainInsertSQL(tablePath, columns, gainValuesString()) + " ON DUPLICATE KEY UPDATE " + setStr; | ||
| 5168 | + } | ||
| 5169 | + | ||
| 5170 | + public String gainUpdateSQL() throws Exception { | ||
| 5171 | + String tablePath = gainTablePath(); | ||
| 5172 | + if (isClickHouse()){ | ||
| 5173 | + return "ALTER TABLE " + tablePath + " UPDATE" + gainSetString() + gainWhereString(true); | ||
| 5174 | + } | ||
| 5175 | + | ||
| 5176 | + String cSql = "UPDATE " + tablePath + gainSetString() + gainWhereString(true) | ||
| 5177 | + + (isMySQL() || KingbaseSQLDialect.from(gainSQLDatabase()).supportsDmlLimit() ? gainLimitString() : ""); | ||
| 5178 | + cSql = buildWithAsExprSql(this, cSql); | ||
| 5179 | + return cSql; | ||
| 5180 | + } | ||
| 5181 | + | ||
| 5182 | + public String gainDeleteSQL() throws Exception { | ||
| 5183 | + String tablePath = gainTablePath(); | ||
| 5184 | + if(isClickHouse()){ | ||
| 5185 | + return "ALTER TABLE " + tablePath + " DELETE" + gainWhereString(true); | ||
| 5186 | + } | ||
| 5187 | + | ||
| 5188 | + String cSql = "DELETE FROM " + tablePath + gainWhereString(true) | ||
| 5189 | + + (isMySQL() || KingbaseSQLDialect.from(gainSQLDatabase()).supportsDmlLimit() ? gainLimitString() : ""); // PostgreSQL 不允许 LIMIT | ||
| 5190 | + cSql = buildWithAsExprSql(this, cSql); | ||
| 5191 | + return cSql; | ||
| 5192 | + } | ||
| 5193 | + | ||
| 5194 | + public String gainSelectSQL() throws Exception { | ||
| 5195 | + KingbaseSQLDialect kingbaseDialect = KingbaseSQLDialect.from(gainSQLDatabase()); | ||
| 5196 | + String kingbaseExplain = kingbaseDialect.getExplainPrefix(); | ||
| 5197 | + String explain = isExplain() ? (kingbaseExplain != null ? kingbaseExplain | ||
| 5198 | + : (isSQLServer() ? "SET STATISTICS PROFILE ON " | ||
| 5199 | + : (isOracle() || isDameng() ? "EXPLAIN PLAN FOR " : "EXPLAIN "))) : ""; | ||
| 5200 | + if (isTest() && RequestMethod.isGetMethod(getMethod(), true)) { // FIXME 为啥是 code 而不是 count ? | ||
| 5201 | + String q = getQuote(); // 生成 SELECT ( (24 >=0 AND 24 <3) ) AS `code` LIMIT 1 OFFSET 0 | ||
| 5202 | + return explain + "SELECT " + gainWhereString(false) | ||
| 5203 | + + gainAs() + q + JSONResponse.KEY_COUNT + q + gainLimitString(); | ||
| 5102 | 5204 | } | |
| 5205 | + | ||
| 5206 | + setPreparedValueList(new ArrayList<>()); | ||
| 5207 | + | ||
| 5208 | + String tablePath = gainTablePath(); | ||
| 5209 | + String column = gainColumnString(); | ||
| 5210 | + if (isOracle() || isDameng() || isKingBaseOracle()) { | ||
| 5211 | + //When config's database is oracle,Using subquery since Oracle12 below does not support OFFSET FETCH paging syntax. | ||
| 5212 | + //针对oracle分组后条数的统计 | ||
| 5213 | + if (StringUtil.isNotEmpty(getGroup(),true) && RequestMethod.isHeadMethod(getMethod(), true)){ | ||
| 5214 | + return explain + "SELECT count(*) FROM (SELECT " + (getCache() == JSONMap.CACHE_RAM | ||
| 5215 | + ? "SQL_NO_CACHE " : "") + column + " FROM " + gainConditionString(tablePath, this) + ") " + gainLimitString(); | ||
| 5216 | + } | ||
| 5217 | + | ||
| 5218 | + String sql = "SELECT " + (getCache() == JSONMap.CACHE_RAM | ||
| 5219 | + ? "SQL_NO_CACHE " : "") + column + " FROM " + gainConditionString(tablePath, this); | ||
| 5220 | + return explain + gainOraclePageSQL(sql); | ||
| 5221 | + } | ||
| 5222 | + | ||
| 5223 | + String cSql = "SELECT " + (getCache() == JSONMap.CACHE_RAM ? "SQL_NO_CACHE " : "") | ||
| 5224 | + + column + " FROM " + gainConditionString(tablePath, this) + gainLimitString(); | ||
| 5225 | + cSql = buildWithAsExprSql(this, cSql); | ||
| 5226 | + if(isElasticsearch()) { // elasticSearch 不支持 explain | ||
| 5227 | + return cSql; | ||
| 5228 | + } | ||
| 5229 | + | ||
| 5230 | + return explain + cSql; | ||
| 5103 | 5231 | } | |
| 5104 | 5232 | ||
| 5105 | 5233 | private static <T, M extends Map<String, Object>, L extends List<Object>> String buildWithAsExprSql(@NotNull AbstractSQLConfig<T, M, L> config, String cSql) throws Exception { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -27,9 +27,9 @@ public interface SQLConfig<T, M extends Map<String, Object>, L extends List<Obje | |||
| 27 | 27 | String DATABASE_COCKROACHDB = "COCKROACHDB"; // https://www.cockroachlabs.com | |
| 28 | 28 | String DATABASE_DAMENG = "DAMENG"; // https://www.dameng.com | |
| 29 | 29 | String DATABASE_KINGBASE = "KINGBASE"; // https://www.kingbase.com.cn | |
| 30 | - String DATABASE_KINGBASE_MYSQL = "KINGBASE-MYSQL"; // Kingbase in MySQL compatibility mode | ||
| 31 | - String DATABASE_KINGBASE_ORACLE = "KINGBASE-ORACLE"; // Kingbase in Oracle compatibility mode | ||
| 32 | - String DATABASE_KINGBASE_SQLSERVER = "KINGBASE-SQLSERVER"; // Kingbase in SQL Server compatibility mode | ||
| 30 | + String DATABASE_KINGBASE_MYSQL = "KINGBASE_MYSQL"; // Kingbase in MySQL compatibility mode | ||
| 31 | + String DATABASE_KINGBASE_ORACLE = "KINGBASE_ORACLE"; // Kingbase in Oracle compatibility mode | ||
| 32 | + String DATABASE_KINGBASE_SQLSERVER = "KINGBASE_SQLSERVER"; // Kingbase in SQL Server compatibility mode | ||
| 33 | 33 | String DATABASE_ELASTICSEARCH = "ELASTICSEARCH"; // https://www.elastic.co/guide/en/elasticsearch/reference/7.4/xpack-sql.html | |
| 34 | 34 | String DATABASE_MANTICORE = "MANTICORE"; // https://manticoresearch.com | |
| 35 | 35 | String DATABASE_CLICKHOUSE = "CLICKHOUSE"; // https://clickhouse.com | |
| Back | FazBrowse Home | New Git URL |
0 commit comments