From c39cd1ec7c5b568cfbeac19e82e4688a9c26a679 Mon Sep 17 00:00:00 2001 From: TommyLemon <1184482681@qq.com> Date: Mon, 14 Mar 2022 01:55:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=95=B0=E7=BB=84=E5=85=B3?= =?UTF-8?q?=E9=94=AE=E8=AF=8D=20compat=20=E8=A7=A3=E5=86=B3=E5=AF=B9?= =?UTF-8?q?=E8=81=9A=E5=90=88=E5=87=BD=E6=95=B0=E5=AD=97=E6=AE=B5=E9=80=9A?= =?UTF-8?q?=E8=BF=87=20query:2=20=E5=88=86=E9=A1=B5=E6=9F=A5=E6=80=BB?= =?UTF-8?q?=E6=95=B0=E8=BF=94=E5=9B=9E=E5=80=BC=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/apijson/JSONRequest.java | 2 + APIJSONORM/src/main/java/apijson/SQL.java | 33 ++++---- .../main/java/apijson/orm/AbstractParser.java | 35 +++++++- .../java/apijson/orm/AbstractSQLConfig.java | 81 +++++++++++++++---- .../src/main/java/apijson/orm/SQLConfig.java | 3 + 5 files changed, 120 insertions(+), 34 deletions(-) diff --git a/APIJSONORM/src/main/java/apijson/JSONRequest.java b/APIJSONORM/src/main/java/apijson/JSONRequest.java index 5b49f608..8707cc2c 100755 --- a/APIJSONORM/src/main/java/apijson/JSONRequest.java +++ b/APIJSONORM/src/main/java/apijson/JSONRequest.java @@ -87,6 +87,7 @@ public class JSONRequest extends JSONObject { public static final String SUBQUERY_RANGE_ANY = "ANY"; public static final String KEY_QUERY = "query"; + public static final String KEY_COMPAT = "compat"; public static final String KEY_COUNT = "count"; public static final String KEY_PAGE = "page"; public static final String KEY_JOIN = "join"; @@ -97,6 +98,7 @@ public class JSONRequest extends JSONObject { static { ARRAY_KEY_LIST = new ArrayList(); ARRAY_KEY_LIST.add(KEY_QUERY); + ARRAY_KEY_LIST.add(KEY_COMPAT); ARRAY_KEY_LIST.add(KEY_COUNT); ARRAY_KEY_LIST.add(KEY_PAGE); ARRAY_KEY_LIST.add(KEY_JOIN); diff --git a/APIJSONORM/src/main/java/apijson/SQL.java b/APIJSONORM/src/main/java/apijson/SQL.java index 4f2a1ccb..391d5db4 100755 --- a/APIJSONORM/src/main/java/apijson/SQL.java +++ b/APIJSONORM/src/main/java/apijson/SQL.java @@ -21,7 +21,7 @@ public class SQL { public static final String IS_NOT = " IS NOT "; public static final String IS_NULL = " IS NULL "; public static final String IS_NOT_NULL = " IS NOT NULL "; - + //括号必须紧跟函数名! count (...) 报错! public static final String COUNT = "count"; public static final String SUM = "sum"; @@ -191,7 +191,7 @@ public class SQL { public static String replace(String s, String c1, String c2) { return "replace(" + s + ", " + c1 + ", " + c2 + ")"; } - + /** * @param s1 * @param s2 @@ -225,11 +225,11 @@ public class SQL { return "lower(" + s + ")"; } - - + + //column and function<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - + /**字段 * @param column * @return column.isEmpty() ? "*" : column; @@ -245,15 +245,16 @@ public class SQL { public static String columnAs(String column) { return count(column) + AS; } - + /**函数 * @param column if (StringUtil.isEmpty(column, true) || column.contains(",")) -> column = null; * @return " " + fun + "(" + {@link #column(String)} + ") "; */ public static String function(String fun, String column) { - if (StringUtil.isEmpty(column, true) || column.contains(",")) { - column = null; //解决 count(id,name) 这种多个字段导致的SQL异常 - } + // 支持 fun(col1,col2..) + // if (StringUtil.isEmpty(column, true) || column.contains(",")) { + // column = null; //解决 count(id,name) 这种多个字段导致的SQL异常 + // } return " " + fun + "(" + column(column) + ") "; } /**有别名的函数 @@ -263,7 +264,7 @@ public class SQL { public static String functionAs(String fun, String column) { return function(fun, column) + AS + fun + " "; } - + /**计数 * column = null * @return {@link #count(String)} @@ -313,9 +314,9 @@ public class SQL { } //column and function>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - - - + + + //search<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< public static final int SEARCH_TYPE_CONTAIN_FULL = 0; @@ -391,13 +392,13 @@ public class SQL { //search>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - + public static boolean isBooleanOrNumber(String type) { type = StringUtil.toUpperCase(type, true); return type.isEmpty() || (type.endsWith("INT") && type.endsWith("POINT") == false) || type.endsWith("BOOLEAN") || type.endsWith("ENUM") || type.endsWith("FLOAT") || type.endsWith("DOUBLE") || type.endsWith("DECIMAL"); } - - + + } diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java index 73341404..2d0f7748 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java @@ -42,6 +42,7 @@ import apijson.JSONResponse; import apijson.Log; import apijson.NotNull; import apijson.RequestMethod; +import apijson.SQL; import apijson.StringUtil; import apijson.orm.exception.ConditionErrorException; import apijson.orm.exception.ConflictException; @@ -1051,9 +1052,33 @@ public abstract class AbstractParser implements Parser, ParserCreator, //total 这里不能用arrayConfig.getType(),因为在createObjectParser.onChildParse传到onObjectParse时已被改掉 if (type == SQLConfig.TYPE_ITEM_CHILD_0 && query != JSONRequest.QUERY_TABLE && position == 0) { - RequestMethod method = op.getMethod(); - JSONObject rp = op.setMethod(RequestMethod.HEAD).setSQLConfig().executeSQL().getSqlReponse(); - op.setMethod(method); + JSONObject rp; + Boolean compat = arrayConfig.getCompat(); + if (compat != null && compat) { + // 解决对聚合函数字段通过 query:2 分页查总数返回值错误 + // 这里可能改变了内部的一些数据,下方通过 arrayConfig 还原 + SQLConfig cfg = op.setSQLConfig(0, 0, 0).getSQLConfig(); + boolean isExplain = cfg.isExplain(); + cfg.setExplain(false); + + Subquery subq = new Subquery(); + subq.setFrom(cfg.getTable()); + subq.setConfig(cfg); + + SQLConfig countSQLCfg = createSQLConfig(); + countSQLCfg.setColumn(Arrays.asList("count(*):count")); + countSQLCfg.setFrom(subq); + + rp = executeSQL(countSQLCfg, false); + + cfg.setExplain(isExplain); + } + else { + // 对聚合函数字段通过 query:2 分页查总数返回值错误 + RequestMethod method = op.getMethod(); + rp = op.setMethod(RequestMethod.HEAD).setSQLConfig().executeSQL().getSqlReponse(); + op.setMethod(method); + } if (rp != null) { int index = parentPath.lastIndexOf("]/"); @@ -1147,6 +1172,7 @@ public abstract class AbstractParser implements Parser, ParserCreator, //不能改变,因为后面可能继续用到,导致1以上都改变 []:{0:{Comment[]:{0:{Comment:{}},1:{...},...}},1:{...},...} final String query = request.getString(JSONRequest.KEY_QUERY); + final Boolean compat = request.getBoolean(JSONRequest.KEY_COMPAT); final Integer count = request.getInteger(JSONRequest.KEY_COUNT); //TODO 如果不想用默认数量可以改成 getIntValue(JSONRequest.KEY_COUNT); final Integer page = request.getInteger(JSONRequest.KEY_PAGE); final Object join = request.get(JSONRequest.KEY_JOIN); @@ -1189,6 +1215,7 @@ public abstract class AbstractParser implements Parser, ParserCreator, } request.remove(JSONRequest.KEY_QUERY); + request.remove(JSONRequest.KEY_COMPAT); request.remove(JSONRequest.KEY_COUNT); request.remove(JSONRequest.KEY_PAGE); request.remove(JSONRequest.KEY_JOIN); @@ -1227,6 +1254,7 @@ public abstract class AbstractParser implements Parser, ParserCreator, .setCount(size) .setPage(page2) .setQuery(query2) + .setCompat(compat) .setTable(arrTableKey) .setJoinList(onJoinParse(join, request)); @@ -1301,6 +1329,7 @@ public abstract class AbstractParser implements Parser, ParserCreator, } finally { //后面还可能用到,要还原 request.put(JSONRequest.KEY_QUERY, query); + request.put(JSONRequest.KEY_COMPAT, compat); request.put(JSONRequest.KEY_COUNT, count); request.put(JSONRequest.KEY_PAGE, page); request.put(JSONRequest.KEY_JOIN, join); diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java index 15482a88..2b5f1760 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java @@ -104,6 +104,7 @@ public abstract class AbstractSQLConfig implements SQLConfig { // 自定义原始 SQL 片段 Map:当 substring 为 null 时忽略;当 substring 为 "" 时整个 value 是 raw SQL;其它情况则只是 substring 这段为 raw SQL public static final Map RAW_MAP; // 允许调用的 SQL 函数:当 substring 为 null 时忽略;当 substring 为 "" 时整个 value 是 raw SQL;其它情况则只是 substring 这段为 raw SQL + public static final Map SQL_AGGREGATE_FUNCTION_MAP; public static final Map SQL_FUNCTION_MAP; @@ -242,6 +243,13 @@ public abstract class AbstractSQLConfig implements SQLConfig { + SQL_AGGREGATE_FUNCTION_MAP = new LinkedHashMap<>(); // 保证顺序,避免配置冲突等意外情况 + SQL_AGGREGATE_FUNCTION_MAP.put("max", ""); + SQL_AGGREGATE_FUNCTION_MAP.put("min", ""); + SQL_AGGREGATE_FUNCTION_MAP.put("avg", ""); + SQL_AGGREGATE_FUNCTION_MAP.put("count", ""); + SQL_AGGREGATE_FUNCTION_MAP.put("sum", ""); + SQL_FUNCTION_MAP = new LinkedHashMap<>(); // 保证顺序,避免配置冲突等意外情况 //窗口函数 @@ -761,6 +769,7 @@ public abstract class AbstractSQLConfig implements SQLConfig { private int page; //Table所在页码 private int position; //Table在[]中的位置 private int query; //JSONRequest.query + private Boolean compat; //JSONRequest.compat query total private int type; //ObjectParser.type private int cache; private boolean explain; @@ -1458,14 +1467,9 @@ public abstract class AbstractSQLConfig implements SQLConfig { case HEAD: case HEADS: //StringUtil.isEmpty(column, true) || column.contains(",") 时SQL.count(column)会return "*" if (isPrepared() && column != null) { - List raw = getRaw(); boolean containRaw = raw != null && raw.contains(KEY_COLUMN); - - String origin; - String alias; - int index; - + for (String c : column) { if (containRaw) { // 由于 HashMap 对 key 做了 hash 处理,所以 get 比 containsValue 更快 @@ -1476,9 +1480,9 @@ public abstract class AbstractSQLConfig implements SQLConfig { } } - index = c.lastIndexOf(":"); //StringUtil.split返回数组中,子项不会有null - origin = index < 0 ? c : c.substring(0, index); - alias = index < 0 ? null : c.substring(index + 1); + int index = c.lastIndexOf(":"); //StringUtil.split返回数组中,子项不会有null + String origin = index < 0 ? c : c.substring(0, index); + String alias = index < 0 ? null : c.substring(index + 1); if (alias != null && StringUtil.isName(alias) == false) { throw new IllegalArgumentException("HEAD请求: 字符 " + alias + " 不合法!预编译模式下 @column:value 中 value里面用 , 分割的每一项" @@ -1499,8 +1503,41 @@ public abstract class AbstractSQLConfig implements SQLConfig { } } } + + boolean onlyOne = column != null && column.size() == 1; + String c0 = onlyOne ? column.get(0) : null; + + if (onlyOne) { + int index = c0 == null ? -1 : c0.lastIndexOf(":"); + if (index > 0) { + c0 = c0.substring(0, index); + } - return SQL.count(column != null && column.size() == 1 && StringUtil.isName(column.get(0)) ? getKey(column.get(0)) : "*"); + int start = c0 == null ? -1 : c0.indexOf("("); + int end = start <= 0 ? -1 : c0.lastIndexOf(")"); + if (start > 0 && end > start) { + String fun = c0.substring(0, start); + + // Invalid use of group function SELECT count(max(`id`)) AS count FROM `sys`.`Comment` + if (SQL_AGGREGATE_FUNCTION_MAP.containsKey(fun)) { + String group = getGroup(); // TODO 唯一 100% 兼容的可能只有 SELECT count(*) FROM (原语句) AS table + return StringUtil.isEmpty(group, true) ? "1" : "count(DISTINCT " + group + ")"; + } + + String[] args = start == end - 1 ? null : StringUtil.split(c0.substring(start + 1, end)); + if (args == null || args.length <= 0) { + return SQL.count(c0); + } + + List raw = getRaw(); + boolean containRaw = raw != null && raw.contains(KEY_COLUMN); + + return SQL.count(parseColumn(c0, containRaw)); + } + } + + return SQL.count(onlyOne ? getKey(c0) : "*"); + // return SQL.count(onlyOne && StringUtil.isName(column.get(0)) ? getKey(column.get(0)) : "*"); case POST: if (column == null || column.isEmpty()) { throw new IllegalArgumentException("POST 请求必须在Table内设置要保存的 key:value !"); @@ -1997,6 +2034,16 @@ public abstract class AbstractSQLConfig implements SQLConfig { this.query = query; return this; } + @Override + public Boolean getCompat() { + return compat; + } + @Override + public AbstractSQLConfig setCompat(Boolean compat) { + this.compat = compat; + return this; + } + @Override public int getType() { return type; @@ -3753,14 +3800,13 @@ public abstract class AbstractSQLConfig implements SQLConfig { //根据方法不同,聚合语句不同。GROUP BY 和 HAVING 可以加在 HEAD 上, HAVING 可以加在 PUT, DELETE 上,GET 全加,POST 全都不加 String aggregation = ""; - if (RequestMethod.isGetMethod(config.getMethod(), true)){ - aggregation = config.getGroupString(true) + config.getHavingString(true) + - config.getOrderString(true); + if (RequestMethod.isGetMethod(config.getMethod(), true)) { + aggregation = config.getGroupString(true) + config.getHavingString(true) + config.getOrderString(true); } - if (RequestMethod.isHeadMethod(config.getMethod(), true)){ + if (RequestMethod.isHeadMethod(config.getMethod(), true)) { // TODO 加参数 isPagenation 判断是 GET 内分页 query:2 查总数,不用加这些条件 aggregation = config.getGroupString(true) + config.getHavingString(true) ; } - if (config.getMethod() == PUT || config.getMethod() == DELETE){ + if (config.getMethod() == PUT || config.getMethod() == DELETE) { aggregation = config.getHavingString(true) ; } @@ -3825,6 +3871,8 @@ public abstract class AbstractSQLConfig implements SQLConfig { // 主表不用别名 String ta; for (Join j : joinList) { + onGetJoinString(j); + if (j.isAppJoin()) { // APP JOIN,只是作为一个标记,执行完主表的查询后自动执行副表的查询 User.id IN($commentIdList) continue; } @@ -4089,6 +4137,9 @@ public abstract class AbstractSQLConfig implements SQLConfig { protected void onJoinComplextRelation(String sql, String quote, Join j, String jt, List onList, On on) { throw new UnsupportedOperationException("JOIN 已禁用 $, ~, {}, <>, >, <, >=, <= 等复杂关联 !性能很差、需求极少,默认只允许 = 等价关联,如要取消禁用可在后端重写相关方法!"); } + + protected void onGetJoinString(Join j) throws UnsupportedOperationException { + } protected void onGetCrossJoinString(Join j) throws UnsupportedOperationException { throw new UnsupportedOperationException("已禁用 * CROSS JOIN !性能很差、需求极少,如要取消禁用可在后端重写相关方法!"); } diff --git a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java index 25978877..51a8df36 100755 --- a/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java +++ b/APIJSONORM/src/main/java/apijson/orm/SQLConfig.java @@ -98,6 +98,9 @@ public interface SQLConfig { int getQuery(); SQLConfig setQuery(int query); + Boolean getCompat(); + SQLConfig setCompat(Boolean compat); + int getPosition(); SQLConfig setPosition(int position);