4444import java .net .URLEncoder ;
4545import java .nio .charset .StandardCharsets ;
4646import java .rmi .ServerException ;
47- import java .sql .PreparedStatement ;
48- import java .sql .ResultSet ;
49- import java .sql .ResultSetMetaData ;
50- import java .sql .Statement ;
47+ import java .sql .*;
5148import java .util .*;
5249import java .util .Map .Entry ;
5350import java .util .concurrent .TimeoutException ;
@@ -2315,6 +2312,8 @@ public String execute(@RequestBody String request, HttpSession session) {
23152312 if (StringUtil .isEmpty (database , true ) && StringUtil .isEmpty (uri , true )) {
23162313 throw new NullPointerException ("database 和 uri 不能同时为空!至少其中一个要传有效值!" );
23172314 }
2315+
2316+ String datasource = req .getString ("datasource" );
23182317 String schema = req .getString ("schema" );
23192318
23202319 String account = req .getString ("account" );
@@ -2355,12 +2354,130 @@ public String execute(@RequestBody String request, HttpSession session) {
23552354 }
23562355
23572356 String trimmedSQL = sql .trim ();
2358-
23592357 String sqlPrefix = trimmedSQL .length () < 7 ? "" : trimmedSQL .substring (0 , 7 ).toUpperCase ();
23602358 boolean isInsert = sqlPrefix .startsWith ("INSERT " );
2361- boolean isWrite = isInsert || sqlPrefix .startsWith ("UPDATE " ) || sqlPrefix .startsWith ("DELETE " );
2359+ boolean isEdit = (! isInsert ) && sqlPrefix .startsWith ("UPDATE " );
2360+ boolean isWrite = isInsert || isEdit || sqlPrefix .startsWith ("DELETE " );
2361+
2362+ DemoSQLConfig config = new DemoSQLConfig ();
2363+ config .setDatabase (database );
2364+ config .setDatasource (datasource );
2365+ config .setDBUri (uri );
2366+ config .setSchema (schema );
2367+
23622368 if (WRITE_STRICTLY && isWrite && ! isInsert ) {
2363- // 只包含筛选出 apijson 字段的 Map<String, SortedMap<Integer, Map<String, Object>>> documemtMap = DemoVerifier.DOCUMENT_MAP;
2369+ int fromInd = isEdit ? 0 : findKeyIndex (trimmedSQL , "FROM" );
2370+ if (fromInd < 0 ) {
2371+ throw new IllegalArgumentException ("SQL 缺 FORM 关键词!" );
2372+ }
2373+
2374+ String sqlRest = trimmedSQL .substring (fromInd + (isEdit ? "UPDATE " : " FROM " ).length ()).trim ();
2375+ int blankRest = sqlRest .indexOf (" " );
2376+ if (blankRest < 0 ) {
2377+ throw new IllegalArgumentException ("SQL 缺少 表名!" );
2378+ }
2379+
2380+ String tblPath = sqlRest .substring (0 , blankRest ).trim ();
2381+ sqlRest = sqlRest .substring (blankRest + 1 ).trim ();
2382+ String [] tblArr = tblPath .indexOf ("." ) >= 0 ? tblPath .split ("\\ ." ) : new String []{tblPath }; // StringUtil.split(tblPath.Pattern.quota("."), true);
2383+ int len = tblArr == null ? 0 : tblArr .length ;
2384+ String sch = len < 2 ? null : tblArr [len - 2 ];
2385+ if (StringUtil .isNotEmpty (sch )){
2386+ if ((sch .startsWith ("`" ) && sch .endsWith ("`" )) || (sch .startsWith ("[" ) && sch .endsWith ("]" )) || (sch .startsWith ("\" " ) && sch .endsWith ("\" " ))) {
2387+ sch = sch .substring (1 , sch .length () - 1 );
2388+ }
2389+
2390+ if (StringUtil .isNotEmpty (sch )) {
2391+ if (StringUtil .isNotEmpty (schema ) && ! schema .equals (sch )) {
2392+ throw new ConflictException ("传参 schema: " + schema + " 和 SQL 中" + tblPath + " 里的" + sch + "不一致!" );
2393+ }
2394+
2395+ config .setSchema (sch );
2396+ }
2397+ }
2398+
2399+ String tbl = len < 1 ? null : tblArr [len - 1 ];
2400+ if ((tbl .startsWith ("`" ) && tbl .endsWith ("`" )) || (tbl .startsWith ("[" ) & tbl .endsWith ("]" )) || (tbl .startsWith ("\" " ) && tbl .endsWith ("\" " ))) {
2401+ tbl = tbl .substring (1 , tbl .length () -1 );
2402+ }
2403+
2404+ config .setTable (tbl );
2405+ String idKey = config .getIdKey ();
2406+ String userIdKey = config .getUserIdKey ();
2407+ int whereInd = findKeyIndex (sqlRest , "WHERE" );
2408+ if (whereInd < 0 ) {
2409+ throw new IllegalArgumentException ("SQL 缺少 WHERE 关键词!" );
2410+ }
2411+
2412+ sqlRest = sqlRest .substring (whereInd + " WHERE " .length ()).trim ();
2413+ String key ;
2414+ if (sqlRest .startsWith (idKey )) {
2415+ key = idKey ;
2416+ } else if (sqlRest .startsWith ("`" + idKey + "`" ) || sqlRest .startsWith ("[" + idKey + "]" ) || sqlRest .startsWith ("\" " + idKey + "\" " )) {
2417+ key = "`" + idKey + "`" ;
2418+ } else if (sqlRest .startsWith (userIdKey )) {
2419+ key = userIdKey ;
2420+ } else if (sqlRest .startsWith ("`" + userIdKey + "`" ) || sqlRest .startsWith ("[" + userIdKey + "]" ) || sqlRest .startsWith ("\" " + userIdKey + "\" " )) {
2421+ key = "`" + userIdKey + "`" ;
2422+ } else {
2423+ throw new IllegalArgumentException ("SQL WHERE 后必须接着 " + idKey + " 或 " + idKey + " = ? 或 IN(?,?..) !" );
2424+ }
2425+
2426+ sqlRest = sqlRest .substring (key . length ()).trim ();
2427+ boolean isEq = sqlRest .startsWith ("=" );
2428+ if (! (isEq || sqlRest .startsWith ("IN(" ) || sqlRest .startsWith ("in(" ))) {
2429+ throw new IllegalArgumentException ("SQL WHERE " + key + " 后必须接着 = ? 或 IN(?, ? ...) ! " );
2430+ }
2431+
2432+ sqlRest = sqlRest .substring (isEq ? 1 : "IN(" .length ()).trim ();
2433+ int endInd = isEq && ! sqlRest .startsWith ("'" ) ? -1 : sqlRest .indexOf (isEq ? "'" : ")" , 1 );
2434+ if (isEq && ! sqlRest .startsWith ("'" )) {
2435+ for (int i = 0 ; i < sqlRest .length (); i ++) {
2436+ char c = sqlRest .charAt (i );
2437+ if (c == '+' || c == '-' || (c >= '0' && c <= '9' ) || (c >= 'A' && c <= 'Z' ) || (c >= 'a' && c <= 'z' )) {
2438+ continue ;
2439+ }
2440+
2441+ endInd = i ;
2442+ }
2443+ } else {
2444+ endInd = sqlRest .indexOf (isEq ? "'" : ")" , isEq ? 1 : 0 );
2445+ }
2446+
2447+ sqlRest = endInd < 0 ? sqlRest : sqlRest .substring (endInd + 1 ).trim ();
2448+ if (sqlRest .startsWith ("OR " ) || sqlRest .startsWith ("or " ) || sqlRest .startsWith ("NOT " ) || sqlRest .startsWith ("not " )) {
2449+ throw new IllegalArgumentException ("SQL WHERE " + key + (isEq ? " = val" : " IN(val0, val1 ...)" ) + " 后条件不允许 OR/NOT 连接,必须 AND 连接 ! " );
2450+ }
2451+
2452+ int orInd = findKeyIndex (sqlRest , "OR" );
2453+
2454+ if (orInd >= 0 ) { // TODO 兼容 AND ( OR ) ?
2455+ throw new IllegalArgumentException ("SQL WHERE " + key + (isEq ? " = val" : " IN(val0, val1 ...)" ) + " 后条件不允许 OR 连接,必须 AND 连接 ! " );
2456+ }
2457+
2458+ //int andInd = sqlRest.startsWith("AND ") || sqlRest.startsWith("and ") ? 1 : sqlRest.indexOf(" AND ");
2459+ //if (andInd < 0 && StringUtil.isNotEmpty(sqlRest, true)) {
2460+ // andInd = sqlRest.indexOf(" and ");
2461+ //
2462+ // if (andInd < 0) {
2463+ // //int groupInd = findLastKeyIndex(sqlRest, "GROUP BY");
2464+ // int havingInd = -1;
2465+ // //int orderInd = -1;
2466+ // int limitInd = -1;
2467+ //
2468+ // //if (groupInd < 0) {
2469+ // havingInd = findLastKeyIndex(sqlRest, "HAVING");
2470+ // //orderInd = findLastKeyIndex(sqlRest, "ORDER BY");
2471+ // limitInd = findLastKeyIndex(sqlRest, "LIMIT");
2472+ // //}
2473+ //
2474+ // if (havingInd >= 0 || limitInd >= 0) {
2475+ // throw new IllegalArgumentException("SQL WHERE " + key + (isEq ? " = val" : " IN(val0, val1 ...)") + " 后条件必须 AND 连接 ! ");
2476+ // }
2477+ // }
2478+ //}
2479+
2480+ // 只包含筛选出 apijson 字段的 Map<String, SortedMap<Integer, Map<String, Object>>> documentMap = DemoVerifier.DOCUMENT_MAP;
23642481 Map <String , List <Map <String , Object >>> sqlautoMap = SQLAUTO_MAP ;
23652482 //if (sqlautoMap == null || sqlautoMap.isEmpty()) {
23662483 //
@@ -2385,19 +2502,46 @@ public String execute(@RequestBody String request, HttpSession session) {
23852502 }
23862503 }
23872504
2505+ APIJSONParser <Long > parser = newParser (session , RequestMethod .PUT ); // : DELETE);
2506+ int maxCount = isWrite ? parser .getMaxUpdateCount () : parser .getMaxQueryCount ();
2507+ if (isInsert == false && (config .isMySQL () || config .isMariaDB ())) {
2508+ int limitIndex = findLastKeyIndex (trimmedSQL , "LIMIT" );
2509+
2510+ if (limitIndex < 0 ) {
2511+ trimmedSQL += " LIMIT " + parser .getDefaultQueryCount ();
2512+ } else {
2513+ String sqlRest = trimmedSQL .substring (limitIndex + " LIMIT " .length ()).trim ();
2514+ int ind = sqlRest .indexOf (" " );
2515+ String s = ind < 0 ? sqlRest : sqlRest .substring (0 , ind ).trim ();
2516+ String [] arr = StringUtil .split (s );
2517+
2518+ for (int i = 0 ; i < arr .length ; i ++) {
2519+ String as = arr [i ];
2520+ if ("?" .endsWith (as )) {
2521+ continue ; // FIXME 不知道在第几个 as = arg.getString();
2522+ }
2523+
2524+ try {
2525+ int limit = Integer .parseInt (as );
2526+ if (limit <= 0 || limit > maxCount ) {
2527+ throw new IllegalArgumentException ("SQL LIMIT " + s + " 超出范围 1~" + maxCount + " !" );
2528+ }
2529+ } catch (Throwable e ) {
2530+ throw new IllegalArgumentException ("SQL LIMIT " + s + " 不合法!" + e .getMessage (), e );
2531+ }
2532+ }
2533+ }
2534+ }
2535+
23882536 List <Object > valueList = arg ;
23892537
23902538 DemoSQLExecutor executor = new DemoSQLExecutor ();
2391- DemoSQLConfig config = new DemoSQLConfig ();
23922539
2393- config .setDatabase (database );
2394- config .setSchema (schema );
2395- config .setDBUri (uri );
23962540 config .setDBAccount (account );
23972541 config .setDBPassword (password );
23982542 config .setPrepared (true );
23992543 config .setPreparedValueList (valueList );
2400- config .setSql (sql );
2544+ config .setSql (trimmedSQL );
24012545
24022546 JSONArray arr = null ;
24032547
@@ -2423,11 +2567,22 @@ public String execute(@RequestBody String request, HttpSession session) {
24232567 }
24242568 }
24252569 } else {
2570+ Connection connection = executor .getConnection (config );
24262571 Statement statement = executor .getStatement (config , trimmedSQL );
24272572 if (statement instanceof PreparedStatement ) {
24282573 if (EXECUTE_STRICTLY ) {
24292574 if (isWrite ) {
2430- ((PreparedStatement ) statement ).executeUpdate ();
2575+ try {
2576+ connection .setAutoCommit (false );
2577+ // connection.beginRequest();
2578+ int rows = ((PreparedStatement ) statement ).executeUpdate ();
2579+ if (rows >= maxCount ) {
2580+ throw new UnsupportedOperationException ("实际影响数量超过上限 " + maxCount + " ! 已回滚变更" );
2581+ }
2582+ } catch (Throwable e ) {
2583+ connection .rollback ();
2584+ throw e ;
2585+ }
24312586 } else {
24322587 ((PreparedStatement ) statement ).executeQuery ();
24332588 }
@@ -2441,7 +2596,17 @@ public String execute(@RequestBody String request, HttpSession session) {
24412596
24422597 if (EXECUTE_STRICTLY ) {
24432598 if (isWrite ) {
2444- statement .executeUpdate (sql );
2599+ try {
2600+ connection .setAutoCommit (false );
2601+ // connection.beginRequest();
2602+ int rows = statement .executeUpdate (sql );
2603+ if (rows >= maxCount ) {
2604+ throw new UnsupportedOperationException ("实际影响数量超过上限 " + maxCount + " ! 已回滚变更" );
2605+ }
2606+ } catch (Throwable e ) {
2607+ connection .rollback ();
2608+ throw e ;
2609+ }
24452610 } else {
24462611 statement .executeQuery (sql );
24472612 }
@@ -2519,6 +2684,45 @@ public String execute(@RequestBody String request, HttpSession session) {
25192684
25202685 }
25212686
2687+ // TODO 抽取到 SQL 或 StringUitl
2688+ private static int findKeyIndex (String sql , String key ) {
2689+ return findKeyIndex (sql , key , false , false );
2690+ }
2691+ private static int findLastKeyIndex (String sql , String key ) {
2692+ return findKeyIndex (sql , key , true , false );
2693+ }
2694+
2695+ private static final String [] LEFT_CHARS = new String []{" " , "\n " , ")" };
2696+ private static final String [] RIGHT_CHARS = new String []{" " , "\n " , "(" };
2697+ private static int findKeyIndex (String sql , String key , boolean last , boolean cased ) {
2698+ for (int i = 0 ; i < LEFT_CHARS .length ; i ++) {
2699+ String l = LEFT_CHARS [i ];
2700+ for (int j = 0 ; j < RIGHT_CHARS .length ; j ++) {
2701+ String r = RIGHT_CHARS [j ];
2702+ int ind = last ? sql .lastIndexOf (l + key + r ) : sql .indexOf (l + key + r );
2703+ if (ind >= 0 ) {
2704+ return ind ;
2705+ }
2706+ }
2707+ }
2708+
2709+ if (cased ) {
2710+ return -1 ;
2711+ }
2712+
2713+ String lower = key .toLowerCase ();
2714+ if (! lower .equals (key )) {
2715+ return findKeyIndex (sql , lower , last , true );
2716+ }
2717+
2718+ String upper = key .toUpperCase ();
2719+ if (! upper .equals (key )) {
2720+ return findKeyIndex (sql , upper , last , true );
2721+ }
2722+
2723+ return -1 ;
2724+ }
2725+
25222726
25232727 /**Swagger 文档 Demo,供 APIAuto 测试导入 Swagger 文档到数据库用
25242728 * @return
0 commit comments