1 /* 2 * Copyright 2015-2018 HuntLabs.cn 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 module hunt.sql.visitor.ParameterizedOutputVisitorUtils; 17 18 import hunt.collection; 19 20 import hunt.sql.SQLUtils; 21 import hunt.sql.ast; 22 import hunt.sql.ast.statement; 23 // import hunt.sql.dialect.db2.visitor.DB2OutputVisitor; 24 import hunt.sql.dialect.mysql.ast.statement.MySqlInsertStatement; 25 import hunt.sql.dialect.mysql.visitor.MySqlASTVisitor; 26 import hunt.sql.dialect.mysql.visitor.MySqlOutputVisitor; 27 // import hunt.sql.dialect.oracle.visitor.OracleParameterizedOutputVisitor; 28 // import hunt.sql.dialect.phoenix.visitor.PhoenixOutputVisitor; 29 import hunt.sql.dialect.postgresql.visitor.PGOutputVisitor; 30 // import hunt.sql.dialect.sqlserver.visitor.SQLServerOutputVisitor; 31 import hunt.sql.parser; 32 import hunt.sql.util.FnvHash; 33 import hunt.sql.util.DBType; 34 import hunt.sql.visitor.ParameterizedVisitor; 35 import hunt.sql.visitor.VisitorFeature; 36 import hunt.String; 37 import hunt.sql.visitor.SQLASTOutputVisitor; 38 import hunt.util.Appendable; 39 import hunt.util.Common; 40 import hunt.text; 41 42 public class ParameterizedOutputVisitorUtils { 43 private static SQLParserFeature[] defaultFeatures = [ 44 SQLParserFeature.EnableSQLBinaryOpExprGroup, 45 SQLParserFeature.UseInsertColumnsCache, 46 SQLParserFeature.OptimizedForParameterized 47 ]; 48 49 private static SQLParserFeature[] defaultFeatures2 = [ 50 SQLParserFeature.EnableSQLBinaryOpExprGroup, 51 SQLParserFeature.UseInsertColumnsCache, 52 SQLParserFeature.OptimizedForParameterized, 53 SQLParserFeature.OptimizedForForParameterizedSkipValue, 54 ]; 55 56 public static string parameterize(string sql, string dbType) { 57 return parameterize(sql, dbType, null, null,null); 58 } 59 60 public static string parameterize(string sql 61 , string dbType 62 , SQLSelectListCache selectListCache) { 63 return parameterize(sql, dbType, selectListCache, null); 64 } 65 66 public static string parameterize(string sql 67 , string dbType 68 , List!(Object) outParameters) { 69 return parameterize(sql, dbType, null, outParameters); 70 } 71 72 73 private static void configVisitorFeatures(ParameterizedVisitor visitor, VisitorFeature[] features...) { 74 if(features !is null) { 75 for (int i = 0; i < features.length; i++) { 76 visitor.config(features[i], true); 77 } 78 } 79 } 80 81 public static string parameterize(string sql 82 , string dbType 83 , List!(Object) outParameters, VisitorFeature[] features...) { 84 return parameterize(sql, dbType, null, outParameters, features); 85 } 86 87 public static string parameterize(string sql 88 , string dbType 89 , SQLSelectListCache selectListCache, List!(Object) outParameters, VisitorFeature[] visitorFeatures...) { 90 91 SQLParserFeature[] features = outParameters is null 92 ? defaultFeatures2 93 : defaultFeatures; 94 95 SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType, features); 96 97 if (selectListCache !is null) { 98 parser.setSelectListCache(selectListCache); 99 } 100 101 List!(SQLStatement) statementList = parser.parseStatementList(); 102 if (statementList.size() == 0) { 103 return sql; 104 } 105 106 StringBuilder out_p = new StringBuilder(sql.length); 107 ParameterizedVisitor visitor = createParameterizedOutputVisitor(out_p, dbType); 108 if (outParameters !is null) { 109 visitor.setOutputParameters(outParameters); 110 } 111 configVisitorFeatures(visitor, visitorFeatures); 112 113 for (int i = 0; i < statementList.size(); i++) { 114 SQLStatement stmt = statementList.get(i); 115 116 if (i > 0) { 117 SQLStatement preStmt = statementList.get(i - 1); 118 119 if (typeid(preStmt) == typeid(stmt)) { 120 StringBuilder buf = new StringBuilder(); 121 ParameterizedVisitor v1 = createParameterizedOutputVisitor(buf, dbType); 122 preStmt.accept(v1); 123 if (out_p.toString() == (buf.toString())) { 124 continue; 125 } 126 } 127 128 if (!preStmt.isAfterSemi()) { 129 out_p.append(";\n"); 130 } else { 131 out_p.append('\n'); 132 } 133 } 134 135 if (stmt.hasBeforeComment()) { 136 stmt.getBeforeCommentsDirect().clear(); 137 } 138 139 auto stmtClass = typeid(stmt); 140 if (stmtClass == typeid(SQLSelectStatement)) { // only for performance 141 SQLSelectStatement selectStatement = cast(SQLSelectStatement) stmt; 142 visitor.visit(selectStatement); 143 visitor.postVisit(selectStatement); 144 } else { 145 stmt.accept(visitor); 146 } 147 } 148 149 if (visitor.getReplaceCount() == 0 150 && parser.getLexer().getCommentCount() == 0 && charAt(sql, 0) != '/') { 151 return sql; 152 } 153 154 return out_p.toString(); 155 } 156 157 public static long parameterizeHash(string sql 158 , string dbType 159 , List!(Object) outParameters) { 160 return parameterizeHash(sql, dbType, null, outParameters, null); 161 } 162 163 public static long parameterizeHash(string sql 164 , string dbType 165 , SQLSelectListCache selectListCache 166 , List!(Object) outParameters, VisitorFeature[] visitorFeatures...) { 167 168 SQLParserFeature[] features = outParameters is null 169 ? defaultFeatures2 170 : defaultFeatures; 171 172 SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType, features); 173 174 if (selectListCache !is null) { 175 parser.setSelectListCache(selectListCache); 176 } 177 178 List!(SQLStatement) statementList = parser.parseStatementList(); 179 int stmtSize = statementList.size(); 180 if (stmtSize == 0) { 181 return 0L; 182 } 183 184 StringBuilder out_p = new StringBuilder(sql.length); 185 ParameterizedVisitor visitor = createParameterizedOutputVisitor(out_p, dbType); 186 if (outParameters !is null) { 187 visitor.setOutputParameters(outParameters); 188 } 189 configVisitorFeatures(visitor, visitorFeatures); 190 191 if (stmtSize == 1) { 192 SQLStatement stmt = statementList.get(0); 193 if (typeid(stmt) == typeid(SQLSelectStatement)) { 194 SQLSelectStatement selectStmt = cast(SQLSelectStatement) stmt; 195 196 if (selectListCache !is null) { 197 SQLSelectQueryBlock queryBlock = selectStmt.getSelect().getQueryBlock(); 198 if (queryBlock !is null) { 199 string cachedSelectList = queryBlock.getCachedSelectList(); 200 long cachedSelectListHash = queryBlock.getCachedSelectListHash(); 201 if (cachedSelectList !is null) { 202 visitor.config(VisitorFeature.OutputSkipSelectListCacheString, true); 203 } 204 205 visitor.visit(selectStmt); 206 return FnvHash.fnv1a_64_lower(cachedSelectListHash, out_p); 207 } 208 } 209 210 visitor.visit(selectStmt); 211 } else if (typeid(stmt) == typeid(MySqlInsertStatement)) { 212 MySqlInsertStatement insertStmt = cast(MySqlInsertStatement) stmt; 213 string columnsString = insertStmt.getColumnsString(); 214 if (columnsString !is null) { 215 long columnsStringHash = insertStmt.getColumnsStringHash(); 216 visitor.config(VisitorFeature.OutputSkipInsertColumnsString, true); 217 218 (cast(MySqlASTVisitor) visitor).visit(insertStmt); 219 return FnvHash.fnv1a_64_lower(columnsStringHash, out_p); 220 } 221 } else { 222 stmt.accept(visitor); 223 } 224 225 return FnvHash.fnv1a_64_lower(out_p); 226 } 227 228 for (int i = 0; i < statementList.size(); i++) { 229 if (i > 0) { 230 out_p.append(";\n"); 231 } 232 SQLStatement stmt = statementList.get(i); 233 234 if (stmt.hasBeforeComment()) { 235 stmt.getBeforeCommentsDirect().clear(); 236 } 237 238 auto stmtClass = typeid(stmt); 239 if (stmtClass == typeid(SQLSelectStatement)) { // only for performance 240 SQLSelectStatement selectStatement = cast(SQLSelectStatement) stmt; 241 visitor.visit(selectStatement); 242 visitor.postVisit(selectStatement); 243 } else { 244 stmt.accept(visitor); 245 } 246 } 247 248 return FnvHash.fnv1a_64_lower(out_p); 249 } 250 251 public static string parameterize(List!(SQLStatement) statementList, string dbType) { 252 StringBuilder out_p = new StringBuilder(); 253 ParameterizedVisitor visitor = createParameterizedOutputVisitor(out_p, dbType); 254 255 for (int i = 0; i < statementList.size(); i++) { 256 if (i > 0) { 257 out_p.append(";\n"); 258 } 259 SQLStatement stmt = statementList.get(i); 260 261 if (stmt.hasBeforeComment()) { 262 stmt.getBeforeCommentsDirect().clear(); 263 } 264 stmt.accept(visitor); 265 } 266 267 return out_p.toString(); 268 } 269 270 public static ParameterizedVisitor createParameterizedOutputVisitor(Appendable out_p, string dbType) { 271 // if (DBType.ORACLE.opEquals(dbType) || DBType.ALI_ORACLE.opEquals(dbType)) { 272 // return new OracleParameterizedOutputVisitor(out_p); 273 // } 274 275 if (DBType.MYSQL.opEquals(dbType) 276 || DBType.MARIADB.opEquals(dbType) 277 || DBType.H2.opEquals(dbType)) { 278 return new MySqlOutputVisitor(out_p, true); 279 } 280 281 if (DBType.POSTGRESQL.opEquals(dbType) 282 || DBType.ENTERPRISEDB.opEquals(dbType)) { 283 return new PGOutputVisitor(out_p, true); 284 } 285 286 // if (DBType.SQL_SERVER.opEquals(dbType) || DBType.JTDS.opEquals(dbType)) { 287 // return new SQLServerOutputVisitor(out_p, true); 288 // } 289 290 // if (DBType.DB2.opEquals(dbType)) { 291 // return new DB2OutputVisitor(out_p, true); 292 // } 293 294 // if (DBType.PHOENIX.opEquals(dbType)) { 295 // return new PhoenixOutputVisitor(out_p, true); 296 // } 297 298 if (DBType.ELASTIC_SEARCH.opEquals(dbType)) { 299 return new MySqlOutputVisitor(out_p, true); 300 } 301 302 return new SQLASTOutputVisitor(out_p, true); 303 } 304 305 public static string restore(string sql, string dbType, List!(Object) parameters) { 306 List!(SQLStatement) stmtList = SQLUtils.parseStatements(sql, dbType); 307 308 StringBuilder out_p = new StringBuilder(); 309 SQLASTOutputVisitor visitor = SQLUtils.createOutputVisitor(out_p, dbType); 310 visitor.setInputParameters(parameters); 311 312 foreach(SQLStatement stmt ; stmtList) { 313 stmt.accept(visitor); 314 } 315 316 return out_p.toString(); 317 } 318 }