新增数组关键词 compat 解决对聚合函数字段通过 query:2 分页查总数返回值错误

This commit is contained in:
TommyLemon 2022-03-14 01:55:32 +08:00
parent 38c19975ea
commit c39cd1ec7c
5 changed files with 120 additions and 34 deletions

View File

@ -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<String>();
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);

View File

@ -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");
}
}

View File

@ -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<T> implements Parser<T>, ParserCreator<T>,
//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<T> implements Parser<T>, ParserCreator<T>,
//不能改变因为后面可能继续用到导致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<T> implements Parser<T>, ParserCreator<T>,
}
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<T> implements Parser<T>, ParserCreator<T>,
.setCount(size)
.setPage(page2)
.setQuery(query2)
.setCompat(compat)
.setTable(arrTableKey)
.setJoinList(onJoinParse(join, request));
@ -1301,6 +1329,7 @@ public abstract class AbstractParser<T> implements Parser<T>, ParserCreator<T>,
} 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);

View File

@ -104,6 +104,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
// 自定义原始 SQL 片段 Map<key, substring> substring null 时忽略 substring "" 时整个 value raw SQL其它情况则只是 substring 这段为 raw SQL
public static final Map<String, String> RAW_MAP;
// 允许调用的 SQL 函数 substring null 时忽略 substring "" 时整个 value raw SQL其它情况则只是 substring 这段为 raw SQL
public static final Map<String, String> SQL_AGGREGATE_FUNCTION_MAP;
public static final Map<String, String> 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<String> 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<String> 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<On> 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 !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
}

View File

@ -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);