From 0e5c41761ff2358ce0f6ebc8c1905edc955d7727 Mon Sep 17 00:00:00 2001 From: rene-ye Date: Mon, 24 Jun 2019 14:43:27 -0700 Subject: [PATCH 1/8] FIx | Changes after Fuzz Testing --- .../sqlserver/jdbc/ParameterUtils.java | 2 +- .../sqlserver/jdbc/SQLServerFMTQuery.java | 22 +- .../sqlserver/jdbc/SQLServerParser.java | 239 ++++++++++-------- .../sqlserver/jdbc/SQLServerResource.java | 3 +- .../jdbc/connection/DBMetadataTest.java | 67 +++-- 5 files changed, 196 insertions(+), 137 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ParameterUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/ParameterUtils.java index e51f97dd1..4be648d37 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ParameterUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ParameterUtils.java @@ -87,7 +87,7 @@ static int scanSQLForChar(char ch, String sql, int offset) { // Fall through - will fail next if and end up in default case case '-': - if (sql.charAt(offset) == '-') { // If '-- ... \n' comment + if (offset >= 0 && offset < sql.length() && sql.charAt(offset) == '-') { // If '-- ... \n' comment while (++offset < len) { // Go thru comment. if (sql.charAt(offset) == '\n' || sql.charAt(offset) == '\r') { // If end of comment diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java index 93f67b32d..17fba54b5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java @@ -13,7 +13,10 @@ import java.util.List; import java.util.stream.Collectors; +import org.antlr.v4.runtime.BaseErrorListener; import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.Token; @@ -100,7 +103,8 @@ String getFMTQuery() { } catch (IOException e) { SQLServerException.makeFromDriverError(null, userSql, e.getLocalizedMessage(), "", false); } - + lexer.removeErrorListeners(); + lexer.addErrorListener(new SQLServerErrorListener()); this.tokenList = (ArrayList) lexer.getAllTokens(); if (tokenList.size() <= 0) { SQLServerException.makeFromDriverError(null, this, @@ -111,3 +115,19 @@ String getFMTQuery() { SQLServerParser.parseQuery(iter, this); } } + + +class SQLServerErrorListener extends BaseErrorListener { + static final private java.util.logging.Logger logger = java.util.logging.Logger + .getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerFMTQuery"); + + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, + String msg, RecognitionException e) { + if (logger.isLoggable(java.util.logging.Level.FINE)) { + logger.fine("Error occured during token parsing: " + msg); + logger.fine("line " + line + ":" + charPositionInLine + " token recognition error at: " + + offendingSymbol.toString()); + } + } +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java index c6b7e1181..4bc0656b8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java @@ -66,31 +66,40 @@ static void parseQuery(SQLServerTokenIterator iter, SQLServerFMTQuery query) thr } query.getTableTarget().add(getTableTargetChunk(iter, query.getAliases(), INSERT_DELIMITING_WORDS)); - List tableValues = getValuesList(iter); - // VALUES case - boolean valuesFound = false; - int valuesMarker = iter.nextIndex(); - while (!valuesFound && iter.hasNext()) { - t = iter.next(); - if (t.getType() == SQLServerLexer.VALUES) { - valuesFound = true; - do { - query.getValuesList().add(getValuesList(iter)); - } while (iter.hasNext() && iter.next().getType() == SQLServerLexer.COMMA); - iter.previous(); - } - } - if (!valuesFound) { - resetIteratorIndex(iter, valuesMarker); - } - if (!query.getValuesList().isEmpty()) { - for (List ls : query.getValuesList()) { - if (tableValues.isEmpty()) { - query.getColumns().add("*"); + if (iter.hasNext()) { + List tableValues = getValuesList(iter); + // VALUES case + boolean valuesFound = false; + int valuesMarker = iter.nextIndex(); + while (!valuesFound && iter.hasNext()) { + t = iter.next(); + if (t.getType() == SQLServerLexer.VALUES) { + valuesFound = true; + do { + query.getValuesList().add(getValuesList(iter)); + } while (iter.hasNext() && iter.next().getType() == SQLServerLexer.COMMA); + iter.previous(); } - for (int i = 0; i < ls.size(); i++) { - if (ls.get(i).equalsIgnoreCase("?")) { - query.getColumns().add((tableValues.size() == 0) ? "?" : tableValues.get(i)); + } + if (!valuesFound) { + resetIteratorIndex(iter, valuesMarker); + } + if (!query.getValuesList().isEmpty()) { + for (List ls : query.getValuesList()) { + if (tableValues.isEmpty()) { + query.getColumns().add("*"); + } + for (int i = 0; i < ls.size(); i++) { + if (ls.get(i).equalsIgnoreCase("?")) { + if (i < tableValues.size()) { + query.getColumns() + .add((tableValues.size() == 0) ? "?" : tableValues.get(i)); + } else { + SQLServerException.makeFromDriverError(null, null, + SQLServerResource.getResource("R_invalidInsertValuesQuery"), "", + false); + } + } } } } @@ -201,7 +210,7 @@ private static String findColumnAfterParameter(SQLServerTokenIterator iter) { StringBuilder sb = new StringBuilder(); while (sb.length() == 0 && iter.hasNext()) { Token t = iter.next(); - if (t.getType() == SQLServerLexer.NOT) { + if (t.getType() == SQLServerLexer.NOT && iter.hasNext()) { t = iter.next(); // skip NOT } if (OPERATORS.contains(t.getType()) && iter.hasNext()) { @@ -216,8 +225,10 @@ private static String findColumnAfterParameter(SQLServerTokenIterator iter) { t = iter.next(); if (t.getType() == SQLServerLexer.DOT) { sb.append("."); - t = iter.next(); - sb.append(t.getText()); + if (iter.hasNext()) { + t = iter.next(); + sb.append(t.getText()); + } } } } @@ -232,20 +243,18 @@ private static String findColumnBeforeParameter(SQLServerTokenIterator iter) { StringBuilder sb = new StringBuilder(); while (sb.length() == 0 && iter.hasPrevious()) { Token t = iter.previous(); - if (t.getType() == SQLServerLexer.DOLLAR) { + if (t.getType() == SQLServerLexer.DOLLAR && iter.hasPrevious()) { t = iter.previous(); // skip if it's a $ sign } - if (t.getType() == SQLServerLexer.AND) { + if (t.getType() == SQLServerLexer.AND && iter.hasPrevious()) { + t = iter.previous(); if (iter.hasPrevious()) { - t = iter.previous(); - if (iter.hasPrevious()) { - t = iter.previous(); // try to find BETWEEN - if (t.getType() == SQLServerLexer.BETWEEN) { - iter.next(); - continue; - } else { - return ""; - } + t = iter.previous(); // try to find BETWEEN + if (t.getType() == SQLServerLexer.BETWEEN && iter.hasNext()) { + iter.next(); + continue; + } else { + return ""; } } } @@ -262,12 +271,14 @@ private static String findColumnBeforeParameter(SQLServerTokenIterator iter) { d.push(t.getText()); } // Linked-servers can have a maximum of 4 parts - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 3 && iter.hasPrevious(); i++) { t = iter.previous(); if (t.getType() == SQLServerLexer.DOT) { d.push("."); - t = iter.previous(); - d.push(t.getText()); + if (iter.hasPrevious()) { + t = iter.previous(); + d.push(t.getText()); + } } } d.stream().forEach(sb::append); @@ -362,88 +373,102 @@ static Token skipTop(SQLServerTokenIterator iter) throws SQLServerException { } static String getCTE(SQLServerTokenIterator iter) throws SQLServerException { - Token t = iter.next(); - if (t.getType() == SQLServerLexer.WITH) { - StringBuilder sb = new StringBuilder("WITH "); - getCTESegment(iter, sb); - return sb.toString(); - } else { - iter.previous(); - return ""; + if (iter.hasNext()) { + Token t = iter.next(); + if (t.getType() == SQLServerLexer.WITH) { + StringBuilder sb = new StringBuilder("WITH "); + getCTESegment(iter, sb); + return sb.toString(); + } else { + iter.previous(); + } } + return ""; } static void getCTESegment(SQLServerTokenIterator iter, StringBuilder sb) throws SQLServerException { - sb.append(getTableTargetChunk(iter, null, Arrays.asList(SQLServerLexer.AS))); - iter.next(); - Token t = iter.next(); - sb.append(" AS "); - if (t.getType() != SQLServerLexer.LR_BRACKET) { - SQLServerException.makeFromDriverError(null, null, SQLServerResource.getResource("R_invalidCTEFormat"), "", - false); - } - int leftRoundBracketCount = 0; - do { - sb.append(t.getText()).append(' '); - if (t.getType() == SQLServerLexer.LR_BRACKET) { - leftRoundBracketCount++; - } else if (t.getType() == SQLServerLexer.RR_BRACKET) { - leftRoundBracketCount--; + try { + sb.append(getTableTargetChunk(iter, null, Arrays.asList(SQLServerLexer.AS))); + iter.next(); + Token t = iter.next(); + sb.append(" AS "); + if (t.getType() != SQLServerLexer.LR_BRACKET) { + SQLServerException.makeFromDriverError(null, null, SQLServerResource.getResource("R_invalidCTEFormat"), + "", false); } - t = iter.next(); - } while (leftRoundBracketCount > 0); + int leftRoundBracketCount = 0; + do { + sb.append(t.getText()).append(' '); + if (t.getType() == SQLServerLexer.LR_BRACKET) { + leftRoundBracketCount++; + } else if (t.getType() == SQLServerLexer.RR_BRACKET) { + leftRoundBracketCount--; + } + t = iter.next(); + } while (leftRoundBracketCount > 0); - if (t.getType() == SQLServerLexer.COMMA) { - sb.append(", "); - getCTESegment(iter, sb); - } else { - iter.previous(); + if (t.getType() == SQLServerLexer.COMMA) { + sb.append(", "); + getCTESegment(iter, sb); + } else { + iter.previous(); + } + } catch (java.util.NoSuchElementException e) { + SQLServerException.makeFromDriverError(null, null, SQLServerResource.getResource("R_invalidCTEFormat"), "", + false); } } private static String getTableTargetChunk(SQLServerTokenIterator iter, List possibleAliases, List delimiters) throws SQLServerException { StringBuilder sb = new StringBuilder(); - Token t = iter.next(); - do { - switch (t.getType()) { - case SQLServerLexer.LR_BRACKET: - sb.append(getRoundBracketChunk(iter, t)); - break; - case SQLServerLexer.OPENDATASOURCE: - case SQLServerLexer.OPENJSON: - case SQLServerLexer.OPENQUERY: - case SQLServerLexer.OPENROWSET: - case SQLServerLexer.OPENXML: - sb.append(t.getText()); + if (iter.hasNext()) { + Token t = iter.next(); + do { + switch (t.getType()) { + case SQLServerLexer.LR_BRACKET: + sb.append(getRoundBracketChunk(iter, t)); + break; + case SQLServerLexer.OPENDATASOURCE: + case SQLServerLexer.OPENJSON: + case SQLServerLexer.OPENQUERY: + case SQLServerLexer.OPENROWSET: + case SQLServerLexer.OPENXML: + sb.append(t.getText()); + t = iter.next(); + if (t.getType() != SQLServerLexer.LR_BRACKET) { + SQLServerException.makeFromDriverError(null, null, + SQLServerResource.getResource("R_invalidOpenqueryCall"), "", false); + } + sb.append(getRoundBracketChunk(iter, t)); + break; + case SQLServerLexer.AS: + sb.append(t.getText()); + if (iter.hasNext()) { + String s = iter.next().getText(); + if (possibleAliases != null) { + possibleAliases.add(s); + } else { + SQLServerException.makeFromDriverError(null, null, + SQLServerResource.getResource("R_invalidCTEFormat"), "", false); + } + sb.append(" ").append(s); + } + break; + default: + sb.append(t.getText()); + break; + } + if (iter.hasNext()) { + sb.append(' '); t = iter.next(); - if (t.getType() != SQLServerLexer.LR_BRACKET) { - SQLServerException.makeFromDriverError(null, null, - SQLServerResource.getResource("R_invalidOpenqueryCall"), "", false); - } - sb.append(getRoundBracketChunk(iter, t)); - break; - case SQLServerLexer.AS: - sb.append(t.getText()); - if (iter.hasNext()) { - String s = iter.next().getText(); - possibleAliases.add(s); - sb.append(" ").append(s); - } - break; - default: - sb.append(t.getText()); + } else { break; - } + } + } while (!delimiters.contains(t.getType()) && t.getType() != SQLServerLexer.SEMI); if (iter.hasNext()) { - sb.append(' '); - t = iter.next(); - } else { - break; + iter.previous(); } - } while (!delimiters.contains(t.getType()) && t.getType() != SQLServerLexer.SEMI); - if (iter.hasNext()) { - iter.previous(); } return sb.toString().trim(); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index fd99f83b3..f7241f520 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -590,5 +590,6 @@ protected Object[][] getContents() { "Invalid syntax: OPENQUERY/OPENJSON/OPENDATASOURCE/OPENROWSET/OPENXML must be preceded by round brackets"}, {"R_invalidCTEFormat", "Invalid syntax: AS must be followed by round brackets in Common Table Expressions."}, - {"R_noTokensFoundInUserQuery", "Invalid query: No tokens were parsed from the SQL provided."}}; + {"R_noTokensFoundInUserQuery", "Invalid query: No tokens were parsed from the SQL provided."}, + {"R_invalidInsertValuesQuery", "Error when matching VALUES list to Table columns. Please verify user SQL."}}; }; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/DBMetadataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/DBMetadataTest.java index 6a4d814e3..7490e40ea 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/DBMetadataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/DBMetadataTest.java @@ -9,6 +9,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -27,34 +30,44 @@ @Tag(Constants.xAzureSQLDW) public class DBMetadataTest extends AbstractTest { @Test - public void testDatabaseMetaData() throws SQLException { - String functionName = RandomUtil.getIdentifier("proc"); - functionName = DBTable.escapeIdentifier(functionName); - SQLServerDataSource ds = new SQLServerDataSource(); - ds.setURL(connectionString); - - String sqlDropFunction = "if exists (select * from dbo.sysobjects where id = object_id(N'[dbo]." - + TestUtils.escapeSingleQuotes(functionName) + "')" + "and xtype in (N'FN', N'IF', N'TF'))" - + "drop function " + functionName; - String sqlCreateFunction = "CREATE FUNCTION " + functionName - + " (@text varchar(8000), @delimiter varchar(20) = ' ') RETURNS @Strings TABLE " - + "(position int IDENTITY PRIMARY KEY, value varchar(8000)) AS BEGIN INSERT INTO @Strings VALUES ('DDD') RETURN END "; - - try (Connection con = ds.getConnection(); Statement stmt = con.createStatement()) { - // drop function - stmt.execute(sqlDropFunction); - // create function - stmt.execute(sqlCreateFunction); - - DatabaseMetaData md = con.getMetaData(); - try (ResultSet arguments = md.getProcedureColumns(null, null, null, "@TABLE_RETURN_VALUE")) { - - if (arguments.next()) { - arguments.getString("COLUMN_NAME"); - arguments.getString("DATA_TYPE"); // call this function to make sure it does not crash + public void testDatabaseMetaData() throws SQLException, InterruptedException { + ExecutorService threadExecutor = Executors.newFixedThreadPool(10); + for (int i = 0; i < 25; i++) { + threadExecutor.execute(() -> { + String functionName = RandomUtil.getIdentifier("proc"); + functionName = DBTable.escapeIdentifier(functionName); + SQLServerDataSource ds = new SQLServerDataSource(); + ds.setURL(connectionString); + + String sqlDropFunction = "if exists (select * from dbo.sysobjects where id = object_id(N'[dbo]." + + TestUtils.escapeSingleQuotes(functionName) + "')" + "and xtype in (N'FN', N'IF', N'TF'))" + + "drop function " + functionName; + String sqlCreateFunction = "CREATE FUNCTION " + functionName + + " (@text varchar(8000), @delimiter varchar(20) = ' ') RETURNS @Strings TABLE " + + "(position int IDENTITY PRIMARY KEY, value varchar(8000)) AS BEGIN INSERT INTO @Strings VALUES ('DDD') RETURN END "; + + try (Connection con = ds.getConnection(); Statement stmt = con.createStatement()) { + // drop function + stmt.execute(sqlDropFunction); + // create function + stmt.execute(sqlCreateFunction); + + DatabaseMetaData md = con.getMetaData(); + try (ResultSet arguments = md.getProcedureColumns(null, null, null, "@TABLE_RETURN_VALUE")) { + + if (arguments.next()) { + arguments.getString("COLUMN_NAME"); + arguments.getString("DATA_TYPE"); // call this function to make sure it does not crash + } + } + stmt.execute(sqlDropFunction); + } catch (SQLException e) { + System.out.println(e.getLocalizedMessage()); } - } - stmt.execute(sqlDropFunction); + System.out.println("DONE"); + }); } + threadExecutor.shutdown(); + threadExecutor.awaitTermination(10000000, TimeUnit.SECONDS); } } From 0db52c0355d7de427d9d1be6ee60238e599a6af5 Mon Sep 17 00:00:00 2001 From: rene-ye Date: Mon, 24 Jun 2019 15:06:34 -0700 Subject: [PATCH 2/8] Fix | Junit test fixes --- .../microsoft/sqlserver/jdbc/SQLServerParser.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java index 4bc0656b8..2eaa72451 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java @@ -91,13 +91,16 @@ static void parseQuery(SQLServerTokenIterator iter, SQLServerFMTQuery query) thr } for (int i = 0; i < ls.size(); i++) { if (ls.get(i).equalsIgnoreCase("?")) { - if (i < tableValues.size()) { - query.getColumns() - .add((tableValues.size() == 0) ? "?" : tableValues.get(i)); + if (tableValues.size() == 0) { + query.getColumns().add("?"); } else { - SQLServerException.makeFromDriverError(null, null, - SQLServerResource.getResource("R_invalidInsertValuesQuery"), "", - false); + if (i < tableValues.size()) { + query.getColumns().add(tableValues.get(i)); + } else { + SQLServerException.makeFromDriverError(null, null, + SQLServerResource.getResource("R_invalidInsertValuesQuery"), "", + false); + } } } } From 18c05147a6ad92efd2529291a4f9c3c145838a38 Mon Sep 17 00:00:00 2001 From: rene-ye Date: Tue, 25 Jun 2019 08:31:55 -0700 Subject: [PATCH 3/8] Fix | Reorder comparisons --- .../com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java | 2 +- .../java/com/microsoft/sqlserver/jdbc/SQLServerParser.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java index 17fba54b5..10647bbfe 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java @@ -92,7 +92,7 @@ String getFMTQuery() { private SQLServerFMTQuery() {}; SQLServerFMTQuery(String userSql) throws SQLServerException { - if (null == userSql || userSql.length() == 0) { + if (null == userSql || 0 == userSql.length()) { SQLServerException.makeFromDriverError(null, this, SQLServerResource.getResource("R_noTokensFoundInUserQuery"), "", false); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java index 2eaa72451..180e398e8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java @@ -91,7 +91,7 @@ static void parseQuery(SQLServerTokenIterator iter, SQLServerFMTQuery query) thr } for (int i = 0; i < ls.size(); i++) { if (ls.get(i).equalsIgnoreCase("?")) { - if (tableValues.size() == 0) { + if (0 == tableValues.size()) { query.getColumns().add("?"); } else { if (i < tableValues.size()) { @@ -211,7 +211,7 @@ static String findColumnAroundParameter(SQLServerTokenIterator iter) { private static String findColumnAfterParameter(SQLServerTokenIterator iter) { StringBuilder sb = new StringBuilder(); - while (sb.length() == 0 && iter.hasNext()) { + while (0 == sb.length() && iter.hasNext()) { Token t = iter.next(); if (t.getType() == SQLServerLexer.NOT && iter.hasNext()) { t = iter.next(); // skip NOT @@ -244,7 +244,7 @@ private static String findColumnAfterParameter(SQLServerTokenIterator iter) { private static String findColumnBeforeParameter(SQLServerTokenIterator iter) { StringBuilder sb = new StringBuilder(); - while (sb.length() == 0 && iter.hasPrevious()) { + while (0 == sb.length() && iter.hasPrevious()) { Token t = iter.previous(); if (t.getType() == SQLServerLexer.DOLLAR && iter.hasPrevious()) { t = iter.previous(); // skip if it's a $ sign From 42678511a4b06c1b63d3f95a1fd06a90beea13ae Mon Sep 17 00:00:00 2001 From: rene-ye Date: Tue, 25 Jun 2019 10:04:16 -0700 Subject: [PATCH 4/8] Fix | Change "" to null when adding SQLState --- .../sqlserver/jdbc/SQLServerFMTQuery.java | 6 +++--- .../microsoft/sqlserver/jdbc/SQLServerParser.java | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java index 10647bbfe..a89d95252 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerFMTQuery.java @@ -94,21 +94,21 @@ String getFMTQuery() { SQLServerFMTQuery(String userSql) throws SQLServerException { if (null == userSql || 0 == userSql.length()) { SQLServerException.makeFromDriverError(null, this, - SQLServerResource.getResource("R_noTokensFoundInUserQuery"), "", false); + SQLServerResource.getResource("R_noTokensFoundInUserQuery"), null, false); } InputStream stream = new ByteArrayInputStream(userSql.getBytes(StandardCharsets.UTF_8)); SQLServerLexer lexer = null; try { lexer = new SQLServerLexer(CharStreams.fromStream(stream)); } catch (IOException e) { - SQLServerException.makeFromDriverError(null, userSql, e.getLocalizedMessage(), "", false); + SQLServerException.makeFromDriverError(null, userSql, e.getLocalizedMessage(), null, false); } lexer.removeErrorListeners(); lexer.addErrorListener(new SQLServerErrorListener()); this.tokenList = (ArrayList) lexer.getAllTokens(); if (tokenList.size() <= 0) { SQLServerException.makeFromDriverError(null, this, - SQLServerResource.getResource("R_noTokensFoundInUserQuery"), "", false); + SQLServerResource.getResource("R_noTokensFoundInUserQuery"), null, false); } SQLServerTokenIterator iter = new SQLServerTokenIterator(tokenList); this.prefix = SQLServerParser.getCTE(iter); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java index 180e398e8..1f7b3f044 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java @@ -98,8 +98,8 @@ static void parseQuery(SQLServerTokenIterator iter, SQLServerFMTQuery query) thr query.getColumns().add(tableValues.get(i)); } else { SQLServerException.makeFromDriverError(null, null, - SQLServerResource.getResource("R_invalidInsertValuesQuery"), "", - false); + SQLServerResource.getResource("R_invalidInsertValuesQuery"), + null, false); } } } @@ -397,7 +397,7 @@ static void getCTESegment(SQLServerTokenIterator iter, StringBuilder sb) throws sb.append(" AS "); if (t.getType() != SQLServerLexer.LR_BRACKET) { SQLServerException.makeFromDriverError(null, null, SQLServerResource.getResource("R_invalidCTEFormat"), - "", false); + null, false); } int leftRoundBracketCount = 0; do { @@ -417,8 +417,8 @@ static void getCTESegment(SQLServerTokenIterator iter, StringBuilder sb) throws iter.previous(); } } catch (java.util.NoSuchElementException e) { - SQLServerException.makeFromDriverError(null, null, SQLServerResource.getResource("R_invalidCTEFormat"), "", - false); + SQLServerException.makeFromDriverError(null, null, SQLServerResource.getResource("R_invalidCTEFormat"), + null, false); } } @@ -441,7 +441,7 @@ private static String getTableTargetChunk(SQLServerTokenIterator iter, List Date: Tue, 25 Jun 2019 15:14:43 -0700 Subject: [PATCH 5/8] Fix | More fixes --- .../sqlserver/jdbc/SQLServerParser.java | 17 ++++++++++++----- .../sqlserver/jdbc/SQLServerResource.java | 4 +++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java index 1f7b3f044..c17af811b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParser.java @@ -150,12 +150,12 @@ static void resetIteratorIndex(SQLServerTokenIterator iter, int index) { } } - private static String getRoundBracketChunk(SQLServerTokenIterator iter, Token t) { + private static String getRoundBracketChunk(SQLServerTokenIterator iter, Token t) throws SQLServerException { StringBuilder sb = new StringBuilder(); sb.append('('); Stack s = new Stack<>(); s.push("("); - while (!s.empty()) { + while (!s.empty() && iter.hasNext()) { t = iter.next(); if (t.getType() == SQLServerLexer.RR_BRACKET) { sb.append(")"); @@ -197,7 +197,7 @@ private static String getRoundBracketChunkBefore(SQLServerTokenIterator iter, To SQLServerLexer.OR_ASSIGN, SQLServerLexer.STAR, SQLServerLexer.DIVIDE, SQLServerLexer.MODULE, SQLServerLexer.PLUS, SQLServerLexer.MINUS, SQLServerLexer.LIKE, SQLServerLexer.IN, SQLServerLexer.BETWEEN); - static String findColumnAroundParameter(SQLServerTokenIterator iter) { + static String findColumnAroundParameter(SQLServerTokenIterator iter) throws SQLServerException { int index = iter.nextIndex(); iter.previous(); String value = findColumnBeforeParameter(iter); @@ -209,7 +209,7 @@ static String findColumnAroundParameter(SQLServerTokenIterator iter) { return value; } - private static String findColumnAfterParameter(SQLServerTokenIterator iter) { + private static String findColumnAfterParameter(SQLServerTokenIterator iter) throws SQLServerException { StringBuilder sb = new StringBuilder(); while (0 == sb.length() && iter.hasNext()) { Token t = iter.next(); @@ -293,7 +293,7 @@ private static String findColumnBeforeParameter(SQLServerTokenIterator iter) { return sb.toString(); } - static List getValuesList(SQLServerTokenIterator iter) { + static List getValuesList(SQLServerTokenIterator iter) throws SQLServerException { Token t = iter.next(); if (t.getType() == SQLServerLexer.LR_BRACKET) { ArrayList parameterColumns = new ArrayList<>(); @@ -331,6 +331,9 @@ static List getValuesList(SQLServerTokenIterator iter) { } if (iter.hasNext() && !d.isEmpty()) { t = iter.next(); + } else if (!iter.hasNext() && !d.isEmpty()) { + SQLServerException.makeFromDriverError(null, null, + SQLServerResource.getResource("R_invalidValuesList"), null, false); } } while (!d.isEmpty()); return parameterColumns; @@ -345,6 +348,10 @@ static List getValuesList(SQLServerTokenIterator iter) { */ static Token skipTop(SQLServerTokenIterator iter) throws SQLServerException { // Look for the TOP token + if (!iter.hasNext()) { + SQLServerException.makeFromDriverError(null, null, SQLServerResource.getResource("R_invalidUserSQL"), null, + false); + } Token t = iter.next(); if (t.getType() == SQLServerLexer.TOP) { t = iter.next(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index f7241f520..d4c4e31fe 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -591,5 +591,7 @@ protected Object[][] getContents() { {"R_invalidCTEFormat", "Invalid syntax: AS must be followed by round brackets in Common Table Expressions."}, {"R_noTokensFoundInUserQuery", "Invalid query: No tokens were parsed from the SQL provided."}, - {"R_invalidInsertValuesQuery", "Error when matching VALUES list to Table columns. Please verify user SQL."}}; + {"R_invalidUserSQL", "An error occured when attempting to parse user SQL. Please verify SQL syntax."}, + {"R_invalidInsertValuesQuery", "Error when matching VALUES list to Table columns. Please verify user SQL."}, + {"R_invalidValuesList", "Error when reading VALUES list. Please verify user SQL."}}; }; From 143c0c608ccd68398487db7d908bd18c66cad175 Mon Sep 17 00:00:00 2001 From: rene-ye Date: Fri, 28 Jun 2019 08:36:19 -0700 Subject: [PATCH 6/8] Fix | Addressing comments --- .../com/microsoft/sqlserver/jdbc/SQLServerResource.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index d4c4e31fe..5bc307d0a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -591,7 +591,7 @@ protected Object[][] getContents() { {"R_invalidCTEFormat", "Invalid syntax: AS must be followed by round brackets in Common Table Expressions."}, {"R_noTokensFoundInUserQuery", "Invalid query: No tokens were parsed from the SQL provided."}, - {"R_invalidUserSQL", "An error occured when attempting to parse user SQL. Please verify SQL syntax."}, - {"R_invalidInsertValuesQuery", "Error when matching VALUES list to Table columns. Please verify user SQL."}, - {"R_invalidValuesList", "Error when reading VALUES list. Please verify user SQL."}}; + {"R_invalidUserSQL", "An error occurred when attempting to parse user SQL. Please verify SQL syntax."}, + {"R_invalidInsertValuesQuery", "An error occurred when matching VALUES list to Table columns. Please verify user SQL."}, + {"R_invalidValuesList", "An error occurred when reading VALUES list. Please verify user SQL."}}; }; From ed13466dbc71f0e44f49d218fae4cbf019cf9b86 Mon Sep 17 00:00:00 2001 From: rene-ye Date: Fri, 28 Jun 2019 13:13:10 -0700 Subject: [PATCH 7/8] Fix | Format --- .../java/com/microsoft/sqlserver/jdbc/SQLServerResource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 5bc307d0a..bd23be2ab 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -592,6 +592,7 @@ protected Object[][] getContents() { "Invalid syntax: AS must be followed by round brackets in Common Table Expressions."}, {"R_noTokensFoundInUserQuery", "Invalid query: No tokens were parsed from the SQL provided."}, {"R_invalidUserSQL", "An error occurred when attempting to parse user SQL. Please verify SQL syntax."}, - {"R_invalidInsertValuesQuery", "An error occurred when matching VALUES list to Table columns. Please verify user SQL."}, + {"R_invalidInsertValuesQuery", + "An error occurred when matching VALUES list to table columns. Please verify user SQL."}, {"R_invalidValuesList", "An error occurred when reading VALUES list. Please verify user SQL."}}; }; From 58111c01f37d44c486bbea27cfe6c5d6777dc02e Mon Sep 17 00:00:00 2001 From: rene-ye Date: Fri, 28 Jun 2019 13:30:55 -0700 Subject: [PATCH 8/8] Fix | COmments --- .../java/com/microsoft/sqlserver/jdbc/SQLServerResource.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index bd23be2ab..eb7c7b0c5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -593,6 +593,6 @@ protected Object[][] getContents() { {"R_noTokensFoundInUserQuery", "Invalid query: No tokens were parsed from the SQL provided."}, {"R_invalidUserSQL", "An error occurred when attempting to parse user SQL. Please verify SQL syntax."}, {"R_invalidInsertValuesQuery", - "An error occurred when matching VALUES list to table columns. Please verify user SQL."}, - {"R_invalidValuesList", "An error occurred when reading VALUES list. Please verify user SQL."}}; + "An error occurred when matching VALUES list to table columns. Please verify SQL syntax."}, + {"R_invalidValuesList", "An error occurred when reading VALUES list. Please verify SQL syntax."}}; };