/*
* 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.enterprisedb;
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 EnterpriseDBSqlStatementBuilder 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)(?!.)");
/**
* Matches $$, $BODY$, $xyz123$, ...
*/
/*private -> for testing*/
static final String DOLLAR_QUOTE_REGEX = "(\\$[A-Za-z0-9_]*\\$).*";
/**
* Delimiter of PL/SQL blocks and statements.
*/
private static final Delimiter SPL_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 SPL_DELIMITER;
}
if (StringUtils.countOccurrencesOf(statementStart, " ") < 8) {
statementStart += line;
statementStart += " ";
statementStart = statementStart.replaceAll("\\s+", " ");
}
if (statementStart.matches("CREATE( OR REPLACE)? (FUNCTION|PROCEDURE|PACKAGE|TYPE|TRIGGER).*")) {
return SPL_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) {
Matcher matcher = Pattern.compile(DOLLAR_QUOTE_REGEX).matcher(token);
if (matcher.find()) {
return matcher.group(1);
}
return null;
}
@Override
public boolean canDiscard() {
return super.canDiscard() || statementStart.startsWith("SET DEFINE OFF");
}
}