diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java index 5d8fc5110..f923dd791 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java @@ -73,10 +73,11 @@ final public String toString() { * the list of columns * @param columnStartToken * the token that prfixes the column set + * @throws SQLServerException */ /* L2 */ private String parseColumns(String columnSet, - String columnStartToken) { - StringTokenizer st = new StringTokenizer(columnSet, " =?<>!", true); + String columnStartToken) throws SQLServerException { + StringTokenizer st = new StringTokenizer(columnSet, " =?<>!\r\n\t\f", true); final int START = 0; final int PARAMNAME = 1; final int PARAMVALUE = 2; @@ -130,9 +131,10 @@ final public String toString() { * the sql syntax * @param columnMarker * the token that denotes the start of the column set + * @throws SQLServerException */ /* L2 */ private String parseInsertColumns(String sql, - String columnMarker) { + String columnMarker) throws SQLServerException { StringTokenizer st = new StringTokenizer(sql, " (),", true); int nState = 0; String sLastField = null; @@ -322,23 +324,29 @@ private void parseQueryMetaFor2008(ResultSet rsQueryMeta) throws SQLServerExcept * @param st * string tokenizer * @param firstToken + * @throws SQLServerException * @returns the full token */ private String escapeParse(StringTokenizer st, - String firstToken) { - String nameFragment; - String fullName; - nameFragment = firstToken; + String firstToken) throws SQLServerException { + + if (null == firstToken) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + Object[] msgArgs1 = {"firstToken"}; + throw new SQLServerException(form.format(msgArgs1), null); + } + // skip spaces - while (" ".equals(nameFragment) && st.hasMoreTokens()) { - nameFragment = st.nextToken(); + while ((0 == firstToken.trim().length()) && st.hasMoreTokens()) { + firstToken = st.nextToken(); } - fullName = nameFragment; - if (nameFragment.charAt(0) == '[' && nameFragment.charAt(nameFragment.length() - 1) != ']') { + + String fullName = firstToken; + if (firstToken.charAt(0) == '[' && firstToken.charAt(firstToken.length() - 1) != ']') { while (st.hasMoreTokens()) { - nameFragment = st.nextToken(); - fullName = fullName.concat(nameFragment); - if (nameFragment.charAt(nameFragment.length() - 1) == ']') { + firstToken = st.nextToken(); + fullName = fullName.concat(firstToken); + if (firstToken.charAt(firstToken.length() - 1) == ']') { break; } @@ -366,10 +374,11 @@ private class MetaInfo { * String * @param sTableMarker * the location of the table in the syntax + * @throws SQLServerException */ private MetaInfo parseStatement(String sql, - String sTableMarker) { - StringTokenizer st = new StringTokenizer(sql, " ,\r\n", true); + String sTableMarker) throws SQLServerException { + StringTokenizer st = new StringTokenizer(sql, " ,\r\n\t\f(", true); /* Find the table */ @@ -412,7 +421,7 @@ else if (sTableMarker.equalsIgnoreCase("INTO")) // insert * @throws SQLServerException */ private MetaInfo parseStatement(String sql) throws SQLServerException { - StringTokenizer st = new StringTokenizer(sql, " "); + StringTokenizer st = new StringTokenizer(sql, " \r\n\t\f"); if (st.hasMoreTokens()) { String sToken = st.nextToken().trim(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataWhiteSpaceTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataWhiteSpaceTest.java new file mode 100644 index 000000000..6911066b0 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataWhiteSpaceTest.java @@ -0,0 +1,145 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ +package com.microsoft.sqlserver.jdbc.parametermetadata; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.testframework.util.RandomUtil; + +@RunWith(JUnitPlatform.class) +public class ParameterMetaDataWhiteSpaceTest extends AbstractTest { + private static final String tableName = "[" + RandomUtil.getIdentifier("ParameterMetaDataWhiteSpaceTest") + "]"; + + private static Statement stmt = null; + + @BeforeAll + public static void BeforeTests() throws SQLException { + connection = (SQLServerConnection) DriverManager.getConnection(connectionString); + stmt = connection.createStatement(); + createCharTable(); + } + + @AfterAll + public static void dropTables() throws SQLException { + Utils.dropTableIfExists(tableName, stmt); + + if (null != stmt) { + stmt.close(); + } + + if (null != connection) { + connection.close(); + } + } + + private static void createCharTable() throws SQLException { + stmt.execute("Create table " + tableName + " (c1 int)"); + } + + /** + * Test regular simple query + * + * @throws SQLException + */ + @Test + public void NormalTest() throws SQLException { + testUpdateWithTwoParameters("update " + tableName + " set c1 = ? where c1 = ?"); + testInsertWithOneParameter("insert into " + tableName + " (c1) values (?)"); + } + + /** + * Test query with new line character + * + * @throws SQLException + */ + @Test + public void NewLineTest() throws SQLException { + testQueriesWithWhiteSpaces("\n"); + } + + /** + * Test query with tab character + * + * @throws SQLException + */ + @Test + public void TabTest() throws SQLException { + testQueriesWithWhiteSpaces("\t"); + } + + /** + * Test query with form feed character + * + * @throws SQLException + */ + @Test + public void FormFeedTest() throws SQLException { + testQueriesWithWhiteSpaces("\f"); + } + + private void testQueriesWithWhiteSpaces(String whiteSpace) throws SQLException { + testUpdateWithTwoParameters("update" + whiteSpace + tableName + " set c1 = ? where c1 = ?"); + testUpdateWithTwoParameters("update " + tableName + " set" + whiteSpace + "c1 = ? where c1 = ?"); + testUpdateWithTwoParameters("update " + tableName + " set c1 = ? where" + whiteSpace + "c1 = ?"); + + testInsertWithOneParameter("insert into " + tableName + "(c1) values (?)"); // no space between table name and column name + testInsertWithOneParameter("insert into" + whiteSpace + tableName + " (c1) values (?)"); + } + + private void testUpdateWithTwoParameters(String sql) throws SQLException { + insertTestRow(1); + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setInt(1, 2); + ps.setInt(2, 1); + ps.executeUpdate(); + assertTrue(isIdPresentInTable(2), "Expected ID is not present"); + assertEquals(2, ps.getParameterMetaData().getParameterCount(), "Parameter count mismatch"); + } + } + + private void testInsertWithOneParameter(String sql) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement(sql)) { + ps.setInt(1, 1); + ps.executeUpdate(); + assertTrue(isIdPresentInTable(1), "Insert statement did not work"); + assertEquals(1, ps.getParameterMetaData().getParameterCount(), "Parameter count mismatch"); + } + } + + private void insertTestRow(int id) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement("insert into " + tableName + " (c1) values (?)")) { + ps.setInt(1, id); + ps.executeUpdate(); + } + } + + private boolean isIdPresentInTable(int id) throws SQLException { + try (PreparedStatement ps = connection.prepareStatement("select c1 from " + tableName + " where c1 = ?")) { + ps.setInt(1, id); + try (ResultSet rs = ps.executeQuery()) { + return rs.next(); + } + } + } +}