/*
* Copyright 2010-2017 Boxfuse GmbH
*
* 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 org.flywaydb.core.internal.dbsupport.oracle;
import org.flywaydb.core.internal.dbsupport.Delimiter;
import org.flywaydb.core.internal.dbsupport.SqlStatementBuilder;
import org.flywaydb.core.internal.util.StringUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* SqlStatementBuilder supporting Oracle-specific PL/SQL constructs.
*/
public class OracleSqlStatementBuilder extends SqlStatementBuilder {
/**
* Regex for keywords that can appear before a string literal without being separated by a space.
*/
private static final Pattern KEYWORDS_BEFORE_STRING_LITERAL_REGEX = Pattern.compile("^(N|IF|ELSIF|SELECT|IMMEDIATE|RETURN|IS)('.*)");
/**
* Regex for keywords that can appear after a string literal without being separated by a space.
*/
private static final Pattern KEYWORDS_AFTER_STRING_LITERAL_REGEX = Pattern.compile("(.*')(USING|THEN|FROM|AND|OR|AS)(?!.)");
/**
* Delimiter of PL/SQL blocks and statements.
*/
private static final Delimiter PLSQL_DELIMITER = new Delimiter("/", true);
/**
* Holds the beginning of the statement.
*/
private String statementStart = "";
@Override
protected Delimiter changeDelimiterIfNecessary(String line, Delimiter delimiter) {
if (line.matches("DECLARE|DECLARE\\s.*") || line.matches("BEGIN|BEGIN\\s.*")) {
return PLSQL_DELIMITER;
}
if (StringUtils.countOccurrencesOf(statementStart, " ") < 8) {
statementStart += line;
statementStart += " ";
statementStart = statementStart.replaceAll("\\s+", " ");
}
if (statementStart.matches("CREATE(\\s+OR\\s+REPLACE)?(\\s+(NON)?EDITIONABLE)?\\s+(FUNCTION|PROCEDURE|PACKAGE|TYPE|TRIGGER).*")
|| statementStart.matches("CREATE(\\s+OR\\s+REPLACE)?(\\s+AND\\s+(RESOLVE|COMPILE))?(\\s+NOFORCE)?\\s+JAVA\\s+(SOURCE|RESOURCE|CLASS).*")) {
return PLSQL_DELIMITER;
}
return delimiter;
}
@Override
protected String cleanToken(String token) {
if (token.startsWith("'") && token.endsWith("'")) {
return token;
}
Matcher beforeMatcher = KEYWORDS_BEFORE_STRING_LITERAL_REGEX.matcher(token);
if (beforeMatcher.find()) {
token = beforeMatcher.group(2);
}
Matcher afterMatcher = KEYWORDS_AFTER_STRING_LITERAL_REGEX.matcher(token);
if (afterMatcher.find()) {
token = afterMatcher.group(1);
}
return token;
}
@Override
protected String simplifyLine(String line) {
String simplifiedQQuotes = StringUtils.replaceAll(StringUtils.replaceAll(line, "q'(", "q'["), ")'", "]'");
return super.simplifyLine(simplifiedQQuotes);
}
@Override
protected String extractAlternateOpenQuote(String token) {
if (token.startsWith("Q'") && (token.length() >= 3)) {
return token.substring(0, 3);
}
return null;
}
@Override
protected String computeAlternateCloseQuote(String openQuote) {
char specialChar = openQuote.charAt(2);
switch (specialChar) {
case '[':
return "]'";
case '(':
return ")'";
case '{':
return "}'";
case '<':
return ">'";
default:
return specialChar + "'";
}
}
@Override
public boolean canDiscard() {
return super.canDiscard() || statementStart.startsWith("SET DEFINE OFF");
}
}