001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package org.kuali.db;
021
022 import org.apache.commons.lang.StringUtils;
023
024 /**
025 * Utility class to split a long sql batch script into single SQL commands.
026 */
027 public class SqlSplitter {
028 /**
029 * Value indicating the sql has no end-delimiter like i.e. semicolon
030 */
031 public static final int NO_END = -1;
032
033 /**
034 * Check if the given sql line contains an end of command ';' Please note that we do <em>not</em> fully parse the
035 * SQL, so if we get a malformed statement, we cannot detect it.
036 *
037 * @param line
038 * to parse
039 * @param delimiter
040 * which should be used to split SQL commands
041 * @return position after the end character if the given line contains the end of a SQL script,
042 * {@value SqlSplitter#NO_END} if it doesn't contain an end char.
043 */
044 public static int containsSqlEnd(String line, String delimiter) {
045 // / * * / comments
046 boolean isComment = false;
047
048 boolean isAlphaDelimiter = StringUtils.isAlpha(delimiter);
049
050 if (line == null || line.length() == 0) {
051 return NO_END;
052 }
053
054 int pos = 0;
055
056 do {
057 if (isComment) {
058 if (line.startsWith("*/", pos)) {
059 isComment = false;
060 } else {
061 pos++;
062 continue;
063 }
064 }
065
066 if (line.startsWith("/*", pos)) {
067 isComment = true;
068 pos += 2;
069 continue;
070 }
071
072 if (line.startsWith("--", pos)) {
073 return NO_END;
074 }
075
076 if (line.startsWith("'", pos) || line.startsWith("\"", pos)) {
077 String quoteChar = "" + line.charAt(pos);
078 String quoteEscape = "\\" + quoteChar;
079 pos++;
080
081 if (line.length() <= pos) {
082 return NO_END;
083 }
084
085 do {
086 if (line.startsWith(quoteEscape, pos)) {
087 pos += 2;
088 }
089 } while (!line.startsWith(quoteChar, pos++));
090
091 continue;
092 }
093
094 if (line.startsWith(delimiter, pos)) {
095 if (isAlphaDelimiter) {
096 // check if delimiter is at start or end of line, surrounded
097 // by non-alpha character
098 if ((pos == 0 || !isAlpha(line.charAt(pos - 1))) && (line.length() == pos + delimiter.length() || !isAlpha(line.charAt(pos + delimiter.length())))) {
099 return pos + delimiter.length();
100 }
101 } else {
102 return pos + delimiter.length();
103 }
104 }
105
106 pos++;
107
108 } while (line.length() >= pos);
109
110 return NO_END;
111 }
112
113 /**
114 * @param c
115 * the char to check
116 * @return <code>true</code> if the given character is either a lower or an upperchase alphanumerical character
117 */
118 private static boolean isAlpha(char c) {
119 return Character.isUpperCase(c) || Character.isLowerCase(c);
120 }
121
122 }