@@ -93,6 +93,11 @@ public abstract class AbstractSQLConfig implements SQLConfig {
9393 */
9494 public static boolean IS_HAVING_ALLOW_NOT_FUNCTION = false ;
9595
96+ /**
97+ * 开启 WITH AS 表达式(在支持这种语法的数据库及版本)来简化 SQL 和提升性能
98+ */
99+ public static boolean ENABLE_WITH_AS = false ;
100+
96101 public static int MAX_HAVING_COUNT = 5 ;
97102 public static int MAX_WHERE_COUNT = 10 ;
98103 public static int MAX_COMBINE_DEPTH = 2 ;
@@ -102,7 +107,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
102107
103108 public static String DEFAULT_DATABASE = DATABASE_MYSQL ;
104109 public static String DEFAULT_SCHEMA = "sys" ;
105- public static String PREFFIX_DISTINCT = "DISTINCT " ;
110+ public static String PREFIX_DISTINCT = "DISTINCT " ;
106111
107112 // * 和 / 不能同时出现,防止 /* */ 段注释! # 和 -- 不能出现,防止行注释! ; 不能出现,防止隔断SQL语句!空格不能出现,防止 CRUD,DROP,SHOW TABLES等语句!
108113 private static Pattern PATTERN_RANGE ;
@@ -767,8 +772,8 @@ public abstract class AbstractSQLConfig implements SQLConfig {
767772 }
768773
769774 // mysql8版本以上,子查询支持with as表达式
770- private List <String > withAsExpreSqlList = null ;
771- protected List <Object > withAsExprePreparedValueList = new ArrayList <>();
775+ private List <String > withAsExprSqlList = null ;
776+ protected List <Object > withAsExprPreparedValueList = new ArrayList <>();
772777 private int [] dbVersionNums = null ;
773778 @ Override
774779 public int [] getDBVersionNums () {
@@ -1885,7 +1890,7 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
18851890
18861891 String c = StringUtil .getString (keys );
18871892 c = c + (StringUtil .isEmpty (joinColumn , true ) ? "" : ", " + joinColumn );//不能在这里改,后续还要用到:
1888- return isMain () && isDistinct () ? PREFFIX_DISTINCT + c : c ;
1893+ return isMain () && isDistinct () ? PREFIX_DISTINCT + c : c ;
18891894 default :
18901895 throw new UnsupportedOperationException (
18911896 "服务器内部错误:getColumnString 不支持 " + RequestMethod .getName (getMethod ())
@@ -1966,9 +1971,9 @@ public String parseSQLExpression(String key, String expression, boolean containR
19661971 }
19671972
19681973 String s = expression .substring (start + 1 , end );
1969- boolean distinct = s .startsWith (PREFFIX_DISTINCT );
1974+ boolean distinct = s .startsWith (PREFIX_DISTINCT );
19701975 if (distinct ) {
1971- s = s .substring (PREFFIX_DISTINCT .length ());
1976+ s = s .substring (PREFIX_DISTINCT .length ());
19721977 }
19731978
19741979 // 解析函数内的参数
@@ -1994,7 +1999,7 @@ public String parseSQLExpression(String key, String expression, boolean containR
19941999 + " 中 ?value 必须符合正则表达式 " + PATTERN_RANGE + " 且不包含连续减号 -- 或注释符 /* !不允许多余的空格!" );
19952000 }
19962001
1997- String origin = fun + "(" + (distinct ? PREFFIX_DISTINCT : "" ) + StringUtil .getString (ckeys ) + ")" + suffix ;
2002+ String origin = fun + "(" + (distinct ? PREFIX_DISTINCT : "" ) + StringUtil .getString (ckeys ) + ")" + suffix ;
19982003 expression = origin + (StringUtil .isEmpty (alias , true ) ? "" : " AS " + quote + alias + quote );
19992004 }
20002005 else {
@@ -3985,28 +3990,39 @@ else if (isPresto() || isTrino()) {
39853990 * @throws Exception
39863991 */
39873992 private String withAsExpreSubqueryString (SQLConfig cfg , Subquery subquery ) throws Exception {
3988- if (cfg .getMethod () != RequestMethod .POST && this .withAsExpreSqlList == null ) {
3989- this .setWithAsExpreList ();
3993+ List <String > list = isWithAsEnable () ? getWithAsExprSqlList () : null ;
3994+ if (cfg .getMethod () != RequestMethod .POST && list == null ) {
3995+ clearWithAsExprListIfNeed ();
39903996 }
3997+
3998+ String quote = getQuote ();
3999+
39914000 String withAsExpreSql ;
3992- if ( this . withAsExpreSqlList != null ) {
3993- String withQuoteName = getQuote () + subquery .getKey () + getQuote () ;
3994- this . withAsExpreSqlList .add (" " + withQuoteName + " AS " + "(" + cfg .getSQL (isPrepared ()) + ") " );
4001+ if ( list != null ) {
4002+ String withQuoteName = quote + subquery .getKey () + quote ;
4003+ list .add (" " + withQuoteName + " AS " + "(" + cfg .getSQL (isPrepared ()) + ") " );
39954004 withAsExpreSql = " SELECT * FROM " + withQuoteName ;
39964005
3997- // 预编译参数
4006+ // 预编译参数 FIXME 这里重复添加了,导致子查询都报错参数超过 ? 数量 Parameter index out of range (5 > number of parameters, which is 4)
39984007 List <Object > subPvl = cfg .getPreparedValueList ();
39994008 if (subPvl != null && subPvl .isEmpty () == false ) {
4000- this .withAsExprePreparedValueList .addAll (subPvl );
4009+ List <Object > valueList = getWithAsExprPreparedValueList ();
4010+ if (valueList == null ) {
4011+ valueList = new ArrayList <>();
4012+ }
4013+ valueList .addAll (subPvl );
4014+ setWithAsExprPreparedValueList (valueList );
4015+
40014016 cfg .setPreparedValueList (new ArrayList <>());
40024017 }
4003- }else {
4018+ } else {
40044019 withAsExpreSql = cfg .getSQL (isPrepared ());
40054020 // mysql 才存在这个问题, 主表和子表是一张表
4006- if ( this . isMySQL () && StringUtil .equals (this . getTable (), subquery .getFrom ())) {
4007- withAsExpreSql = " SELECT * FROM (" + withAsExpreSql + ") AS " + getQuote () + subquery .getKey () + getQuote () ;
4021+ if ( isMySQL () && StringUtil .equals (getTable (), subquery .getFrom ())) {
4022+ withAsExpreSql = " SELECT * FROM (" + withAsExpreSql + ") AS " + quote + subquery .getKey () + quote ;
40084023 }
40094024 }
4025+
40104026 return withAsExpreSql ;
40114027 }
40124028
@@ -4024,8 +4040,8 @@ public String getSubqueryString(Subquery subquery) throws Exception {
40244040 cfg .setDatasource (this .getDatasource ());
40254041 }
40264042 cfg .setPreparedValueList (new ArrayList <>());
4027- String withAsExpreSql = withAsExpreSubqueryString (cfg , subquery );
4028- String sql = (range == null || range .isEmpty () ? "" : range ) + "(" + withAsExpreSql + ") " ;
4043+ String withAsExprSql = withAsExpreSubqueryString (cfg , subquery );
4044+ String sql = (range == null || range .isEmpty () ? "" : range ) + "(" + withAsExprSql + ") " ;
40294045
40304046 //// SELECT .. FROM(SELECT ..) .. WHERE .. 格式需要把子查询中的预编译值提前
40314047 //// 如果外查询 SELECT concat(`name`,?) 这种 SELECT 里也有预编译值,那就不能这样简单反向
@@ -4236,75 +4252,77 @@ public static String getSQL(AbstractSQLConfig config) throws Exception {
42364252 return null ;
42374253 }
42384254
4255+ config .setPreparedValueList (new ArrayList <>()); // 解决重复添加导致报错:Parameter index out of range (6 > number of parameters, which is 5)
42394256 String cSql = null ;
42404257 switch (config .getMethod ()) {
4241- case POST :
4242- return "INSERT INTO " + tablePath + config .getColumnString () + " VALUES" + config .getValuesString ();
4243- case PUT :
4244- if (config .isClickHouse ()){
4245- return "ALTER TABLE " + tablePath + " UPDATE" + config .getSetString () + config .getWhereString (true );
4246- }
4247- cSql = "UPDATE " + tablePath + config .getSetString () + config .getWhereString (true ) + (config .isMySQL () ? config .getLimitString () : "" );
4248- cSql = buildWithAsExpreSql (config , cSql );
4249- return cSql ;
4250- case DELETE :
4251- if (config .isClickHouse ()){
4252- return "ALTER TABLE " + tablePath + " DELETE" + config .getWhereString (true );
4253- }
4254- cSql = "DELETE FROM " + tablePath + config .getWhereString (true ) + (config .isMySQL () ? config .getLimitString () : "" ); // PostgreSQL 不允许 LIMIT
4255- cSql = buildWithAsExpreSql (config , cSql );
4256- return cSql ;
4257- default :
4258- String explain = config .isExplain () ? (config .isSQLServer () ? "SET STATISTICS PROFILE ON " : (config .isOracle () || config .isDameng () || config .isKingBase () ? "EXPLAIN PLAN FOR " : "EXPLAIN " )) : "" ;
4259- if (config .isTest () && RequestMethod .isGetMethod (config .getMethod (), true )) { // FIXME 为啥是 code 而不是 count ?
4260- String q = config .getQuote (); // 生成 SELECT ( (24 >=0 AND 24 <3) ) AS `code` LIMIT 1 OFFSET 0
4261- return explain + "SELECT " + config .getWhereString (false ) + " AS " + q + JSONResponse .KEY_COUNT + q + config .getLimitString ();
4262- }
4263-
4264- config .setPreparedValueList (new ArrayList <Object >());
4265- String column = config .getColumnString ();
4266- if (config .isOracle () || config .isDameng () || config .isKingBase ()) {
4267- //When config's database is oracle,Using subquery since Oracle12 below does not support OFFSET FETCH paging syntax.
4268- //针对oracle分组后条数的统计
4269- if (StringUtil .isNotEmpty (config .getGroup (),true ) && RequestMethod .isHeadMethod (config .getMethod (), true )){
4270- return explain + "SELECT count(*) FROM (SELECT " + (config .getCache () == JSONRequest .CACHE_RAM ? "SQL_NO_CACHE " : "" ) + column + " FROM " + getConditionString (tablePath , config ) + ") " + config .getLimitString ();
4258+ case POST :
4259+ return "INSERT INTO " + tablePath + config .getColumnString () + " VALUES" + config .getValuesString ();
4260+ case PUT :
4261+ if (config .isClickHouse ()){
4262+ return "ALTER TABLE " + tablePath + " UPDATE" + config .getSetString () + config .getWhereString (true );
4263+ }
4264+ cSql = "UPDATE " + tablePath + config .getSetString () + config .getWhereString (true ) + (config .isMySQL () ? config .getLimitString () : "" );
4265+ cSql = buildWithAsExprSql (config , cSql );
4266+ return cSql ;
4267+ case DELETE :
4268+ if (config .isClickHouse ()){
4269+ return "ALTER TABLE " + tablePath + " DELETE" + config .getWhereString (true );
4270+ }
4271+ cSql = "DELETE FROM " + tablePath + config .getWhereString (true ) + (config .isMySQL () ? config .getLimitString () : "" ); // PostgreSQL 不允许 LIMIT
4272+ cSql = buildWithAsExprSql (config , cSql );
4273+ return cSql ;
4274+ default :
4275+ String explain = config .isExplain () ? (config .isSQLServer () ? "SET STATISTICS PROFILE ON " : (config .isOracle () || config .isDameng () || config .isKingBase () ? "EXPLAIN PLAN FOR " : "EXPLAIN " )) : "" ;
4276+ if (config .isTest () && RequestMethod .isGetMethod (config .getMethod (), true )) { // FIXME 为啥是 code 而不是 count ?
4277+ String q = config .getQuote (); // 生成 SELECT ( (24 >=0 AND 24 <3) ) AS `code` LIMIT 1 OFFSET 0
4278+ return explain + "SELECT " + config .getWhereString (false ) + " AS " + q + JSONResponse .KEY_COUNT + q + config .getLimitString ();
42714279 }
42724280
4273- String sql = "SELECT " + (config .getCache () == JSONRequest .CACHE_RAM ? "SQL_NO_CACHE " : "" ) + column + " FROM " + getConditionString (tablePath , config );
4274- return explain + config .getOraclePageSql (sql );
4275- }
4281+ config .setPreparedValueList (new ArrayList <Object >());
4282+ String column = config .getColumnString ();
4283+ if (config .isOracle () || config .isDameng () || config .isKingBase ()) {
4284+ //When config's database is oracle,Using subquery since Oracle12 below does not support OFFSET FETCH paging syntax.
4285+ //针对oracle分组后条数的统计
4286+ if (StringUtil .isNotEmpty (config .getGroup (),true ) && RequestMethod .isHeadMethod (config .getMethod (), true )){
4287+ return explain + "SELECT count(*) FROM (SELECT " + (config .getCache () == JSONRequest .CACHE_RAM ? "SQL_NO_CACHE " : "" ) + column + " FROM " + getConditionString (tablePath , config ) + ") " + config .getLimitString ();
4288+ }
42764289
4277- cSql = "SELECT " + (config .getCache () == JSONRequest .CACHE_RAM ? "SQL_NO_CACHE " : "" ) + column + " FROM " + getConditionString (tablePath , config ) + config .getLimitString ();
4278- cSql = buildWithAsExpreSql (config , cSql );
4279- if (config .isElasticsearch ()) { // elasticSearch 不支持 explain
4280- return cSql ;
4281- }
4282- return explain + cSql ;
4290+ String sql = "SELECT " + (config .getCache () == JSONRequest .CACHE_RAM ? "SQL_NO_CACHE " : "" ) + column + " FROM " + getConditionString (tablePath , config );
4291+ return explain + config .getOraclePageSql (sql );
4292+ }
4293+
4294+ cSql = "SELECT " + (config .getCache () == JSONRequest .CACHE_RAM ? "SQL_NO_CACHE " : "" ) + column + " FROM " + getConditionString (tablePath , config ) + config .getLimitString ();
4295+ cSql = buildWithAsExprSql (config , cSql );
4296+ if (config .isElasticsearch ()) { // elasticSearch 不支持 explain
4297+ return cSql ;
4298+ }
4299+ return explain + cSql ;
42834300 }
42844301 }
42854302
4286- private static String buildWithAsExpreSql (@ NotNull AbstractSQLConfig config , String cSql ) throws Exception {
4287- if (config .withAsExpreSqlList != null && config .withAsExpreSqlList .size () > 0 ) {
4303+ private static String buildWithAsExprSql (@ NotNull AbstractSQLConfig config , String cSql ) throws Exception {
4304+ if (config .isWithAsEnable () == false ) {
4305+ return cSql ;
4306+ }
4307+
4308+ List <String > list = config .getWithAsExprSqlList ();
4309+ int size = list == null ? 0 : list .size ();
4310+ if (size > 0 ) {
42884311 String withAsExpreSql = "WITH " ;
4289- // 只有一条
4290- if (config .withAsExpreSqlList .size () == 1 ) {
4291- withAsExpreSql += config .withAsExpreSqlList .get (0 ) + "\n " + cSql ;
4292- } else {
4293- int lastIndex = config .withAsExpreSqlList .size () - 1 ;
4294- for (int i = 0 ; i < config .withAsExpreSqlList .size (); i ++) {
4295- if (i == lastIndex ) {
4296- withAsExpreSql += config .withAsExpreSqlList .get (i ) + "\n " + cSql ;
4297- } else {
4298- withAsExpreSql += config .withAsExpreSqlList .get (i ) + ",\n " ;
4299- }
4300- }
4312+ for (int i = 0 ; i < size ; i ++) {
4313+ withAsExpreSql += (i <= 0 ? "" : "," ) + list .get (i ) + "\n " ;
43014314 }
4302- cSql = withAsExpreSql ;
4303- config .setWithAsExpreList ();
4315+ cSql = withAsExpreSql + cSql ;
4316+ config .clearWithAsExprListIfNeed ();
43044317 }
43054318 return cSql ;
43064319 }
43074320
4321+ @ Override
4322+ public boolean isWithAsEnable () {
4323+ return ENABLE_WITH_AS && (isMySQL () == false || getDBVersionNums ()[0 ] >= 8 );
4324+ }
4325+
43084326 /**Oracle的分页获取
43094327 * @param sql
43104328 * @return
@@ -5197,9 +5215,9 @@ else if (w.startsWith("!")) {
51975215 }
51985216 }
51995217
5200- boolean distinct = column == null || rawColumnSQL != null ? false : column .startsWith (PREFFIX_DISTINCT );
5218+ boolean distinct = column == null || rawColumnSQL != null ? false : column .startsWith (PREFIX_DISTINCT );
52015219 if (rawColumnSQL == null ) {
5202- String [] fks = StringUtil .split (distinct ? column .substring (PREFFIX_DISTINCT .length ()) : column , ";" ); // key0,key1;fun0(key0,...);fun1(key0,...);key3;fun2(key0,...)
5220+ String [] fks = StringUtil .split (distinct ? column .substring (PREFIX_DISTINCT .length ()) : column , ";" ); // key0,key1;fun0(key0,...);fun1(key0,...);key3;fun2(key0,...)
52035221 if (fks != null ) {
52045222 String [] ks ;
52055223 for (String fk : fks ) {
@@ -5698,21 +5716,25 @@ private static boolean keyInCombineExpr(String combineExpr, String key) {
56985716 }
56995717 return false ;
57005718 }
5701-
5702- private void setWithAsExpreList () {
5719+
5720+
5721+ public List <String > getWithAsExprSqlList () {
5722+ return withAsExprSqlList ;
5723+ }
5724+ private void clearWithAsExprListIfNeed () {
57035725 // mysql8版本以上,子查询支持with as表达式
57045726 if (this .isMySQL () && this .getDBVersionNums ()[0 ] >= 8 ) {
5705- this .withAsExpreSqlList = new ArrayList <>();
5727+ this .withAsExprSqlList = new ArrayList <>();
57065728 }
57075729 }
57085730
57095731 @ Override
5710- public List <Object > getWithAsExprePreparedValueList () {
5711- return this .withAsExprePreparedValueList ;
5732+ public List <Object > getWithAsExprPreparedValueList () {
5733+ return this .withAsExprPreparedValueList ;
57125734 }
57135735
57145736 @ Override
5715- public void setWithAsExprePreparedValueList (List <Object > list ) {
5716- this .withAsExprePreparedValueList = list ;
5737+ public void setWithAsExprPreparedValueList (List <Object > list ) {
5738+ this .withAsExprPreparedValueList = list ;
57175739 }
57185740}
0 commit comments