/* * Copyright 2015 the original author or authors. * @https://github.com/scouter-project/scouter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package scouter.util; import java.io.File; /** * bugfix : parse error for '*' by 2016.08.12 Paul S.J.Kim */ public class EscapeLiteralSQL { static enum STAT { NORMAL, COMMENT, ALPABET, NUMBER, QUTATION, COLON }; private String substitute = "@"; private String substitute_num = "@"; private boolean substitute_str_mode = true; private char[] chars; private int pos; private int length; private int count; private int comment_su; final StringBuffer parsedSql; final StringBuffer param; private STAT status; public EscapeLiteralSQL(String sql) { this.chars = sql.toCharArray(); this.length = this.chars.length; this.parsedSql = new StringBuffer(this.length + 10); this.param = new StringBuffer(); } public EscapeLiteralSQL setSubstitute(String chr) { this.substitute = chr; if (this.substitute_str_mode) { this.substitute_num = "'" + chr + "'"; } else { this.substitute_num = this.substitute; } return this; } public EscapeLiteralSQL setSubstituteStringMode(boolean b) { if (this.substitute_str_mode == b) return this; this.substitute_str_mode = b; if (this.substitute_str_mode) { this.substitute_num = "'" + this.substitute + "'"; } else { this.substitute_num = this.substitute; } return this; } public EscapeLiteralSQL process() { status = STAT.NORMAL; for (pos = 0; pos < chars.length; pos++) { switch (chars[pos]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': _number(); break; case ':': _colon(); break; case '.': _dot(); break; case '-': _minus(); break; case '/': _slash(); break; case '*': _astar(); break; case '\'': _qutation(); break; default: _others(); } } return this; } private void _others() { // System.out.println("other=>'"+chars[pos]+"' " +status); switch (status) { case COMMENT: parsedSql.append(chars[pos]); break; case ALPABET: parsedSql.append(chars[pos]); if (isProgLetter(chars[pos]) == false) { status = STAT.NORMAL; } break; case NUMBER: parsedSql.append(chars[pos]); status = STAT.NORMAL; break; case QUTATION: param.append(chars[pos]); break; default: if (isProgLetter(chars[pos])) { status = STAT.ALPABET; } else { status = STAT.NORMAL; } parsedSql.append(chars[pos]); break; } } private boolean isProgLetter(char c) { return Character.isLetter(c) || c == '_'; } private void _colon() { switch (status) { case COMMENT: parsedSql.append(chars[pos]); break; case QUTATION: param.append(chars[pos]); break; default: parsedSql.append(chars[pos]); status = STAT.COLON; break; } } private void _qutation() { switch (status) { case NORMAL: if (param.length() > 0) { param.append(","); } param.append(chars[pos]); status = STAT.QUTATION; break; case COMMENT: parsedSql.append(chars[pos]); break; case ALPABET: parsedSql.append(chars[pos]); status = STAT.QUTATION; break; case NUMBER: parsedSql.append(chars[pos]); status = STAT.QUTATION; break; case QUTATION: param.append("'"); parsedSql.append('\'').append(substitute).append("{").append(++count).append("}").append('\''); status = STAT.NORMAL; break; } } private void _astar() { switch (status) { case COMMENT: parsedSql.append(chars[pos]); if (getNext(pos) == '/') { parsedSql.append('/'); pos++; if(--comment_su == 0){ status = STAT.NORMAL; } } break; case QUTATION: param.append(chars[pos]); break; default: parsedSql.append(chars[pos]); status = STAT.NORMAL; } } private void _slash() { switch (status) { case COMMENT: parsedSql.append(chars[pos]); break; case QUTATION: param.append(chars[pos]); break; default: if (getNext(pos) == '*') { pos++; comment_su++; parsedSql.append("/*"); status = STAT.COMMENT; } } } private void _minus() { switch (status) { case COMMENT: parsedSql.append(chars[pos]); break; case QUTATION: param.append(chars[pos]); break; default: if (getNext(pos) == '-') { parsedSql.append(chars[pos]); while (chars[pos] != '\n') { pos++; if (pos < length) { parsedSql.append(chars[pos]); } else { break; } } }else{ parsedSql.append(chars[pos]); } status = STAT.NORMAL; } } private void _dot() { switch (status) { case NORMAL: parsedSql.append(chars[pos]); break; case COMMENT: parsedSql.append(chars[pos]); break; case ALPABET: parsedSql.append(chars[pos]); status = STAT.NORMAL; break; case NUMBER: param.append(chars[pos]); break; case QUTATION: param.append(chars[pos]); break; } } private void _number() { switch (status) { case NORMAL: if(param.length() > 0){ param.append(","); } param.append(chars[pos]); parsedSql.append(substitute_num).append("{").append(++count).append("}"); status = STAT.NUMBER; break; case COMMENT: case COLON: case ALPABET: parsedSql.append(chars[pos]); break; case NUMBER: case QUTATION: param.append(chars[pos]); break; } } private char getNext(int x) { return x < length ? chars[x + 1] : 0; } public static void main(String[] args) throws Exception { //String s = new String(FileUtil.readAll(new File("d:/tmp/sample-query2.sql")), "EUC_KR"); String s = "select aa_1 ,( a - b) as b from tab";//new // String(FileUtil.readAll(new // File("d:/tmp/sample-query2.sql")),"EUC_KR"); long time = System.currentTimeMillis(); EscapeLiteralSQL ec = new EscapeLiteralSQL(s).process(); long etime = System.currentTimeMillis(); //FileUtil.save("d:/tmp/sample-query2.out", ec.parsedSql.toString().getBytes()); System.out.println("SQL Orgin: " + s); System.out.println("SQL Parsed: " + ec.getParsedSql()); System.out.println("PARAM: " + ec.param); } public String getParsedSql() { return this.parsedSql.toString(); } public String getParameter() { return this.param.toString(); } }