8143165: Add Statement.isSimpleIdentifier and update enquoteLiteral
authorlancea
Wed, 25 Nov 2015 15:28:30 -0500
changeset 34337 b088bf935e4b
parent 34336 3f251ae0f2d3
child 34338 67d0e5867568
8143165: Add Statement.isSimpleIdentifier and update enquoteLiteral Reviewed-by: rriggs, joehw
jdk/src/java.sql/share/classes/java/sql/Statement.java
jdk/test/java/sql/testng/test/sql/StatementTests.java
--- a/jdk/src/java.sql/share/classes/java/sql/Statement.java	Wed Nov 25 09:23:07 2015 +0100
+++ b/jdk/src/java.sql/share/classes/java/sql/Statement.java	Wed Nov 25 15:28:30 2015 -0500
@@ -1397,9 +1397,10 @@
      * @param val a character string
      * @return A string enclosed by single quotes with every single quote
      * converted to two single quotes
-     * @throws NullPointerException if val is null
+     * @throws NullPointerException if val is {@code null}
+     * @throws SQLException if a database access error occurs
      */
-     default String enquoteLiteral(String val) {
+     default String enquoteLiteral(String val)  throws SQLException {
          return "'" + val.replace("'", "''") +  "'";
     }
 
@@ -1437,7 +1438,7 @@
      *
      * The default implementation will throw a {@code SQLException} if:
      * <ul>
-     * <li>{@code identifier} contains a null character or double quote, and is not
+     * <li>{@code identifier} contains a {@code null} character or double quote and is not
      * a simple SQL identifier.</li>
      * <li>The length of {@code identifier} is less than 1 or greater than 128 characters
      * </ul>
@@ -1501,14 +1502,14 @@
      * @throws SQLException if identifier is not a valid identifier
      * @throws SQLFeatureNotSupportedException if the datasource does not support
      * delimited identifiers
-     * @throws NullPointerException if identifier is null
+     * @throws NullPointerException if identifier is {@code null}
      */
     default String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException {
         int len = identifier.length();
         if (len < 1 || len > 128) {
             throw new SQLException("Invalid name");
         }
-        if (Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]+").matcher(identifier).matches()) {
+        if (Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches()) {
             return alwaysQuote ?  "\"" + identifier + "\"" : identifier;
         }
         if (identifier.matches("^\".+\"$")) {
@@ -1520,4 +1521,65 @@
             throw new SQLException("Invalid name");
         }
     }
+
+    /**
+     * Retrieves whether {@code identifier} is a simple SQL identifier.
+     *
+     * @implSpec The default implementation uses the following criteria to
+     * determine a valid simple SQL identifier:
+     * <ul>
+     * <li>The string is not enclosed in double quotes</li>
+     * <li>The first character is an alphabetic character from a through z, or
+     * from A through Z</li>
+     * <li>The string only contains alphanumeric characters or the character
+     * "_"</li>
+     * <li>The string is between 1 and 128 characters in length inclusive</li>
+     * </ul>
+     *
+     * <blockquote>
+     * <table border = 1 cellspacing=0 cellpadding=5 >
+     * <caption>Examples of the conversion:</caption>
+     * <tr>
+     * <th>identifier</th>
+     * <th>Simple Identifier</th>
+     *
+     * <tr>
+     * <td align='center'>Hello</td>
+     * <td align='center'>true</td>
+     * </tr>
+     * <tr>
+     * <td align='center'>G'Day</td>
+     * <td align='center'>false</td>
+     * </tr>
+     * <tr>
+     * <td align='center'>"Bruce Wayne"</td>
+     * <td align='center'>false</td>
+     * </tr>
+     * <tr>
+     * <td align='center'>GoodDay$</td>
+     * <td align='center'>false</td>
+     * </tr>
+     * <tr>
+     * <td align='center'>Hello"World</td>
+     * <td align='center'>false</td>
+     * </tr>
+     * <tr>
+     * <td align='center'>"Hello"World"</td>
+     * <td align='center'>false</td>
+     * </tr>
+     * </table>
+     * </blockquote>
+     * @implNote JDBC driver implementations may need to provide their own
+     * implementation of this method in order to meet the requirements of the
+     * underlying datasource.
+     * @param identifier a SQL identifier
+     * @return  true if  a simple SQL identifier, false otherwise
+     * @throws NullPointerException if identifier is {@code null}
+     * @throws SQLException if a database access error occurs
+     */
+    default boolean isSimpleIdentifier(String identifier) throws SQLException {
+        int len = identifier.length();
+        return len >= 1 && len <= 128
+                && Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches();
+    }
 }
--- a/jdk/test/java/sql/testng/test/sql/StatementTests.java	Wed Nov 25 09:23:07 2015 +0100
+++ b/jdk/test/java/sql/testng/test/sql/StatementTests.java	Wed Nov 25 15:28:30 2015 -0500
@@ -24,6 +24,7 @@
 
 import java.sql.SQLException;
 import static org.testng.Assert.assertEquals;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
@@ -33,18 +34,29 @@
 public class StatementTests extends BaseTest {
 
     protected StubStatement stmt;
+    protected static String maxIdentifier;
 
     @BeforeMethod
     public void setUpMethod() throws Exception {
         stmt = new StubStatement();
     }
 
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        int maxLen = 128;
+        StringBuilder s = new StringBuilder(maxLen);
+        for (int i = 0; i < maxLen; i++) {
+            s.append('a');
+        }
+        maxIdentifier = s.toString();
+    }
     /*
      * Verify that enquoteLiteral creates a  valid literal and converts every
      * single quote to two single quotes
      */
+
     @Test(dataProvider = "validEnquotedLiteralValues")
-    public void test00(String s, String expected) {
+    public void test00(String s, String expected) throws SQLException {
         assertEquals(stmt.enquoteLiteral(s), expected);
     }
 
@@ -53,7 +65,7 @@
      * enquoteLiteral is null
      */
     @Test(expectedExceptions = NullPointerException.class)
-    public void test01() {
+    public void test01()  throws SQLException {
         stmt.enquoteLiteral(null);
 
     }
@@ -90,6 +102,24 @@
     }
 
     /*
+     * Validate that isSimpleIdentifier returns the expected value
+     */
+    @Test(dataProvider = "simpleIdentifierValues")
+    public void test05(String s, boolean expected) throws SQLException {
+        assertEquals(stmt.isSimpleIdentifier(s), expected);
+    }
+
+    /*
+     * Validate a NullPointerException is thrown is the string passed to
+     * isSimpleIdentifier is null
+     */
+    @Test(expectedExceptions = NullPointerException.class)
+    public void test06() throws SQLException {
+        stmt.isSimpleIdentifier(null);
+
+    }
+
+    /*
      * DataProvider used to provide strings that will be used to validate
      * that enquoteLiteral converts a string to a literal and every instance of
      * a single quote will be converted into two single quotes in the literal.
@@ -114,6 +144,10 @@
     @DataProvider(name = "validIdentifierValues")
     protected Object[][] validEnquotedIdentifierValues() {
         return new Object[][]{
+            {"b", false, "b"},
+            {"b", true, "\"b\""},
+            {maxIdentifier, false, maxIdentifier},
+            {maxIdentifier, true, "\"" + maxIdentifier + "\""},
             {"Hello", false, "Hello"},
             {"Hello", true, "\"Hello\""},
             {"G'Day", false, "\"G'Day\""},
@@ -130,16 +164,34 @@
      */
     @DataProvider(name = "invalidIdentifierValues")
     protected Object[][] invalidEnquotedIdentifierValues() {
-        int invalidLen = 129;
-        StringBuilder s = new StringBuilder(invalidLen);
-        for (int i = 0; i < invalidLen; i++) {
-            s.append('a');
-        }
         return new Object[][]{
             {"Hel\"lo", false},
             {"\"Hel\"lo\"", true},
             {"Hello" + '\0', false},
             {"", false},
-            {s.toString(), false},};
+            {maxIdentifier + 'a', false},
+            };
     }
+
+    /*
+     * DataProvider used to provide strings that will be used to validate
+     * that isSimpleIdentifier returns the correct value based on the
+     * identifier specified.
+     */
+    @DataProvider(name = "simpleIdentifierValues")
+    protected Object[][] simpleIdentifierValues() {
+        return new Object[][]{
+            {"b", true},
+            {"Hello", true},
+            {"\"Gotham\"", false},
+            {"G'Day", false},
+            {"Bruce Wayne", false},
+            {"GoodDay$", false},
+            {"Dick_Grayson", true},
+            {"Batmobile1966", true},
+            {maxIdentifier, true},
+            {maxIdentifier + 'a', false},
+            {"", false},};
+    }
+
 }