From f5fd2dd785935a8529641d4e4a82faa27ab7f2ef Mon Sep 17 00:00:00 2001 From: Roland Tapken Date: Thu, 28 Nov 2024 17:22:11 +0100 Subject: [PATCH 1/3] Added support for SET TERMINATOR instruction This allows to temporarily change the statement separator with an inline comment and thus supports to execute statements that uses the semicolon within their expression, for example triggers or procedures with `BEGIN..END` blocks. Example: ```sql DROP PROCEDURE foobar; --#SET TERMINATOR ! CREATE PROCEDURE foobar BEGIN declare v char(10); select value into v from sometable; if v = 'test' then update sometable set value = ''; end if; END! --#SET TERMINATOR ; LABEL ON PROCEDURE foobar IS 'Example'; ``` This commit fixes feature request 433 on sourceforge (https://sourceforge.net/p/squirrel-sql/feature-requests/433/). It currently only supports the default QueryTokenizer. The statement separator is only changed for the current execution. --- .../fw/sql/querytokenizer/QueryTokenizer.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java b/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java index 5c4700340..8d4052a1b 100755 --- a/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java +++ b/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java @@ -164,6 +164,22 @@ public void setScriptToTokenize(String script) for (int i = 0; i < script.length(); ++i) { final NextPositionAction nextPositionAction = commentAndLiteralHandler.nextPosition(i); + if (!commentAndLiteralHandler.isInLiteral() && !commentAndLiteralHandler.isInMultiLineComment()) { + // Check for a line that contains '--#SET TERMINATOR x' to change the current new statement separator + if (script.startsWith(_lineCommentBegin + "#SET TERMINATOR ", i)) { + if (i == 0 || script.charAt(i-1) == '\n') { + // Only when the comment starts on a new line + int newLinePos = script.indexOf('\n', i); + if (newLinePos > 0 && newLinePos > i+18) { + String terminator = script.substring(i + 16 + _lineCommentBegin.length(), newLinePos).trim(); + if (!terminator.isEmpty()) { + s_log.info("changing statement separator to '" + terminator + "'"); + setQuerySep(terminator); + } + } + } + } + } curOriginalQuery.append(script.charAt(i)); From 0894f47d2c44b2cdfe0b99898b206938602cc1ff Mon Sep 17 00:00:00 2001 From: Roland Tapken Date: Thu, 28 Nov 2024 17:36:35 +0100 Subject: [PATCH 2/3] Fixed check for 'newLinePos' when using a custom line comment prefix with other length than 2 --- .../squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java b/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java index 8d4052a1b..218a7940b 100755 --- a/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java +++ b/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java @@ -170,7 +170,7 @@ public void setScriptToTokenize(String script) if (i == 0 || script.charAt(i-1) == '\n') { // Only when the comment starts on a new line int newLinePos = script.indexOf('\n', i); - if (newLinePos > 0 && newLinePos > i+18) { + if (newLinePos > i + 16 + _lineCommentBegin.length()) { String terminator = script.substring(i + 16 + _lineCommentBegin.length(), newLinePos).trim(); if (!terminator.isEmpty()) { s_log.info("changing statement separator to '" + terminator + "'"); From 48f499d674f2c2abd1f1fffc6ab2f6ee8be9fd43 Mon Sep 17 00:00:00 2001 From: Roland Tapken Date: Thu, 28 Nov 2024 18:09:13 +0100 Subject: [PATCH 3/3] Allow leading space or tab characters before instruction. This makes this feature compatible to IBM CLP and Data Architect. The documentation states: > The statement must be uppercase and in a comment. There can be leading spaces before the --, but there cannot be spaces between -- and #SET TERMINATOR x https://www.ibm.com/docs/en/ida/9.1.2?topic=terminators-changing-sql-statement-terminator --- .../fw/sql/querytokenizer/QueryTokenizer.java | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java b/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java index 218a7940b..c76176881 100755 --- a/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java +++ b/sql12/core/src/net/sourceforge/squirrel_sql/fw/sql/querytokenizer/QueryTokenizer.java @@ -165,19 +165,10 @@ public void setScriptToTokenize(String script) { final NextPositionAction nextPositionAction = commentAndLiteralHandler.nextPosition(i); if (!commentAndLiteralHandler.isInLiteral() && !commentAndLiteralHandler.isInMultiLineComment()) { - // Check for a line that contains '--#SET TERMINATOR x' to change the current new statement separator - if (script.startsWith(_lineCommentBegin + "#SET TERMINATOR ", i)) { - if (i == 0 || script.charAt(i-1) == '\n') { - // Only when the comment starts on a new line - int newLinePos = script.indexOf('\n', i); - if (newLinePos > i + 16 + _lineCommentBegin.length()) { - String terminator = script.substring(i + 16 + _lineCommentBegin.length(), newLinePos).trim(); - if (!terminator.isEmpty()) { - s_log.info("changing statement separator to '" + terminator + "'"); - setQuerySep(terminator); - } - } - } + String newQuerySep = findSetTerminatorInstruction(script, i); + if (newQuerySep != null) { + s_log.info("changing statement separator to '" + newQuerySep + "'"); + setQuerySep(newQuerySep); } } @@ -222,6 +213,37 @@ public void setScriptToTokenize(String script) _queryIterator = _queries.iterator(); } + /** + * Check for a line that contains '--#SET TERMINATOR x' to change the current new statement separator + * The line may start with spaces or tabs. Other characters are not allowed. + */ + private String findSetTerminatorInstruction(String script, final int i) { + if (script.startsWith(_lineCommentBegin + "#SET TERMINATOR ", i)) { + // Check if the comment is only preceded with space or tabs + int j = i; + while (j-- > 0) { + char c = script.charAt(j); + if (c == '\n') { + // Found the start of the line, break the loop + break; + } else if (c != ' ' && c != '\t') { + // Found non-whitespace character + return null; + } + } + + // Only when the comment starts on a new line + int newLinePos = script.indexOf('\n', i); + if (newLinePos > i + 16 + _lineCommentBegin.length()) { + String terminator = script.substring(i + 16 + _lineCommentBegin.length(), newLinePos).trim(); + if (!terminator.isEmpty()) { + return terminator; + } + } + } + return null; + } + /** * Returns the number of queries that the tokenizer found in the script * given in the last call to setScriptToTokenize, or 0 if