Skip to content

Commit

Permalink
Feature: Pull request: #51
Browse files Browse the repository at this point in the history
  To change the statement separator during SQL execution, use the following command on a separate line:
  --#SET TERMINATOR <separator>
  This command does not change the separator permanently but for a single SQL execution only.
  See also menu File --> New Session Properties --> tab SQL --> section "Statement separator"
  Thanks to Roland Tapken for the pull request
  • Loading branch information
Cybso authored and gerdwagner committed Nov 30, 2024
1 parent 384023f commit 368a60e
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 57 deletions.
7 changes: 7 additions & 0 deletions sql12/core/doc/changes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ Not yet released, available in our GIT repository, snapshots and future releases

Enhancements:

Pull request: https://github.com/squirrel-sql-client/squirrel-sql-code/pull/51
To change the statement separator during SQL execution, use the following command on a separate line:
--#SET TERMINATOR <separator>
This command does not change the separator permanently but for a single SQL execution only.
See also menu File --> New Session Properties --> tab SQL --> section "Statement separator"
Thanks to Roland Tapken for the pull request

Find function in cell data popup and Object tree's text detail tabs:
The "Mark all" toggle button now writes the number of occurrences to SQuirreL's message panel.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package net.sourceforge.squirrel_sql.client.session.mainpanel.resulttabheader;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.JTabbedPane;
import javax.swing.Timer;

import net.sourceforge.squirrel_sql.client.Main;
import net.sourceforge.squirrel_sql.client.session.ISQLEntryPanel;
import net.sourceforge.squirrel_sql.client.session.editorpaint.TextAreaPaintListener;
import net.sourceforge.squirrel_sql.client.session.mainpanel.IResultTab;
import net.sourceforge.squirrel_sql.client.session.mainpanel.SQLResultExecutorPanel;
import net.sourceforge.squirrel_sql.client.session.sqlbounds.BoundsOfSqlHandler;
import net.sourceforge.squirrel_sql.fw.sql.querytokenizer.IQueryTokenizer;
import net.sourceforge.squirrel_sql.fw.sql.querytokenizer.QueryTokenizePurpose;
import org.apache.commons.lang3.StringUtils;

import javax.swing.JTabbedPane;
import javax.swing.Timer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ResultTabMatchingCurrentSqlHandler
{
private boolean _resultTabHeaderMarkingActive;
Expand Down Expand Up @@ -84,7 +85,7 @@ private void onTextAreaPaint(boolean activateLastMarked)
{

IQueryTokenizer qt = _entryPanel.getSession().getNewQueryTokenizer();
qt.setScriptToTokenize(sqlToBeExecuted);
qt.setScriptToTokenize(sqlToBeExecuted, QueryTokenizePurpose.OTHER);

if(false == qt.hasQuery())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,9 @@ NullValuesSortingPanel.sort.null.highest=Highest
NullValuesSortingPanel.sort.null.lowest=Lowest


SQLPropertiesPanel.separators.info=Multiple character separators must be surrounded by white spaces.\nSingle character separators can be used without surrounding white spaces.
SQLPropertiesPanel.separators.info=Multiple character separators must be surrounded by white spaces.\nSingle character separators can be used without surrounding white spaces.

SQLPropertiesPanel.separators.set.statement.separator.info.begin=Info:\nTo change the statement separator during SQL execution, use the following command on a separate line:
SQLPropertiesPanel.separators.set.statement.separator.info.command=--#SET TERMINATOR <separator>
SQLPropertiesPanel.separators.set.statement.separator.info.end=Note this command does not change the separator permanently but for a single SQL execution only.
SQLPropertiesPanel.separators.title=Statement separator
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,31 @@
import net.sourceforge.squirrel_sql.client.mainframe.action.findprefs.AddressablePrefJPanel;
import net.sourceforge.squirrel_sql.client.mainframe.action.findprefs.PreferencesAddressBook;
import net.sourceforge.squirrel_sql.client.session.ISession;
import net.sourceforge.squirrel_sql.fw.gui.*;
import net.sourceforge.squirrel_sql.fw.gui.FontChooser;
import net.sourceforge.squirrel_sql.fw.gui.FontInfo;
import net.sourceforge.squirrel_sql.fw.gui.GUIUtils;
import net.sourceforge.squirrel_sql.fw.gui.IntegerField;
import net.sourceforge.squirrel_sql.fw.gui.MultipleLineLabel;
import net.sourceforge.squirrel_sql.fw.sql.querytokenizer.IQueryTokenizer;
import net.sourceforge.squirrel_sql.fw.sql.querytokenizer.TokenizerSessPropsInteractions;
import net.sourceforge.squirrel_sql.fw.util.StringManager;
import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;

import javax.swing.*;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

Expand Down Expand Up @@ -397,7 +412,19 @@ private JPanel createStatementSeparatorPanel()
gbc = new GridBagConstraints(1,1,1,1,0,0,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5,3,2,2), 0,0);
ret.add(_stmtSepField, gbc);

ret.setBorder(BorderFactory.createEtchedBorder());

gbc = new GridBagConstraints(0,2,2,1,0,0,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10,3,0,2), 0,0);
ret.add(new MultipleLineLabel(s_stringMgr.getString("SQLPropertiesPanel.separators.set.statement.separator.info.begin")), gbc);

gbc = new GridBagConstraints(0,3,2,1,0,0,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0,3,0,2), 0,0);
MultipleLineLabel lblSepCommand = new MultipleLineLabel(s_stringMgr.getString("SQLPropertiesPanel.separators.set.statement.separator.info.command"));
lblSepCommand.setFont(new Font(Font.MONOSPACED, lblSepCommand.getFont().getStyle(), lblSepCommand.getFont().getSize()));
ret.add(lblSepCommand, gbc);

gbc = new GridBagConstraints(0,4,2,1,0,0,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0,3,2,2), 0,0);
ret.add(new MultipleLineLabel(s_stringMgr.getString("SQLPropertiesPanel.separators.set.statement.separator.info.end")), gbc);

ret.setBorder(BorderFactory.createTitledBorder(s_stringMgr.getString("SQLPropertiesPanel.separators.title")));

return ret;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package net.sourceforge.squirrel_sql.fw.sql.querytokenizer;

import net.sourceforge.squirrel_sql.fw.sql.commentandliteral.SQLCommentAndLiteralHandler;
import org.apache.commons.lang3.StringUtils;

public class ChangeStatementSeparatorSupport
{
private static final String SET_TERMINATOR_COMMAND = "#SET TERMINATOR ";
private final String _script;
private final String _lineCommentBegin;
private String _terminatorCommandInclLineCommentPrefix;

private final boolean _scriptContainsTerminatorActive;

public ChangeStatementSeparatorSupport(QueryTokenizePurpose queryTokenizePurpose, String script, String lineCommentBegin)
{
_script = script;
_lineCommentBegin = lineCommentBegin;
_terminatorCommandInclLineCommentPrefix = _lineCommentBegin + SET_TERMINATOR_COMMAND;

_scriptContainsTerminatorActive =
queryTokenizePurpose == QueryTokenizePurpose.STATEMENT_EXECUTION
&& StringUtils.containsIgnoreCase(script, _terminatorCommandInclLineCommentPrefix);
}

/**
* Check for a line that contains '--#SET TERMINATOR x' to change the current new statement separator
* The line may start by spaces or tabs. Other characters are not allowed.
*/
public String findSetTerminatorInstruction(final int searchStartPos, SQLCommentAndLiteralHandler commentAndLiteralHandler)
{
if(false == isActive())
{
return null;
}

if(commentAndLiteralHandler.isInLiteral() || commentAndLiteralHandler.isInMultiLineComment())
{
return null;
}

if(false == StringUtils.startsWithIgnoreCase(_script.substring(searchStartPos), _terminatorCommandInclLineCommentPrefix))

This comment has been minimized.

Copy link
@Cybso

Cybso Dec 1, 2024

Author Contributor

The IBM documentation specifies that #SET TERMINATOR has to be uppercase, but personally I'm fine with this

https://www.ibm.com/docs/en/ida/9.1.2?topic=terminators-changing-sql-statement-terminator

{
return null;
}

if(isCommandPrecededBySpacesOrTabsOnly(_script, searchStartPos))

This comment has been minimized.

Copy link
@Cybso

Cybso Dec 1, 2024

Author Contributor

The method is incorrectly named or the return value is the wrong way round

This comment has been minimized.

Copy link
@gerdwagner

gerdwagner Dec 1, 2024

Member

Fixed

{
return null;
}

// Only when the comment starts on a new line

This comment has been minimized.

Copy link
@Cybso

Cybso Dec 1, 2024

Author Contributor

This comment belongs to the above statement "isCommandPreceededBySpacesOrTabsOnly"

This comment has been minimized.

Copy link
@gerdwagner

gerdwagner Dec 1, 2024

Member

Fixed.

int newLinePos = _script.indexOf('\n', searchStartPos);
if(newLinePos > searchStartPos + _terminatorCommandInclLineCommentPrefix.length())
{
String terminator = _script.substring(searchStartPos + _terminatorCommandInclLineCommentPrefix.length(), newLinePos).trim();
if(!terminator.isEmpty())
{
return terminator;
}
}


return null;
}

private static boolean isCommandPrecededBySpacesOrTabsOnly(String script, int searchStartPos)
{
// Check if the comment is only preceded by spaces or tabs
int j = searchStartPos;
while(j-- > 0)
{
char c = script.charAt(j);
if(c == '\n')
{
// Found the start of the line, break the loop
break;

This comment has been minimized.

Copy link
@Cybso

Cybso Dec 1, 2024

Author Contributor

This could be a "return" statement now.

}
else if(c != ' ' && c != '\t')
{
// Found non-whitespace character
return true;
}
}
return false;
}

public boolean isActive()
{
return _scriptContainsTerminatorActive;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
QueryTokenizer.change.statement.separator.message=Changing statement separator for current execution from statement #{0} on to {1}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ public interface IQueryTokenizer
* @param script a string representing one or more SQL statements.
*/
void setScriptToTokenize(String script);


void setScriptToTokenize(String script, QueryTokenizePurpose queryTokenizePurpose);

/**
* Returns the number of queries that the tokenizer found in the script
* given in the last call to setScriptToTokenize, or 0 if
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package net.sourceforge.squirrel_sql.fw.sql.querytokenizer;

public enum QueryTokenizePurpose
{
STATEMENT_EXECUTION,
OTHER
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

import net.sourceforge.squirrel_sql.client.Main;
import net.sourceforge.squirrel_sql.client.session.action.sqlscript.SQLScriptServices;
import net.sourceforge.squirrel_sql.fw.preferences.IQueryTokenizerPreferenceBean;
import net.sourceforge.squirrel_sql.fw.sql.commentandliteral.NextPositionAction;
import net.sourceforge.squirrel_sql.fw.sql.commentandliteral.SQLCommentAndLiteralHandler;
import net.sourceforge.squirrel_sql.fw.util.StringManager;
import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
import net.sourceforge.squirrel_sql.fw.util.StringUtilities;
import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
Expand All @@ -35,7 +38,11 @@

public class QueryTokenizer implements IQueryTokenizer
{
protected ArrayList<QueryHolder> _queries = new ArrayList<>();
private final static ILogger s_log = LoggerController.createLogger(QueryTokenizer.class);
StringManager s_stringMgr = StringManagerFactory.getStringManager(QueryTokenizer.class);


protected ArrayList<QueryHolder> _queries = new ArrayList<>();

protected Iterator<QueryHolder> _queryIterator;

Expand All @@ -48,7 +55,6 @@ public class QueryTokenizer implements IQueryTokenizer

protected ITokenizerFactory _tokenizerFactory = null;

private final static ILogger s_log = LoggerController.createLogger(QueryTokenizer.class);

public QueryTokenizer(String querySep,
String lineCommentBegin,
Expand Down Expand Up @@ -151,62 +157,79 @@ public QueryHolder nextQuery()

public void setScriptToTokenize(String script)
{
_queries.clear();

script = script.replace('\r', ' ');
setScriptToTokenize(script, QueryTokenizePurpose.STATEMENT_EXECUTION);
}

public void setScriptToTokenize(String script, QueryTokenizePurpose queryTokenizePurpose)
{
_queries.clear();

StringBuffer curQuery = new StringBuffer();
StringBuffer curOriginalQuery = new StringBuffer();
script = script.replace('\r', ' ');

StringBuffer curQuery = new StringBuffer();
StringBuffer curOriginalQuery = new StringBuffer();

SQLCommentAndLiteralHandler commentAndLiteralHandler = new SQLCommentAndLiteralHandler(script, _lineCommentBegin, _removeMultiLineComment, _removeLineComment);
ChangeStatementSeparatorSupport changeStatementSeparatorSupport = new ChangeStatementSeparatorSupport(queryTokenizePurpose, script, _lineCommentBegin);

for (int i = 0; i < script.length(); ++i)
{
final NextPositionAction nextPositionAction = commentAndLiteralHandler.nextPosition(i);
for(int i = 0; i < script.length(); ++i)
{
final NextPositionAction nextPositionAction = commentAndLiteralHandler.nextPosition(i);

curOriginalQuery.append(script.charAt(i));
if(changeStatementSeparatorSupport.isActive())
{
String newQuerySep = changeStatementSeparatorSupport.findSetTerminatorInstruction(i, commentAndLiteralHandler);
if(newQuerySep != null)
{
String msg = s_stringMgr.getString("QueryTokenizer.change.statement.separator.message", _queries.size() + 1, newQuerySep);
Main.getApplication().getMessageHandler().showMessage(msg);
s_log.info(msg);
setQuerySep(newQuerySep);
}
}

if(NextPositionAction.APPEND == nextPositionAction)
{
curQuery.append(script.charAt(i));
}
else
{
continue;
}
curOriginalQuery.append(script.charAt(i));

int querySepLen = getLenOfQuerySepIfAtLastCharOfQuerySep(script, i, _querySep, commentAndLiteralHandler.isInLiteral());
if(NextPositionAction.APPEND == nextPositionAction)
{
curQuery.append(script.charAt(i));
}
else
{
continue;
}

if(-1 < querySepLen && !commentAndLiteralHandler.isInMultiLineComment())
{
int newLength = curQuery.length() - querySepLen;
if(-1 < newLength && curQuery.length() > newLength)
{
curQuery.setLength(newLength);
int querySepLen = getLenOfQuerySepIfAtLastCharOfQuerySep(script, i, _querySep, commentAndLiteralHandler.isInLiteral());

if(-1 < querySepLen && !commentAndLiteralHandler.isInMultiLineComment())
{
int newLength = curQuery.length() - querySepLen;
if(-1 < newLength && curQuery.length() > newLength)
{
curQuery.setLength(newLength);

String newQuery = curQuery.toString().trim();
if(0 < newQuery.length())
{
_queries.add(new QueryHolder(curQuery.toString().trim(), curOriginalQuery.toString().trim()));
}
String newQuery = curQuery.toString().trim();
if(0 < newQuery.length())
{
_queries.add(new QueryHolder(curQuery.toString().trim(), curOriginalQuery.toString().trim()));
}
curQuery.setLength(0);
curOriginalQuery.setLength(0);
}
}
}
curQuery.setLength(0);
curOriginalQuery.setLength(0);
}
}

String lastQuery = curQuery.toString().trim();
String lastOriginalQuery = curOriginalQuery.toString().trim();
if(0 < lastQuery.length())
{
_queries.add(new QueryHolder(lastQuery, lastOriginalQuery));
}
String lastQuery = curQuery.toString().trim();
String lastOriginalQuery = curOriginalQuery.toString().trim();
if(0 < lastQuery.length())
{
_queries.add(new QueryHolder(lastQuery, lastOriginalQuery));
}

_queryIterator = _queries.iterator();
_queryIterator = _queries.iterator();
}

/**
/**
* Returns the number of queries that the tokenizer found in the script
* given in the last call to setScriptToTokenize, or 0 if
* setScriptToTokenize has not yet been called.
Expand Down

0 comments on commit 368a60e

Please sign in to comment.