mirror of
https://github.com/Wisser/Jailer.git
synced 2026-05-24 11:39:31 -05:00
SQL-Console: added support for substitution variables
git-svn-id: https://svn.code.sf.net/p/jailer/code/trunk@1604 3dd849cd-670e-4645-a7cd-dd197c8d0e81
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
7.6.7
|
||||
- SQL-Console: added support for variables.
|
||||
- SQL-Console: added support for substitution variables.
|
||||
|
||||
7.6.6
|
||||
- Improved SQL Outline rendering.
|
||||
|
||||
@@ -89,9 +89,11 @@ public class MemorizedResultSet implements ResultSet {
|
||||
throws SQLException {
|
||||
this.rowList = new ArrayList<Object[]>();
|
||||
ResultSetMetaData rmd = resultSet.getMetaData();
|
||||
prepareHook(rmd);
|
||||
CellContentConverter cellContentConverter = new CellContentConverter(rmd, session, session.dbms);
|
||||
final int numCol = projection == null? rmd.getColumnCount() : projection.length;
|
||||
while (resultSet.next()) {
|
||||
readRowHook(resultSet);
|
||||
Object[] row = new Object[numCol];
|
||||
for (int i = 1; i <= numCol; ++i) {
|
||||
row[i - 1] = convertCellContent(cellContentConverter.getObject(resultSet, projection == null? i : projection[i - 1]));
|
||||
@@ -114,6 +116,12 @@ public class MemorizedResultSet implements ResultSet {
|
||||
resultSetMetaData = new MemorizedResultSetMetaData(numCol, names, types);
|
||||
}
|
||||
|
||||
protected void prepareHook(ResultSetMetaData rmd) throws SQLException {
|
||||
}
|
||||
|
||||
protected void readRowHook(ResultSet resultSet) throws SQLException {
|
||||
}
|
||||
|
||||
protected Object convertCellContent(Object object) {
|
||||
return object;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ public abstract class SQLConsole extends javax.swing.JPanel {
|
||||
private final AtomicBoolean updatingStatus = new AtomicBoolean(false);
|
||||
private final ImageIcon scaledCancelIcon;
|
||||
private final ImageIcon scaledExplainIcon;
|
||||
private final VariableSupport variableSupport = new VariableSupport();
|
||||
private final SQLPlusSupport sqlPlusSupport = new SQLPlusSupport();
|
||||
|
||||
/**
|
||||
* Creates new form SQLConsole
|
||||
@@ -260,7 +260,11 @@ public abstract class SQLConsole extends javax.swing.JPanel {
|
||||
});
|
||||
restoreHistory();
|
||||
|
||||
provider = new MetaDataBasedSQLCompletionProvider(session, metaDataSource);
|
||||
provider = new MetaDataBasedSQLCompletionProvider(session, metaDataSource) {
|
||||
protected String prepareStatementForAliasAnalysis(String statement) {
|
||||
return sqlPlusSupport.replaceVariables(statement, null);
|
||||
}
|
||||
};
|
||||
new SQLAutoCompletion(provider, editorPane);
|
||||
|
||||
RTextScrollPane jScrollPane = new RTextScrollPane();
|
||||
@@ -561,7 +565,7 @@ public abstract class SQLConsole extends javax.swing.JPanel {
|
||||
CancellationHandler.begin(statement, SQLConsole.this);
|
||||
long startTime = System.currentTimeMillis();
|
||||
sqlStatement = sql.replaceFirst("(?is)(;\\s*)+$", "");
|
||||
sqlStatement = variableSupport.replaceVariables(sqlStatement, positionOffsets);
|
||||
sqlStatement = sqlPlusSupport.replaceVariables(sqlStatement, positionOffsets);
|
||||
boolean hasResultSet;
|
||||
boolean isDefine = false;
|
||||
if (explain) {
|
||||
@@ -575,7 +579,7 @@ public abstract class SQLConsole extends javax.swing.JPanel {
|
||||
statement = session.getConnection().createStatement();
|
||||
hasResultSet = statement.execute(String.format(session.dbms.getExplainQuery(), sqlStatement, stmtId));
|
||||
} else {
|
||||
if (variableSupport.executeDefine(sqlStatement)) {
|
||||
if (sqlPlusSupport.executeSQLPLusStatement(sqlStatement)) {
|
||||
isDefine = true;
|
||||
hasResultSet = false;
|
||||
} else {
|
||||
@@ -622,6 +626,20 @@ public abstract class SQLConsole extends javax.swing.JPanel {
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareHook(ResultSetMetaData rmd) throws SQLException {
|
||||
sqlPlusSupport.prepareColumnSubstitution(rmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readRowHook(ResultSet resultSet) throws SQLException {
|
||||
try {
|
||||
sqlPlusSupport.substituteColumns(resultSet);
|
||||
} catch (SQLException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
};
|
||||
resultSet.close();
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2007 - 2018 the original author or authors.
|
||||
*
|
||||
* 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 net.sf.jailer.ui.databrowser.sqlconsole;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.SortedMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import net.sf.jailer.ui.databrowser.metadata.MetaDataDetailsPanel;
|
||||
|
||||
/**
|
||||
* Supports some Oracle SQL+ statements.
|
||||
*
|
||||
* @author Ralf Wisser
|
||||
*/
|
||||
public class SQLPlusSupport {
|
||||
|
||||
/**
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(MetaDataDetailsPanel.class);
|
||||
|
||||
/**
|
||||
* The variables.
|
||||
*/
|
||||
private Map<String, String> variables = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Column substitutions.
|
||||
*/
|
||||
private Map<String, String[]> columnSubstitutions = new HashMap<String, String[]>();
|
||||
|
||||
private Pattern DEFINE_PATTERN = Pattern.compile("\\s*DEFINE\\s+(\\w+)\\s*=\\s*(.*)\\s*", Pattern.CASE_INSENSITIVE);
|
||||
private Pattern UNDEFINE_PATTERN = Pattern.compile("\\s*UNDEFINE\\s+((?:\\w+\\s*)+)", Pattern.CASE_INSENSITIVE);
|
||||
private Pattern COLUMN_PATTERN = Pattern.compile("\\s*COLUMN\\s+(\\w+)\\s*((?:(?:new_value|old_value)\\s+\\w+\\s*)+)", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
/**
|
||||
* Executes a SQLPlus statement.
|
||||
*
|
||||
* @param statement the statement
|
||||
* @return <code>true</code> if statement is a valid SQLPlus statement
|
||||
*/
|
||||
public boolean executeSQLPLusStatement(String statement) {
|
||||
Matcher matcher = DEFINE_PATTERN.matcher(statement);
|
||||
if (matcher.matches()) {
|
||||
String var = matcher.group(1);
|
||||
String val = matcher.group(2);
|
||||
if (val.length() > 1 && val.startsWith("\"") && val.endsWith("\"")) {
|
||||
val = val.substring(1, val.length() - 1);
|
||||
}
|
||||
variables.put(var.toUpperCase(), val);
|
||||
logger.info("DEFINE " + var + " = " + val);
|
||||
return true;
|
||||
}
|
||||
matcher = UNDEFINE_PATTERN.matcher(statement);
|
||||
if (matcher.matches()) {
|
||||
for (String var: matcher.group(1).split("\\s+")) {
|
||||
variables.remove(var.toUpperCase());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
matcher = COLUMN_PATTERN.matcher(statement);
|
||||
if (matcher.matches()) {
|
||||
String column = matcher.group(1);
|
||||
String[] variables = matcher.group(2).split("(?i:\\s*(new_value|old_value)\\s*)");
|
||||
columnSubstitutions.put(column.toUpperCase(), variables);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all occurrences of "&<var>[.]" with variable values.
|
||||
*
|
||||
* @param statement the statement
|
||||
* @param positionOffsets map to put position offsets into
|
||||
* @return statement with variable replacements
|
||||
*/
|
||||
public String replaceVariables(String statement, SortedMap<Integer, Integer> positionOffsets) {
|
||||
if (!variables.isEmpty()) {
|
||||
Matcher matcher = Pattern.compile("&(\\w+)(\\.|\\b)").matcher(statement);
|
||||
matcher.reset();
|
||||
int offset = 0;
|
||||
boolean result = matcher.find();
|
||||
if (result) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
do {
|
||||
String var = matcher.group(1);
|
||||
String replacement = variables.get(var.toUpperCase());
|
||||
if (replacement != null) {
|
||||
matcher.appendReplacement(sb, replacement);
|
||||
if (positionOffsets != null) {
|
||||
offset += matcher.group().length() - replacement.length();
|
||||
for (int i = 0; i < replacement.length(); ++i) {
|
||||
positionOffsets.put(sb.length() - replacement.length() + i, offset + (replacement.length() - i - 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
result = matcher.find();
|
||||
} while (result);
|
||||
matcher.appendTail(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
return statement;
|
||||
}
|
||||
|
||||
private Map<Integer, String[]> varsPerIndex = new HashMap<Integer, String[]>();
|
||||
|
||||
public void prepareColumnSubstitution(ResultSetMetaData metaData) throws SQLException {
|
||||
varsPerIndex.clear();
|
||||
if (!columnSubstitutions.isEmpty()) {
|
||||
for (Entry<String, String[]> e: columnSubstitutions.entrySet()) {
|
||||
for (int i = 1; i <= metaData.getColumnCount(); ++i) {
|
||||
if (e.getKey().equalsIgnoreCase(metaData.getColumnLabel(i))) {
|
||||
varsPerIndex.put(i, e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void substituteColumns(ResultSet resultSet) throws SQLException {
|
||||
if (!varsPerIndex.isEmpty()) {
|
||||
for (Entry<Integer, String[]> e: varsPerIndex.entrySet()) {
|
||||
String value = resultSet.getString(e.getKey());
|
||||
for (String var: e.getValue()) {
|
||||
variables.put(var.toUpperCase(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -802,6 +802,8 @@ public abstract class SQLCompletionProvider<SOURCE, SCHEMA, TABLE> extends Defau
|
||||
final int MAX_OUTLINE_INFOS = 500;
|
||||
Map<String, String> scopeDescriptionPerLastKeyword = new HashMap<String, String>();
|
||||
|
||||
statement = prepareStatementForAliasAnalysis(statement);
|
||||
|
||||
scopeDescriptionPerLastKeyword.put("select", "Select");
|
||||
scopeDescriptionPerLastKeyword.put("from", "From");
|
||||
scopeDescriptionPerLastKeyword.put("with", "With");
|
||||
@@ -1155,7 +1157,11 @@ public abstract class SQLCompletionProvider<SOURCE, SCHEMA, TABLE> extends Defau
|
||||
return aliases;
|
||||
}
|
||||
|
||||
public void mergeOutlineInfos(List<OutlineInfo> outlineInfos, int endIndex) {
|
||||
protected String prepareStatementForAliasAnalysis(String statement) {
|
||||
return statement;
|
||||
}
|
||||
|
||||
public void mergeOutlineInfos(List<OutlineInfo> outlineInfos, int endIndex) {
|
||||
// merge "select from dual"
|
||||
if (outlineInfos != null && endIndex >= 2
|
||||
&& "from".equalsIgnoreCase(outlineInfos.get(endIndex - 2).scopeDescriptor)
|
||||
|
||||
Reference in New Issue
Block a user