diff --git a/src/main/java/com/profiler/common/util/SqlUtils.java b/src/main/java/com/profiler/common/util/SqlParser.java similarity index 84% rename from src/main/java/com/profiler/common/util/SqlUtils.java rename to src/main/java/com/profiler/common/util/SqlParser.java index 80c65a6af..8c8481673 100644 --- a/src/main/java/com/profiler/common/util/SqlUtils.java +++ b/src/main/java/com/profiler/common/util/SqlParser.java @@ -3,10 +3,12 @@ package com.profiler.common.util; /** * */ -public class SqlUtils { +public class SqlParser { public static final char SYMBOL_REPLACE = '$'; + public static final char NUMBER_REPLACE = '#'; + public static final char SEPARATOR = ','; - public static final String normalizedSql(String sql, StringBuilder outputParam) { + public String normalizedSql(String sql, StringBuilder outputParam) { if (sql == null) { return ""; } @@ -42,14 +44,7 @@ public class SqlUtils { } else if (lookAhead1Char == '/') { normalized.append("//"); i += 2; - for (; i < length; i++) { - char stateCh = sql.charAt(i); - if (stateCh == '\n') { - normalized.append(stateCh); - break; - } - normalized.append(stateCh); - } + i = readLine(sql, normalized, i); break; } else { @@ -65,14 +60,7 @@ public class SqlUtils { if (lookAhead1(sql, i) == '-') { normalized.append("--"); i += 2; - for (; i < length; i++) { - char stateCh = sql.charAt(i); - if (stateCh == '\n') { - normalized.append(stateCh); - break; - } - normalized.append(stateCh); - } + i = readLine(sql, normalized, i); break; } else { // unary operator @@ -125,6 +113,8 @@ public class SqlUtils { // http://www.h2database.com/html/grammar.html 추가로 state machine을 더볼것. if (numberTokenStartEnable) { normalized.append('#'); + // number token start + appendSeparator(outputParam); outputParam.append(ch); i++; tokenEnd: @@ -147,6 +137,7 @@ public class SqlUtils { break; default: // 여기서 처리하지 말고 루프 바깥으로 나가서 다시 token을 봐야 된다. +// outputParam.append(SEPARATOR); i--; break tokenEnd; } @@ -192,6 +183,7 @@ public class SqlUtils { case '.': case '_': case '@': // Assignment Operator + case ':': // 오라클쪽의 bind 변수는 :bindvalue로도 가능. numberTokenStartEnable = false; normalized.append(ch); break; @@ -210,6 +202,17 @@ public class SqlUtils { return normalized.toString(); } + private int readLine(String sql, StringBuilder normalized, int index) { + for (; index < sql.length(); index++) { + char ch = sql.charAt(index); + normalized.append(ch); + if (ch == '\n') { + break; + } + } + return index; + } + /** * 미리 다음 문자열 하나를 까본다. * @@ -217,7 +220,7 @@ public class SqlUtils { * @param index * @return */ - private static int lookAhead1(String sql, int index) { + private int lookAhead1(String sql, int index) { index++; if (index < sql.length()) { return sql.charAt(index); @@ -226,4 +229,10 @@ public class SqlUtils { } } + private void appendSeparator(StringBuilder outputParam) { + if (outputParam.length() != 0) { + outputParam.append(SEPARATOR); + } + } + } diff --git a/src/test/java/com/profiler/common/util/SqlUtilsTest.java b/src/test/java/com/profiler/common/util/SqlParserTest.java similarity index 65% rename from src/test/java/com/profiler/common/util/SqlUtilsTest.java rename to src/test/java/com/profiler/common/util/SqlParserTest.java index 75c646f7d..901505396 100644 --- a/src/test/java/com/profiler/common/util/SqlUtilsTest.java +++ b/src/test/java/com/profiler/common/util/SqlParserTest.java @@ -7,18 +7,20 @@ import org.junit.Test; /** * */ -public class SqlUtilsTest { +public class SqlParserTest { + private SqlParser sqlParser = new SqlParser(); + @Test public void normalizedSql() { StringBuilder sb = new StringBuilder(10); - String s = SqlUtils.normalizedSql("select * from table a = 1 and b=50 and c=? and d='11'", sb); + String s = sqlParser.normalizedSql("select * from table a = 1 and b=50 and c=? and d='11'", sb); System.out.println(s); System.out.println(sb.toString()); StringBuilder sb2 = new StringBuilder(10); - String s2 = SqlUtils.normalizedSql(" ", sb2); + String s2 = sqlParser.normalizedSql(" ", sb2); System.out.println(s2); System.out.println((char) -1); @@ -32,11 +34,16 @@ public class SqlUtilsTest { System.out.println((int) Character.MAX_HIGH_SURROGATE); StringBuilder sb3 = new StringBuilder(); - String s3 = SqlUtils.normalizedSql("''", sb3); + String s3 = sqlParser.normalizedSql("''", sb3); System.out.println("s3:" + s3); System.out.println("sb3:" + sb3.toString()); } + @Test + public void nullCheck() { + sqlParser.normalizedSql(null, new StringBuilder()); + } + @Test public void complex() { @@ -69,39 +76,39 @@ public class SqlUtilsTest { @Test public void numberState() { - assertEqual("123", "#"); + assertEqual("123", "#", "123"); // -가 진짜 숫자의 -인지 알려면 구문분석이 필요하므로 그냥 숫자만 치환한다. - assertEqual("-123", "-#"); - assertEqual("+123", "+#"); - assertEqual("1.23", "#"); - assertEqual("1.23.34", "#"); - assertEqual("123 456", "# #"); - assertEqual("1.23 4.56", "# #"); - assertEqual("1.23-4.56", "#-#"); + assertEqual("-123", "-#", "123"); + assertEqual("+123", "+#", "123"); + assertEqual("1.23", "#", "1.23"); + assertEqual("1.23.34", "#", "1.23.34"); + assertEqual("123 456", "# #", "123,456"); + assertEqual("1.23 4.56", "# #", "1.23,4.56"); + assertEqual("1.23-4.56", "#-#", "1.23,4.56"); - assertEqual("1<2", "#<#"); - assertEqual("1< 2", "#< #"); - assertEqual("(1< 2)", "(#< #)"); + assertEqual("1<2", "#<#", "1,2"); + assertEqual("1< 2", "#< #", "1,2"); + assertEqual("(1< 2)", "(#< #)", "1,2"); - assertEqual("-- 1.23", "-- 1.23"); - assertEqual("- -1.23", "- -#"); - assertEqual("--1.23", "--1.23"); - assertEqual("/* 1.23 */", "/* 1.23 */"); - assertEqual("/*1.23*/", "/*1.23*/"); - assertEqual("/* 1.23 \n*/", "/* 1.23 \n*/"); + assertEqual("-- 1.23", "-- 1.23", ""); + assertEqual("- -1.23", "- -#", "1.23"); + assertEqual("--1.23", "--1.23", ""); + assertEqual("/* 1.23 */", "/* 1.23 */", ""); + assertEqual("/*1.23*/", "/*1.23*/", ""); + assertEqual("/* 1.23 \n*/", "/* 1.23 \n*/", ""); - assertEqual("test123", "test123"); - assertEqual("test_123", "test_123"); - assertEqual("test_ 123", "test_ #"); + assertEqual("test123", "test123", ""); + assertEqual("test_123", "test_123", ""); + assertEqual("test_ 123", "test_ #", "123"); // 사실 이건 불가능한 토큰임. - assertEqual("123tst", "#tst"); + assertEqual("123tst", "#tst", "123"); } @Test public void singleLineCommentState() { - assertEqual("--", "--"); + assertEqual("--", "--", ""); assertEqual("//", "//"); assertEqual("--123", "--123"); assertEqual("//123", "//123"); @@ -109,6 +116,7 @@ public class SqlUtilsTest { assertEqual("//test", "//test"); assertEqual("--test\ntest", "--test\ntest"); assertEqual("--test\t\n", "--test\t\n"); + assertEqual("--test\n123 test", "--test\n# test"); } @@ -161,7 +169,7 @@ public class SqlUtilsTest { private void assertEqual(String expected, String actual) { StringBuilder sb = new StringBuilder(); - String normalizedSql = SqlUtils.normalizedSql(expected, sb); + String normalizedSql = sqlParser.normalizedSql(expected, sb); try { Assert.assertEquals(actual, normalizedSql); } catch (AssertionFailedError e) { @@ -170,9 +178,16 @@ public class SqlUtilsTest { } } - private void assertEqual(String expected, String actual, String expectedSb) { - StringBuilder sb = new StringBuilder(); - String normalizedSql = SqlUtils.normalizedSql(expected, sb); - Assert.assertEquals(actual, normalizedSql); + private void assertEqual(String expected, String actual, String ouputExpected) { + StringBuilder output = new StringBuilder(); + String normalizedSql = sqlParser.normalizedSql(expected, output); + try { + Assert.assertEquals("normalizedSql check", actual, normalizedSql); + } catch (AssertionFailedError e) { + System.err.println("Original :" + expected); + throw e; + } + + Assert.assertEquals("outputParam check", ouputExpected, output.toString()); } }