src/jdk.incubator.adba/share/classes/jdk/incubator/sql2/OperationGroup.java
branchJDK-8188051-branch
changeset 56828 64304e37e9b1
parent 56824 62e92191354d
child 56832 4f7713e6a308
--- a/src/jdk.incubator.adba/share/classes/jdk/incubator/sql2/OperationGroup.java	Wed Jul 11 19:30:26 2018 -0400
+++ b/src/jdk.incubator.adba/share/classes/jdk/incubator/sql2/OperationGroup.java	Wed Jul 11 19:36:23 2018 -0400
@@ -28,6 +28,7 @@
 import java.util.concurrent.CompletionStage;
 import java.util.function.Consumer;
 import java.util.logging.Logger;
+import java.util.regex.Pattern;
 import java.util.stream.Collector;
 
 /**
@@ -391,28 +392,28 @@
    * The type argument {@link S} of the containing {@link OperationGroup} must
    * be a supertype of {@link TransactionOutcome}.
    *
-   * @param trans the TransactionEnd that determines whether the Operation does a 
-   * database commit or a database rollback.
+   * @param trans the TransactionCompletion that determines whether the Operation does a 
+ database commit or a database rollback.
    * @return an {@link Operation} that will end the database transaction.
    * @throws IllegalStateException if this {@link OperationGroup} has been
    * submitted and is not held or is parallel.
    */
-  public Operation<TransactionOutcome> endTransactionOperation(TransactionEnd trans);
+  public Operation<TransactionOutcome> endTransactionOperation(TransactionCompletion trans);
 
   /**
    * Convenience method that creates and submits a endTransaction
    * {@link Operation} that commits by default but can be set to rollback by
-   * calling {@link TransactionEnd#setRollbackOnly}. The endTransaction Operation
+   * calling {@link TransactionCompletion#setRollbackOnly}. The endTransaction Operation
    * is never skipped.
    *
-   * @param trans the TransactionEnd that determines whether the {@link Operation} is a
+   * @param trans the TransactionCompletion that determines whether the {@link Operation} is a
    * database commit or a database rollback.
    * @return a {@link CompletionStage} that is completed with the outcome of the 
    * transaction
    * @throws IllegalStateException if this {@link OperationGroup} has been
    * submitted and is not held or is parallel.
    */
-  public default CompletionStage<TransactionOutcome> commitMaybeRollback(TransactionEnd trans) {
+  public default CompletionStage<TransactionOutcome> commitMaybeRollback(TransactionCompletion trans) {
     catchErrors();
     return this.endTransactionOperation(trans).submit().getCompletionStage();
   }
@@ -461,7 +462,269 @@
    */
   public OperationGroup<S, T> logger(Logger logger);
 
-  // Covariant overrides
+  /**
+   * Returns a {@code String} enclosed in single quotes. Any occurrence of a
+   * single quote within the string will be replaced by two single quotes.
+   *
+   * <blockquote>
+   * <table class="striped">
+   * <caption>Examples of the conversion:</caption>
+   * <thead>
+   * <tr><th scope="col">Value</th><th scope="col">Result</th></tr>
+   * </thead>
+   * <tbody style="text-align:center">
+   * <tr> <th scope="row">Hello</th> <td>'Hello'</td> </tr>
+   * <tr> <th scope="row">G'Day</th> <td>'G''Day'</td> </tr>
+   * <tr> <th scope="row">'G''Day'</th>
+   * <td>'''G''''Day'''</td> </tr>
+   * <tr> <th scope="row">I'''M</th> <td>'I''''''M'</td>
+   * </tr>
+   *
+   * </tbody>
+   * </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 val a character string. Not null
+   * @return A string enclosed by single quotes with every single quote
+   * converted to two single quotes. Not null
+   * @throws NullPointerException if {@code val} is {@code null}
+   * @throws IllegalArgumentException if {@code val} cannot be enquoted
+   */
+  default String enquoteLiteral(String val) {
+    return "'" + val.replace("'", "''") + "'";
+  }
+
+  /**
+   * Returns a SQL identifier. If {@code identifier} is a simple SQL identifier:
+   * <ul>
+   * <li>Return the original value if {@code alwaysQuote} is {@code false}</li>
+   * <li>Return a delimited identifier if {@code alwaysQuote} is
+   * {@code true}</li>
+   * </ul>
+   *
+   * If {@code identifier} is not a simple SQL identifier, {@code identifier}
+   * will be enclosed in double quotes if not already present. If the datasource
+   * does not support double quotes for delimited identifiers, the identifier
+   * should be enclosed by the string returned from
+   * {@link DatabaseMetaData#getIdentifierQuoteString}. If the datasource does
+   * not support delimited identifiers, a
+   * {@code SQLFeatureNotSupportedException} should be thrown.
+   * <p>
+   * A {@code SQLException} will be thrown if {@code identifier} contains any
+   * characters invalid in a delimited identifier or the identifier length is
+   * invalid for the datasource.
+   *
+   * @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 name only contains alphanumeric characters or the character
+   * "_"</li>
+   * </ul>
+   *
+   * The default implementation will throw a {@code SQLException} if:
+   * <ul>
+   * <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>
+   * <blockquote>
+   * <table class="striped" >
+   * <caption>Examples of the conversion:</caption>
+   * <thead>
+   * <tr>
+   * <th scope="col">identifier</th>
+   * <th scope="col">alwaysQuote</th>
+   * <th scope="col">Result</th></tr>
+   * </thead>
+   * <tbody>
+   * <tr>
+   * <th scope="row">Hello</th>
+   * <td>false</td>
+   * <td>Hello</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">Hello</th>
+   * <td>true</td>
+   * <td>"Hello"</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">G'Day</th>
+   * <td>false</td>
+   * <td>"G'Day"</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">"Bruce Wayne"</th>
+   * <td>false</td>
+   * <td>"Bruce Wayne"</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">"Bruce Wayne"</th>
+   * <td>true</td>
+   * <td>"Bruce Wayne"</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">GoodDay$</th>
+   * <td>false</td>
+   * <td>"GoodDay$"</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">Hello"World</th>
+   * <td>false</td>
+   * <td>SQLException</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">"Hello"World"</th>
+   * <td>false</td>
+   * <td>SQLException</td>
+   * </tr>
+   * </tbody>
+   * </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. Not null
+   * @param alwaysQuote indicates if a simple SQL identifier should be returned
+   * as a quoted identifier
+   * @return A simple SQL identifier or a delimited identifier. Not null
+   * @throws NullPointerException if identifier is {@code null}
+   * @throws IllegalArgumentException if {@code identifier} can not be converted 
+   * to a valid identifier
+   */
+  default String enquoteIdentifier(String identifier, boolean alwaysQuote) {
+    int len = identifier.length();
+    if (len < 1 || len > 128) {
+      throw new IllegalArgumentException("Invalid name");
+    }
+    if (Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches()) {
+      return alwaysQuote ? "\"" + identifier + "\"" : identifier;
+    }
+    if (identifier.matches("^\".+\"$")) {
+      identifier = identifier.substring(1, len - 1);
+    }
+    if (Pattern.compile("[^\u0000\"]+").matcher(identifier).matches()) {
+      return "\"" + identifier + "\"";
+    }
+    else {
+      throw new IllegalArgumentException("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 class="striped" >
+   * <caption>Examples of the conversion:</caption>
+   * <thead>
+   * <tr>
+   * <th scope="col">identifier</th>
+   * <th scope="col">Simple Identifier</th>
+   * </thead>
+   *
+   * <tbody>
+   * <tr>
+   * <th scope="row">Hello</th>
+   * <td>true</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">G'Day</th>
+   * <td>false</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">"Bruce Wayne"</th>
+   * <td>false</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">GoodDay$</th>
+   * <td>false</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">Hello"World</th>
+   * <td>false</td>
+   * </tr>
+   * <tr>
+   * <th scope="row">"Hello"World"</th>
+   * <td>false</td>
+   * </tr>
+   * </tbody>
+   * </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. Not null
+   * @return true if a simple SQL identifier, false otherwise
+   * @throws NullPointerException if identifier is {@code null}
+   */
+  default boolean isSimpleIdentifier(String identifier) {
+    int len = identifier.length();
+    return len >= 1 && len <= 128
+            && Pattern.compile("[\\p{Alpha}][\\p{Alnum}_]*").matcher(identifier).matches();
+  }
+
+  /**
+   * Returns a {@code String} representing a National Character Set Literal
+   * enclosed in single quotes and prefixed with a upper case letter N. Any
+   * occurrence of a single quote within the string will be replaced by two
+   * single quotes.
+   *
+   * <blockquote>
+   * <table class="striped">
+   * <caption>Examples of the conversion:</caption>
+   * <thead>
+   * <tr>
+   * <th scope="col">Value</th>
+   * <th scope="col">Result</th>
+   * </tr>
+   * </thead>
+   * <tbody>
+   * <tr> <th scope="row">Hello</th> <td>N'Hello'</td> </tr>
+   * <tr> <th scope="row">G'Day</th> <td>N'G''Day'</td> </tr>
+   * <tr> <th scope="row">'G''Day'</th>
+   * <td>N'''G''''Day'''</td> </tr>
+   * <tr> <th scope="row">I'''M</th> <td>N'I''''''M'</td>
+   * <tr> <th scope="row">N'Hello'</th> <td>N'N''Hello'''</td> </tr>
+   *
+   * </tbody>
+   * </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. An implementation of enquoteNCharLiteral may accept
+   * a different set of characters than that accepted by the same drivers
+   * implementation of enquoteLiteral.
+   * @param val a character string. Not null
+   * @return the result of replacing every single quote character in the
+   * argument by two single quote characters where this entire result is then
+   * prefixed with 'N'. Not null.
+   * @throws NullPointerException if {@code val} is {@code null}
+   * @throws IllegalArgumentException if {@code val} cannot be enquoted
+   */
+  default String enquoteNCharLiteral(String val) {
+    return "N'" + val.replace("'", "''") + "'";
+  }
+
+    // Covariant overrides
   @Override
   public OperationGroup<S, T> timeout(Duration minTime);